tiny-essentials 1.13.2 → 1.15.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 +656 -26
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyDragger.js +196 -26
- package/dist/v1/TinyEssentials.js +896 -26
- 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 +198 -26
- package/dist/v1/basics/collision.cjs +413 -0
- package/dist/v1/basics/collision.d.mts +187 -0
- package/dist/v1/basics/collision.mjs +350 -0
- package/dist/v1/basics/html.cjs +201 -26
- package/dist/v1/basics/html.d.mts +71 -7
- package/dist/v1/basics/html.mjs +186 -23
- package/dist/v1/basics/index.cjs +24 -0
- package/dist/v1/basics/index.d.mts +24 -1
- package/dist/v1/basics/index.mjs +4 -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 +26 -0
- package/dist/v1/index.d.mts +25 -1
- package/dist/v1/index.mjs +5 -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 +5 -3
- package/docs/v1/basics/array.md +16 -43
- package/docs/v1/basics/collision.md +237 -0
- package/docs/v1/basics/html.md +117 -28
- package/docs/v1/basics/text.md +44 -14
- package/docs/v1/libs/TinyNotifications.md +189 -0
- package/package.json +5 -2
|
@@ -3439,6 +3439,7 @@ class ColorSafeStringify {
|
|
|
3439
3439
|
;// ./src/v1/basics/html.mjs
|
|
3440
3440
|
|
|
3441
3441
|
|
|
3442
|
+
|
|
3442
3443
|
/**
|
|
3443
3444
|
* Checks if two DOM elements are colliding on the screen.
|
|
3444
3445
|
*
|
|
@@ -3449,44 +3450,157 @@ class ColorSafeStringify {
|
|
|
3449
3450
|
function areHtmlElsColliding(elem1, elem2) {
|
|
3450
3451
|
const rect1 = elem1.getBoundingClientRect();
|
|
3451
3452
|
const rect2 = elem2.getBoundingClientRect();
|
|
3453
|
+
return areElsColliding(rect1, rect2);
|
|
3454
|
+
}
|
|
3452
3455
|
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3456
|
+
/**
|
|
3457
|
+
* Checks if two DOM elements are colliding on the screen, and locks the collision
|
|
3458
|
+
* until the element exits through the same side it entered.
|
|
3459
|
+
*
|
|
3460
|
+
* @param {Element} elem1 - First DOM element (e.g. draggable or moving element).
|
|
3461
|
+
* @param {Element} elem2 - Second DOM element (e.g. a container or boundary element).
|
|
3462
|
+
* @param {'top'|'bottom'|'left'|'right'} lockDirection - Direction that must be respected to unlock the collision.
|
|
3463
|
+
* @param {WeakMap<Element, string>} stateMap - A shared WeakMap to track persistent entry direction per element.
|
|
3464
|
+
* @returns {boolean} True if collision is still active.
|
|
3465
|
+
*/
|
|
3466
|
+
function areHtmlElsCollidingWithLock(elem1, elem2, lockDirection, stateMap) {
|
|
3467
|
+
const rect1 = elem1.getBoundingClientRect();
|
|
3468
|
+
const rect2 = elem2.getBoundingClientRect();
|
|
3469
|
+
const isColliding = areElsColliding(rect1, rect2);
|
|
3470
|
+
|
|
3471
|
+
if (isColliding) {
|
|
3472
|
+
// Save entry direction
|
|
3473
|
+
if (!stateMap.has(elem1)) {
|
|
3474
|
+
stateMap.set(elem1, lockDirection);
|
|
3475
|
+
}
|
|
3476
|
+
return true;
|
|
3477
|
+
}
|
|
3478
|
+
|
|
3479
|
+
// Handle unlock logic
|
|
3480
|
+
if (stateMap.has(elem1)) {
|
|
3481
|
+
const lastDirection = stateMap.get(elem1);
|
|
3482
|
+
|
|
3483
|
+
switch (lastDirection) {
|
|
3484
|
+
case 'top':
|
|
3485
|
+
if (areElsCollTop(rect1, rect2)) stateMap.delete(elem1); // exited from top
|
|
3486
|
+
break;
|
|
3487
|
+
case 'bottom':
|
|
3488
|
+
if (areElsCollBottom(rect1, rect2)) stateMap.delete(elem1); // exited from bottom
|
|
3489
|
+
break;
|
|
3490
|
+
case 'left':
|
|
3491
|
+
if (areElsCollLeft(rect1, rect2)) stateMap.delete(elem1); // exited from left
|
|
3492
|
+
break;
|
|
3493
|
+
case 'right':
|
|
3494
|
+
if (areElsCollRight(rect1, rect2)) stateMap.delete(elem1); // exited from right
|
|
3495
|
+
break;
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
return stateMap.has(elem1); // still colliding (locked)
|
|
3499
|
+
}
|
|
3500
|
+
|
|
3501
|
+
return false;
|
|
3459
3502
|
}
|
|
3460
3503
|
|
|
3461
3504
|
/**
|
|
3462
|
-
* Reads
|
|
3463
|
-
*
|
|
3464
|
-
* @param {File} file
|
|
3465
|
-
* @
|
|
3505
|
+
* Reads the contents of a file using the specified FileReader method.
|
|
3506
|
+
*
|
|
3507
|
+
* @param {File} file - The file to be read.
|
|
3508
|
+
* @param {'readAsArrayBuffer'|'readAsDataURL'|'readAsText'|'readAsBinaryString'} method -
|
|
3509
|
+
* The FileReader method to use for reading the file.
|
|
3510
|
+
* @returns {Promise<any>} - A promise that resolves with the file content, according to the chosen method.
|
|
3511
|
+
* @throws {Error} - If an unexpected error occurs while handling the result.
|
|
3512
|
+
* @throws {DOMException} - If the FileReader encounters an error while reading the file.
|
|
3466
3513
|
*/
|
|
3467
|
-
function
|
|
3514
|
+
function readFileBlob(file, method) {
|
|
3468
3515
|
return new Promise((resolve, reject) => {
|
|
3469
3516
|
const reader = new FileReader();
|
|
3470
|
-
|
|
3471
3517
|
reader.onload = () => {
|
|
3472
3518
|
try {
|
|
3473
|
-
|
|
3474
|
-
const result = JSON.parse(reader.result);
|
|
3475
|
-
resolve(result);
|
|
3519
|
+
resolve(reader.result);
|
|
3476
3520
|
} catch (error) {
|
|
3477
|
-
|
|
3478
|
-
reject(new Error(`Invalid JSON in file: ${file.name}\n${error.message}`));
|
|
3521
|
+
reject(error);
|
|
3479
3522
|
}
|
|
3480
3523
|
};
|
|
3481
|
-
|
|
3482
3524
|
reader.onerror = () => {
|
|
3483
|
-
reject(
|
|
3525
|
+
reject(reader.error);
|
|
3484
3526
|
};
|
|
3527
|
+
reader[method](file);
|
|
3528
|
+
});
|
|
3529
|
+
}
|
|
3485
3530
|
|
|
3486
|
-
|
|
3531
|
+
/**
|
|
3532
|
+
* Reads a file as a Base64 string using FileReader, and optionally formats it as a full data URL.
|
|
3533
|
+
*
|
|
3534
|
+
* Performs strict validation to ensure the result is a valid Base64 string or a proper data URL.
|
|
3535
|
+
*
|
|
3536
|
+
* @param {File} file - The file to be read.
|
|
3537
|
+
* @param {boolean|string} [isDataUrl=false] - If true, returns a full data URL; if false, returns only the Base64 string;
|
|
3538
|
+
* if a string is passed, it is used as the MIME type in the data URL.
|
|
3539
|
+
* @returns {Promise<string>} - A promise that resolves with the Base64 string or data URL.
|
|
3540
|
+
*
|
|
3541
|
+
* @throws {TypeError} - If the result is not a string or if `isDataUrl` is not a valid type.
|
|
3542
|
+
* @throws {Error} - If the result does not match the expected data URL format or Base64 structure.
|
|
3543
|
+
* @throws {DOMException} - If the FileReader fails to read the file.
|
|
3544
|
+
*/
|
|
3545
|
+
function readBase64Blob(file, isDataUrl = false) {
|
|
3546
|
+
return new Promise((resolve, reject) => {
|
|
3547
|
+
if (typeof isDataUrl !== 'string' && typeof isDataUrl !== 'boolean')
|
|
3548
|
+
reject(new TypeError('The isDataUrl parameter must be a boolean or a string.'));
|
|
3549
|
+
readFileBlob(file, 'readAsDataURL')
|
|
3550
|
+
.then(
|
|
3551
|
+
/**
|
|
3552
|
+
* Ensure that the URL format is correct in the required pattern
|
|
3553
|
+
* @param {string} base64Data
|
|
3554
|
+
*/ (base64Data) => {
|
|
3555
|
+
if (typeof base64Data !== 'string')
|
|
3556
|
+
throw new TypeError('Expected file content to be a string.');
|
|
3557
|
+
|
|
3558
|
+
const match = base64Data.match(/^data:(.+);base64,(.*)$/);
|
|
3559
|
+
if (!match || !match[2])
|
|
3560
|
+
throw new Error('Invalid data URL format or missing Base64 content.');
|
|
3561
|
+
const [, mimeType, base64] = match;
|
|
3562
|
+
if (!/^[\w/+]+=*$/.test(base64)) throw new Error('Base64 content is malformed.');
|
|
3563
|
+
|
|
3564
|
+
if (typeof isDataUrl === 'boolean') return resolve(isDataUrl ? base64Data : base64);
|
|
3565
|
+
if (!/^[\w-]+\/[\w.+-]+$/.test(isDataUrl))
|
|
3566
|
+
throw new Error(`Invalid MIME type string: ${isDataUrl}`);
|
|
3567
|
+
|
|
3568
|
+
return resolve(`data:${isDataUrl};base64,${base64}`);
|
|
3569
|
+
},
|
|
3570
|
+
)
|
|
3571
|
+
.catch(reject);
|
|
3487
3572
|
});
|
|
3488
3573
|
}
|
|
3489
3574
|
|
|
3575
|
+
/**
|
|
3576
|
+
* Reads a file and strictly validates its content as proper JSON using FileReader.
|
|
3577
|
+
*
|
|
3578
|
+
* Performs several checks to ensure the file contains valid, parsable JSON data.
|
|
3579
|
+
*
|
|
3580
|
+
* @param {File} file - The file to be read. It must contain valid JSON as plain text.
|
|
3581
|
+
* @returns {Promise<Record<string|number|symbol, any>|any[]>} - A promise that resolves with the parsed JSON object.
|
|
3582
|
+
*
|
|
3583
|
+
* @throws {SyntaxError} - If the file content is not valid JSON syntax.
|
|
3584
|
+
* @throws {TypeError} - If the result is not a string or does not represent a JSON value.
|
|
3585
|
+
* @throws {Error} - If the result is empty or structurally invalid as JSON.
|
|
3586
|
+
* @throws {DOMException} - If the FileReader fails to read the file.
|
|
3587
|
+
*/
|
|
3588
|
+
function readJsonBlob(file) {
|
|
3589
|
+
return new Promise((resolve, reject) =>
|
|
3590
|
+
readFileBlob(file, 'readAsText')
|
|
3591
|
+
.then((data) => {
|
|
3592
|
+
if (typeof data !== 'string') throw new TypeError('Expected file content to be a string.');
|
|
3593
|
+
const trimmed = data.trim();
|
|
3594
|
+
if (trimmed.length === 0) throw new Error('File is empty or contains only whitespace.');
|
|
3595
|
+
const parsed = JSON.parse(trimmed);
|
|
3596
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
3597
|
+
throw new Error('Parsed content is not a valid JSON object or array.');
|
|
3598
|
+
resolve(parsed);
|
|
3599
|
+
})
|
|
3600
|
+
.catch(reject),
|
|
3601
|
+
);
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3490
3604
|
/**
|
|
3491
3605
|
* Saves a JSON object as a downloadable file.
|
|
3492
3606
|
* @param {string} filename
|
|
@@ -3589,7 +3703,8 @@ async function fetchJson(
|
|
|
3589
3703
|
|
|
3590
3704
|
const data = await response.json();
|
|
3591
3705
|
|
|
3592
|
-
if (!
|
|
3706
|
+
if (!Array.isArray(data) && !isJsonObject(data))
|
|
3707
|
+
throw new Error('Received invalid data instead of valid JSON.');
|
|
3593
3708
|
|
|
3594
3709
|
return data;
|
|
3595
3710
|
} catch (err) {
|
|
@@ -3691,19 +3806,34 @@ const getHtmlElPadding = (el) => {
|
|
|
3691
3806
|
|
|
3692
3807
|
/**
|
|
3693
3808
|
* Installs a script that toggles CSS classes on a given element
|
|
3694
|
-
* based on the page's visibility or focus state
|
|
3809
|
+
* based on the page's visibility or focus state, and optionally
|
|
3810
|
+
* triggers callbacks on visibility changes.
|
|
3695
3811
|
*
|
|
3696
3812
|
* @param {Object} [settings={}]
|
|
3697
3813
|
* @param {HTMLElement} [settings.element=document.body] - The element to receive visibility classes.
|
|
3698
3814
|
* @param {string} [settings.hiddenClass='windowHidden'] - CSS class applied when the page is hidden.
|
|
3699
3815
|
* @param {string} [settings.visibleClass='windowVisible'] - CSS class applied when the page is visible.
|
|
3816
|
+
* @param {() => void} [settings.onVisible] - Callback called when page becomes visible.
|
|
3817
|
+
* @param {() => void} [settings.onHidden] - Callback called when page becomes hidden.
|
|
3700
3818
|
* @returns {() => void} Function that removes all installed event listeners.
|
|
3819
|
+
* @throws {TypeError} If any provided setting is invalid.
|
|
3701
3820
|
*/
|
|
3702
3821
|
function installWindowHiddenScript({
|
|
3703
3822
|
element = document.body,
|
|
3704
3823
|
hiddenClass = 'windowHidden',
|
|
3705
3824
|
visibleClass = 'windowVisible',
|
|
3825
|
+
onVisible,
|
|
3826
|
+
onHidden,
|
|
3706
3827
|
} = {}) {
|
|
3828
|
+
if (!(element instanceof HTMLElement))
|
|
3829
|
+
throw new TypeError(`"element" must be an instance of HTMLElement.`);
|
|
3830
|
+
if (typeof hiddenClass !== 'string') throw new TypeError(`"hiddenClass" must be a string.`);
|
|
3831
|
+
if (typeof visibleClass !== 'string') throw new TypeError(`"visibleClass" must be a string.`);
|
|
3832
|
+
if (onVisible !== undefined && typeof onVisible !== 'function')
|
|
3833
|
+
throw new TypeError(`"onVisible" must be a function if provided.`);
|
|
3834
|
+
if (onHidden !== undefined && typeof onHidden !== 'function')
|
|
3835
|
+
throw new TypeError(`"onHidden" must be a function if provided.`);
|
|
3836
|
+
|
|
3707
3837
|
const removeClass = () => {
|
|
3708
3838
|
element.classList.remove(hiddenClass);
|
|
3709
3839
|
element.classList.remove(visibleClass);
|
|
@@ -3711,8 +3841,6 @@ function installWindowHiddenScript({
|
|
|
3711
3841
|
|
|
3712
3842
|
/** @type {string|null} */
|
|
3713
3843
|
let hiddenProp = null;
|
|
3714
|
-
/** @type {(this: any, evt: Event) => void} */
|
|
3715
|
-
let handler;
|
|
3716
3844
|
|
|
3717
3845
|
const visibilityEvents = [
|
|
3718
3846
|
'visibilitychange',
|
|
@@ -3730,7 +3858,8 @@ function installWindowHiddenScript({
|
|
|
3730
3858
|
}
|
|
3731
3859
|
}
|
|
3732
3860
|
|
|
3733
|
-
|
|
3861
|
+
/** @type {(this: any, evt: Event) => void} */
|
|
3862
|
+
const handler = function (evt) {
|
|
3734
3863
|
removeClass();
|
|
3735
3864
|
|
|
3736
3865
|
const type = evt?.type;
|
|
@@ -3742,10 +3871,18 @@ function installWindowHiddenScript({
|
|
|
3742
3871
|
|
|
3743
3872
|
if (visibleEvents.includes(type)) {
|
|
3744
3873
|
element.classList.add(visibleClass);
|
|
3874
|
+
onVisible?.();
|
|
3745
3875
|
} else if (hiddenEvents.includes(type)) {
|
|
3746
3876
|
element.classList.add(hiddenClass);
|
|
3877
|
+
onHidden?.();
|
|
3747
3878
|
} else {
|
|
3748
|
-
|
|
3879
|
+
if (isHidden) {
|
|
3880
|
+
element.classList.add(hiddenClass);
|
|
3881
|
+
onHidden?.();
|
|
3882
|
+
} else {
|
|
3883
|
+
element.classList.add(visibleClass);
|
|
3884
|
+
onVisible?.();
|
|
3885
|
+
}
|
|
3749
3886
|
}
|
|
3750
3887
|
};
|
|
3751
3888
|
|
|
@@ -3782,6 +3919,7 @@ function installWindowHiddenScript({
|
|
|
3782
3919
|
};
|
|
3783
3920
|
}
|
|
3784
3921
|
|
|
3922
|
+
// Trigger initial state
|
|
3785
3923
|
// @ts-ignore
|
|
3786
3924
|
const simulatedEvent = new Event(hiddenProp && document[hiddenProp] ? 'blur' : 'focus');
|
|
3787
3925
|
handler(simulatedEvent);
|
|
@@ -3789,6 +3927,38 @@ function installWindowHiddenScript({
|
|
|
3789
3927
|
return uninstall;
|
|
3790
3928
|
}
|
|
3791
3929
|
|
|
3930
|
+
/**
|
|
3931
|
+
* Checks if the given element is at least partially visible in the viewport.
|
|
3932
|
+
*
|
|
3933
|
+
* @param {HTMLElement} element - The DOM element to check.
|
|
3934
|
+
* @returns {boolean} True if the element is partially in the viewport, false otherwise.
|
|
3935
|
+
*/
|
|
3936
|
+
function isInViewport(element) {
|
|
3937
|
+
const elementTop = element.offsetTop;
|
|
3938
|
+
const elementBottom = elementTop + element.offsetHeight;
|
|
3939
|
+
|
|
3940
|
+
const viewportTop = window.scrollY;
|
|
3941
|
+
const viewportBottom = viewportTop + window.innerHeight;
|
|
3942
|
+
|
|
3943
|
+
return elementBottom > viewportTop && elementTop < viewportBottom;
|
|
3944
|
+
}
|
|
3945
|
+
|
|
3946
|
+
/**
|
|
3947
|
+
* Checks if the given element is fully visible in the viewport (top and bottom).
|
|
3948
|
+
*
|
|
3949
|
+
* @param {HTMLElement} element - The DOM element to check.
|
|
3950
|
+
* @returns {boolean} True if the element is fully visible in the viewport, false otherwise.
|
|
3951
|
+
*/
|
|
3952
|
+
function isScrolledIntoView(element) {
|
|
3953
|
+
const viewportTop = window.scrollY;
|
|
3954
|
+
const viewportBottom = viewportTop + window.innerHeight;
|
|
3955
|
+
|
|
3956
|
+
const elemTop = element.offsetTop;
|
|
3957
|
+
const elemBottom = elemTop + element.offsetHeight;
|
|
3958
|
+
|
|
3959
|
+
return elemBottom <= viewportBottom && elemTop >= viewportTop;
|
|
3960
|
+
}
|
|
3961
|
+
|
|
3792
3962
|
// EXTERNAL MODULE: fs (ignored)
|
|
3793
3963
|
var fs_ignored_ = __webpack_require__(133);
|
|
3794
3964
|
// EXTERNAL MODULE: ./node_modules/path-browserify/index.js
|
|
@@ -5389,6 +5559,8 @@ class TinyDragger {
|
|
|
5389
5559
|
|
|
5390
5560
|
|
|
5391
5561
|
|
|
5562
|
+
|
|
5563
|
+
|
|
5392
5564
|
|
|
5393
5565
|
|
|
5394
5566
|
|