tiny-essentials 1.13.1 β 1.14.0
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/TinyBasicsEs.js +256 -15
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyDragger.js +210 -15
- package/dist/v1/TinyEssentials.js +496 -15
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyNotifications.js +408 -0
- package/dist/v1/TinyNotifications.min.js +1 -0
- package/dist/v1/TinyUploadClicker.js +211 -15
- package/dist/v1/basics/html.cjs +213 -15
- package/dist/v1/basics/html.d.mts +59 -5
- package/dist/v1/basics/html.mjs +192 -13
- package/dist/v1/basics/index.cjs +4 -0
- package/dist/v1/basics/index.d.mts +5 -1
- package/dist/v1/basics/index.mjs +3 -3
- package/dist/v1/basics/text.cjs +43 -0
- package/dist/v1/basics/text.d.mts +16 -0
- package/dist/v1/basics/text.mjs +37 -0
- package/dist/v1/build/TinyNotifications.cjs +7 -0
- package/dist/v1/build/TinyNotifications.d.mts +3 -0
- package/dist/v1/build/TinyNotifications.mjs +2 -0
- package/dist/v1/index.cjs +6 -0
- package/dist/v1/index.d.mts +6 -1
- package/dist/v1/index.mjs +4 -3
- package/dist/v1/libs/TinyNotifications.cjs +238 -0
- package/dist/v1/libs/TinyNotifications.d.mts +106 -0
- package/dist/v1/libs/TinyNotifications.mjs +211 -0
- package/docs/v1/README.md +4 -3
- package/docs/v1/basics/array.md +16 -43
- package/docs/v1/basics/html.md +199 -33
- package/docs/v1/basics/text.md +44 -14
- package/docs/v1/libs/TinyNotifications.md +189 -0
- package/package.json +1 -1
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { safeTextTrim } from '../basics/text.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* A utility class to manage browser notifications with sound and custom behavior.
|
|
4
|
+
* Useful for triggering system notifications with optional sound, avatar icon, body truncation, and click actions.
|
|
5
|
+
*
|
|
6
|
+
* @class
|
|
7
|
+
*/
|
|
8
|
+
class TinyNotifications {
|
|
9
|
+
/** @type {boolean} Whether notifications are currently allowed by the user. */
|
|
10
|
+
#allowed = false;
|
|
11
|
+
/** @type {boolean} Indicates whether the user has already requested permission at least once. */
|
|
12
|
+
#permissionRequested = false;
|
|
13
|
+
/** @type {HTMLAudioElement|null} Audio element to play when a notification is triggered. */
|
|
14
|
+
#audio = null;
|
|
15
|
+
/** @type {number} Maximum number of characters in the notification body. */
|
|
16
|
+
#bodyLimit = 100;
|
|
17
|
+
/** @type {string|null} Default avatar icon URL for notifications. */
|
|
18
|
+
#defaultIcon = null;
|
|
19
|
+
/** @type {(this: Notification, evt: Event) => any} Default handler when a notification is clicked. */
|
|
20
|
+
#defaultOnClick;
|
|
21
|
+
/**
|
|
22
|
+
* Constructs a new instance of TinyNotifications.
|
|
23
|
+
*
|
|
24
|
+
* @param {Object} [settings={}] - Optional settings to initialize the notification manager.
|
|
25
|
+
* @param {string|HTMLAudioElement|null} [settings.audio] - Path or URL to the audio file for notification sounds.
|
|
26
|
+
* @param {string|null} [settings.defaultIcon] - Default icon URL to be used in notifications.
|
|
27
|
+
* @param {number} [settings.bodyLimit=100] - Maximum number of characters allowed in the notification body.
|
|
28
|
+
* @param {(this: Notification, evt: Event) => any} [settings.defaultOnClick] - Default function to execute when a notification is clicked.
|
|
29
|
+
* @throws {TypeError} If any of the parameters are of an invalid type.
|
|
30
|
+
*/
|
|
31
|
+
constructor({ audio = null, defaultIcon = null, bodyLimit = 100, defaultOnClick = function (event) {
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
if (window.focus)
|
|
34
|
+
window.focus();
|
|
35
|
+
this.close();
|
|
36
|
+
}, } = {}) {
|
|
37
|
+
if (!(audio instanceof HTMLAudioElement) && typeof audio !== 'string' && audio !== null)
|
|
38
|
+
throw new TypeError('audio must be an instance of HTMLAudioElement or null.');
|
|
39
|
+
if (defaultIcon !== null && typeof defaultIcon !== 'string')
|
|
40
|
+
throw new TypeError('defaultIcon must be a string or null.');
|
|
41
|
+
if (!Number.isFinite(bodyLimit) || bodyLimit < 0)
|
|
42
|
+
throw new TypeError('bodyLimit must be a non-negative number.');
|
|
43
|
+
if (typeof defaultOnClick !== 'function')
|
|
44
|
+
throw new TypeError('defaultOnClick must be a function.');
|
|
45
|
+
this.#audio = typeof audio !== 'string' ? audio : new Audio(audio);
|
|
46
|
+
this.#defaultIcon = defaultIcon;
|
|
47
|
+
this.#bodyLimit = bodyLimit;
|
|
48
|
+
this.#defaultOnClick = defaultOnClick;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Requests permission from the user to show notifications.
|
|
52
|
+
* Updates the internal `#allowed` flag.
|
|
53
|
+
*
|
|
54
|
+
* @returns {Promise<boolean>} Resolves to `true` if permission is granted, otherwise `false`.
|
|
55
|
+
*/
|
|
56
|
+
requestPerm() {
|
|
57
|
+
const tinyThis = this;
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
if (tinyThis.isCompatible()) {
|
|
60
|
+
if (Notification.permission === 'default') {
|
|
61
|
+
Notification.requestPermission()
|
|
62
|
+
.then((permission) => {
|
|
63
|
+
this.#permissionRequested = true;
|
|
64
|
+
tinyThis.#allowed = permission === 'granted';
|
|
65
|
+
resolve(tinyThis.#allowed);
|
|
66
|
+
})
|
|
67
|
+
.catch(reject);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.#permissionRequested = true;
|
|
71
|
+
tinyThis.#allowed = Notification.permission === 'granted';
|
|
72
|
+
resolve(tinyThis.#allowed);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.#permissionRequested = true;
|
|
77
|
+
tinyThis.#allowed = false;
|
|
78
|
+
resolve(false);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Checks if the Notification API is supported by the current browser.
|
|
84
|
+
*
|
|
85
|
+
* @returns {boolean} Returns `true` if notifications are supported, otherwise `false`.
|
|
86
|
+
*/
|
|
87
|
+
isCompatible() {
|
|
88
|
+
return 'Notification' in window;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Sends a browser notification with the provided title and configuration.
|
|
92
|
+
* Truncates the body if necessary and plays a sound if configured.
|
|
93
|
+
*
|
|
94
|
+
* @param {string} title - The title of the notification.
|
|
95
|
+
* @param {NotificationOptions & { vibrate?: number[] }} [config={}] - Optional configuration for the notification.
|
|
96
|
+
* @returns {Notification|null} The created `Notification` instance, or `null` if permission is not granted.
|
|
97
|
+
* @throws {TypeError} If the title is not a string or config is not a valid object.
|
|
98
|
+
*/
|
|
99
|
+
send(title, config = {}) {
|
|
100
|
+
if (!this.#permissionRequested)
|
|
101
|
+
throw new Error('You must call requestPerm() before sending a notification.');
|
|
102
|
+
if (typeof title !== 'string')
|
|
103
|
+
throw new TypeError('title must be a string.');
|
|
104
|
+
if (typeof config !== 'object' || config === null)
|
|
105
|
+
throw new TypeError('config must be a non-null object.');
|
|
106
|
+
if (!this.#allowed)
|
|
107
|
+
return null;
|
|
108
|
+
const { icon = this.#defaultIcon || undefined, vibrate = [200, 100, 200] } = config;
|
|
109
|
+
const options = { ...config };
|
|
110
|
+
if (typeof icon === 'string')
|
|
111
|
+
options.icon = icon;
|
|
112
|
+
if (Array.isArray(vibrate))
|
|
113
|
+
options.vibrate = vibrate;
|
|
114
|
+
if (typeof options.body === 'string')
|
|
115
|
+
options.body = safeTextTrim(options.body, this.#bodyLimit);
|
|
116
|
+
const notification = new Notification(title, options);
|
|
117
|
+
notification.addEventListener('show', () => {
|
|
118
|
+
if (!(this.#audio instanceof HTMLAudioElement))
|
|
119
|
+
return;
|
|
120
|
+
this.#audio.currentTime = 0;
|
|
121
|
+
this.#audio.play().catch((err) => console.error(err));
|
|
122
|
+
});
|
|
123
|
+
if (typeof this.#defaultOnClick === 'function')
|
|
124
|
+
notification.addEventListener('click', this.#defaultOnClick);
|
|
125
|
+
return notification;
|
|
126
|
+
}
|
|
127
|
+
// === Getters and Setters ===
|
|
128
|
+
/**
|
|
129
|
+
* Whether the requestPerm() method was already called.
|
|
130
|
+
* @returns {boolean}
|
|
131
|
+
*/
|
|
132
|
+
wasPermissionRequested() {
|
|
133
|
+
return this.#permissionRequested;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Returns the current permission status.
|
|
137
|
+
* @returns {boolean} `true` if permission was granted, otherwise `false`.
|
|
138
|
+
*/
|
|
139
|
+
isAllowed() {
|
|
140
|
+
return this.#allowed;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Gets the current notification audio.
|
|
144
|
+
* @returns {HTMLAudioElement|null} The sound element, or `null` if not set.
|
|
145
|
+
*/
|
|
146
|
+
getAudio() {
|
|
147
|
+
return this.#audio;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Sets the audio element used for notification sounds.
|
|
151
|
+
* @param {HTMLAudioElement|string|null} value - A valid `HTMLAudioElement` or `null` to disable sound.
|
|
152
|
+
* @throws {TypeError} If the value is not an `HTMLAudioElement` or `null`.
|
|
153
|
+
*/
|
|
154
|
+
setAudio(value) {
|
|
155
|
+
if (!(value instanceof HTMLAudioElement) && typeof value !== 'string' && value !== null)
|
|
156
|
+
throw new TypeError('sound must be an instance of HTMLAudioElement or null.');
|
|
157
|
+
this.#audio = typeof value !== 'string' ? value : new Audio(value);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Gets the maximum length of the notification body text.
|
|
161
|
+
* @returns {number} Number of characters allowed.
|
|
162
|
+
*/
|
|
163
|
+
getBodyLimit() {
|
|
164
|
+
return this.#bodyLimit;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Sets the maximum number of characters allowed in the notification body.
|
|
168
|
+
* @param {number} value - A non-negative integer.
|
|
169
|
+
* @throws {TypeError} If the value is not a valid non-negative number.
|
|
170
|
+
*/
|
|
171
|
+
setBodyLimit(value) {
|
|
172
|
+
if (!Number.isFinite(value) || value < 0)
|
|
173
|
+
throw new TypeError('bodyLimit must be a non-negative number.');
|
|
174
|
+
this.#bodyLimit = value;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Gets the default avatar icon URL.
|
|
178
|
+
* @returns {string|null} The URL string or `null`.
|
|
179
|
+
*/
|
|
180
|
+
getDefaultAvatar() {
|
|
181
|
+
return this.#defaultIcon;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Sets the default avatar icon URL.
|
|
185
|
+
* @param {string|null} value - A string URL or `null` to disable default icon.
|
|
186
|
+
* @throws {TypeError} If the value is not a string or `null`.
|
|
187
|
+
*/
|
|
188
|
+
setDefaultAvatar(value) {
|
|
189
|
+
if (!(typeof value === 'string' || value === null))
|
|
190
|
+
throw new TypeError('defaultIcon must be a string or null.');
|
|
191
|
+
this.#defaultIcon = value;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Gets the default click event handler for notifications.
|
|
195
|
+
* @returns {(this: Notification, evt: Event) => any} The current click handler function.
|
|
196
|
+
*/
|
|
197
|
+
getDefaultOnClick() {
|
|
198
|
+
return this.#defaultOnClick;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Sets the default click event handler for notifications.
|
|
202
|
+
* @param {(this: Notification, evt: Event) => any} value - A function to handle the notification click event.
|
|
203
|
+
* @throws {TypeError} If the value is not a function.
|
|
204
|
+
*/
|
|
205
|
+
setDefaultOnClick(value) {
|
|
206
|
+
if (typeof value !== 'function')
|
|
207
|
+
throw new TypeError('defaultOnClick must be a function.');
|
|
208
|
+
this.#defaultOnClick = value;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
export default TinyNotifications;
|
package/docs/v1/README.md
CHANGED
|
@@ -29,9 +29,10 @@ This folder contains the core scripts we have worked on so far. Each file is a m
|
|
|
29
29
|
- π **[TinyNotifyCenter](./libs/TinyNotifyCenter.md)** β A dynamic notification center class to display, manage, and interact with notifications, supporting avatars, clickable items, HTML/text modes, and clean UI controls.
|
|
30
30
|
- π **[TinyToastNotify](./libs/TinyToastNotify.md)** β A lightweight toast notification system supporting positioning, timing customization, avatars, click actions, and fade-out animations.
|
|
31
31
|
- π₯ **[TinyDragDropDetector](./libs/TinyDragDropDetector.md)** β A lightweight drag-and-drop detector for files, handling the full drag lifecycle (`enter`, `over`, `leave`, `drop`) with CSS hover management and safe event handling on any DOM element or the full page.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
- π **[TinyUploadClicker](./libs/TinyUploadClicker.md)** β A minimal utility to bind any clickable element to a hidden file input, offering full control over styling, behavior, and upload event hooks.
|
|
33
|
+
- π§² **[TinyDragger](./libs/TinyDragger.md)** β A flexible drag-and-drop manager with collision detection, jail constraints, vibration feedback, visual proxies, revert-on-drop, and full custom event support.
|
|
34
|
+
- π **[TinyDomReadyManager](./libs/TinyDomReadyManager.md)** β A readiness manager for DOM and async conditions, supporting prioritized callbacks, custom filters, and event-based or promise-based bootstrapping.
|
|
35
|
+
- π£ **[TinyNotifications](./libs/TinyNotifications.md)** β A browser notification utility with sound support, permission management, truncation logic, default icons, and enforced validation to ensure safe and predictable usage.
|
|
35
36
|
|
|
36
37
|
### 3. **`fileManager/`**
|
|
37
38
|
* π **[main](./fileManager/main.md)** β A Node.js file/directory utility module with support for JSON, backups, renaming, size analysis, and more.
|
package/docs/v1/basics/array.md
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
# π¦ array.mjs
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A minimal and handy module offering a couple of focused utilities for working with arrays β from shuffling items randomly to generating smart sort functions for object arrays. Ideal for quick scripting or modular use in larger projects.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## `shuffleArray(items: string[]): string[]`
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
- π Shuffles arrays **in-place**
|
|
9
|
-
- π― Guarantees **uniform distribution** of permutations
|
|
10
|
-
- π Well-documented and easy to read
|
|
11
|
-
- π§ͺ No dependencies β just plug and play
|
|
9
|
+
π A tiny utility for shuffling arrays using the good old FisherβYates algorithm. Simple, efficient, and perfectly random (as far as JavaScript's `Math.random()` allows, anyway).
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
- **items** β An array of strings to shuffle.
|
|
12
|
+
- **Returns** β The same array instance, but now shuffled.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### π Usage
|
|
14
16
|
|
|
15
17
|
```js
|
|
16
18
|
import { shuffleArray } from './array.mjs';
|
|
@@ -23,54 +25,29 @@ console.log(fruits); // ['banana', 'cherry', 'apple', 'date'] (order will vary)
|
|
|
23
25
|
|
|
24
26
|
> Note: The original array is shuffled in place. If you want to preserve the original order, make a copy before shuffling.
|
|
25
27
|
|
|
26
|
-
## π§ How It Works
|
|
27
|
-
|
|
28
|
-
This function implements the **FisherβYates shuffle** (also known as the Knuth shuffle), which runs in linear time and guarantees uniform randomness:
|
|
29
|
-
|
|
30
|
-
```js
|
|
31
|
-
while (currentIndex !== 0) {
|
|
32
|
-
randomIndex = Math.floor(Math.random() * currentIndex);
|
|
33
|
-
currentIndex--;
|
|
34
|
-
[items[currentIndex], items[randomIndex]] = [items[randomIndex], items[currentIndex]];
|
|
35
|
-
}
|
|
36
|
-
```
|
|
37
|
-
|
|
38
28
|
You can find the original discussion here:
|
|
39
29
|
π https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
|
|
40
30
|
|
|
41
|
-
## π API
|
|
42
|
-
|
|
43
|
-
### `shuffleArray(items: string[]): string[]`
|
|
44
|
-
|
|
45
|
-
- **items** β An array of strings to shuffle.
|
|
46
|
-
- **Returns** β The same array instance, but now shuffled.
|
|
47
|
-
|
|
48
31
|
---
|
|
49
32
|
|
|
50
|
-
|
|
33
|
+
## π¦ `arraySortPositions`
|
|
51
34
|
|
|
52
35
|
π§ **Generates a comparator function** to sort an array of objects by a specified key, with optional reverse order.
|
|
53
36
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
## π Function Signature
|
|
37
|
+
### π Function Signature
|
|
57
38
|
|
|
58
39
|
```js
|
|
59
40
|
arraySortPositions(item: string, isReverse?: boolean): (a: Object<string|number, *>, b: Object<string|number, *>) => number
|
|
60
41
|
```
|
|
61
42
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
## π§ Parameters
|
|
43
|
+
### π§ Parameters
|
|
65
44
|
|
|
66
45
|
| Name | Type | Default | Description |
|
|
67
46
|
| ----------- | --------- | ------- | ---------------------------------------------------------- |
|
|
68
47
|
| `item` | `string` | β | π The key to sort the objects by. |
|
|
69
48
|
| `isReverse` | `boolean` | `false` | π If `true`, the sorting will be in **descending** order. |
|
|
70
49
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
## π― Returns
|
|
50
|
+
### π― Returns
|
|
74
51
|
|
|
75
52
|
π§© A **comparator function** compatible with `Array.prototype.sort()`:
|
|
76
53
|
|
|
@@ -80,9 +57,7 @@ arraySortPositions(item: string, isReverse?: boolean): (a: Object<string|number,
|
|
|
80
57
|
|
|
81
58
|
It compares two objects based on the specified `item` key.
|
|
82
59
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
## π‘ Examples
|
|
60
|
+
### π‘ Examples
|
|
86
61
|
|
|
87
62
|
```js
|
|
88
63
|
const arr = [{ pos: 2 }, { pos: 1 }, { pos: 3 }];
|
|
@@ -96,9 +71,7 @@ arr.sort(arraySortPositions('pos', true));
|
|
|
96
71
|
// π½ Descending: [{ pos: 3 }, { pos: 2 }, { pos: 1 }]
|
|
97
72
|
```
|
|
98
73
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
## π οΈ Use Case
|
|
74
|
+
### π οΈ Use Case
|
|
102
75
|
|
|
103
76
|
Great for situations where you need to **dynamically sort objects** by one of their keys, such as:
|
|
104
77
|
|
package/docs/v1/basics/html.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
### π `areHtmlElsColliding()`
|
|
2
2
|
|
|
3
3
|
Check if two DOM elements are **colliding on the screen**! Perfect for games, draggable elements, UI interactions, and more.
|
|
4
4
|
|
|
@@ -11,34 +11,26 @@ It compares the bounding rectangles of both elements:
|
|
|
11
11
|
* β
Checks if **rect1** is NOT entirely to the left, right, above, or below **rect2**.
|
|
12
12
|
* β
If none of these are true, then the elements are overlapping.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
## π§ Syntax
|
|
14
|
+
#### π§ Syntax
|
|
17
15
|
|
|
18
16
|
```javascript
|
|
19
17
|
areHtmlElsColliding(elem1, elem2);
|
|
20
18
|
```
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
## π― Parameters
|
|
20
|
+
#### π― Parameters
|
|
25
21
|
|
|
26
22
|
| Parameter | Type | Description |
|
|
27
23
|
| --------- | --------- | ----------------------- |
|
|
28
24
|
| `elem1` | `Element` | The first DOM element. |
|
|
29
25
|
| `elem2` | `Element` | The second DOM element. |
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
## π Return
|
|
27
|
+
#### π Return
|
|
34
28
|
|
|
35
29
|
| Type | Description |
|
|
36
30
|
| --------- | ------------------------------------------------------------------ |
|
|
37
31
|
| `boolean` | β
`true` if elements are colliding. <br>β `false` if they are not. |
|
|
38
32
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
## π¦ Example
|
|
33
|
+
#### π¦ Example
|
|
42
34
|
|
|
43
35
|
```javascript
|
|
44
36
|
const box1 = document.getElementById('box1');
|
|
@@ -51,33 +43,115 @@ if (areHtmlElsColliding(box1, box2)) {
|
|
|
51
43
|
}
|
|
52
44
|
```
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
## π§ Limitations
|
|
46
|
+
#### π§ Limitations
|
|
57
47
|
|
|
58
48
|
* Only works with **axis-aligned elements** (rectangular shapes).
|
|
59
49
|
* Does not handle rotated elements or complex shapes.
|
|
60
50
|
|
|
61
51
|
---
|
|
62
52
|
|
|
63
|
-
|
|
53
|
+
### π `readBase64Blob(file: File, isDataUrl?: boolean | string): Promise<string>`
|
|
54
|
+
|
|
55
|
+
Reads a file and returns its Base64 content using the FileReader API, with optional formatting as a full Data URL.
|
|
56
|
+
|
|
57
|
+
#### π₯ Parameters
|
|
58
|
+
|
|
59
|
+
* `file` *(File)*: The file object selected by the user (e.g., from an `<input type="file">` element).
|
|
60
|
+
* `isDataUrl` *(boolean | string, optional)*:
|
|
61
|
+
|
|
62
|
+
* If `false` *(default)*: returns only the Base64 portion.
|
|
63
|
+
* If `true`: returns the original Data URL string from `FileReader`.
|
|
64
|
+
* If a string: treated as a custom MIME type for building a new Data URL.
|
|
65
|
+
|
|
66
|
+
#### π€ Returns
|
|
67
|
+
|
|
68
|
+
* `Promise<string>`: Resolves with either the Base64 string or a complete Data URL, depending on `isDataUrl`.
|
|
69
|
+
|
|
70
|
+
#### β οΈ Throws
|
|
71
|
+
|
|
72
|
+
* `TypeError` if:
|
|
73
|
+
|
|
74
|
+
* The result is not a string.
|
|
75
|
+
* The `isDataUrl` argument is not a boolean or a string.
|
|
76
|
+
* `Error` if:
|
|
77
|
+
|
|
78
|
+
* The string is not a valid Base64 or Data URL format.
|
|
79
|
+
* The MIME string format is invalid.
|
|
80
|
+
* `DOMException` if the file cannot be read by `FileReader`.
|
|
81
|
+
|
|
82
|
+
#### π§ͺ Example
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
const input = document.querySelector('input[type="file"]');
|
|
86
|
+
input.addEventListener('change', async () => {
|
|
87
|
+
try {
|
|
88
|
+
const base64 = await readBase64Blob(input.files[0], false);
|
|
89
|
+
console.log(base64); // Logs only the Base64 string
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error('Error reading file:', err.message);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### π `readFileBlob(file: File, method: 'readAsArrayBuffer' | 'readAsDataURL' | 'readAsText' | 'readAsBinaryString'): Promise<any>`
|
|
99
|
+
|
|
100
|
+
Reads the contents of a file using a specified FileReader method.
|
|
101
|
+
|
|
102
|
+
#### π₯ Parameters
|
|
103
|
+
|
|
104
|
+
* `file` *(File)*: The file object selected by the user (e.g., from an `<input type="file">` element).
|
|
105
|
+
* `method` *(string)*: The FileReader method to use:
|
|
106
|
+
|
|
107
|
+
* `'readAsArrayBuffer'` β for binary buffers
|
|
108
|
+
* `'readAsDataURL'` β for Base64 encoded data URLs
|
|
109
|
+
* `'readAsText'` β for plain text
|
|
110
|
+
* `'readAsBinaryString'` β for legacy binary string output
|
|
111
|
+
|
|
112
|
+
#### π€ Returns
|
|
113
|
+
|
|
114
|
+
* `Promise<any>`: Resolves with the file content, depending on the method used.
|
|
115
|
+
|
|
116
|
+
#### β οΈ Throws
|
|
117
|
+
|
|
118
|
+
* `Error` if an unexpected error occurs while resolving the result.
|
|
119
|
+
* `DOMException` if `FileReader` encounters a failure during the read process.
|
|
120
|
+
|
|
121
|
+
#### π§ͺ Example
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
const input = document.querySelector('input[type="file"]');
|
|
125
|
+
input.addEventListener('change', async () => {
|
|
126
|
+
try {
|
|
127
|
+
const text = await readFileBlob(input.files[0], 'readAsText');
|
|
128
|
+
console.log(text); // Logs the file content as plain text
|
|
129
|
+
} catch (err) {
|
|
130
|
+
console.error('Error reading file:', err.message);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### π `readJsonBlob(file: File): Promise<any>`
|
|
64
138
|
|
|
65
139
|
Reads and parses a JSON file using the [`FileReader`](https://developer.mozilla.org/en-US/docs/Web/API/FileReader) API.
|
|
66
140
|
|
|
67
|
-
|
|
141
|
+
#### π₯ Parameters
|
|
68
142
|
|
|
69
143
|
* `file` *(File)*: The file object selected by the user (e.g., from an `<input type="file">` element).
|
|
70
144
|
|
|
71
|
-
|
|
145
|
+
#### π€ Returns
|
|
72
146
|
|
|
73
147
|
* `Promise<any>`: Resolves with the parsed JSON object, or rejects with an error if the content is invalid.
|
|
74
148
|
|
|
75
|
-
|
|
149
|
+
#### β οΈ Throws
|
|
76
150
|
|
|
77
151
|
* An error if the content is not valid JSON.
|
|
78
152
|
* An error if the file can't be read.
|
|
79
153
|
|
|
80
|
-
|
|
154
|
+
#### π§ͺ Example
|
|
81
155
|
|
|
82
156
|
```js
|
|
83
157
|
const input = document.querySelector('input[type="file"]');
|
|
@@ -93,36 +167,36 @@ input.addEventListener('change', async () => {
|
|
|
93
167
|
|
|
94
168
|
---
|
|
95
169
|
|
|
96
|
-
|
|
170
|
+
### πΎ `saveJsonFile(filename: string, data: any, spaces: number = 2): void`
|
|
97
171
|
|
|
98
172
|
Converts a JavaScript object to JSON and triggers a download in the browser.
|
|
99
173
|
|
|
100
|
-
|
|
174
|
+
#### π₯ Parameters
|
|
101
175
|
|
|
102
176
|
* `filename` *(string)*: The name of the file to save (e.g., `"data.json"`).
|
|
103
177
|
* `data` *(any)*: The JavaScript object to convert to JSON.
|
|
104
178
|
* `spaces` *(number)* *(optional)*: Indentation level for formatting the JSON string. Default is `2`.
|
|
105
179
|
|
|
106
|
-
|
|
180
|
+
#### π€ Returns
|
|
107
181
|
|
|
108
182
|
* `void`
|
|
109
183
|
|
|
110
|
-
|
|
184
|
+
#### π Behavior
|
|
111
185
|
|
|
112
186
|
Creates a temporary `<a>` element, downloads the file, and cleans up the URL.
|
|
113
187
|
|
|
114
|
-
|
|
188
|
+
#### π§ͺ Example
|
|
115
189
|
|
|
116
190
|
```js
|
|
117
191
|
const data = { name: 'Yasmin', type: 'dev' };
|
|
118
192
|
saveJsonFile('yasmin.json', data);
|
|
119
193
|
```
|
|
120
194
|
|
|
121
|
-
|
|
195
|
+
### π `fetchJson(url, options?): Promise<any>`
|
|
122
196
|
|
|
123
197
|
Loads and parses a JSON from a remote URL using the Fetch API, with support for custom HTTP methods, retries, timeouts, headers, and even external abort controllers.
|
|
124
198
|
|
|
125
|
-
|
|
199
|
+
#### π₯ Parameters
|
|
126
200
|
|
|
127
201
|
* `url` *(string)*: The full URL to fetch JSON from (must start with `http://`, `https://`, `/`, `./`, or `../`).
|
|
128
202
|
* `options` *(object)* *(optional)*:
|
|
@@ -134,7 +208,7 @@ Loads and parses a JSON from a remote URL using the Fetch API, with support for
|
|
|
134
208
|
* `headers` *(object)*: Additional headers to include in the request.
|
|
135
209
|
* `body` *(object)*: Request body. If the value is a plain object, it will be automatically stringified as JSON.
|
|
136
210
|
|
|
137
|
-
|
|
211
|
+
##### `signal` (`AbortSignal` | `null`) β *optional*
|
|
138
212
|
|
|
139
213
|
Custom abort signal. If set:
|
|
140
214
|
|
|
@@ -142,21 +216,21 @@ Custom abort signal. If set:
|
|
|
142
216
|
* Retry logic is **disabled**
|
|
143
217
|
* Abortion is handled externally
|
|
144
218
|
|
|
145
|
-
|
|
219
|
+
#### π€ Returns
|
|
146
220
|
|
|
147
221
|
* `Promise<any>`: Resolves with the parsed JSON data.
|
|
148
222
|
|
|
149
|
-
|
|
223
|
+
#### β οΈ Throws
|
|
150
224
|
|
|
151
225
|
* `Error` if the fetch fails or exceeds the timeout
|
|
152
226
|
* `Error` if the response is not `application/json`
|
|
153
227
|
* `Error` if the result is not a plain JSON object
|
|
154
228
|
|
|
155
|
-
|
|
229
|
+
#### π§ Tip
|
|
156
230
|
|
|
157
231
|
If you pass your own `signal`, this disables both `timeout` and `retries`. Use it when you're managing cancellation manually (e.g. in UI components or async workflows).
|
|
158
232
|
|
|
159
|
-
|
|
233
|
+
#### π§ͺ Example
|
|
160
234
|
|
|
161
235
|
```js
|
|
162
236
|
const controller = new AbortController();
|
|
@@ -241,3 +315,95 @@ getHtmlElPadding(el: Element): HtmlElBoxSides
|
|
|
241
315
|
|
|
242
316
|
* `el`: The target DOM element.
|
|
243
317
|
* **Returns**: Padding values for all sides and summed horizontal (`x`) and vertical (`y`) values.
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
### π `installWindowHiddenScript`
|
|
322
|
+
|
|
323
|
+
Automatically toggles CSS classes on a given element based on the browser window or tab **visibility** and **focus** state.
|
|
324
|
+
|
|
325
|
+
Perfect for UI states like dimming, pausing animations, or showing "away" statuses.
|
|
326
|
+
|
|
327
|
+
#### π§ Features
|
|
328
|
+
|
|
329
|
+
* β
Adds or removes custom CSS classes depending on page visibility or focus
|
|
330
|
+
* β
Supports modern and legacy browsers (including IE9)
|
|
331
|
+
* β
Automatically dispatches an initial state check on load
|
|
332
|
+
* β
Allows custom **callbacks** for visibility changes (`onVisible`, `onHidden`)
|
|
333
|
+
* β
Returns a cleanup function to remove all listeners
|
|
334
|
+
|
|
335
|
+
#### π§ͺ Usage
|
|
336
|
+
|
|
337
|
+
```js
|
|
338
|
+
import { installWindowHiddenScript } from 'tiny-essentials';
|
|
339
|
+
|
|
340
|
+
const uninstall = installWindowHiddenScript({
|
|
341
|
+
element: document.getElementById('app'),
|
|
342
|
+
hiddenClass: 'is-hidden',
|
|
343
|
+
visibleClass: 'is-visible',
|
|
344
|
+
onVisible: () => console.log('Window is now visible'),
|
|
345
|
+
onHidden: () => console.log('Window is now hidden'),
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// To remove all listeners later
|
|
349
|
+
uninstall();
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
#### βοΈ Options
|
|
353
|
+
|
|
354
|
+
| Option | Type | Default | Description |
|
|
355
|
+
| -------------- | ------------- | ----------------- | ----------------------------------------------------------------- |
|
|
356
|
+
| `element` | `HTMLElement` | `document.body` | The element to which the visibility classes will be applied |
|
|
357
|
+
| `hiddenClass` | `string` | `'windowHidden'` | Class name to apply when the window is **not visible or blurred** |
|
|
358
|
+
| `visibleClass` | `string` | `'windowVisible'` | Class name to apply when the window is **visible or focused** |
|
|
359
|
+
| `onVisible` | `() => void` | `undefined` | Optional callback fired when the window becomes visible |
|
|
360
|
+
| `onHidden` | `() => void` | `undefined` | Optional callback fired when the window becomes hidden |
|
|
361
|
+
|
|
362
|
+
#### π Return Value
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
() => void
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Returns a function that, when called, will:
|
|
369
|
+
|
|
370
|
+
* π§Ή Remove all attached event listeners
|
|
371
|
+
* β Remove both visibility classes from the target element
|
|
372
|
+
|
|
373
|
+
#### π¦ Events Supported
|
|
374
|
+
|
|
375
|
+
The script handles multiple events depending on browser support:
|
|
376
|
+
|
|
377
|
+
* `visibilitychange`, `webkitvisibilitychange`, `mozvisibilitychange`, `msvisibilitychange`
|
|
378
|
+
* `focus`, `blur`, `focusin`, `focusout`
|
|
379
|
+
* `pageshow`, `pagehide`
|
|
380
|
+
* IE fallback: `onfocusin`, `onfocusout`
|
|
381
|
+
|
|
382
|
+
#### π Initial Trigger
|
|
383
|
+
|
|
384
|
+
Immediately after installation, the script simulates a `focus` or `blur` event based on the current visibility state to **ensure the classes and callbacks are applied from the start**.
|
|
385
|
+
|
|
386
|
+
#### π§― Uninstalling
|
|
387
|
+
|
|
388
|
+
Donβt forget to call the returned function if you dynamically load/unload components or scripts:
|
|
389
|
+
|
|
390
|
+
```js
|
|
391
|
+
const stopWatching = installWindowHiddenScript(...);
|
|
392
|
+
stopWatching(); // later
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
#### π¨ CSS Example
|
|
396
|
+
|
|
397
|
+
```css
|
|
398
|
+
.windowVisible {
|
|
399
|
+
opacity: 1;
|
|
400
|
+
pointer-events: auto;
|
|
401
|
+
transition: opacity 0.3s ease;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.windowHidden {
|
|
405
|
+
opacity: 0.4;
|
|
406
|
+
pointer-events: none;
|
|
407
|
+
transition: opacity 0.3s ease;
|
|
408
|
+
}
|
|
409
|
+
```
|