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.
@@ -0,0 +1,320 @@
1
+ /*! Form fields functions | (c) 2025 Derafu DEV | MIT */
2
+
3
+ // Object where form fields functions will be assigned.
4
+ const FormFields = {};
5
+
6
+ /**
7
+ * Sends a form via POST to a specific URL.
8
+ * Can optionally open the form in a new window.
9
+ *
10
+ * @param {string} url URL where the form should be sent.
11
+ * @param {Object} data Object with the variables to pass to the form.
12
+ * @param {boolean} [newWindow] Indicates if the form should be opened in a new
13
+ * window.
14
+ * @returns {void}
15
+ */
16
+ FormFields.post = function (url, data, newWindow) {
17
+ 'use strict';
18
+ if (typeof url !== 'string' || typeof data !== 'object') {
19
+ console.error('FormFields.post: Invalid arguments.');
20
+ return;
21
+ }
22
+ if (newWindow === undefined) {
23
+ newWindow = false;
24
+ }
25
+
26
+ const form = document.createElement('form');
27
+ form.method = 'post';
28
+ form.action = url;
29
+ if (newWindow) {
30
+ form.target = '_blank';
31
+ }
32
+
33
+ Object.keys(data).forEach(key => {
34
+ const input = document.createElement('input');
35
+ input.type = 'hidden';
36
+ input.name = key;
37
+ input.value = data[key];
38
+ form.appendChild(input);
39
+ });
40
+
41
+ document.body.appendChild(form);
42
+ form.submit();
43
+ document.body.removeChild(form);
44
+ };
45
+
46
+ /**
47
+ * Allows toggling the display of a password field between hidden and visible.
48
+ *
49
+ * Changes the input type from 'password' to 'text' and vice versa, and updates
50
+ * the corresponding icon.
51
+ *
52
+ * @param {HTMLElement} button - Button used to activate the function, located
53
+ * next to the password field.
54
+ * @returns {void}
55
+ */
56
+ FormFields.showPassword = function (button) {
57
+ 'use strict';
58
+ if (!(button instanceof HTMLElement)) {
59
+ console.error(
60
+ 'FormFields.showPassword: The provided argument is not a valid HTML element.',
61
+ );
62
+ return;
63
+ }
64
+
65
+ // Try to find the related password field.
66
+ const input = button
67
+ .closest('.input-group')
68
+ .querySelector('input[type="password"], input[type="text"]');
69
+ const icon = button.querySelector('i');
70
+
71
+ // Check if the necessary elements are present.
72
+ if (!input || !icon) {
73
+ console.error('FormFields.showPassword: Input or icon not found.');
74
+ return;
75
+ }
76
+
77
+ // Toggle between showing and hiding the password.
78
+ if (input.type === 'password') {
79
+ input.type = 'text';
80
+ icon.className = 'fa-regular fa-eye-slash fa-fw';
81
+ } else {
82
+ input.type = 'password';
83
+ icon.className = 'fa-regular fa-eye fa-fw';
84
+ }
85
+ };
86
+
87
+ /**
88
+ * Allows editing the content of a text field in a larger dialog box.
89
+ * Useful for text fields with extensive content that would benefit from an
90
+ * expanded view.
91
+ *
92
+ * @param {HTMLElement} field - Text field that is desired to be edited in a
93
+ * larger dialog box.
94
+ * @returns {void}
95
+ */
96
+ FormFields.growup = function (field) {
97
+ 'use strict';
98
+ if (!(field instanceof HTMLElement)) {
99
+ console.error(
100
+ 'FormFields.growup: The provided argument is not a valid HTML element.',
101
+ );
102
+ return;
103
+ }
104
+
105
+ const maxLength = field.getAttribute('maxlength');
106
+ bootbox.prompt({
107
+ title: `Edit field: ${field.name.replace('[]', '')}`,
108
+ inputType: 'textarea',
109
+ value: field.value,
110
+ rows: 5,
111
+ backdrop: true,
112
+ centerVertical: true,
113
+ buttons: {
114
+ confirm: {
115
+ label: 'Save changes',
116
+ className: 'btn-success',
117
+ },
118
+ cancel: {
119
+ label: 'Cancel',
120
+ className: 'btn-danger',
121
+ },
122
+ },
123
+ callback(result) {
124
+ if (result !== null) {
125
+ field.value = maxLength
126
+ ? result.substring(0, maxLength)
127
+ : result;
128
+ }
129
+ },
130
+ });
131
+ };
132
+
133
+ /**
134
+ * Changes the check state of a group of checkboxes with the same name.
135
+ *
136
+ * @param {string} name - Name of the checkbox array to modify.
137
+ * @param {boolean} checked - Check state to apply to the checkboxes.
138
+ * @returns {void}
139
+ */
140
+ FormFields.checkboxesSet = function (name, checked) {
141
+ 'use strict';
142
+ const checkboxes = document.querySelectorAll(`input[name='${name}[]']`);
143
+ checkboxes.forEach(function (checkbox) {
144
+ checkbox.checked = checked;
145
+ });
146
+ };
147
+
148
+ /**
149
+ * Removes options from a select element.
150
+ *
151
+ * @param {HTMLSelectElement} selectbox - Select element to be cleared.
152
+ * @param {number} [from] - Index from which to start removing options.
153
+ * Default is 0.
154
+ * @returns {void}
155
+ */
156
+ FormFields.removeOptions = function (selectbox, from) {
157
+ 'use strict';
158
+ if (from === undefined) {
159
+ from = 0;
160
+ }
161
+ if (!(selectbox instanceof HTMLSelectElement)) {
162
+ console.error(
163
+ 'FormFields.removeOptions: The provided element is not a valid select.',
164
+ );
165
+ return;
166
+ }
167
+ while (selectbox.options.length > from) {
168
+ selectbox.remove(from);
169
+ }
170
+ };
171
+
172
+ /**
173
+ * Adds options to a select element using a list of options.
174
+ *
175
+ * @param {string} selectID - Identifier of the select element to modify.
176
+ * @param {Object} opcionesListado - Object with options indexed by a higher
177
+ * category.
178
+ * @param {string} seleccionada - Selected category from which options are
179
+ * wanted to be loaded.
180
+ * @param {number} [dejar] - Number of initial options that should be kept when
181
+ * updating. Default is 1.
182
+ * @returns {void}
183
+ */
184
+ FormFields.addOptions = function (
185
+ selectID,
186
+ opcionesListado,
187
+ seleccionada,
188
+ dejar,
189
+ ) {
190
+ 'use strict';
191
+ const select = document.getElementById(selectID);
192
+ if (!select) {
193
+ console.error(
194
+ `FormFields.addOptions: No select element found with ID '${selectID}'.`,
195
+ );
196
+ return;
197
+ }
198
+ if (dejar === undefined) {
199
+ dejar = 1;
200
+ }
201
+
202
+ FormFields.removeOptions(select, dejar);
203
+ select.disabled = opcionesListado[seleccionada] === undefined;
204
+ if (!select.disabled) {
205
+ const opciones =
206
+ opcionesListado[seleccionada] instanceof Array
207
+ ? opcionesListado[seleccionada]
208
+ : [opcionesListado[seleccionada]];
209
+ opciones.forEach(function (opcion) {
210
+ const option = document.createElement('option');
211
+ option.value = opcion.id;
212
+ option.textContent = opcion.glosa;
213
+ select.appendChild(option);
214
+ });
215
+ }
216
+ };
217
+
218
+ /**
219
+ * Processes form elements that require additional configuration or
220
+ * initialization within a specific element. This method searches for elements
221
+ * that have specific attributes, such as `data-wrapper-method` and
222
+ * `data-wrapper-config`, and applies the required configuration or
223
+ * initialization, such as activating select2 with specific options.
224
+ *
225
+ * @param {Element} element - The DOM element in which to search and process
226
+ * fields. Usually would be a row recently inserted in a table.
227
+ * @returns {void}
228
+ */
229
+ FormFields.fixFields = function (element) {
230
+ 'use strict';
231
+ // Check if window or jQuery is not defined and return immediately.
232
+ if (typeof window === 'undefined' || typeof window.jQuery !== 'function') {
233
+ console.error('window or jQuery is not available.');
234
+ return;
235
+ }
236
+ // If element is not provided, use the entire document body.
237
+ if (element === undefined) {
238
+ element = document.body;
239
+ }
240
+ // Adjust select fields through their wrapper if it exists.
241
+ const selects = element.querySelectorAll('select[data-wrapper-method]');
242
+ selects.forEach(function (select) {
243
+ const method = select.getAttribute('data-wrapper-method');
244
+ let config = select.getAttribute('data-wrapper-config');
245
+ if (method && config) {
246
+ try {
247
+ if (typeof $(select)[method] === 'function') {
248
+ config = JSON.parse(config.replace(/'/g, '"'));
249
+ if (method === 'select2') {
250
+ const width = Math.round(select.offsetWidth);
251
+ if (width) {
252
+ // Fix size prevents overflow in tables.
253
+ config['width'] = `${width}px`;
254
+ } else {
255
+ // TODO: if there's no size, some width should be
256
+ // assigned or something should be done because as
257
+ // it is, without width, it stays at 100% and
258
+ // overflows the same in these cases where there's
259
+ // no width of the select field. Using:
260
+ // let width = Math.round(select.parentElement.offsetWidth);
261
+ // didn't work either. New ideas?
262
+ }
263
+ }
264
+ // Initialize (e.g., select2) in the select field.
265
+ $(select)[method](config);
266
+ }
267
+ } catch (e) {
268
+ console.error('Error parsing data-wrapper-config: ', e);
269
+ }
270
+ }
271
+ });
272
+ // Adjust input fields that use datepicker.
273
+ const dates = element.querySelectorAll('input[data-datepicker-config]');
274
+ dates.forEach(function (date) {
275
+ // Datepicker configuration.
276
+ const defaultConfig = {
277
+ format: 'yyyy-mm-dd',
278
+ weekStart: 1,
279
+ todayBtn: 'linked',
280
+ language: 'es',
281
+ todayHighlight: true,
282
+ orientation: 'auto',
283
+ autoclose: true,
284
+ };
285
+ let elementConfig;
286
+ try {
287
+ elementConfig = date.getAttribute('data-datepicker-config')
288
+ ? JSON.parse(
289
+ date
290
+ .getAttribute('data-datepicker-config')
291
+ .replace(/'/g, '"'),
292
+ )
293
+ : {};
294
+ } catch (e) {
295
+ elementConfig = {};
296
+ console.error('Error parsing data-datepicker-config: ', e);
297
+ }
298
+ const config = { ...defaultConfig, ...elementConfig };
299
+ // Initialize datepicker according to configuration.
300
+ if (config.format === 'yyyymm' && date.value) {
301
+ const year = date.value.substring(0, 4);
302
+ const month = date.value.substring(4, 6) - 1;
303
+ $(date)
304
+ .datepicker(config)
305
+ .datepicker('update', new Date(year, month));
306
+ } else {
307
+ $(date).datepicker(config);
308
+ }
309
+ });
310
+ };
311
+
312
+ // Fix form fields automatically when loading the script.
313
+ document.addEventListener('DOMContentLoaded', function () {
314
+ FormFields.fixFields();
315
+ });
316
+
317
+ // Export module for use in node.js.
318
+ if (typeof module === 'object' && module.exports) {
319
+ module.exports = FormFields;
320
+ }
@@ -0,0 +1,140 @@
1
+ /*! Form tables functions | (c) 2025 Derafu DEV | MIT */
2
+
3
+ // Object where form tables functions will be assigned.
4
+ const FormTables = {};
5
+
6
+ /**
7
+ * Adds a new row to a table in a form.
8
+ * This function is useful for dynamically expanding forms with multiple
9
+ * entries.
10
+ *
11
+ * @param {string} id - ID of the table where fields should be added.
12
+ * @param {HTMLElement} [trigger] - Element that triggered the addition, used to
13
+ * focus the new field.
14
+ * @param {Function} [callback] - Callback function that executes after adding
15
+ * the new row.
16
+ * @returns {void}
17
+ */
18
+ FormTables.addJS = function (id, trigger, callback) {
19
+ 'use strict';
20
+ const tbody = document.getElementById(id).getElementsByTagName('tbody')[0];
21
+
22
+ // Insert the new row in the table.
23
+ tbody.insertAdjacentHTML('beforeend', window[`inputsJS_${id}`]);
24
+
25
+ // Process form elements that require adjustments.
26
+ FormFields.fixFields(tbody.lastElementChild);
27
+
28
+ // If a trigger is provided, focus the first field of the new row.
29
+ if (trigger instanceof HTMLElement) {
30
+ const newRow = tbody.lastElementChild;
31
+ const firstInput = newRow.querySelector('input, select, textarea');
32
+ if (firstInput) {
33
+ firstInput.focus();
34
+ }
35
+ }
36
+
37
+ // Execute the callback if provided.
38
+ if (typeof callback === 'function') {
39
+ const newRow = tbody.lastElementChild;
40
+ callback(newRow);
41
+ }
42
+ };
43
+
44
+ /**
45
+ * Removes a row from a table in a form.
46
+ * This function is useful for dynamically removing rows added to forms.
47
+ *
48
+ * @param {HTMLElement} link - Link element (<a>) that is part of the row to be
49
+ * removed.
50
+ * @returns {void}
51
+ */
52
+ FormTables.delJS = function (link) {
53
+ 'use strict';
54
+ if (!(link instanceof HTMLElement)) {
55
+ console.error(
56
+ 'FormTables.delJS: The provided element is not a valid HTML element.',
57
+ );
58
+ return;
59
+ }
60
+ link.closest('tr').remove();
61
+ };
62
+
63
+ /**
64
+ * Dynamically updates the rows of a checkbox table based on a list of options.
65
+ * Useful for representing and selecting a set of categorized options.
66
+ *
67
+ * @param {string} tableID - Identifier of the table element to be modified.
68
+ * @param {string} name - Name assigned to the checkboxes.
69
+ * @param {Object} optionsList - Object with options indexed by a higher
70
+ * category.
71
+ * @param {string} selectedCategory - Selected category from which options are
72
+ * wanted to be loaded.
73
+ * @param {Array} keys - Object attributes that correspond to the key of each
74
+ * row.
75
+ * @param {Array} cols - Object attributes that are desired to be displayed as
76
+ * columns in the table.
77
+ * @param {number} [keepRows] - Number of rows that should be kept at the
78
+ * beginning of the table. Default is 0.
79
+ * @returns {void}
80
+ */
81
+ FormTables.updateTablecheck = function (
82
+ tableID,
83
+ name,
84
+ optionsList,
85
+ selectedCategory,
86
+ keys,
87
+ cols,
88
+ keepRows,
89
+ ) {
90
+ 'use strict';
91
+ if (keepRows === undefined) {
92
+ keepRows = 0;
93
+ }
94
+ const tableBody = document.getElementById(tableID).tBodies[0];
95
+ if (!tableBody) {
96
+ console.error(
97
+ `FormTables.updateTablecheck: Table body with ID '${tableID}' not found.`,
98
+ );
99
+ return;
100
+ }
101
+
102
+ // Clear existing rows, except the first 'keepRows'.
103
+ while (tableBody.rows.length > keepRows) {
104
+ tableBody.deleteRow(keepRows);
105
+ }
106
+
107
+ // Get options for the selected category.
108
+ let options = optionsList[selectedCategory];
109
+ if (!options) {
110
+ console.warn(
111
+ `FormTables.updateTablecheck: No options found for category '${selectedCategory}'.`,
112
+ );
113
+ return;
114
+ }
115
+ if (!Array.isArray(options)) {
116
+ options = [options]; // Ensure options is an array.
117
+ }
118
+
119
+ // Add new rows to the table.
120
+ options.forEach(option => {
121
+ const row = tableBody.insertRow();
122
+ cols.forEach(col => {
123
+ const cell = row.insertCell();
124
+ cell.textContent = option[col];
125
+ });
126
+
127
+ // Add cell for the checkbox.
128
+ const checkboxCell = row.insertCell();
129
+ const checkbox = document.createElement('input');
130
+ checkbox.type = 'checkbox';
131
+ checkbox.name = `${name}[]`;
132
+ checkbox.value = keys.map(key => option[key]).join(';');
133
+ checkboxCell.appendChild(checkbox);
134
+ });
135
+ };
136
+
137
+ // Export module for use in node.js.
138
+ if (typeof module === 'object' && module.exports) {
139
+ module.exports = FormTables;
140
+ }