tiny-essentials 1.13.2 → 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 +160 -20
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyDragger.js +115 -20
- package/dist/v1/TinyEssentials.js +400 -20
- 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 +116 -20
- package/dist/v1/basics/html.cjs +117 -20
- package/dist/v1/basics/html.d.mts +46 -7
- package/dist/v1/basics/html.mjs +112 -19
- package/dist/v1/basics/index.cjs +3 -0
- package/dist/v1/basics/index.d.mts +4 -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 +5 -0
- package/dist/v1/index.d.mts +5 -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 +90 -27
- package/docs/v1/basics/text.md +44 -14
- package/docs/v1/libs/TinyNotifications.md +189 -0
- package/package.json +1 -1
package/dist/v1/TinyDragger.js
CHANGED
|
@@ -2520,34 +2520,105 @@ function areHtmlElsColliding(elem1, elem2) {
|
|
|
2520
2520
|
}
|
|
2521
2521
|
|
|
2522
2522
|
/**
|
|
2523
|
-
* Reads
|
|
2524
|
-
*
|
|
2525
|
-
* @param {File} file
|
|
2526
|
-
* @
|
|
2523
|
+
* Reads the contents of a file using the specified FileReader method.
|
|
2524
|
+
*
|
|
2525
|
+
* @param {File} file - The file to be read.
|
|
2526
|
+
* @param {'readAsArrayBuffer'|'readAsDataURL'|'readAsText'|'readAsBinaryString'} method -
|
|
2527
|
+
* The FileReader method to use for reading the file.
|
|
2528
|
+
* @returns {Promise<any>} - A promise that resolves with the file content, according to the chosen method.
|
|
2529
|
+
* @throws {Error} - If an unexpected error occurs while handling the result.
|
|
2530
|
+
* @throws {DOMException} - If the FileReader encounters an error while reading the file.
|
|
2527
2531
|
*/
|
|
2528
|
-
function
|
|
2532
|
+
function readFileBlob(file, method) {
|
|
2529
2533
|
return new Promise((resolve, reject) => {
|
|
2530
2534
|
const reader = new FileReader();
|
|
2531
|
-
|
|
2532
2535
|
reader.onload = () => {
|
|
2533
2536
|
try {
|
|
2534
|
-
|
|
2535
|
-
const result = JSON.parse(reader.result);
|
|
2536
|
-
resolve(result);
|
|
2537
|
+
resolve(reader.result);
|
|
2537
2538
|
} catch (error) {
|
|
2538
|
-
|
|
2539
|
-
reject(new Error(`Invalid JSON in file: ${file.name}\n${error.message}`));
|
|
2539
|
+
reject(error);
|
|
2540
2540
|
}
|
|
2541
2541
|
};
|
|
2542
|
-
|
|
2543
2542
|
reader.onerror = () => {
|
|
2544
|
-
reject(
|
|
2543
|
+
reject(reader.error);
|
|
2545
2544
|
};
|
|
2545
|
+
reader[method](file);
|
|
2546
|
+
});
|
|
2547
|
+
}
|
|
2546
2548
|
|
|
2547
|
-
|
|
2549
|
+
/**
|
|
2550
|
+
* Reads a file as a Base64 string using FileReader, and optionally formats it as a full data URL.
|
|
2551
|
+
*
|
|
2552
|
+
* Performs strict validation to ensure the result is a valid Base64 string or a proper data URL.
|
|
2553
|
+
*
|
|
2554
|
+
* @param {File} file - The file to be read.
|
|
2555
|
+
* @param {boolean|string} [isDataUrl=false] - If true, returns a full data URL; if false, returns only the Base64 string;
|
|
2556
|
+
* if a string is passed, it is used as the MIME type in the data URL.
|
|
2557
|
+
* @returns {Promise<string>} - A promise that resolves with the Base64 string or data URL.
|
|
2558
|
+
*
|
|
2559
|
+
* @throws {TypeError} - If the result is not a string or if `isDataUrl` is not a valid type.
|
|
2560
|
+
* @throws {Error} - If the result does not match the expected data URL format or Base64 structure.
|
|
2561
|
+
* @throws {DOMException} - If the FileReader fails to read the file.
|
|
2562
|
+
*/
|
|
2563
|
+
function readBase64Blob(file, isDataUrl = false) {
|
|
2564
|
+
return new Promise((resolve, reject) => {
|
|
2565
|
+
if (typeof isDataUrl !== 'string' && typeof isDataUrl !== 'boolean')
|
|
2566
|
+
reject(new TypeError('The isDataUrl parameter must be a boolean or a string.'));
|
|
2567
|
+
readFileBlob(file, 'readAsDataURL')
|
|
2568
|
+
.then(
|
|
2569
|
+
/**
|
|
2570
|
+
* Ensure that the URL format is correct in the required pattern
|
|
2571
|
+
* @param {string} base64Data
|
|
2572
|
+
*/ (base64Data) => {
|
|
2573
|
+
if (typeof base64Data !== 'string')
|
|
2574
|
+
throw new TypeError('Expected file content to be a string.');
|
|
2575
|
+
|
|
2576
|
+
const match = base64Data.match(/^data:(.+);base64,(.*)$/);
|
|
2577
|
+
if (!match || !match[2])
|
|
2578
|
+
throw new Error('Invalid data URL format or missing Base64 content.');
|
|
2579
|
+
const [, mimeType, base64] = match;
|
|
2580
|
+
if (!/^[\w/+]+=*$/.test(base64)) throw new Error('Base64 content is malformed.');
|
|
2581
|
+
|
|
2582
|
+
if (typeof isDataUrl === 'boolean') return resolve(isDataUrl ? base64Data : base64);
|
|
2583
|
+
if (!/^[\w-]+\/[\w.+-]+$/.test(isDataUrl))
|
|
2584
|
+
throw new Error(`Invalid MIME type string: ${isDataUrl}`);
|
|
2585
|
+
|
|
2586
|
+
return resolve(`data:${isDataUrl};base64,${base64}`);
|
|
2587
|
+
},
|
|
2588
|
+
)
|
|
2589
|
+
.catch(reject);
|
|
2548
2590
|
});
|
|
2549
2591
|
}
|
|
2550
2592
|
|
|
2593
|
+
/**
|
|
2594
|
+
* Reads a file and strictly validates its content as proper JSON using FileReader.
|
|
2595
|
+
*
|
|
2596
|
+
* Performs several checks to ensure the file contains valid, parsable JSON data.
|
|
2597
|
+
*
|
|
2598
|
+
* @param {File} file - The file to be read. It must contain valid JSON as plain text.
|
|
2599
|
+
* @returns {Promise<Record<string|number|symbol, any>|any[]>} - A promise that resolves with the parsed JSON object.
|
|
2600
|
+
*
|
|
2601
|
+
* @throws {SyntaxError} - If the file content is not valid JSON syntax.
|
|
2602
|
+
* @throws {TypeError} - If the result is not a string or does not represent a JSON value.
|
|
2603
|
+
* @throws {Error} - If the result is empty or structurally invalid as JSON.
|
|
2604
|
+
* @throws {DOMException} - If the FileReader fails to read the file.
|
|
2605
|
+
*/
|
|
2606
|
+
function readJsonBlob(file) {
|
|
2607
|
+
return new Promise((resolve, reject) =>
|
|
2608
|
+
readFileBlob(file, 'readAsText')
|
|
2609
|
+
.then((data) => {
|
|
2610
|
+
if (typeof data !== 'string') throw new TypeError('Expected file content to be a string.');
|
|
2611
|
+
const trimmed = data.trim();
|
|
2612
|
+
if (trimmed.length === 0) throw new Error('File is empty or contains only whitespace.');
|
|
2613
|
+
const parsed = JSON.parse(trimmed);
|
|
2614
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
2615
|
+
throw new Error('Parsed content is not a valid JSON object or array.');
|
|
2616
|
+
resolve(parsed);
|
|
2617
|
+
})
|
|
2618
|
+
.catch(reject),
|
|
2619
|
+
);
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2551
2622
|
/**
|
|
2552
2623
|
* Saves a JSON object as a downloadable file.
|
|
2553
2624
|
* @param {string} filename
|
|
@@ -2650,7 +2721,8 @@ async function fetchJson(
|
|
|
2650
2721
|
|
|
2651
2722
|
const data = await response.json();
|
|
2652
2723
|
|
|
2653
|
-
if (!
|
|
2724
|
+
if (!Array.isArray(data) && !isJsonObject(data))
|
|
2725
|
+
throw new Error('Received invalid data instead of valid JSON.');
|
|
2654
2726
|
|
|
2655
2727
|
return data;
|
|
2656
2728
|
} catch (err) {
|
|
@@ -2752,19 +2824,34 @@ const getHtmlElPadding = (el) => {
|
|
|
2752
2824
|
|
|
2753
2825
|
/**
|
|
2754
2826
|
* Installs a script that toggles CSS classes on a given element
|
|
2755
|
-
* based on the page's visibility or focus state
|
|
2827
|
+
* based on the page's visibility or focus state, and optionally
|
|
2828
|
+
* triggers callbacks on visibility changes.
|
|
2756
2829
|
*
|
|
2757
2830
|
* @param {Object} [settings={}]
|
|
2758
2831
|
* @param {HTMLElement} [settings.element=document.body] - The element to receive visibility classes.
|
|
2759
2832
|
* @param {string} [settings.hiddenClass='windowHidden'] - CSS class applied when the page is hidden.
|
|
2760
2833
|
* @param {string} [settings.visibleClass='windowVisible'] - CSS class applied when the page is visible.
|
|
2834
|
+
* @param {() => void} [settings.onVisible] - Callback called when page becomes visible.
|
|
2835
|
+
* @param {() => void} [settings.onHidden] - Callback called when page becomes hidden.
|
|
2761
2836
|
* @returns {() => void} Function that removes all installed event listeners.
|
|
2837
|
+
* @throws {TypeError} If any provided setting is invalid.
|
|
2762
2838
|
*/
|
|
2763
2839
|
function installWindowHiddenScript({
|
|
2764
2840
|
element = document.body,
|
|
2765
2841
|
hiddenClass = 'windowHidden',
|
|
2766
2842
|
visibleClass = 'windowVisible',
|
|
2843
|
+
onVisible,
|
|
2844
|
+
onHidden,
|
|
2767
2845
|
} = {}) {
|
|
2846
|
+
if (!(element instanceof HTMLElement))
|
|
2847
|
+
throw new TypeError(`"element" must be an instance of HTMLElement.`);
|
|
2848
|
+
if (typeof hiddenClass !== 'string') throw new TypeError(`"hiddenClass" must be a string.`);
|
|
2849
|
+
if (typeof visibleClass !== 'string') throw new TypeError(`"visibleClass" must be a string.`);
|
|
2850
|
+
if (onVisible !== undefined && typeof onVisible !== 'function')
|
|
2851
|
+
throw new TypeError(`"onVisible" must be a function if provided.`);
|
|
2852
|
+
if (onHidden !== undefined && typeof onHidden !== 'function')
|
|
2853
|
+
throw new TypeError(`"onHidden" must be a function if provided.`);
|
|
2854
|
+
|
|
2768
2855
|
const removeClass = () => {
|
|
2769
2856
|
element.classList.remove(hiddenClass);
|
|
2770
2857
|
element.classList.remove(visibleClass);
|
|
@@ -2772,8 +2859,6 @@ function installWindowHiddenScript({
|
|
|
2772
2859
|
|
|
2773
2860
|
/** @type {string|null} */
|
|
2774
2861
|
let hiddenProp = null;
|
|
2775
|
-
/** @type {(this: any, evt: Event) => void} */
|
|
2776
|
-
let handler;
|
|
2777
2862
|
|
|
2778
2863
|
const visibilityEvents = [
|
|
2779
2864
|
'visibilitychange',
|
|
@@ -2791,7 +2876,8 @@ function installWindowHiddenScript({
|
|
|
2791
2876
|
}
|
|
2792
2877
|
}
|
|
2793
2878
|
|
|
2794
|
-
|
|
2879
|
+
/** @type {(this: any, evt: Event) => void} */
|
|
2880
|
+
const handler = function (evt) {
|
|
2795
2881
|
removeClass();
|
|
2796
2882
|
|
|
2797
2883
|
const type = evt?.type;
|
|
@@ -2803,10 +2889,18 @@ function installWindowHiddenScript({
|
|
|
2803
2889
|
|
|
2804
2890
|
if (visibleEvents.includes(type)) {
|
|
2805
2891
|
element.classList.add(visibleClass);
|
|
2892
|
+
onVisible?.();
|
|
2806
2893
|
} else if (hiddenEvents.includes(type)) {
|
|
2807
2894
|
element.classList.add(hiddenClass);
|
|
2895
|
+
onHidden?.();
|
|
2808
2896
|
} else {
|
|
2809
|
-
|
|
2897
|
+
if (isHidden) {
|
|
2898
|
+
element.classList.add(hiddenClass);
|
|
2899
|
+
onHidden?.();
|
|
2900
|
+
} else {
|
|
2901
|
+
element.classList.add(visibleClass);
|
|
2902
|
+
onVisible?.();
|
|
2903
|
+
}
|
|
2810
2904
|
}
|
|
2811
2905
|
};
|
|
2812
2906
|
|
|
@@ -2843,6 +2937,7 @@ function installWindowHiddenScript({
|
|
|
2843
2937
|
};
|
|
2844
2938
|
}
|
|
2845
2939
|
|
|
2940
|
+
// Trigger initial state
|
|
2846
2941
|
// @ts-ignore
|
|
2847
2942
|
const simulatedEvent = new Event(hiddenProp && document[hiddenProp] ? 'blur' : 'focus');
|
|
2848
2943
|
handler(simulatedEvent);
|