@liedekef/ftable 1.1.46 → 1.1.47

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.
Files changed (40) hide show
  1. package/ftable.esm.js +206 -217
  2. package/ftable.js +206 -217
  3. package/ftable.min.js +2 -2
  4. package/ftable.umd.js +206 -217
  5. package/package.json +1 -1
  6. package/themes/basic/ftable_basic.css +1 -1
  7. package/themes/basic/ftable_basic.min.css +1 -1
  8. package/themes/ftable_theme_base.less +1 -2
  9. package/themes/lightcolor/blue/ftable.css +2 -3
  10. package/themes/lightcolor/blue/ftable.min.css +1 -1
  11. package/themes/lightcolor/ftable_lightcolor_base.less +1 -2
  12. package/themes/lightcolor/gray/ftable.css +2 -3
  13. package/themes/lightcolor/gray/ftable.min.css +1 -1
  14. package/themes/lightcolor/green/ftable.css +2 -3
  15. package/themes/lightcolor/green/ftable.min.css +1 -1
  16. package/themes/lightcolor/orange/ftable.css +2 -3
  17. package/themes/lightcolor/orange/ftable.min.css +1 -1
  18. package/themes/lightcolor/red/ftable.css +2 -3
  19. package/themes/lightcolor/red/ftable.min.css +1 -1
  20. package/themes/metro/blue/ftable.css +1 -1
  21. package/themes/metro/blue/ftable.min.css +1 -1
  22. package/themes/metro/brown/ftable.css +1 -1
  23. package/themes/metro/brown/ftable.min.css +1 -1
  24. package/themes/metro/crimson/ftable.css +1 -1
  25. package/themes/metro/crimson/ftable.min.css +1 -1
  26. package/themes/metro/darkgray/ftable.css +1 -1
  27. package/themes/metro/darkgray/ftable.min.css +1 -1
  28. package/themes/metro/darkorange/ftable.css +1 -1
  29. package/themes/metro/darkorange/ftable.min.css +1 -1
  30. package/themes/metro/green/ftable.css +1 -1
  31. package/themes/metro/green/ftable.min.css +1 -1
  32. package/themes/metro/lightgray/ftable.css +1 -1
  33. package/themes/metro/lightgray/ftable.min.css +1 -1
  34. package/themes/metro/pink/ftable.css +1 -1
  35. package/themes/metro/pink/ftable.min.css +1 -1
  36. package/themes/metro/purple/ftable.css +1 -1
  37. package/themes/metro/purple/ftable.min.css +1 -1
  38. package/themes/metro/red/ftable.css +1 -1
  39. package/themes/metro/red/ftable.min.css +1 -1
  40. package/themes/lightcolor/bg-thead.png +0 -0
package/ftable.js CHANGED
@@ -30,7 +30,7 @@
30
30
  sortingInfoNone: 'No sorting applied',
31
31
  resetSorting: 'Reset sorting',
32
32
  csvExport: 'CSV',
33
- printTable: '🖨️ Print',
33
+ printTable: '🖨️ Print',
34
34
  cloneRecord: 'Clone Record',
35
35
  resetTable: 'Reset table',
36
36
  resetTableConfirm: 'This will reset all columns, pagesize, sorting to their defaults. Do you want to continue?',
@@ -187,36 +187,45 @@ class FTableLogger {
187
187
  }
188
188
 
