derafu-js 0.1.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/LICENSE +21 -0
- package/README.md +3 -0
- package/package.json +47 -0
- package/src/__.js +203 -0
- package/src/derafu.js +140 -0
- package/src/form/fields.js +320 -0
- package/src/form/tables.js +140 -0
- package/src/form/validation.js +482 -0
- package/src/form.js +45 -0
- package/src/tabs.js +267 -0
- package/src/ui.js +493 -0
- package/src/utils.js +353 -0
- package/src/validation/l10n-cl.js +66 -0
package/src/ui.js
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
/*! UI functions | (c) 2025 Derafu DEV | MIT */
|
|
2
|
+
|
|
3
|
+
// Object where UI functions will be assigned.
|
|
4
|
+
const UI = {};
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Opens a new browser window as a popup.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} url - URL to open in the popup.
|
|
10
|
+
* @param {number} w - Width of the popup window.
|
|
11
|
+
* @param {number} h - Height of the popup window.
|
|
12
|
+
* @param {string} [s] - Indicates if scrollbars are shown ("yes" or "no").
|
|
13
|
+
* Default is "no".
|
|
14
|
+
* @returns {boolean} Always returns false.
|
|
15
|
+
*/
|
|
16
|
+
UI.popup = function (url, w, h, s) {
|
|
17
|
+
'use strict';
|
|
18
|
+
if (s === undefined) {
|
|
19
|
+
s = 'no';
|
|
20
|
+
}
|
|
21
|
+
window.open(
|
|
22
|
+
url,
|
|
23
|
+
'_blank',
|
|
24
|
+
`width=${w},height=${h},directories=no,location=no,menubar=no,` +
|
|
25
|
+
`scrollbars=${s},status=no,toolbar=no,resizable=no`,
|
|
26
|
+
);
|
|
27
|
+
return false;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates and returns an HTML <table> element based on the provided data.
|
|
32
|
+
*
|
|
33
|
+
* @param {Object} titles - Object containing the column titles of the table.
|
|
34
|
+
* @param {Array} data - Array of objects where each object represents a table
|
|
35
|
+
* row.
|
|
36
|
+
* @returns {HTMLElement} Constructed <table> element.
|
|
37
|
+
*/
|
|
38
|
+
UI.table = function (titles, data) {
|
|
39
|
+
'use strict';
|
|
40
|
+
const table = document.createElement('table');
|
|
41
|
+
table.className = 'table table-striped';
|
|
42
|
+
|
|
43
|
+
// Create table header.
|
|
44
|
+
const thead = document.createElement('thead');
|
|
45
|
+
const headerRow = document.createElement('tr');
|
|
46
|
+
Object.keys(titles).forEach(col => {
|
|
47
|
+
const th = document.createElement('th');
|
|
48
|
+
th.textContent = titles[col];
|
|
49
|
+
headerRow.appendChild(th);
|
|
50
|
+
});
|
|
51
|
+
thead.appendChild(headerRow);
|
|
52
|
+
table.appendChild(thead);
|
|
53
|
+
|
|
54
|
+
// Create table body.
|
|
55
|
+
const tbody = document.createElement('tbody');
|
|
56
|
+
data.forEach(rowData => {
|
|
57
|
+
const row = document.createElement('tr');
|
|
58
|
+
Object.keys(titles).forEach(col => {
|
|
59
|
+
const td = document.createElement('td');
|
|
60
|
+
td.textContent = rowData[col];
|
|
61
|
+
row.appendChild(td);
|
|
62
|
+
});
|
|
63
|
+
tbody.appendChild(row);
|
|
64
|
+
});
|
|
65
|
+
table.appendChild(tbody);
|
|
66
|
+
|
|
67
|
+
return table;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Shows a notification on screen.
|
|
72
|
+
* Uses Notyf if available; otherwise uses bootbox.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} message - Notification message.
|
|
75
|
+
* @param {number} [duration] - Notification duration in milliseconds.
|
|
76
|
+
* Default is 3000.
|
|
77
|
+
* @param {string} [icon] - Font Awesome icon to prepend to the notification.
|
|
78
|
+
* Default is 'fa-regular fa-bell'.
|
|
79
|
+
* @param {string} [type] - Notyf notification type. Default is 'success'.
|
|
80
|
+
* @returns {void}
|
|
81
|
+
*/
|
|
82
|
+
UI.notify = function (message, duration, icon, type) {
|
|
83
|
+
'use strict';
|
|
84
|
+
if (duration === undefined) {
|
|
85
|
+
duration = 3000;
|
|
86
|
+
}
|
|
87
|
+
if (icon === undefined) {
|
|
88
|
+
icon = 'fa-regular fa-bell';
|
|
89
|
+
}
|
|
90
|
+
if (type === undefined) {
|
|
91
|
+
type = 'success';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check if Notyf is available.
|
|
95
|
+
if (typeof Notyf !== 'undefined') {
|
|
96
|
+
const notyf = new Notyf();
|
|
97
|
+
notyf.open({
|
|
98
|
+
type,
|
|
99
|
+
icon: icon ? `<i class="${icon}"></i>` : false,
|
|
100
|
+
message,
|
|
101
|
+
duration,
|
|
102
|
+
position: { x: 'right', y: 'bottom' },
|
|
103
|
+
dismissible: true,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Use bootbox if Notyf is not available.
|
|
108
|
+
else {
|
|
109
|
+
icon = icon ? `<i class="${icon}"></i> ` : '';
|
|
110
|
+
const bb = bootbox.dialog({
|
|
111
|
+
message: `<div class="text-center">${icon}${message}</div>`,
|
|
112
|
+
centerVertical: true,
|
|
113
|
+
closeButton: false,
|
|
114
|
+
onEscape: true,
|
|
115
|
+
});
|
|
116
|
+
if (duration) {
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
bb.modal('hide');
|
|
119
|
+
}, duration);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Copies a string to the clipboard and shows a confirmation message.
|
|
126
|
+
*
|
|
127
|
+
* @param {string|HTMLElement} source - String or element whose content will be
|
|
128
|
+
* copied to the clipboard.
|
|
129
|
+
* @param {string} [message] - Message to show after copying.
|
|
130
|
+
* Default is 'Copied!'.
|
|
131
|
+
* @returns {void}
|
|
132
|
+
*/
|
|
133
|
+
UI.copy = function (source, message) {
|
|
134
|
+
'use strict';
|
|
135
|
+
|
|
136
|
+
// Determine text to copy.
|
|
137
|
+
let textToCopy;
|
|
138
|
+
if (typeof source === 'string') {
|
|
139
|
+
textToCopy = source;
|
|
140
|
+
} else if (source instanceof HTMLElement) {
|
|
141
|
+
if (source.tagName === 'INPUT' || source.tagName === 'TEXTAREA') {
|
|
142
|
+
textToCopy = source.value;
|
|
143
|
+
} else {
|
|
144
|
+
textToCopy = source.innerText;
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
console.error(
|
|
148
|
+
"The 'source' parameter must be a string or an HTML element.",
|
|
149
|
+
);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Define default message if not provided.
|
|
154
|
+
if (message === undefined) {
|
|
155
|
+
message = 'Copied!';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Copy text to clipboard.
|
|
159
|
+
navigator.clipboard
|
|
160
|
+
.writeText(textToCopy)
|
|
161
|
+
.then(() => {
|
|
162
|
+
UI.notify(message, 3000, 'fa-regular fa-copy');
|
|
163
|
+
})
|
|
164
|
+
.catch(err => {
|
|
165
|
+
console.error('Error copying text: ', err);
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Pastes text from the clipboard into a form element, returns it as a string,
|
|
171
|
+
* or executes a callback with the pasted text.
|
|
172
|
+
*
|
|
173
|
+
* @param {HTMLElement|function} target - Element where clipboard text will be
|
|
174
|
+
* pasted or callback that receives the text.
|
|
175
|
+
* @param {string} [message] - Message to show after pasting.
|
|
176
|
+
* Default is 'Pasted!'.
|
|
177
|
+
* @returns {void}
|
|
178
|
+
*/
|
|
179
|
+
UI.paste = function (target, message) {
|
|
180
|
+
'use strict';
|
|
181
|
+
|
|
182
|
+
// Define default message if not provided.
|
|
183
|
+
if (message === undefined) {
|
|
184
|
+
message = 'Pasted!';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Paste text from clipboard.
|
|
188
|
+
navigator.clipboard
|
|
189
|
+
.readText()
|
|
190
|
+
.then(text => {
|
|
191
|
+
if (typeof target === 'function') {
|
|
192
|
+
target(text);
|
|
193
|
+
} else if (target instanceof HTMLElement) {
|
|
194
|
+
if (
|
|
195
|
+
target.tagName === 'INPUT' ||
|
|
196
|
+
target.tagName === 'TEXTAREA'
|
|
197
|
+
) {
|
|
198
|
+
target.value = text;
|
|
199
|
+
} else {
|
|
200
|
+
target.innerText = text;
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
console.error(
|
|
204
|
+
"The 'target' parameter must be an HTML element or a function.",
|
|
205
|
+
);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
UI.notify(message, 3000, 'fa-regular fa-paste');
|
|
210
|
+
})
|
|
211
|
+
.catch(err => {
|
|
212
|
+
console.error('Error pasting text: ', err);
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Creates a form to share a message by phone.
|
|
218
|
+
*
|
|
219
|
+
* @param {string} telephone - Predefined phone number for the message.
|
|
220
|
+
* @param {string} message - Message to share.
|
|
221
|
+
* @param {string} [method] - Sharing method (default is whatsapp).
|
|
222
|
+
* @returns {void}
|
|
223
|
+
*/
|
|
224
|
+
UI.share = function (telephone, message, method) {
|
|
225
|
+
'use strict';
|
|
226
|
+
if (method === undefined) {
|
|
227
|
+
method = 'whatsapp';
|
|
228
|
+
}
|
|
229
|
+
bootbox.prompt({
|
|
230
|
+
title: `Send message via ${method}`,
|
|
231
|
+
message: '<p>Phone:</p>',
|
|
232
|
+
centerVertical: true,
|
|
233
|
+
locale: 'es',
|
|
234
|
+
backdrop: true,
|
|
235
|
+
buttons: {
|
|
236
|
+
confirm: {
|
|
237
|
+
label: 'Send',
|
|
238
|
+
className: 'btn-success',
|
|
239
|
+
},
|
|
240
|
+
cancel: {
|
|
241
|
+
className: 'btn-danger',
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
value: telephone ? telephone : '+56 9 ',
|
|
245
|
+
callback(telephone) {
|
|
246
|
+
if (telephone) {
|
|
247
|
+
telephone = telephone.replace(/\+| /g, '');
|
|
248
|
+
if (method === 'whatsapp') {
|
|
249
|
+
const url = `https://wa.me/${telephone}?text=${encodeURI(message)}`;
|
|
250
|
+
const win = window.open(url, '_blank');
|
|
251
|
+
win.focus();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Creates and displays a loading message in a modal dialog using bootbox.
|
|
260
|
+
* Useful for indicating to the user that an operation is in progress.
|
|
261
|
+
*
|
|
262
|
+
* @param {string} [message] - Message to show to the user.
|
|
263
|
+
* Default is 'Loading...'.
|
|
264
|
+
* @returns {boolean} Always returns true, indicating that the loading message
|
|
265
|
+
* was shown.
|
|
266
|
+
*/
|
|
267
|
+
UI.loading = function (message) {
|
|
268
|
+
'use strict';
|
|
269
|
+
|
|
270
|
+
// Assign default message if none was passed.
|
|
271
|
+
if (message === undefined) {
|
|
272
|
+
message = 'Loading...';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Validate that the message is a string.
|
|
276
|
+
if (typeof message !== 'string') {
|
|
277
|
+
console.error(
|
|
278
|
+
'UI.loading: The provided message is not a valid string.',
|
|
279
|
+
);
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
bootbox.dialog({
|
|
284
|
+
message: `<div class="text-center"><i class="fa fa-spin fa-spinner"></i> ${message}</div>`,
|
|
285
|
+
centerVertical: true,
|
|
286
|
+
closeButton: false,
|
|
287
|
+
onEscape: false,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
return true;
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Creates and displays an alert dialog using bootbox.
|
|
295
|
+
* Useful for showing warning or information messages to the user.
|
|
296
|
+
*
|
|
297
|
+
* @param {string} message - Message to show to the user.
|
|
298
|
+
* @param {HTMLElement} [element] - Element to focus after closing the dialog.
|
|
299
|
+
* @returns {void}
|
|
300
|
+
*/
|
|
301
|
+
UI.alert = function (message, element) {
|
|
302
|
+
'use strict';
|
|
303
|
+
if (typeof message !== 'string') {
|
|
304
|
+
console.error('UI.alert: A string was expected as message.');
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const box = bootbox.alert({
|
|
309
|
+
title: '<i class="fas fa-exclamation-circle fa-fw text-danger"></i> A problem occurred',
|
|
310
|
+
message,
|
|
311
|
+
locale: 'en',
|
|
312
|
+
backdrop: true,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
if (element instanceof HTMLElement) {
|
|
316
|
+
box.on('hidden.bs.modal', function () {
|
|
317
|
+
element.focus();
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Creates a confirmation dialog using bootbox.
|
|
324
|
+
* Useful for requesting user confirmation before performing an action, such as
|
|
325
|
+
* submitting a form.
|
|
326
|
+
*
|
|
327
|
+
* @param {HTMLElement} element - Element (form or a) that is being confirmed.
|
|
328
|
+
* @param {string} [message] - Message to show to the user.
|
|
329
|
+
* Default is 'Are you sure you want to perform this action?'.
|
|
330
|
+
* @param {string} [loading] - Loading message to show during the operation, if
|
|
331
|
+
* provided.
|
|
332
|
+
* @returns {boolean} Always returns false to prevent the default action in
|
|
333
|
+
* forms.
|
|
334
|
+
*/
|
|
335
|
+
UI.confirm = function (element, message, loading) {
|
|
336
|
+
'use strict';
|
|
337
|
+
if (!(element instanceof HTMLElement)) {
|
|
338
|
+
console.error(
|
|
339
|
+
'UI.confirm: The provided element is not a valid HTML element.',
|
|
340
|
+
);
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
if (message === undefined) {
|
|
344
|
+
message = 'Are you sure you want to perform this action?';
|
|
345
|
+
}
|
|
346
|
+
if (typeof message !== 'string') {
|
|
347
|
+
console.error(
|
|
348
|
+
'UI.confirm: The provided message is not a valid string.',
|
|
349
|
+
);
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
bootbox.confirm({
|
|
354
|
+
title: '<i class="fas fa-question-circle fa-fw text-warning"></i> Confirmation required',
|
|
355
|
+
message,
|
|
356
|
+
locale: 'es',
|
|
357
|
+
backdrop: true,
|
|
358
|
+
buttons: {
|
|
359
|
+
confirm: {
|
|
360
|
+
className: 'btn-success',
|
|
361
|
+
},
|
|
362
|
+
cancel: {
|
|
363
|
+
className: 'btn-danger',
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
callback(result) {
|
|
367
|
+
if (result) {
|
|
368
|
+
if (loading && typeof loading === 'string') {
|
|
369
|
+
UI.loading(loading);
|
|
370
|
+
}
|
|
371
|
+
if (element.tagName.toUpperCase() === 'FORM') {
|
|
372
|
+
element.removeAttribute('onsubmit');
|
|
373
|
+
if (typeof element.submit === 'function') {
|
|
374
|
+
element.submit();
|
|
375
|
+
} else if (
|
|
376
|
+
element.submit &&
|
|
377
|
+
typeof element.submit.click !== 'undefined'
|
|
378
|
+
) {
|
|
379
|
+
element.submit.click();
|
|
380
|
+
} else {
|
|
381
|
+
console.error(
|
|
382
|
+
'UI.confirm: It was not possible to perform the action on the form.',
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
} else if (element.tagName.toUpperCase() === 'A') {
|
|
386
|
+
window.location = element.href;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
return false;
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Smoothly scrolls the page to the specified element.
|
|
397
|
+
*
|
|
398
|
+
* @param {string|HTMLElement} element - Element or ID of the element to scroll
|
|
399
|
+
* to.
|
|
400
|
+
* @param {number} [header_offset] - Offset to adjust the scroll position.
|
|
401
|
+
* Default is 100.
|
|
402
|
+
* @returns {void}
|
|
403
|
+
*/
|
|
404
|
+
UI.scroll = function (element, header_offset) {
|
|
405
|
+
'use strict';
|
|
406
|
+
if (header_offset === undefined) {
|
|
407
|
+
header_offset = 100;
|
|
408
|
+
}
|
|
409
|
+
if (typeof element === 'string') {
|
|
410
|
+
element = document.getElementById(element);
|
|
411
|
+
}
|
|
412
|
+
const elementPosition = element.getBoundingClientRect().top;
|
|
413
|
+
const offsetPosition = elementPosition + window.pageYOffset - header_offset;
|
|
414
|
+
setTimeout(() => {
|
|
415
|
+
window.scrollTo({
|
|
416
|
+
top: offsetPosition,
|
|
417
|
+
behavior: 'smooth',
|
|
418
|
+
});
|
|
419
|
+
}, 300);
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Updates the browser URL without reloading, adding an anchor link. Uses the
|
|
424
|
+
* `_base` and `_request` variables to construct the URL. If they are not
|
|
425
|
+
* defined, `_base` is defined as the current URL without the hash, and
|
|
426
|
+
* `_request` is left empty.
|
|
427
|
+
*
|
|
428
|
+
* @param {string} link - Anchor to add to the current URL.
|
|
429
|
+
* @returns {void}
|
|
430
|
+
*/
|
|
431
|
+
UI.deeplink = function (link) {
|
|
432
|
+
'use strict';
|
|
433
|
+
|
|
434
|
+
const _base =
|
|
435
|
+
typeof window._base !== 'undefined'
|
|
436
|
+
? window._base
|
|
437
|
+
: location.href.split('#')[0];
|
|
438
|
+
const _request = window._request || '';
|
|
439
|
+
const url = `${_base}${_request}#${link}`;
|
|
440
|
+
history.replaceState(null, null, url);
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Prints a specific element of the page.
|
|
445
|
+
*
|
|
446
|
+
* @param {string} element_id - The ID of the element to be printed.
|
|
447
|
+
* @returns {void}
|
|
448
|
+
*/
|
|
449
|
+
UI.print = function (element_id) {
|
|
450
|
+
// Creates a temporary iframe in the document.
|
|
451
|
+
const iframe = document.createElement('iframe');
|
|
452
|
+
iframe.style.position = 'absolute';
|
|
453
|
+
iframe.style.width = '0';
|
|
454
|
+
iframe.style.height = '0';
|
|
455
|
+
iframe.style.border = '0';
|
|
456
|
+
document.body.appendChild(iframe);
|
|
457
|
+
|
|
458
|
+
// Gets the content of the element you want to print.
|
|
459
|
+
const contenido = document.getElementById(element_id).innerHTML;
|
|
460
|
+
|
|
461
|
+
// Writes the content to the iframe document.
|
|
462
|
+
// You can also include the necessary styles here.
|
|
463
|
+
iframe.contentDocument.write('<html><head><title>Print</title>');
|
|
464
|
+
const links = document.getElementsByTagName('link');
|
|
465
|
+
for (let i = 0; i < links.length; i++) {
|
|
466
|
+
const link = links[i];
|
|
467
|
+
if (link.rel === 'stylesheet') {
|
|
468
|
+
iframe.contentDocument.write(
|
|
469
|
+
`<link rel="stylesheet" href="${link.href}" type="text/css" />`,
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
iframe.contentDocument.write('</head><body>');
|
|
474
|
+
iframe.contentDocument.write(contenido);
|
|
475
|
+
iframe.contentDocument.write('</body></html>');
|
|
476
|
+
iframe.contentDocument.close();
|
|
477
|
+
|
|
478
|
+
// Wait for the iframe to load completely before printing.
|
|
479
|
+
iframe.onload = function () {
|
|
480
|
+
// Call the print method of the iframe.
|
|
481
|
+
iframe.contentWindow.print();
|
|
482
|
+
|
|
483
|
+
// Optional: Remove the iframe after printing.
|
|
484
|
+
setTimeout(function () {
|
|
485
|
+
document.body.removeChild(iframe);
|
|
486
|
+
}, 1000); // Adjust the time as necessary.
|
|
487
|
+
};
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// Export module for use in node.js.
|
|
491
|
+
if (typeof module === 'object' && module.exports) {
|
|
492
|
+
module.exports = UI;
|
|
493
|
+
}
|