@vettvangur/vanilla 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/README.md ADDED
File without changes
@@ -0,0 +1,587 @@
1
+ import path from 'node:path';
2
+ import process from 'node:process';
3
+ import dotenv from 'dotenv';
4
+
5
+ /**
6
+ * @memberof @vettvangur/react
7
+ */
8
+
9
+
10
+ /**
11
+ * Capitalizes the first letter of a given string.
12
+ *
13
+ * @function capitalize
14
+ * @param {string|null|undefined} value - The string to capitalize. If null or undefined, returns an empty string.
15
+ * @param {boolean} [lowerRest=false] - If true, converts the rest of the string to lowercase.
16
+ * @returns {string} The capitalized string, or an empty string if the input is falsy.
17
+ *
18
+ * @example capitalize(string)
19
+ */
20
+ function capitalize(value, lowerRest = false) {
21
+ if (!value) {
22
+ return '';
23
+ }
24
+ const firstChar = value[0].toUpperCase();
25
+ const rest = lowerRest ? value.slice(1).toLowerCase() : value.slice(1);
26
+ return firstChar + rest;
27
+ }
28
+
29
+ /**
30
+ * Retrieves a translated string from a dictionary based on a given key and culture.
31
+ *
32
+ * @function dictionary
33
+ * @param {string} key - The key for the translation.
34
+ * @param {Array} data - The array of translation objects.
35
+ * @param {string} [culture='is-IS'] - The language code to search for (default: 'is-IS').
36
+ * @returns {string} The translated string if found, otherwise '[Translation not found]'.
37
+ *
38
+ * @example
39
+ * dictionary(key)
40
+ */
41
+ function dictionary(key, data, culture = 'is-IS') {
42
+ const notFound = '[Translation not found]';
43
+ for (const item of data) {
44
+ if (item.itemKey === key) {
45
+ const translation = item.values.find(b => b.language === culture)?.value;
46
+ return translation || notFound;
47
+ }
48
+ }
49
+ return notFound;
50
+ }
51
+
52
+ /**
53
+ * Fetches data from a given URL with customizable options and error handling.
54
+ *
55
+ * @async
56
+ * @function fetcher
57
+ * @param {Object} params - The parameters for the fetcher function.
58
+ * @param {string} params.url - The URL to fetch data from.
59
+ * @param {RequestInit|null} [params.options={}] - Fetch API options (headers, method, body, etc.).
60
+ * @param {boolean} [params.throwOnError=false] - Whether to throw an error on non-OK HTTP responses.
61
+ * @returns {Promise<Object|null>} The parsed JSON response or null if an error occurs.
62
+ * @throws Will throw an error if `throwOnError` is true and the fetch fails or returns a non-OK status.
63
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
64
+ *
65
+ * @example
66
+ * const getData = await fetcher({
67
+ * url,
68
+ * {
69
+ * method: 'POST',
70
+ * headers: {
71
+ * 'Content-Type': 'application/json'
72
+ * }
73
+ * }
74
+ * })
75
+ */
76
+
77
+ async function fetcher({
78
+ url,
79
+ options = null,
80
+ throwOnError = false
81
+ }) {
82
+ const errorMessage = `[fetcher :: fetcher] Error: ${url}`;
83
+ try {
84
+ const request = await fetch(url, options);
85
+ if (!request.ok) {
86
+ const errorDetails = await request.text();
87
+ const fullErrorMessage = `${errorMessage} - Status: ${request.status}, ${request.statusText}, Details: ${errorDetails}`;
88
+ console.error(fullErrorMessage);
89
+ if (throwOnError) {
90
+ throw new Error(fullErrorMessage);
91
+ }
92
+
93
+ // Return null instead of undefined on error for predictable handling
94
+ return null;
95
+ }
96
+ return await request.json();
97
+ } catch (error) {
98
+ const fullErrorMessage = `${errorMessage} - ${error}`;
99
+ console.error(fullErrorMessage);
100
+ if (throwOnError) {
101
+ throw error;
102
+ }
103
+
104
+ // Return null instead of undefined on error for predictable handling
105
+ return null;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Loads environment variables from `.env` and `.env.[NODE_ENV]` files.
111
+ *
112
+ * This function first loads the base `.env` file, then loads an environment-specific file
113
+ * based on `NODE_ENV` (e.g., `.env.production`, `.env.development`). If `NODE_ENV` is not set,
114
+ * it defaults to `'development'`.
115
+ *
116
+ * @async
117
+ * @function loadEnvFiles
118
+ * @param {boolean} [showMessage=true] - Whether to log a message indicating which env file was loaded.
119
+ * @throws {Error} Throws an error if loading the environment-specific file fails.
120
+ * @returns {Promise<void>} Resolves when environment files are loaded.
121
+ */
122
+ async function loadEnvFiles(showMessage = true) {
123
+ // Always load the .env file first
124
+ const envFilePath = path.resolve(process.cwd(), '.env');
125
+ const baseResult = dotenv.config({
126
+ path: envFilePath
127
+ });
128
+ if (baseResult.error) {
129
+ console.warn(`Warning: Failed to load environment variables from .env: ${baseResult.error.message}`);
130
+ }
131
+ const env = process.env.NODE_ENV || 'development'; // Default to 'development' if NODE_ENV is not set
132
+ const envFile = `.env.${env}`;
133
+
134
+ // Load the specified environment file
135
+ const result = dotenv.config({
136
+ path: path.resolve(process.cwd(), envFile)
137
+ });
138
+ if (result.error) {
139
+ throw new Error(`Failed to load environment variables from ${envFile}: ${result.error.message}`);
140
+ }
141
+ if (showMessage) {
142
+ console.log(`Loaded environment variables from ${envFile}\n`);
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Checks if a given string is a valid regular expression pattern.
148
+ *
149
+ * @ignore
150
+ * @param {string} pattern - The regex pattern to validate.
151
+ * @returns {boolean} Returns `true` if the pattern is a valid regex, otherwise `false`.
152
+ */
153
+ function isValidRegex(pattern) {
154
+ try {
155
+ new RegExp(pattern);
156
+ return true;
157
+ } catch {
158
+ return false;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Utility object for regex operations.
164
+ */
165
+ const regexr = {
166
+ /**
167
+ * Tests whether a given value matches a regex pattern.
168
+ *
169
+ * @param {Object} params - Parameters for testing regex.
170
+ * @param {string} params.value - The string to test against the regex.
171
+ * @param {string} params.pattern - The regex pattern to match.
172
+ * @param {string} [params.flags='g'] - Regex flags (default: 'g').
173
+ * @returns {boolean} Returns `true` if the value matches the pattern, otherwise `false`.
174
+ *
175
+ * @example
176
+ * const isMatch = regexr.match(pattern)
177
+ * console.log(isMatch) // return true or false
178
+ */
179
+ test({
180
+ value,
181
+ pattern,
182
+ flags = 'g'
183
+ }) {
184
+ if (!value || !pattern || !isValidRegex(pattern)) {
185
+ return false;
186
+ }
187
+ const regexp = new RegExp(pattern, flags);
188
+ return regexp.test(value);
189
+ },
190
+ /**
191
+ * Matches a given value against a regex pattern and returns the matches.
192
+ *
193
+ * @param {Object} params - Parameters for matching regex.
194
+ * @param {string} params.value - The string to match against the regex.
195
+ * @param {string} params.pattern - The regex pattern to match.
196
+ * @param {string} [params.flags='g'] - Regex flags (default: 'g').
197
+ * @returns {Array<string>|null} Returns an array of matches, or `null` if no match is found.
198
+ */
199
+ match({
200
+ value,
201
+ pattern,
202
+ flags = 'g'
203
+ }) {
204
+ if (!value || !pattern || !isValidRegex(pattern)) {
205
+ return null;
206
+ }
207
+ const regexp = new RegExp(pattern, flags);
208
+ return value.match(regexp);
209
+ }
210
+ };
211
+
212
+ /**
213
+ * Recursively flattens a nested object into a single-level object with prefixed keys.
214
+ *
215
+ * @param {Object} obj - The object to flatten.
216
+ * @param {string} [prefix=''] - The prefix to append to keys for nested properties.
217
+ * @returns {Object} A new object with flattened keys.
218
+ *
219
+ * @example
220
+ * const nested = { sm: '640px', md: { base: '768px', xl: '1024px' } };
221
+ * const result = flatten(nested);
222
+ * console.log(result); // { sm: '640px', md-base: '768px', md-xl: '1024px' }
223
+ */
224
+ const flatten = (obj, prefix = '') => {
225
+ try {
226
+ if (!obj || typeof obj !== 'object') {
227
+ console.error('❌ FLATTEN ERROR: Received invalid data:', obj);
228
+ return {};
229
+ }
230
+ return Object.keys(obj).reduce((acc, key) => {
231
+ const fullKey = key === 'DEFAULT' ? prefix : prefix ? `${prefix}-${key}` : key;
232
+ if (typeof obj[key] === 'object' && key !== 'DEFAULT') {
233
+ Object.assign(acc, flatten(obj[key], fullKey));
234
+ } else if (typeof obj[key] === 'string') {
235
+ acc[fullKey] = obj[key];
236
+ } else {
237
+ console.warn('⚠️ Skipping Invalid Key:', key, 'Value:', obj[key]);
238
+ }
239
+ return acc;
240
+ }, {});
241
+ } catch (error) {
242
+ console.error('🚨 Flatten Function Error:', error);
243
+ return {}; // Prevents Tailwind from crashing
244
+ }
245
+ };
246
+
247
+ /**
248
+ * Retrieves a template based on an alias, capitalizing the alias before lookup.
249
+ *
250
+ * @param {string} alias - The alias to search for in the templates object.
251
+ * @param {Object} templates - An object containing templates indexed by alias.
252
+ * @returns {*} - The matching template, or the 'Subpage' template if not found.
253
+ *
254
+ * @example
255
+ * const template = getTemplate('name', templates);
256
+ *
257
+ */
258
+ function getTemplate(alias, templates) {
259
+ const template = templates[capitalize(alias)];
260
+ if (!template) {
261
+ return templates.Subpage;
262
+ }
263
+ return template;
264
+ }
265
+
266
+ /**
267
+ * Checks if a given value is empty (i.e., '', undefined, or null).
268
+ *
269
+ * @param {*} value - The value to check.
270
+ * @returns {boolean} - Returns `true` if the value is empty, otherwise `false`.
271
+ *
272
+ * @example
273
+ * console.log(isEmpty('')); // true
274
+ * console.log(isEmpty(null)); // true
275
+ * console.log(isEmpty('hello')); // false
276
+ */
277
+ function isEmpty(value) {
278
+ if (value === '' || value === undefined || value === null) {
279
+ return true;
280
+ }
281
+ return false;
282
+ }
283
+ /**
284
+ * Compiles a module map, converting a selector-importer pair into a `Map` for efficient lookups.
285
+ *
286
+ * @function createModuleMap
287
+ * @param {Object} moduleMap - The module map where selectors are keys and dynamic imports are values.
288
+ * @returns {Map} A Map where selectors are keys and dynamic import functions are values.
289
+ *
290
+ * @example
291
+ * const moduleMap = {
292
+ * '.selector': () => import('components/component'),
293
+ * '.selector-1, .selector-2, .selector-3': () => import('components/component')
294
+ * };
295
+ * const compiledMap = createModuleMap(moduleMap);
296
+ */
297
+ function createModuleMap(moduleMap) {
298
+ const compiledModuleMap = new Map();
299
+ Object.entries(moduleMap).forEach(([selector, importer]) => {
300
+ selector.split(',').forEach(subSelector => {
301
+ compiledModuleMap.set(subSelector.trim(), importer);
302
+ });
303
+ });
304
+ return compiledModuleMap;
305
+ }
306
+
307
+ /**
308
+ * Adds a preload class to the body element after a short delay.
309
+ * Also hides the preload animation after another delay.
310
+ *
311
+ * @function preloadTimeout
312
+ *
313
+ * @example
314
+ * preloadTimeout(); // Adds 'loaded' class after a short delay, hides preload animation after another delay.
315
+ */
316
+ function preloadTimeout() {
317
+ const body = document.querySelector('.body');
318
+ if (!body) {
319
+ return;
320
+ }
321
+ setTimeout(() => body.classList.add('loaded'), 100);
322
+ setTimeout(() => {
323
+ body.classList.add('preload--hidden');
324
+ body.classList.remove('preload--transitions');
325
+ }, 400);
326
+ }
327
+
328
+ /**
329
+ * Lazy loads modules based on a selector in the provided module map using the IntersectionObserver API.
330
+ *
331
+ * @function lazyModules
332
+ * @param {Map} moduleMap - A map of selectors to dynamic import functions.
333
+ * @param {Element} [root=document] - The root element to query for selectors (defaults to `document`).
334
+ *
335
+ * @example
336
+ * const moduleMap = new Map([
337
+ * ['.selector', () => import('components/component')],
338
+ * ['.selector-1, .selector-2, .selector-3', () => import('components/compomnent')],
339
+ * ]);
340
+ * lazyModules(moduleMap); // Lazy load the components when their selectors are in view.
341
+ */
342
+ // @ts-ignore
343
+ function lazyModules(moduleMap, root = document) {
344
+ const observer = new IntersectionObserver(entries => {
345
+ entries.forEach(entry => {
346
+ if (entry.isIntersecting) {
347
+ const target = entry.target;
348
+ const matchedImporters = Array.from(moduleMap.entries()).filter(([selector]) => target.matches(selector)).map(([_, importer]) => importer);
349
+ if (matchedImporters.length > 0) {
350
+ Promise.allSettled(matchedImporters.map(importer => importer())).then(results => {
351
+ results.forEach(result => {
352
+ if (result.status === 'fulfilled' && result.value?.default?.init) {
353
+ result.value.default.init(target);
354
+ }
355
+ });
356
+ }).catch(console.error);
357
+ }
358
+ observer.unobserve(target);
359
+ }
360
+ });
361
+ });
362
+ moduleMap.forEach((_, selector) => {
363
+ root.querySelectorAll(selector).forEach(element => observer.observe(element));
364
+ });
365
+ }
366
+
367
+ /**
368
+ * Initializes core scripts with an optional callback function.
369
+ *
370
+ * @function initCoreScripts
371
+ * @param {Function} callback - The callback function to initialize core scripts.
372
+ *
373
+ * @example
374
+ * initCoreScripts(() => { console.log('Core scripts initialized'); });
375
+ */
376
+ function initCoreScripts(callback = null) {
377
+ import(path.resolve(process.cwd(), 'scripts', 'core', 'prefetch.ts')).then(module => module.default.init());
378
+ import(path.resolve(process.cwd(), 'scripts', 'core', 'recaptcha.ts')).then(module => module.default.init());
379
+ if (callback) {
380
+ callback();
381
+ }
382
+ }
383
+
384
+ /**
385
+ * Handles the `DOMContentLoaded` event to initialize core scripts and lazy load modules.
386
+ *
387
+ * @function onDomReady
388
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
389
+ * @param {Function} initCoreScriptsCallback - The callback function to initialize core scripts.
390
+ * @param {Function} [callback] - An optional callback to execute during `DOMContentLoaded` event handling.
391
+ *
392
+ * @example
393
+ * onDomReady(moduleMap, () => { console.log('Core scripts initialized'); }, () => { console.log('DOM content loaded'); });
394
+ */
395
+ function onDomReady(moduleMap, initCoreScriptsCallback, callback = null) {
396
+ document.addEventListener('DOMContentLoaded', e => {
397
+ preloadTimeout();
398
+ initCoreScripts(initCoreScriptsCallback);
399
+ lazyModules(moduleMap);
400
+ if (callback) {
401
+ callback(e);
402
+ }
403
+ });
404
+ }
405
+
406
+ /**
407
+ * Handles the `popstate` event to initialize core scripts and lazy load modules.
408
+ *
409
+ * @function onPopstate
410
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
411
+ * @param {Function} initCoreScriptsCallback - The callback function to initialize core scripts.
412
+ * @param {Function} [callback] - An optional callback to execute during `popstate` event handling.
413
+ *
414
+ * @example
415
+ * onPopstate(moduleMap, () => { console.log('Core scripts initialized'); }, () => { console.log('Popstate event triggered'); });
416
+ */
417
+ function onPopstate(moduleMap, initCoreScriptsCallback, callback = null) {
418
+ window.addEventListener('popstate', e => {
419
+ initCoreScripts(initCoreScriptsCallback);
420
+ lazyModules(moduleMap);
421
+ if (callback) {
422
+ callback(e);
423
+ }
424
+ });
425
+ }
426
+
427
+ /**
428
+ * Handles the `htmx:beforeSwap` event to perform actions before HTMX swaps.
429
+ *
430
+ * @function onHtmxBeforeSwap
431
+ * @param {Function} callback - The callback function to execute during `htmx:beforeSwap` event handling.
432
+ *
433
+ * @example
434
+ * onHtmxBeforeSwap((e) => { console.log('HTMX before swap:', e); });
435
+ */
436
+ function onHtmxBeforeSwap(callback = null) {
437
+ document.body.addEventListener('htmx:beforeSwap', e => {
438
+ document.querySelectorAll('.button--loading')?.forEach(btn => btn.classList.remove('button--loading'));
439
+ if (callback) {
440
+ callback(e);
441
+ }
442
+ setTimeout(() => window.scrollTo({
443
+ top: 0,
444
+ behavior: 'smooth'
445
+ }), 100);
446
+ });
447
+ }
448
+
449
+ /**
450
+ * Handles the `htmx:afterSwap` event to initialize core scripts and lazy load modules.
451
+ *
452
+ * @function onHtmxAfterSwap
453
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
454
+ * @param {Function} [callback] - An optional callback to execute during `htmx:afterSwap` event handling.
455
+ *
456
+ * @example
457
+ * onHtmxAfterSwap(moduleMap, () => { console.log('HTMX after swap completed'); });
458
+ */
459
+ function onHtmxAfterSwap(moduleMap, callback = null) {
460
+ document.body.addEventListener('htmx:afterSwap', e => {
461
+ // TODO: is initCoreScripts needed here?
462
+ // initCoreScripts()
463
+ lazyModules(moduleMap);
464
+ if (callback) {
465
+ callback(e);
466
+ }
467
+ });
468
+ }
469
+
470
+ /**
471
+ * Handles the `htmx:afterSettle` event to initialize core scripts and lazy load modules.
472
+ *
473
+ * @function onHtmxAfterSettle
474
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
475
+ * @param {Function} initCoreScriptsCallback - The callback function to initialize core scripts.
476
+ * @param {Function} [callback] - An optional callback to execute during `htmx:afterSettle` event handling.
477
+ *
478
+ * @example
479
+ * onHtmxAfterSettle(moduleMap, () => { console.log('Core scripts initialized'); }, () => { console.log('HTMX settle completed'); });
480
+ */
481
+ function onHtmxAfterSettle(moduleMap, initCoreScriptsCallback, callback = null) {
482
+ document.body.addEventListener('htmx:afterSettle', e => {
483
+ initCoreScripts(initCoreScriptsCallback);
484
+ lazyModules(moduleMap);
485
+ if (callback) {
486
+ callback(e);
487
+ }
488
+ });
489
+ }
490
+
491
+ // /**
492
+ // * Handles the `htmx:responseErrnr` event to display error messages based on the HTTP status code.
493
+ // *
494
+ // * @function onHtmxResponseError
495
+ // * @param {Function} [callback] - An optional callback function to execute during handling of the error.
496
+ // * @ignore
497
+ // * @example
498
+ // * onHtmxResponseError(toaster, (e) => {
499
+ // * console.log('Custom error handling:', e);
500
+ // * });
501
+ // */
502
+ // export function onHtmxResponseError(callback) {
503
+ // if (!toaster) {
504
+ // console.error('[@vettvangur/vanilla | onHtmxResponseError | No toaster provided.')
505
+ // return
506
+ // }
507
+
508
+ // document.addEventListener('htmx:responseError', (e) => {
509
+ // e.stopImmediatePropagation()
510
+ // // @ts-ignore
511
+ // const statusCode = e?.detail?.xhr?.status
512
+
513
+ // if (statusCode === 404) {
514
+ // toaster.error('Page not found (404)')
515
+ // } else if (statusCode === 500) {
516
+ // toaster.error('Server error (500)')
517
+ // } else {
518
+ // toaster.error(`HTMX error with status code: ${statusCode}`)
519
+ // }
520
+
521
+ // e.stopPropagation()
522
+
523
+ // if (callback) {
524
+ // callback(e)
525
+ // }
526
+ // })
527
+ // }
528
+
529
+ /**
530
+ * Handles click outside and ESC key events to trigger a callback and close open elements with the `open` class.
531
+ *
532
+ * @function clickOutside
533
+ * @param {Function} callback - The callback function to be executed when a click outside or ESC key event occurs.
534
+ * @description
535
+ * This function listens for `click` events and `keyup` events:
536
+ * - When a click outside of `.co-trigger` is detected, the provided callback is executed.
537
+ * - When the `ESC` key is pressed, the provided callback is executed, and any open elements (with the `open` class) are closed.
538
+ *
539
+ * @example
540
+ * ClickOutside(() => {
541
+ * console.log('Click outside or ESC pressed!');
542
+ * });
543
+ */
544
+ function clickOutside(callback) {
545
+ // Clicks
546
+ document.addEventListener('click', e => {
547
+ const target = e.target;
548
+ const classTargets = document.querySelectorAll('.co-el');
549
+
550
+ // Check if the click target is inside an element with class `.co-trigger`
551
+ // @ts-ignore
552
+ if (target.closest('.co-trigger')) {
553
+ return;
554
+ }
555
+
556
+ // Execute the callback function
557
+ callback(e);
558
+
559
+ // Close open elements
560
+ Array.prototype.forEach.call(classTargets, item => {
561
+ if (item.classList.contains('open')) {
562
+ item.classList.remove('open');
563
+ }
564
+ });
565
+ }, false);
566
+
567
+ // ESC key
568
+ document.addEventListener('keyup', e => {
569
+ const openElements = document.querySelectorAll('.open');
570
+ const keys = e.keyCode || e.which;
571
+
572
+ // If ESC key (key code 27) is pressed
573
+ if (keys === 27) {
574
+ // Execute the callback function
575
+ callback(e);
576
+
577
+ // Close open elements
578
+ Array.prototype.forEach.call(openElements, item => {
579
+ if (item.classList.contains('open')) {
580
+ item.classList.remove('open');
581
+ }
582
+ });
583
+ }
584
+ });
585
+ }
586
+
587
+ export { capitalize, clickOutside, createModuleMap, dictionary, fetcher, flatten, getTemplate, initCoreScripts, isEmpty, lazyModules, loadEnvFiles, onDomReady, onHtmxAfterSettle, onHtmxAfterSwap, onHtmxBeforeSwap, onPopstate, preloadTimeout, regexr };
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Capitalizes the first letter of a given string.
3
+ *
4
+ * @function capitalize
5
+ * @param {string|null|undefined} value - The string to capitalize. If null or undefined, returns an empty string.
6
+ * @param {boolean} [lowerRest=false] - If true, converts the rest of the string to lowercase.
7
+ * @returns {string} The capitalized string, or an empty string if the input is falsy.
8
+ *
9
+ * @example capitalize(string)
10
+ */
11
+ export function capitalize(value: string | null | undefined, lowerRest?: boolean): string;
12
+ /**
13
+ * Retrieves a translated string from a dictionary based on a given key and culture.
14
+ *
15
+ * @function dictionary
16
+ * @param {string} key - The key for the translation.
17
+ * @param {Array} data - The array of translation objects.
18
+ * @param {string} [culture='is-IS'] - The language code to search for (default: 'is-IS').
19
+ * @returns {string} The translated string if found, otherwise '[Translation not found]'.
20
+ *
21
+ * @example
22
+ * dictionary(key)
23
+ */
24
+ export function dictionary(key: string, data: any[], culture?: string): string;
25
+ /**
26
+ * Fetches data from a given URL with customizable options and error handling.
27
+ *
28
+ * @async
29
+ * @function fetcher
30
+ * @param {Object} params - The parameters for the fetcher function.
31
+ * @param {string} params.url - The URL to fetch data from.
32
+ * @param {RequestInit|null} [params.options={}] - Fetch API options (headers, method, body, etc.).
33
+ * @param {boolean} [params.throwOnError=false] - Whether to throw an error on non-OK HTTP responses.
34
+ * @returns {Promise<Object|null>} The parsed JSON response or null if an error occurs.
35
+ * @throws Will throw an error if `throwOnError` is true and the fetch fails or returns a non-OK status.
36
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
37
+ *
38
+ * @example
39
+ * const getData = await fetcher({
40
+ * url,
41
+ * {
42
+ * method: 'POST',
43
+ * headers: {
44
+ * 'Content-Type': 'application/json'
45
+ * }
46
+ * }
47
+ * })
48
+ */
49
+ export function fetcher({ url, options, throwOnError }: {
50
+ url: string;
51
+ options?: RequestInit | null;
52
+ throwOnError?: boolean;
53
+ }): Promise<any | null>;
54
+ /**
55
+ * Loads environment variables from `.env` and `.env.[NODE_ENV]` files.
56
+ *
57
+ * This function first loads the base `.env` file, then loads an environment-specific file
58
+ * based on `NODE_ENV` (e.g., `.env.production`, `.env.development`). If `NODE_ENV` is not set,
59
+ * it defaults to `'development'`.
60
+ *
61
+ * @async
62
+ * @function loadEnvFiles
63
+ * @param {boolean} [showMessage=true] - Whether to log a message indicating which env file was loaded.
64
+ * @throws {Error} Throws an error if loading the environment-specific file fails.
65
+ * @returns {Promise<void>} Resolves when environment files are loaded.
66
+ */
67
+ export function loadEnvFiles(showMessage?: boolean): Promise<void>;
68
+ /**
69
+ * Retrieves a template based on an alias, capitalizing the alias before lookup.
70
+ *
71
+ * @param {string} alias - The alias to search for in the templates object.
72
+ * @param {Object} templates - An object containing templates indexed by alias.
73
+ * @returns {*} - The matching template, or the 'Subpage' template if not found.
74
+ *
75
+ * @example
76
+ * const template = getTemplate('name', templates);
77
+ *
78
+ */
79
+ export function getTemplate(alias: string, templates: any): any;
80
+ /**
81
+ * Checks if a given value is empty (i.e., '', undefined, or null).
82
+ *
83
+ * @param {*} value - The value to check.
84
+ * @returns {boolean} - Returns `true` if the value is empty, otherwise `false`.
85
+ *
86
+ * @example
87
+ * console.log(isEmpty('')); // true
88
+ * console.log(isEmpty(null)); // true
89
+ * console.log(isEmpty('hello')); // false
90
+ */
91
+ export function isEmpty(value: any): boolean;
92
+ /**
93
+ * Compiles a module map, converting a selector-importer pair into a `Map` for efficient lookups.
94
+ *
95
+ * @function createModuleMap
96
+ * @param {Object} moduleMap - The module map where selectors are keys and dynamic imports are values.
97
+ * @returns {Map} A Map where selectors are keys and dynamic import functions are values.
98
+ *
99
+ * @example
100
+ * const moduleMap = {
101
+ * '.selector': () => import('components/component'),
102
+ * '.selector-1, .selector-2, .selector-3': () => import('components/component')
103
+ * };
104
+ * const compiledMap = createModuleMap(moduleMap);
105
+ */
106
+ export function createModuleMap(moduleMap: any): Map<any, any>;
107
+ /**
108
+ * Adds a preload class to the body element after a short delay.
109
+ * Also hides the preload animation after another delay.
110
+ *
111
+ * @function preloadTimeout
112
+ *
113
+ * @example
114
+ * preloadTimeout(); // Adds 'loaded' class after a short delay, hides preload animation after another delay.
115
+ */
116
+ export function preloadTimeout(): void;
117
+ /**
118
+ * Lazy loads modules based on a selector in the provided module map using the IntersectionObserver API.
119
+ *
120
+ * @function lazyModules
121
+ * @param {Map} moduleMap - A map of selectors to dynamic import functions.
122
+ * @param {Element} [root=document] - The root element to query for selectors (defaults to `document`).
123
+ *
124
+ * @example
125
+ * const moduleMap = new Map([
126
+ * ['.selector', () => import('components/component')],
127
+ * ['.selector-1, .selector-2, .selector-3', () => import('components/compomnent')],
128
+ * ]);
129
+ * lazyModules(moduleMap); // Lazy load the components when their selectors are in view.
130
+ */
131
+ export function lazyModules(moduleMap: Map<any, any>, root?: Element): void;
132
+ /**
133
+ * Initializes core scripts with an optional callback function.
134
+ *
135
+ * @function initCoreScripts
136
+ * @param {Function} callback - The callback function to initialize core scripts.
137
+ *
138
+ * @example
139
+ * initCoreScripts(() => { console.log('Core scripts initialized'); });
140
+ */
141
+ export function initCoreScripts(callback?: Function): void;
142
+ /**
143
+ * Handles the `DOMContentLoaded` event to initialize core scripts and lazy load modules.
144
+ *
145
+ * @function onDomReady
146
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
147
+ * @param {Function} initCoreScriptsCallback - The callback function to initialize core scripts.
148
+ * @param {Function} [callback] - An optional callback to execute during `DOMContentLoaded` event handling.
149
+ *
150
+ * @example
151
+ * onDomReady(moduleMap, () => { console.log('Core scripts initialized'); }, () => { console.log('DOM content loaded'); });
152
+ */
153
+ export function onDomReady(moduleMap: Map<any, any>, initCoreScriptsCallback: Function, callback?: Function): void;
154
+ /**
155
+ * Handles the `popstate` event to initialize core scripts and lazy load modules.
156
+ *
157
+ * @function onPopstate
158
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
159
+ * @param {Function} initCoreScriptsCallback - The callback function to initialize core scripts.
160
+ * @param {Function} [callback] - An optional callback to execute during `popstate` event handling.
161
+ *
162
+ * @example
163
+ * onPopstate(moduleMap, () => { console.log('Core scripts initialized'); }, () => { console.log('Popstate event triggered'); });
164
+ */
165
+ export function onPopstate(moduleMap: Map<any, any>, initCoreScriptsCallback: Function, callback?: Function): void;
166
+ /**
167
+ * Handles the `htmx:beforeSwap` event to perform actions before HTMX swaps.
168
+ *
169
+ * @function onHtmxBeforeSwap
170
+ * @param {Function} callback - The callback function to execute during `htmx:beforeSwap` event handling.
171
+ *
172
+ * @example
173
+ * onHtmxBeforeSwap((e) => { console.log('HTMX before swap:', e); });
174
+ */
175
+ export function onHtmxBeforeSwap(callback?: Function): void;
176
+ /**
177
+ * Handles the `htmx:afterSwap` event to initialize core scripts and lazy load modules.
178
+ *
179
+ * @function onHtmxAfterSwap
180
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
181
+ * @param {Function} [callback] - An optional callback to execute during `htmx:afterSwap` event handling.
182
+ *
183
+ * @example
184
+ * onHtmxAfterSwap(moduleMap, () => { console.log('HTMX after swap completed'); });
185
+ */
186
+ export function onHtmxAfterSwap(moduleMap: Map<any, any>, callback?: Function): void;
187
+ /**
188
+ * Handles the `htmx:afterSettle` event to initialize core scripts and lazy load modules.
189
+ *
190
+ * @function onHtmxAfterSettle
191
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
192
+ * @param {Function} initCoreScriptsCallback - The callback function to initialize core scripts.
193
+ * @param {Function} [callback] - An optional callback to execute during `htmx:afterSettle` event handling.
194
+ *
195
+ * @example
196
+ * onHtmxAfterSettle(moduleMap, () => { console.log('Core scripts initialized'); }, () => { console.log('HTMX settle completed'); });
197
+ */
198
+ export function onHtmxAfterSettle(moduleMap: Map<any, any>, initCoreScriptsCallback: Function, callback?: Function): void;
199
+ /**
200
+ * Handles click outside and ESC key events to trigger a callback and close open elements with the `open` class.
201
+ *
202
+ * @function clickOutside
203
+ * @param {Function} callback - The callback function to be executed when a click outside or ESC key event occurs.
204
+ * @description
205
+ * This function listens for `click` events and `keyup` events:
206
+ * - When a click outside of `.co-trigger` is detected, the provided callback is executed.
207
+ * - When the `ESC` key is pressed, the provided callback is executed, and any open elements (with the `open` class) are closed.
208
+ *
209
+ * @example
210
+ * ClickOutside(() => {
211
+ * console.log('Click outside or ESC pressed!');
212
+ * });
213
+ */
214
+ export function clickOutside(callback: Function): void;
215
+ export namespace regexr {
216
+ /**
217
+ * Tests whether a given value matches a regex pattern.
218
+ *
219
+ * @param {Object} params - Parameters for testing regex.
220
+ * @param {string} params.value - The string to test against the regex.
221
+ * @param {string} params.pattern - The regex pattern to match.
222
+ * @param {string} [params.flags='g'] - Regex flags (default: 'g').
223
+ * @returns {boolean} Returns `true` if the value matches the pattern, otherwise `false`.
224
+ *
225
+ * @example
226
+ * const isMatch = regexr.match(pattern)
227
+ * console.log(isMatch) // return true or false
228
+ */
229
+ function test({ value, pattern, flags }: {
230
+ value: string;
231
+ pattern: string;
232
+ flags?: string;
233
+ }): boolean;
234
+ /**
235
+ * Matches a given value against a regex pattern and returns the matches.
236
+ *
237
+ * @param {Object} params - Parameters for matching regex.
238
+ * @param {string} params.value - The string to match against the regex.
239
+ * @param {string} params.pattern - The regex pattern to match.
240
+ * @param {string} [params.flags='g'] - Regex flags (default: 'g').
241
+ * @returns {Array<string>|null} Returns an array of matches, or `null` if no match is found.
242
+ */
243
+ function match({ value, pattern, flags }: {
244
+ value: string;
245
+ pattern: string;
246
+ flags?: string;
247
+ }): Array<string> | null;
248
+ }
249
+ export function flatten(obj: any, prefix?: string): any;
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Capitalizes the first letter of a given string.
3
+ *
4
+ * @function capitalize
5
+ * @param {string|null|undefined} value - The string to capitalize. If null or undefined, returns an empty string.
6
+ * @param {boolean} [lowerRest=false] - If true, converts the rest of the string to lowercase.
7
+ * @returns {string} The capitalized string, or an empty string if the input is falsy.
8
+ *
9
+ * @example capitalize(string)
10
+ */
11
+ export function capitalize(value: string | null | undefined, lowerRest?: boolean): string;
12
+ /**
13
+ * Retrieves a translated string from a dictionary based on a given key and culture.
14
+ *
15
+ * @function dictionary
16
+ * @param {string} key - The key for the translation.
17
+ * @param {Array} data - The array of translation objects.
18
+ * @param {string} [culture='is-IS'] - The language code to search for (default: 'is-IS').
19
+ * @returns {string} The translated string if found, otherwise '[Translation not found]'.
20
+ *
21
+ * @example
22
+ * dictionary(key)
23
+ */
24
+ export function dictionary(key: string, data: any[], culture?: string): string;
25
+ /**
26
+ * Fetches data from a given URL with customizable options and error handling.
27
+ *
28
+ * @async
29
+ * @function fetcher
30
+ * @param {Object} params - The parameters for the fetcher function.
31
+ * @param {string} params.url - The URL to fetch data from.
32
+ * @param {RequestInit|null} [params.options={}] - Fetch API options (headers, method, body, etc.).
33
+ * @param {boolean} [params.throwOnError=false] - Whether to throw an error on non-OK HTTP responses.
34
+ * @returns {Promise<Object|null>} The parsed JSON response or null if an error occurs.
35
+ * @throws Will throw an error if `throwOnError` is true and the fetch fails or returns a non-OK status.
36
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
37
+ *
38
+ * @example
39
+ * const getData = await fetcher({
40
+ * url,
41
+ * {
42
+ * method: 'POST',
43
+ * headers: {
44
+ * 'Content-Type': 'application/json'
45
+ * }
46
+ * }
47
+ * })
48
+ */
49
+ export function fetcher({ url, options, throwOnError }: {
50
+ url: string;
51
+ options?: RequestInit | null;
52
+ throwOnError?: boolean;
53
+ }): Promise<any | null>;
54
+ /**
55
+ * Loads environment variables from `.env` and `.env.[NODE_ENV]` files.
56
+ *
57
+ * This function first loads the base `.env` file, then loads an environment-specific file
58
+ * based on `NODE_ENV` (e.g., `.env.production`, `.env.development`). If `NODE_ENV` is not set,
59
+ * it defaults to `'development'`.
60
+ *
61
+ * @async
62
+ * @function loadEnvFiles
63
+ * @param {boolean} [showMessage=true] - Whether to log a message indicating which env file was loaded.
64
+ * @throws {Error} Throws an error if loading the environment-specific file fails.
65
+ * @returns {Promise<void>} Resolves when environment files are loaded.
66
+ */
67
+ export function loadEnvFiles(showMessage?: boolean): Promise<void>;
68
+ /**
69
+ * Retrieves a template based on an alias, capitalizing the alias before lookup.
70
+ *
71
+ * @param {string} alias - The alias to search for in the templates object.
72
+ * @param {Object} templates - An object containing templates indexed by alias.
73
+ * @returns {*} - The matching template, or the 'Subpage' template if not found.
74
+ *
75
+ * @example
76
+ * const template = getTemplate('name', templates);
77
+ *
78
+ */
79
+ export function getTemplate(alias: string, templates: any): any;
80
+ /**
81
+ * Checks if a given value is empty (i.e., '', undefined, or null).
82
+ *
83
+ * @param {*} value - The value to check.
84
+ * @returns {boolean} - Returns `true` if the value is empty, otherwise `false`.
85
+ *
86
+ * @example
87
+ * console.log(isEmpty('')); // true
88
+ * console.log(isEmpty(null)); // true
89
+ * console.log(isEmpty('hello')); // false
90
+ */
91
+ export function isEmpty(value: any): boolean;
92
+ /**
93
+ * Compiles a module map, converting a selector-importer pair into a `Map` for efficient lookups.
94
+ *
95
+ * @function createModuleMap
96
+ * @param {Object} moduleMap - The module map where selectors are keys and dynamic imports are values.
97
+ * @returns {Map} A Map where selectors are keys and dynamic import functions are values.
98
+ *
99
+ * @example
100
+ * const moduleMap = {
101
+ * '.selector': () => import('components/component'),
102
+ * '.selector-1, .selector-2, .selector-3': () => import('components/component')
103
+ * };
104
+ * const compiledMap = createModuleMap(moduleMap);
105
+ */
106
+ export function createModuleMap(moduleMap: any): Map<any, any>;
107
+ /**
108
+ * Adds a preload class to the body element after a short delay.
109
+ * Also hides the preload animation after another delay.
110
+ *
111
+ * @function preloadTimeout
112
+ *
113
+ * @example
114
+ * preloadTimeout(); // Adds 'loaded' class after a short delay, hides preload animation after another delay.
115
+ */
116
+ export function preloadTimeout(): void;
117
+ /**
118
+ * Lazy loads modules based on a selector in the provided module map using the IntersectionObserver API.
119
+ *
120
+ * @function lazyModules
121
+ * @param {Map} moduleMap - A map of selectors to dynamic import functions.
122
+ * @param {Element} [root=document] - The root element to query for selectors (defaults to `document`).
123
+ *
124
+ * @example
125
+ * const moduleMap = new Map([
126
+ * ['.selector', () => import('components/component')],
127
+ * ['.selector-1, .selector-2, .selector-3', () => import('components/compomnent')],
128
+ * ]);
129
+ * lazyModules(moduleMap); // Lazy load the components when their selectors are in view.
130
+ */
131
+ export function lazyModules(moduleMap: Map<any, any>, root?: Element): void;
132
+ /**
133
+ * Initializes core scripts with an optional callback function.
134
+ *
135
+ * @function initCoreScripts
136
+ * @param {Function} callback - The callback function to initialize core scripts.
137
+ *
138
+ * @example
139
+ * initCoreScripts(() => { console.log('Core scripts initialized'); });
140
+ */
141
+ export function initCoreScripts(callback?: Function): void;
142
+ /**
143
+ * Handles the `DOMContentLoaded` event to initialize core scripts and lazy load modules.
144
+ *
145
+ * @function onDomReady
146
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
147
+ * @param {Function} initCoreScriptsCallback - The callback function to initialize core scripts.
148
+ * @param {Function} [callback] - An optional callback to execute during `DOMContentLoaded` event handling.
149
+ *
150
+ * @example
151
+ * onDomReady(moduleMap, () => { console.log('Core scripts initialized'); }, () => { console.log('DOM content loaded'); });
152
+ */
153
+ export function onDomReady(moduleMap: Map<any, any>, initCoreScriptsCallback: Function, callback?: Function): void;
154
+ /**
155
+ * Handles the `popstate` event to initialize core scripts and lazy load modules.
156
+ *
157
+ * @function onPopstate
158
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
159
+ * @param {Function} initCoreScriptsCallback - The callback function to initialize core scripts.
160
+ * @param {Function} [callback] - An optional callback to execute during `popstate` event handling.
161
+ *
162
+ * @example
163
+ * onPopstate(moduleMap, () => { console.log('Core scripts initialized'); }, () => { console.log('Popstate event triggered'); });
164
+ */
165
+ export function onPopstate(moduleMap: Map<any, any>, initCoreScriptsCallback: Function, callback?: Function): void;
166
+ /**
167
+ * Handles the `htmx:beforeSwap` event to perform actions before HTMX swaps.
168
+ *
169
+ * @function onHtmxBeforeSwap
170
+ * @param {Function} callback - The callback function to execute during `htmx:beforeSwap` event handling.
171
+ *
172
+ * @example
173
+ * onHtmxBeforeSwap((e) => { console.log('HTMX before swap:', e); });
174
+ */
175
+ export function onHtmxBeforeSwap(callback?: Function): void;
176
+ /**
177
+ * Handles the `htmx:afterSwap` event to initialize core scripts and lazy load modules.
178
+ *
179
+ * @function onHtmxAfterSwap
180
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
181
+ * @param {Function} [callback] - An optional callback to execute during `htmx:afterSwap` event handling.
182
+ *
183
+ * @example
184
+ * onHtmxAfterSwap(moduleMap, () => { console.log('HTMX after swap completed'); });
185
+ */
186
+ export function onHtmxAfterSwap(moduleMap: Map<any, any>, callback?: Function): void;
187
+ /**
188
+ * Handles the `htmx:afterSettle` event to initialize core scripts and lazy load modules.
189
+ *
190
+ * @function onHtmxAfterSettle
191
+ * @param {Map} moduleMap - The compiled module map for lazy loading.
192
+ * @param {Function} initCoreScriptsCallback - The callback function to initialize core scripts.
193
+ * @param {Function} [callback] - An optional callback to execute during `htmx:afterSettle` event handling.
194
+ *
195
+ * @example
196
+ * onHtmxAfterSettle(moduleMap, () => { console.log('Core scripts initialized'); }, () => { console.log('HTMX settle completed'); });
197
+ */
198
+ export function onHtmxAfterSettle(moduleMap: Map<any, any>, initCoreScriptsCallback: Function, callback?: Function): void;
199
+ /**
200
+ * Handles click outside and ESC key events to trigger a callback and close open elements with the `open` class.
201
+ *
202
+ * @function clickOutside
203
+ * @param {Function} callback - The callback function to be executed when a click outside or ESC key event occurs.
204
+ * @description
205
+ * This function listens for `click` events and `keyup` events:
206
+ * - When a click outside of `.co-trigger` is detected, the provided callback is executed.
207
+ * - When the `ESC` key is pressed, the provided callback is executed, and any open elements (with the `open` class) are closed.
208
+ *
209
+ * @example
210
+ * ClickOutside(() => {
211
+ * console.log('Click outside or ESC pressed!');
212
+ * });
213
+ */
214
+ export function clickOutside(callback: Function): void;
215
+ export namespace regexr {
216
+ /**
217
+ * Tests whether a given value matches a regex pattern.
218
+ *
219
+ * @param {Object} params - Parameters for testing regex.
220
+ * @param {string} params.value - The string to test against the regex.
221
+ * @param {string} params.pattern - The regex pattern to match.
222
+ * @param {string} [params.flags='g'] - Regex flags (default: 'g').
223
+ * @returns {boolean} Returns `true` if the value matches the pattern, otherwise `false`.
224
+ *
225
+ * @example
226
+ * const isMatch = regexr.match(pattern)
227
+ * console.log(isMatch) // return true or false
228
+ */
229
+ function test({ value, pattern, flags }: {
230
+ value: string;
231
+ pattern: string;
232
+ flags?: string;
233
+ }): boolean;
234
+ /**
235
+ * Matches a given value against a regex pattern and returns the matches.
236
+ *
237
+ * @param {Object} params - Parameters for matching regex.
238
+ * @param {string} params.value - The string to match against the regex.
239
+ * @param {string} params.pattern - The regex pattern to match.
240
+ * @param {string} [params.flags='g'] - Regex flags (default: 'g').
241
+ * @returns {Array<string>|null} Returns an array of matches, or `null` if no match is found.
242
+ */
243
+ function match({ value, pattern, flags }: {
244
+ value: string;
245
+ pattern: string;
246
+ flags?: string;
247
+ }): Array<string> | null;
248
+ }
249
+ export function flatten(obj: any, prefix?: string): any;
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@vettvangur/vanilla",
3
+ "version": "0.0.2",
4
+ "description": "Vettvangur | Vanilla JS Utility Library",
5
+ "access": "private",
6
+ "type": "module",
7
+ "keywords": [],
8
+ "author": "Vettvangur",
9
+ "license": "ISC",
10
+ "main": "./dist/index.esm.js",
11
+ "types": "./dist/index.d.ts",
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "exports": {
16
+ ".": "./dist/index.esm.js"
17
+ },
18
+ "dependencies": {
19
+ "dotenv": "16.4.5"
20
+ },
21
+ "devDependencies": {
22
+ "@rollup/plugin-babel": "6.0.4",
23
+ "@rollup/plugin-commonjs": "28.0.1",
24
+ "@rollup/plugin-json": "6.1.0",
25
+ "@rollup/plugin-node-resolve": "15.3.0",
26
+ "@rollup/plugin-typescript": "12.1.1",
27
+ "rollup": "4.28.1",
28
+ "rollup-plugin-terser": "7.0.2",
29
+ "shx": "0.3.4",
30
+ "typescript": "^5.7.3"
31
+ },
32
+ "scripts": {
33
+ "bundle": "pnpm types & rollup -c",
34
+ "dist": "pnpm publish",
35
+ "types": "tsc -p tsconfig.json & shx mv dist/types/index.d.mts dist/types/index.d.ts"
36
+ }
37
+ }