189
189
  class FTableDOMHelper {
190
+ static PROPERTY_ATTRIBUTES = new Set([
191
+ 'value', 'checked', 'selected', 'disabled', 'readOnly',
192
+ 'name', 'id', 'type', 'placeholder', 'min', 'max',
193
+ 'step', 'required', 'multiple', 'accept', 'className',
194
+ 'textContent', 'innerHTML', 'title'
195
+ ]);
196
+
190
197
  static create(tag, options = {}) {
191
198
  const element = document.createElement(tag);
192
199
 
193
- if (options.className) {
194
- element.className = options.className;
200
+ // Handle special cases first
201
+ if (options.style !== undefined) {
202
+ element.style.cssText = options.style;
195
203
  }
204
+
205
+ FTableDOMHelper.PROPERTY_ATTRIBUTES.forEach(prop => {
206
+ if (prop in options && options[prop] !== null) {
207
+ element[prop] = options[prop];
208
+ }
209
+ });
196
210
 
197
- if (options.style) {
198
- element.style.cssText = options.style;
211
+ if (options.parent !== undefined) {
212
+ options.parent.appendChild(element);
199
213
  }
200
214
 
215
+ // the attributes last, so we can override stuff if needed
201
216
  if (options.attributes) {
202
217
  Object.entries(options.attributes).forEach(([key, value]) => {
203
- if (value !== null)
204
- element.setAttribute(key, value);
218
+ if (value !== null) {
219
+ // Use property if it exists on the element, otherwise use setAttribute
220
+ if (FTableDOMHelper.PROPERTY_ATTRIBUTES.has(key)) {
221
+ element[key] = value;
222
+ } else {
223
+ element.setAttribute(key, value);
224
+ }
225
+ }
205
226
  });
206
227
  }
207
228
 
208
- if (options.text) {
209
- element.textContent = options.text;
210
- }
211
-
212
- if (options.html) {
213
- element.innerHTML = options.html;
214
- }
215
-
216
- if (options.parent) {
217
- options.parent.appendChild(element);
218
- }
219
-
220
229
  return element;
221
230
  }
222
231
 
@@ -473,14 +482,14 @@ class FtableModal {
473
482
  // Header
474
483
  const header = FTableDOMHelper.create('h2', {
475
484
  className: 'ftable-modal-header',
476
- text: this.options.title,
485
+ textContent: this.options.title,
477
486
  parent: this.modal
478
487
  });
479
488
 
480
489
  // Close button
481
490
  const closeBtn = FTableDOMHelper.create('span', {
482
491
  className: 'ftable-modal-close',
483
- html: '×',
492
+ innerHTML: '×',
484
493
  parent: this.modal
485
494
  });
486
495
 
@@ -508,7 +517,7 @@ class FtableModal {
508
517
  this.options.buttons.forEach(button => {
509
518
  const btn = FTableDOMHelper.create('button', {
510
519
  className: `ftable-dialog-button ${button.className || ''}`,
511
- html: `<span>${button.text}</span>`,
520
+ innerHTML: `<span>${button.text}</span>`,
512
521
  parent: footer
513
522
  });
514
523
 
@@ -743,7 +752,7 @@ class FTableFormBuilder {
743
752
  // Label
744
753
  const label = FTableDOMHelper.create('div', {
745
754
  className: 'ftable-input-label',
746
- text: field.inputTitle || field.title,
755
+ textContent: field.inputTitle || field.title,
747
756
  parent: container
748
757
  });
749
758
  }
@@ -1146,7 +1155,7 @@ class FTableFormBuilder {
1146
1155
  if (field.explain) {
1147
1156
  const explain = FTableDOMHelper.create('div', {
1148
1157
  className: 'ftable-field-explain',
1149
- html: `<small>${field.explain}</small>`,
1158
+ innerHTML: `<small>${field.explain}</small>`,
1150
1159
  parent: container
1151
1160
  });
1152
1161
  }
@@ -1162,21 +1171,15 @@ class FTableFormBuilder {
1162
1171
  const container = document.createElement('div');
1163
1172
  // Create hidden input
1164
1173
  const hiddenInput = FTableDOMHelper.create('input', {
1165
- attributes: {
1166
- id: 'real-' + fieldName,
1167
- type: 'hidden',
1168
- value: value,
1169
- name: fieldName
1170
- }
1174
+ id: 'real-' + fieldName,
1175
+ type: 'hidden',
1176
+ value: value,
1177
+ name: fieldName
1171
1178
  });
1172
1179
 
1173
1180
  // Create visible input
1174
1181
  const attributes = {
1175
- id: `Edit-${fieldName}`,
1176
- type: 'text',
1177
- 'data-date': value,
1178
- placeholder: field.placeholder || null,
1179
- readOnly: true
1182
+ 'data-date': value
1180
1183
  };
1181
1184
  // Set any additional attributes
1182
1185
  if (field.inputAttributes) {
@@ -1184,9 +1187,13 @@ class FTableFormBuilder {
1184
1187
  Object.assign(attributes, parsed);
1185
1188
  }
1186
1189
 
1187
- const visibleInput = FTableDOMHelper.create('input', {
1190
+ const visibleInput = FTableDOMHelper.create('input', {
1191
+ attributes: attributes,
1192
+ id: `Edit-${fieldName}`,
1193
+ type: 'text',
1194
+ placeholder: field.placeholder || null,
1188
1195
  className: field.inputClass || 'datepicker-input',
1189
- attributes: attributes
1196
+ readOnly: true
1190
1197
  });
1191
1198
 
1192
1199
  // Append both inputs
@@ -1226,12 +1233,7 @@ class FTableFormBuilder {
1226
1233
 
1227
1234
  createTypedInput(fieldName, field, value) {
1228
1235
  const inputType = field.type || 'text';
1229
- const attributes = {
1230
- type: inputType,
1231
- id: `Edit-${fieldName}`,
1232
- placeholder: field.placeholder || null,
1233
- value: value
1234
- };
1236
+ const attributes = { };
1235
1237
 
1236
1238
  // extra check for name and multiple
1237
1239
  let name = fieldName;
@@ -1248,11 +1250,15 @@ class FTableFormBuilder {
1248
1250
  name = `${fieldName}[]`;
1249
1251
  }
1250
1252
  }
1251
- attributes.name = name;
1252
1253
 
1253
- const input = FTableDOMHelper.create('input', {
1254
+ const input = FTableDOMHelper.create('input', {
1255
+ attributes: attributes,
1256
+ type: inputType,
1257
+ id: `Edit-${fieldName}`,
1254
1258
  className: field.inputClass || null,
1255
- attributes: attributes
1259
+ placeholder: field.placeholder || null,
1260
+ value: value,
1261
+ name: name
1256
1262
  });
1257
1263
 
1258
1264
  // Prevent form submit on Enter, trigger change instead
@@ -1270,11 +1276,6 @@ class FTableFormBuilder {
1270
1276
 
1271
1277
  createDatalistInput(fieldName, field, value) {
1272
1278
  const attributes = {
1273
- type: 'text',
1274
- name: fieldName,
1275
- id: `Edit-${fieldName}`,
1276
- placeholder: field.placeholder || null,
1277
- value: value,
1278
1279
  list: `${fieldName}-datalist`
1279
1280
  };
1280
1281
 
@@ -1285,15 +1286,18 @@ class FTableFormBuilder {
1285
1286
  }
1286
1287
 
1287
1288
  const input = FTableDOMHelper.create('input', {
1289
+ attributes: attributes,
1290
+ type: 'search',
1291
+ name: fieldName,
1292
+ id: `Edit-${fieldName}`,
1288
1293
  className: field.inputClass || null,
1289
- attributes: attributes
1294
+ placeholder: field.placeholder || null,
1295
+ value: value
1290
1296
  });
1291
1297
 
1292
1298
  // Create the datalist element
1293
1299
  const datalist = FTableDOMHelper.create('datalist', {
1294
- attributes: {
1295
- id: `${fieldName}-datalist`
1296
- }
1300
+ id: `${fieldName}-datalist`
1297
1301
  });
1298
1302
 
1299
1303
  // Populate datalist options
@@ -1313,18 +1317,16 @@ class FTableFormBuilder {
1313
1317
  if (Array.isArray(options)) {
1314
1318
  options.forEach(option => {
1315
1319
  FTableDOMHelper.create('option', {
1316
- attributes: {
1317
- value: option.Value || option.value || option
1318
- },
1319
- text: option.DisplayText || option.text || option,
1320
+ value: option.Value || option.value || option,
1321
+ textContent: option.DisplayText || option.text || option,
1320
1322
  parent: datalist
1321
1323
  });
1322
1324
  });
1323
1325
  } else if (typeof options === 'object') {
1324
1326
  Object.entries(options).forEach(([key, text]) => {
1325
1327
  FTableDOMHelper.create('option', {
1326
- attributes: { value: key },
1327
- text: text,
1328
+ value: key,
1329
+ textContent: text,
1328
1330
  parent: datalist
1329
1331
  });
1330
1332
  });
@@ -1332,12 +1334,7 @@ class FTableFormBuilder {
1332
1334
  }
1333
1335
 
1334
1336
  createHiddenInput(fieldName, field, value) {
1335
- const attributes = {
1336
- type: 'hidden',
1337
- name: fieldName,
1338
- id: `Edit-${fieldName}`,
1339
- value: value
1340
- };
1337
+ const attributes = { };
1341
1338
 
1342
1339
  // Apply inputAttributes
1343
1340
  if (field.inputAttributes) {
@@ -1345,15 +1342,17 @@ class FTableFormBuilder {
1345
1342
  Object.assign(attributes, parsed);
1346
1343
  }
1347
1344
 
1348
- return FTableDOMHelper.create('input', { attributes });
1345
+ return FTableDOMHelper.create('input', {
1346
+ attributes: attributes,
1347
+ type: 'hidden',
1348
+ name: fieldName,
1349
+ id: `Edit-${fieldName}`,
1350
+ value: value
1351
+ });
1349
1352
  }
1350
1353
 
1351
1354
  createTextarea(fieldName, field, value) {
1352
- const attributes = {
1353
- name: fieldName,
1354
- id: `Edit-${fieldName}`,
1355
- placeholder: field.placeholder || null
1356
- };
1355
+ const attributes = { };
1357
1356
 
1358
1357
  // Apply inputAttributes
1359
1358
  if (field.inputAttributes) {
@@ -1361,19 +1360,18 @@ class FTableFormBuilder {
1361
1360
  Object.assign(attributes, parsed);
1362
1361
  }
1363
1362
 
1364
- const textarea = FTableDOMHelper.create('textarea', {
1363
+ return FTableDOMHelper.create('textarea', {
1364
+ attributes: attributes,
1365
+ name: fieldName,
1366
+ id: `Edit-${fieldName}`,
1365
1367
  className: field.inputClass || null,
1366
- attributes: attributes
1368
+ placeholder: field.placeholder || null,
1369
+ value = value
1367
1370
  });
1368
- textarea.value = value;
1369
- return textarea;
1370
1371
  }
1371
1372
 
1372
1373
  createSelect(fieldName, field, value) {
1373
- const attributes = {
1374
- name: fieldName,
1375
- id: `Edit-${fieldName}`,
1376
- };
1374
+ const attributes = { };
1377
1375
 
1378
1376
  // extra check for name and multiple
1379
1377
  let name = fieldName;
@@ -1393,8 +1391,10 @@ class FTableFormBuilder {
1393
1391
  attributes.name = name;
1394
1392
 
1395
1393
  const select = FTableDOMHelper.create('select', {
1396
- className: field.inputClass || null,
1397
- attributes: attributes
1394
+ attributes: attributes,
1395
+ name: fieldName,
1396
+ id: `Edit-${fieldName}`,
1397
+ className: field.inputClass || null
1398
1398
  });
1399
1399
 
1400
1400
  if (field.options) {
@@ -1420,15 +1420,7 @@ class FTableFormBuilder {
1420
1420
  });
1421
1421
 
1422
1422
  const radioId = `${fieldName}_${index}`;
1423
- const radioAttributes = {
1424
- type: 'radio',
1425
- name: fieldName,
1426
- id: radioId,
1427
- value: option.Value || option.value || option
1428
- };
1429
-
1430
- if (field.required && index === 0) radioAttributes.required = 'required';
1431
- if (field.disabled) radioAttributes.disabled = 'disabled';
1423
+ const radioAttributes = { };
1432
1424
 
1433
1425
  // Apply inputAttributes
1434
1426
  if (field.inputAttributes) {
@@ -1436,19 +1428,24 @@ class FTableFormBuilder {
1436
1428
  Object.assign(radioAttributes, parsed);
1437
1429
  }
1438
1430
 
1431
+ const fieldValue = option.Value !== undefined ? option.Value :
1432
+ option.value !== undefined ? option.value :
1433
+ option; // fallback for string
1434
+
1439
1435
  const radio = FTableDOMHelper.create('input', {
1440
1436
  attributes: radioAttributes,
1437
+ type: 'radio',
1438
+ name: fieldName,
1439
+ id: radioId,
1440
+ value: fieldValue,
1441
1441
  className: field.inputClass || null,
1442
+ checked: fieldValue == value,
1442
1443
  parent: radioWrapper
1443
1444
  });
1444
1445
 
1445
- if (radioAttributes.value === value) {
1446
- radio.checked = true;
1447
- }
1448
-
1449
1446
  const label = FTableDOMHelper.create('label', {
1450
1447
  attributes: { for: radioId },
1451
- text: option.DisplayText || option.text || option,
1448
+ textContent: option.DisplayText || option.text || option,
1452
1449
  parent: radioWrapper
1453
1450
  });
1454
1451
  });
@@ -1476,12 +1473,10 @@ class FTableFormBuilder {
1476
1473
  // Create the checkbox
1477
1474
  const checkbox = FTableDOMHelper.create('input', {
1478
1475
  className: ['ftable-yesno-check-input', field.inputClass || ''].filter(Boolean).join(' '),
1479
- attributes: {
1480
- type: 'checkbox',
1481
- name: fieldName,
1482
- id: `Edit-${fieldName}`,
1483
- value: '1'
1484
- },
1476
+ type: 'checkbox',
1477
+ name: fieldName,
1478
+ id: `Edit-${fieldName}`,
1479
+ value: '1',
1485
1480
  parent: wrapper
1486
1481
  });
1487
1482
  checkbox.checked = isChecked;
@@ -1493,7 +1488,7 @@ class FTableFormBuilder {
1493
1488
  attributes: {
1494
1489
  for: `Edit-${fieldName}`,
1495
1490
  },
1496
- text: field.label,
1491
+ textContent: field.label,
1497
1492
  parent: wrapper
1498
1493
  });
1499
1494
  } else {
@@ -1520,8 +1515,9 @@ class FTableFormBuilder {
1520
1515
  option.value !== undefined ? option.value :
1521
1516
  option; // fallback for string
1522
1517
  const optionElement = FTableDOMHelper.create('option', {
1523
- attributes: { value: value },
1524
- text: option.DisplayText || option.text || option,
1518
+ value: value,
1519
+ textContent: option.DisplayText || option.text || option,
1520
+ selected: value == selectedValue,
1525
1521
  parent: select
1526
1522
  });
1527
1523
 
@@ -1531,30 +1527,21 @@ class FTableFormBuilder {
1531
1527
  });
1532
1528
  }
1533
1529
 
1534
- if (optionElement.value == selectedValue) {
1535
- optionElement.selected = true;
1536
- }
1537
1530
  });
1538
1531
  } else if (typeof options === 'object') {
1539
1532
  Object.entries(options).forEach(([key, text]) => {
1540
1533
  const optionElement = FTableDOMHelper.create('option', {
1541
- attributes: { value: key },
1542
- text: text,
1534
+ value: key,
1535
+ textContent: text,
1536
+ selected: key == selectedValue,
1543
1537
  parent: select
1544
1538
  });
1545
-
1546
- if (key == selectedValue) {
1547
- optionElement.selected = true;
1548
- }
1549
1539
  });
1550
1540
  }
1551
1541
  }
1552
1542
 
1553
1543
  createFileInput(fieldName, field, value) {
1554
- const attributes = {
1555
- type: 'file',
1556
- id: `Edit-${fieldName}`,
1557
- };
1544
+ const attributes = { };
1558
1545
 
1559
1546
  // extra check for name and multiple
1560
1547
  let name = fieldName;
@@ -1570,9 +1557,11 @@ class FTableFormBuilder {
1570
1557
  name = `${fieldName}[]`;
1571
1558
  }
1572
1559
  }
1573
- attributes.name = name;
1574
1560
 
1575
1561
  return FTableDOMHelper.create('input', {
1562
+ type: 'file',
1563
+ id: `Edit-${fieldName}`,
1564
+ name: name,
1576
1565
  className: field.inputClass || null,
1577
1566
  attributes: attributes
1578
1567
  });
@@ -1898,7 +1887,7 @@ class FTable extends FTableEventEmitter {
1898
1887
  });
1899
1888
 
1900
1889
  FTableDOMHelper.create('span', {
1901
- text: this.options.messages.pageSizeChangeLabel,
1890
+ textContent: this.options.messages.pageSizeChangeLabel,
1902
1891
  parent: container
1903
1892
  });
1904
1893
 
@@ -1911,7 +1900,7 @@ class FTable extends FTableEventEmitter {
1911
1900
  pageSizes.forEach(size => {
1912
1901
  const option = FTableDOMHelper.create('option', {
1913
1902
  attributes: { value: size },
1914
- text: size.toString(),
1903
+ textContent: size.toString(),
1915
1904
  parent: select
1916
1905
  });
1917
1906
 
@@ -2022,7 +2011,7 @@ class FTable extends FTableEventEmitter {
2022
2011
 
2023
2012
  FTableDOMHelper.create('div', {
2024
2013
  className: 'ftable-title-text',
2025
- html: this.options.title,
2014
+ innerHTML: this.options.title,
2026
2015
  parent: this.elements.titleDiv
2027
2016
  });
2028
2017
  }
@@ -2108,7 +2097,7 @@ class FTable extends FTableEventEmitter {
2108
2097
 
2109
2098
  const textHeader = FTableDOMHelper.create('span', {
2110
2099
  className: 'ftable-column-header-text',
2111
- html: field.title || fieldName,
2100
+ innerHTML: field.title || fieldName,
2112
2101
  parent: container
2113
2102
  });
2114
2103
 
@@ -2217,21 +2206,19 @@ class FTable extends FTableEventEmitter {
2217
2206
  // Create hidden input
2218
2207
  const hiddenInput = FTableDOMHelper.create('input', {
2219
2208
  className: 'ftable-toolbarsearch-extra',
2209
+ type: 'hidden',
2210
+ id: 'ftable-toolbarsearch-extra-' + fieldName,
2220
2211
  attributes: {
2221
- type: 'hidden',
2222
2212
  'data-field-name': fieldName,
2223
- id: 'ftable-toolbarsearch-extra-' + fieldName,
2224
2213
  }
2225
2214
  });
2226
2215
  // Create visible input
2227
2216
  const visibleInput = FTableDOMHelper.create('input', {
2228
2217
  className: 'ftable-toolbarsearch',
2229
- attributes: {
2230
- id: 'ftable-toolbarsearch-' + fieldName,
2231
- type: 'text',
2232
- placeholder: field.searchPlaceholder || field.placeholder || '',
2233
- readOnly: true
2234
- }
2218
+ id: 'ftable-toolbarsearch-' + fieldName,
2219
+ type: 'text',
2220
+ placeholder: field.searchPlaceholder || field.placeholder || '',
2221
+ readOnly: true
2235
2222
  });
2236
2223
  // Append both inputs
2237
2224
  containerDiv.appendChild(hiddenInput);
@@ -2246,7 +2233,8 @@ class FTable extends FTableEventEmitter {
2246
2233
  const picker = new FDatepicker(visibleInput, {
2247
2234
  format: dateFormat,
2248
2235
  altField: 'ftable-toolbarsearch-extra-' + fieldName,
2249
- altFormat: 'Y-m-d'
2236
+ altFormat: 'Y-m-d',
2237
+ autoClose: true
2250
2238
  });
2251
2239
  }, 0);
2252
2240
  break;
@@ -2267,10 +2255,10 @@ class FTable extends FTableEventEmitter {
2267
2255
  } else {
2268
2256
  input = FTableDOMHelper.create('input', {
2269
2257
  className: 'ftable-toolbarsearch',
2258
+ type: searchType,
2259
+ id: fieldSearchName,
2270
2260
  attributes: {
2271
- type: 'date',
2272
2261
  'data-field-name': fieldName,
2273
- id: fieldSearchName,
2274
2262
  }
2275
2263
  });
2276
2264
  }
@@ -2289,11 +2277,11 @@ class FTable extends FTableEventEmitter {
2289
2277
  } else {
2290
2278
  input = FTableDOMHelper.create('input', {
2291
2279
  className: 'ftable-toolbarsearch',
2280
+ type: 'text',
2281
+ id: fieldSearchName,
2282
+ placeholder: field.searchPlaceholder || field.placeholder || 'Search...',
2292
2283
  attributes: {
2293
- type: 'text',
2294
- 'data-field-name': fieldName,
2295
- id: fieldSearchName,
2296
- placeholder: field.searchPlaceholder || field.placeholder || 'Search...'
2284
+ 'data-field-name': fieldName
2297
2285
  }
2298
2286
  });
2299
2287
  }
@@ -2306,11 +2294,11 @@ class FTable extends FTableEventEmitter {
2306
2294
  default:
2307
2295
  input = FTableDOMHelper.create('input', {
2308
2296
  className: 'ftable-toolbarsearch',
2297
+ type: 'text',
2298
+ id: fieldSearchName,
2299
+ placeholder: field.searchPlaceholder || field.placeholder || 'Search...',
2309
2300
  attributes: {
2310
- type: 'text',
2311
- 'data-field-name': fieldName,
2312
- id: fieldSearchName,
2313
- placeholder: field.searchPlaceholder || field.placeholder || 'Search...'
2301
+ 'data-field-name': fieldName
2314
2302
  }
2315
2303
  });
2316
2304
  }
@@ -2358,7 +2346,7 @@ class FTable extends FTableEventEmitter {
2358
2346
 
2359
2347
  const resetButton = FTableDOMHelper.create('button', {
2360
2348
  className: 'ftable-toolbarsearch-reset-button',
2361
- text: this.options.messages.resetSearch,
2349
+ textContent: this.options.messages.resetSearch,
2362
2350
  attributes : {
2363
2351
  id: 'ftable-toolbarsearch-reset-button'
2364
2352
  },
@@ -2371,7 +2359,6 @@ class FTable extends FTableEventEmitter {
2371
2359
  async createSelectForSearch(fieldName, field, isCheckboxValues) {
2372
2360
  const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
2373
2361
  const attributes = {
2374
- id: fieldSearchName,
2375
2362
  };
2376
2363
 
2377
2364
  // extra check for name and multiple
@@ -2394,6 +2381,7 @@ class FTable extends FTableEventEmitter {
2394
2381
 
2395
2382
  const select = FTableDOMHelper.create('select', {
2396
2383
  attributes: attributes,
2384
+ id: fieldSearchName,
2397
2385
  className: 'ftable-toolbarsearch'
2398
2386
  });
2399
2387
 
@@ -2416,8 +2404,8 @@ class FTable extends FTableEventEmitter {
2416
2404
 
2417
2405
  if (!hasEmptyFirst) {
2418
2406
  FTableDOMHelper.create('option', {
2419
- attributes: { value: '' },
2420
- html: '&nbsp;',
2407
+ value: '',
2408
+ innerHTML: '&nbsp;',
2421
2409
  parent: select
2422
2410
  });
2423
2411
  }
@@ -2425,18 +2413,16 @@ class FTable extends FTableEventEmitter {
2425
2413
  if (optionsSource && Array.isArray(optionsSource)) {
2426
2414
  optionsSource.forEach(option => {
2427
2415
  const optionElement = FTableDOMHelper.create('option', {
2428
- attributes: {
2429
- value: option.Value !== undefined ? option.Value : option.value !== undefined ? option.value : option
2430
- },
2431
- text: option.DisplayText || option.text || option,
2416
+ value: option.Value !== undefined ? option.Value : option.value !== undefined ? option.value : option,
2417
+ textContent: option.DisplayText || option.text || option,
2432
2418
  parent: select
2433
2419
  });
2434
2420
  });
2435
2421
  } else if (optionsSource && typeof optionsSource === 'object') {
2436
2422
  Object.entries(optionsSource).forEach(([key, text]) => {
2437
2423
  FTableDOMHelper.create('option', {
2438
- attributes: { value: key },
2439
- text: text,
2424
+ value: key,
2425
+ textContent: text,
2440
2426
  parent: select
2441
2427
  });
2442
2428
  });
@@ -2457,12 +2443,12 @@ class FTable extends FTableEventEmitter {
2457
2443
  // Create the input that uses the datalist
2458
2444
  const input = FTableDOMHelper.create('input', {
2459
2445
  className: 'ftable-toolbarsearch',
2446
+ type: 'search',
2447
+ id: fieldSearchName,
2448
+ placeholder: field.searchPlaceholder || field.placeholder || 'Type or select...',
2460
2449
  attributes: {
2461
- type: 'search',
2462
2450
  'data-field-name': fieldName,
2463
- id: fieldSearchName,
2464
- list: datalistId,
2465
- placeholder: field.searchPlaceholder || field.placeholder || 'Type or select...'
2451
+ list: datalistId
2466
2452
  }
2467
2453
  });
2468
2454
 
@@ -2738,7 +2724,7 @@ class FTable extends FTableEventEmitter {
2738
2724
  const colCount = this.elements.table.querySelector('thead tr').children.length;
2739
2725
  FTableDOMHelper.create('td', {
2740
2726
  attributes: { colspan: colCount },
2741
- text: this.options.messages.noDataAvailable,
2727
+ textContent: this.options.messages.noDataAvailable,
2742
2728
  parent: row
2743
2729
  });
2744
2730
  }
@@ -3033,7 +3019,7 @@ class FTable extends FTableEventEmitter {
3033
3019
  }
3034
3020
 
3035
3021
  const labelText = FTableDOMHelper.create('span', {
3036
- text: field.title || fieldName,
3022
+ textContent: field.title || fieldName,
3037
3023
  style: isSeparator ? 'font-weight: bold;' : null,
3038
3024
  parent: label
3039
3025
  });
@@ -3042,7 +3028,7 @@ class FTable extends FTableEventEmitter {
3042
3028
  if (isSorted) {
3043
3029
  const sortIndicator = FTableDOMHelper.create('span', {
3044
3030
  className: 'ftable-sort-indicator',
3045
- text: ' (sorted)',
3031
+ textContent: ' (sorted)',
3046
3032
  parent: labelText
3047
3033
  });
3048
3034
  sortIndicator.style.fontSize = '0.8em';
@@ -3128,25 +3114,61 @@ class FTable extends FTableEventEmitter {
3128
3114
  }
3129
3115
 
3130
3116
  addToolbarButton(options) {
3131
- const button = FTableDOMHelper.create('span', {
3117
+ const button = FTableDOMHelper.create('button', {
3132
3118
  className: `ftable-toolbar-item ${options.className || ''}`,
3119
+ id: options.id || null,
3120
+ title: options.title || null,
3121
+ textContent: options.text || null,
3122
+ type: 'button', // Prevent accidental form submission
3133
3123
  parent: this.elements.toolbarDiv
3134
3124
  });
3125
+
3135
3126
  if (options.addIconSpan) {
3136
3127
  // just the span, the rest is CSS here
3137
- const buttonText = FTableDOMHelper.create('span', {
3128
+ const iconSpan = FTableDOMHelper.create('span', {
3138
3129
  className: `ftable-toolbar-item-icon ${options.className || ''}`,
3139
3130
  parent: button
3140
3131
  });
3132
+ // If we want icon before text, we need to insert it
3133
+ // Since textContent replaces everything, we need to append text node
3134
+ if (options.text) {
3135
+ // Remove the textContent we set earlier
3136
+ button.textContent = '';
3137
+ button.append(iconSpan, options.text || '');
3138
+ }
3139
+ }
3140
+
3141
+ // Add icon if provided
3142
+ if (options.icon) {
3143
+ const img = FTableDOMHelper.create('img', {
3144
+ attributes: {
3145
+ src: options.icon,
3146
+ alt: '',
3147
+ width: 16,
3148
+ height: 16,
3149
+ style: 'margin-right: 6px; vertical-align: middle;'
3150
+ },
3151
+ parent: button
3152
+ });
3153
+ // If we want icon before text, we need to insert it
3154
+ // Since textContent replaces everything, we need to append text node
3155
+ if (options.text) {
3156
+ // Remove the textContent we set earlier
3157
+ button.textContent = '';
3158
+ button.append(img, options.text || '');
3159
+ }
3141
3160
  }
3142
- const buttonText = FTableDOMHelper.create('span', {
3143
- className: `ftable-toolbar-item-text ${options.className || ''}`,
3144
- text: options.text,
3145
- parent: button
3146
- });
3147
3161
 
3148
3162
  if (options.onClick) {
3149
- button.addEventListener('click', options.onClick);
3163
+ button.addEventListener('click', (e) => {
3164
+ e.preventDefault();
3165
+ e.stopPropagation();
3166
+ options.onClick(e);
3167
+ });
3168
+ }
3169
+
3170
+ if (options.disabled) {
3171
+ button.disabled = true;
3150
3172
  }
3151
3173
 
3152
3174
  return button;
@@ -3155,48 +3177,15 @@ class FTable extends FTableEventEmitter {
3155
3177
  createCustomToolbarItems() {
3156
3178
  if (!this.options.toolbar || !this.options.toolbar.items) return;
3157
3179
 
3158
- this.options.toolbar.items.forEach(item => {
3159
- const button = FTableDOMHelper.create('span', {
3160
- className: `ftable-toolbar-item ftable-toolbar-item-custom ${item.buttonClass || ''}`,
3161
- parent: this.elements.toolbarDiv
3180
+ this.options.toolbar.items.forEach((item, index) => {
3181
+ this.addToolbarButton({
3182
+ text: item.text || '',
3183
+ className: `ftable-toolbar-item-custom ${item.buttonClass || ''}`,
3184
+ id: item.buttonId || `ftable-toolbar-item-custom-id-${index}`,
3185
+ title: item.tooltip || '',
3186
+ icon: item.icon || null,
3187
+ onClick: typeof item.click === 'function' ? item.click : null
3162
3188
  });
3163
-
3164
- // Add title/tooltip if provided
3165
- if (item.tooltip) {
3166
- button.setAttribute('title', item.tooltip);
3167
- }
3168
-
3169
- // Add icon if provided
3170
- if (item.icon) {
3171
- const img = FTableDOMHelper.create('img', {
3172
- attributes: {
3173
- src: item.icon,
3174
- alt: '',
3175
- width: 16,
3176
- height: 16,
3177
- style: 'margin-right: 6px; vertical-align: middle;'
3178
- },
3179
- parent: button
3180
- });
3181
- }
3182
-
3183
- // Add text
3184
- if (item.text) {
3185
- FTableDOMHelper.create('span', {
3186
- text: item.text,
3187
- className: `ftable-toolbar-item-text ftable-toolbar-item-custom-text ${item.buttonTextClass || ''}`,
3188
- parent: button
3189
- });
3190
- }
3191
-
3192
- // Attach click handler
3193
- if (typeof item.click === 'function') {
3194
- button.addEventListener('click', (e) => {
3195
- e.preventDefault();
3196
- e.stopPropagation();
3197
- item.click(e);
3198
- });
3199
- }
3200
3189
  });
3201
3190
  }
3202
3191
 
@@ -3450,7 +3439,7 @@ class FTable extends FTableEventEmitter {
3450
3439
 
3451
3440
  const cell = FTableDOMHelper.create('td', {
3452
3441
  className: `${field.listClass || ''} ${field.listClassEntry || ''}`,
3453
- html: field.listEscapeHTML ? FTableDOMHelper.escapeHtml(value) : value,
3442
+ innerHTML: field.listEscapeHTML ? FTableDOMHelper.escapeHtml(value) : value,
3454
3443
  attributes: { 'data-field-name': fieldName },
3455
3444
  parent: row
3456
3445
  });
@@ -3471,7 +3460,7 @@ class FTable extends FTableEventEmitter {
3471
3460
  const button = FTableDOMHelper.create('button', {
3472
3461
  className: 'ftable-command-button ftable-edit-command-button',
3473
3462
  attributes: { title: this.options.messages.editRecord },
3474
- html: `<span>${this.options.messages.editRecord}</span>`,
3463
+ innerHTML: `<span>${this.options.messages.editRecord}</span>`,
3475
3464
  parent: cell
3476
3465
  });
3477
3466
 
@@ -3490,7 +3479,7 @@ class FTable extends FTableEventEmitter {
3490
3479
  const button = FTableDOMHelper.create('button', {
3491
3480
  className: 'ftable-command-button ftable-clone-command-button',
3492
3481
  attributes: { title: this.options.messages.cloneRecord || 'Clone' },
3493
- html: `<span>${this.options.messages.cloneRecord || 'Clone'}</span>`,
3482
+ innerHTML: `<span>${this.options.messages.cloneRecord || 'Clone'}</span>`,
3494
3483
  parent: cell
3495
3484
  });
3496
3485
  button.addEventListener('click', (e) => {
@@ -3509,7 +3498,7 @@ class FTable extends FTableEventEmitter {
3509
3498
  const button = FTableDOMHelper.create('button', {
3510
3499
  className: 'ftable-command-button ftable-delete-command-button',
3511
3500
  attributes: { title: this.options.messages.deleteText },
3512
- html: `<span>${this.options.messages.deleteText}</span>`,
3501
+ innerHTML: `<span>${this.options.messages.deleteText}</span>`,
3513
3502
  parent: cell
3514
3503
  });
3515
3504
 
@@ -4177,7 +4166,7 @@ class FTable extends FTableEventEmitter {
4177
4166
  if (pageNum - lastNumber > 1) {
4178
4167
  FTableDOMHelper.create('span', {
4179
4168
  className: 'ftable-page-number-space',
4180
- text: '...',
4169
+ textContent: '...',
4181
4170
  parent: this.elements.pagingListArea
4182
4171
  });
4183
4172
  }
@@ -4217,7 +4206,7 @@ class FTable extends FTableEventEmitter {
4217
4206
 
4218
4207
  // Label
4219
4208
  const label = FTableDOMHelper.create('span', {
4220
- text: this.options.messages.gotoPageLabel + ': ',
4209
+ textContent: this.options.messages.gotoPageLabel + ': ',
4221
4210
  parent: this.elements.pagingGotoArea
4222
4211
  });
4223
4212
 
@@ -4234,7 +4223,7 @@ class FTable extends FTableEventEmitter {
4234
4223
  for (let i = 1; i <= totalPages; i++) {
4235
4224
  FTableDOMHelper.create('option', {
4236
4225
  attributes: { value: i },
4237
- text: i,
4226
+ textContent: i,
4238
4227
  parent: this.elements.gotoPageSelect
4239
4228
  });
4240
4229
  }
@@ -4278,7 +4267,7 @@ class FTable extends FTableEventEmitter {
4278
4267
  createPageButton(text, pageNumber, disabled, className) {
4279
4268
  const button = FTableDOMHelper.create('span', {
4280
4269
  className: className + (disabled ? ' ftable-page-number-disabled' : ''),
4281
- html: text,
4270
+ innerHTML: text,
4282
4271
  parent: this.elements.pagingListArea
4283
4272
  });
4284
4273