@liedekef/ftable 1.1.6 → 1.1.8

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 (39) hide show
  1. package/ftable.esm.js +66 -193
  2. package/ftable.js +67 -194
  3. package/ftable.min.js +3 -3
  4. package/ftable.umd.js +67 -194
  5. package/package.json +1 -1
  6. package/themes/basic/ftable_basic.css +35 -25
  7. package/themes/basic/ftable_basic.min.css +1 -1
  8. package/themes/ftable_theme_base.less +49 -35
  9. package/themes/lightcolor/blue/ftable.css +43 -33
  10. package/themes/lightcolor/blue/ftable.min.css +1 -1
  11. package/themes/lightcolor/ftable_lightcolor_base.less +6 -5
  12. package/themes/lightcolor/gray/ftable.css +43 -33
  13. package/themes/lightcolor/gray/ftable.min.css +1 -1
  14. package/themes/lightcolor/green/ftable.css +43 -33
  15. package/themes/lightcolor/green/ftable.min.css +1 -1
  16. package/themes/lightcolor/orange/ftable.css +43 -33
  17. package/themes/lightcolor/orange/ftable.min.css +1 -1
  18. package/themes/lightcolor/red/ftable.css +43 -33
  19. package/themes/lightcolor/red/ftable.min.css +1 -1
  20. package/themes/metro/blue/ftable.css +35 -25
  21. package/themes/metro/blue/ftable.min.css +1 -1
  22. package/themes/metro/brown/ftable.css +35 -25
  23. package/themes/metro/brown/ftable.min.css +1 -1
  24. package/themes/metro/crimson/ftable.css +35 -25
  25. package/themes/metro/crimson/ftable.min.css +1 -1
  26. package/themes/metro/darkgray/ftable.css +35 -25
  27. package/themes/metro/darkgray/ftable.min.css +1 -1
  28. package/themes/metro/darkorange/ftable.css +35 -25
  29. package/themes/metro/darkorange/ftable.min.css +1 -1
  30. package/themes/metro/green/ftable.css +35 -25
  31. package/themes/metro/green/ftable.min.css +1 -1
  32. package/themes/metro/lightgray/ftable.css +35 -25
  33. package/themes/metro/lightgray/ftable.min.css +1 -1
  34. package/themes/metro/pink/ftable.css +35 -25
  35. package/themes/metro/pink/ftable.min.css +1 -1
  36. package/themes/metro/purple/ftable.css +35 -25
  37. package/themes/metro/purple/ftable.min.css +1 -1
  38. package/themes/metro/red/ftable.css +35 -25
  39. package/themes/metro/red/ftable.min.css +1 -1
package/ftable.esm.js CHANGED
@@ -1,6 +1,4 @@
1
1
 
2
- // Modern fTable - Vanilla JS Refactor
3
-
4
2
  const FTABLE_DEFAULT_MESSAGES = {
5
3
  serverCommunicationError: 'An error occurred while communicating to the server.',
6
4
  loadingMessage: 'Loading records...',
@@ -748,7 +746,9 @@ class FTableFormBuilder {
748
746
  }
749
747
 
750
748
  try {
751
- const response = await FTableHttpClient.get(url);
749
+ const response = this.options.forcePost
750
+ ? await FTableHttpClient.post(url)
751
+ : await FTableHttpClient.get(url);
752
752
  const options = response.Options || response.options || response || [];
753
753
 
754
754
  // Only cache if noCache is false
@@ -1218,64 +1218,55 @@ class FTableFormBuilder {
1218
1218
  }
1219
1219
 
1220
1220
  createCheckbox(fieldName, field, value) {
1221
- function getCheckboxText(field, value) {
1222
- if (value == undefined ) {
1223
- value = 0;
1224
- }
1225
- if (field.values && field.values[value] !== undefined) {
1226
- return field.values[value];
1227
- }
1228
- return value ? 'Yes' : 'No';
1229
- }
1230
1221
  const wrapper = FTableDOMHelper.create('div', {
1231
- className: 'ftable-checkbox-wrapper'
1222
+ className: 'ftable-yesno-check-wrapper'
1232
1223
  });
1233
1224
 
1234
1225
  const isChecked = [1, '1', true, 'true'].includes(value);
1235
1226
 
1236
- const displayValue = getCheckboxText(field, value); // Uses field.values if defined
1227
+ // Determine "Yes" and "No" labels
1228
+ let dataNo = 'No';
1229
+ let dataYes = 'Yes';
1237
1230
 
1231
+ if (field.values && typeof field.values === 'object') {
1232
+ if (field.values['0'] !== undefined) dataNo = field.values['0'];
1233
+ if (field.values['1'] !== undefined) dataYes = field.values['1'];
1234
+ }
1235
+
1236
+ // Create the checkbox
1238
1237
  const checkbox = FTableDOMHelper.create('input', {
1238
+ className: ['ftable-yesno-check-input', field.inputClass || ''].filter(Boolean).join(' '),
1239
1239
  attributes: {
1240
1240
  type: 'checkbox',
1241
1241
  name: fieldName,
1242
1242
  id: `Edit-${fieldName}`,
1243
- class: field.inputClass || '',
1244
1243
  value: '1'
1245
1244
  },
1245
+ properties: {
1246
+ checked: isChecked
1247
+ },
1246
1248
  parent: wrapper
1247
1249
  });
1248
- checkbox.checked = isChecked;
1249
1250
 
1251
+ // Create the label with data attributes
1250
1252
  const label = FTableDOMHelper.create('label', {
1251
- attributes: { for: `Edit-${fieldName}` },
1253
+ className: 'ftable-yesno-check-text',
1254
+ attributes: {
1255
+ for: `Edit-${fieldName}`,
1256
+ 'data-yes': dataYes,
1257
+ 'data-no': dataNo
1258
+ },
1252
1259
  parent: wrapper
1253
1260
  });
1254
1261
 
1255
- // Add the static formText (e.g., "Is Active?")
1262
+ // Optional: Add a static form label (e.g., "Is Active?")
1256
1263
  if (field.formText) {
1257
- FTableDOMHelper.create('span', {
1264
+ const formSpan = FTableDOMHelper.create('span', {
1258
1265
  text: field.formText,
1259
- parent: label
1266
+ parent: wrapper
1260
1267
  });
1268
+ formSpan.style.marginLeft = '8px';
1261
1269
  }
1262
-
1263
- // Only add dynamic value span if field.values is defined
1264
- if (field.values) {
1265
- const valueSpan = FTableDOMHelper.create('span', {
1266
- className: 'ftable-checkbox-dynamic-value',
1267
- text: ` ${displayValue}`,
1268
- parent: label
1269
- });
1270
-
1271
- // Update on change
1272
- checkbox.addEventListener('change', () => {
1273
- const newValue = checkbox.checked ? '1' : '0';
1274
- const newText = getCheckboxText(field, newValue);
1275
- valueSpan.textContent = ` ${newText}`;
1276
- });
1277
- }
1278
-
1279
1270
  return wrapper;
1280
1271
  }
1281
1272
 
@@ -1354,10 +1345,14 @@ class FTable extends FTableEventEmitter {
1354
1345
  this.element = typeof element === 'string' ?
1355
1346
  document.querySelector(element) : element;
1356
1347
 
1348
+ if (!this.element) {
1349
+ return;
1350
+ }
1351
+
1357
1352
  // Prevent double initialization
1358
- if (element.ftableInstance) {
1353
+ if (this.element.ftableInstance) {
1359
1354
  //console.warn('FTable is already initialized on this element. Using that.');
1360
- return element.ftableInstance;
1355
+ return this.element.ftableInstance;
1361
1356
  }
1362
1357
 
1363
1358
  this.options = this.mergeOptions(options);
@@ -1394,6 +1389,7 @@ class FTable extends FTableEventEmitter {
1394
1389
  logLevel: FTableLogger.LOG_LEVELS.WARN,
1395
1390
  actions: {},
1396
1391
  fields: {},
1392
+ forcePost: true,
1397
1393
  animationsEnabled: true,
1398
1394
  loadingAnimationDelay: 1000,
1399
1395
  defaultDateLocale: '',
@@ -2520,13 +2516,15 @@ class FTable extends FTableEventEmitter {
2520
2516
  // Create overlay to capture clicks outside menu
2521
2517
  this.elements.columnSelectionOverlay = FTableDOMHelper.create('div', {
2522
2518
  className: 'ftable-contextmenu-overlay',
2523
- parent: this.elements.mainContainer
2519
+ //parent: this.elements.mainContainer
2520
+ parent: document.body
2524
2521
  });
2525
2522
 
2526
2523
  // Create the menu
2527
2524
  this.elements.columnSelectionMenu = FTableDOMHelper.create('div', {
2528
2525
  className: 'ftable-column-selection-container',
2529
- parent: this.elements.columnSelectionOverlay
2526
+ //parent: this.elements.columnSelectionOverlay
2527
+ parent: document.body
2530
2528
  });
2531
2529
 
2532
2530
  // Populate menu with column options
@@ -2612,29 +2610,36 @@ class FTable extends FTableEventEmitter {
2612
2610
  }
2613
2611
 
2614
2612
  positionColumnSelectionMenu(e) {
2615
- const containerRect = this.elements.mainContainer.getBoundingClientRect();
2616
- const menuWidth = 200; // Approximate menu width
2617
- const menuHeight = this.columnList.length * 30 + 20; // Approximate height
2613
+ const self = this;
2618
2614
 
2619
- let left = e.clientX - containerRect.left;
2620
- let top = e.clientY - containerRect.top;
2615
+ // menu is bounded to the body for absolute positioning above other content, so safest is to use pageX/Y
2616
+ let left = e.pageX;
2617
+ let top = e.pageY;
2621
2618
 
2622
- // Adjust position to keep menu within container bounds
2623
- if (left + menuWidth > containerRect.width) {
2624
- left = Math.max(0, containerRect.width - menuWidth);
2625
- }
2619
+ // Define minimum width
2620
+ const minWidth = 100;
2626
2621
 
2627
- if (top + menuHeight > containerRect.height) {
2628
- top = Math.max(0, containerRect.height - menuHeight);
2629
- }
2622
+ // Position the menu
2623
+ self.elements.columnSelectionMenu.style.position = 'absolute';
2624
+ self.elements.columnSelectionMenu.style.left = `${left}px`;
2625
+ self.elements.columnSelectionMenu.style.top = `${top}px`;
2626
+ self.elements.columnSelectionMenu.style.minWidth = `${minWidth}px`;
2627
+ self.elements.columnSelectionMenu.style.boxSizing = 'border-box';
2628
+
2629
+ // Optional: Adjust if menu would overflow right edge
2630
+ const menuWidth = self.elements.columnSelectionMenu.offsetWidth;
2631
+ const windowWidth = window.innerWidth;
2630
2632
 
2631
- this.elements.columnSelectionMenu.style.left = left + 'px';
2632
- this.elements.columnSelectionMenu.style.top = top + 'px';
2633
+ if (left + menuWidth > windowWidth) {
2634
+ left = Math.max(10, windowWidth - menuWidth - 10); // 10px margin
2635
+ self.elements.columnSelectionMenu.style.left = `${left}px`;
2636
+ }
2633
2637
  }
2634
2638
 
2635
2639
  hideColumnSelectionMenu() {
2636
2640
  if (this.elements.columnSelectionOverlay) {
2637
2641
  this.elements.columnSelectionOverlay.remove();
2642
+ this.elements.columnSelectionMenu.remove();
2638
2643
  this.elements.columnSelectionOverlay = null;
2639
2644
  this.elements.columnSelectionMenu = null;
2640
2645
  }
@@ -2877,7 +2882,9 @@ class FTable extends FTableEventEmitter {
2877
2882
  if (typeof listAction === 'function') {
2878
2883
  data = await listAction(params);
2879
2884
  } else if (typeof listAction === 'string') {
2880
- data = await FTableHttpClient.get(listAction, params);
2885
+ data = this.options.forcePost
2886
+ ? await FTableHttpClient.post(listAction, params)
2887
+ : await FTableHttpClient.get(listAction, params);
2881
2888
  } else {
2882
2889
  throw new Error('No valid listAction provided');
2883
2890
  }
@@ -4411,7 +4418,9 @@ class FTable extends FTableEventEmitter {
4411
4418
  ...params
4412
4419
  };
4413
4420
 
4414
- const response = await FTableHttpClient.get(url, fullParams);
4421
+ const response = this.options.forcePost
4422
+ ? await FTableHttpClient.post(url, fullParams)
4423
+ : await FTableHttpClient.get(url, fullParams);
4415
4424
 
4416
4425
  if (!response || !response.Record) {
4417
4426
  throw new Error('Invalid response or missing Record');
@@ -4713,140 +4722,4 @@ class FTable extends FTableEventEmitter {
4713
4722
  }
4714
4723
  }
4715
4724
 
4716
- // Export for use
4717
- //window.FTable = FTable;
4718
-
4719
- // Usage example:
4720
- /*
4721
- const table = new FTable('#myTable', {
4722
- title: 'My Data Table',
4723
- paging: true,
4724
- pageSize: 25,
4725
- sorting: true,
4726
- selecting: true,
4727
- actions: {
4728
- listAction: '/api/users',
4729
- createAction: '/api/users',
4730
- updateAction: '/api/users',
4731
- deleteAction: '/api/users'
4732
- },
4733
- fields: {
4734
- id: { key: true, list: false },
4735
- name: {
4736
- title: 'Name',
4737
- type: 'text',
4738
- inputAttributes: "maxlength=100 required"
4739
- },
4740
- email: {
4741
- title: 'Email',
4742
- type: 'email',
4743
- width: '40%',
4744
- inputAttributes: {
4745
- pattern: '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$'
4746
- }
4747
- },
4748
- created: { title: 'Created', type: 'date', width: '30%' }
4749
- },
4750
- toolbarsearch: true,
4751
- childTable: {
4752
- title: 'Child Records',
4753
- actions: {
4754
- listAction: '/api/users/{id}/orders', // {id} will be replaced with parent record id
4755
- createAction: '/api/users/{id}/orders',
4756
- updateAction: '/api/orders',
4757
- deleteAction: '/api/orders'
4758
- },
4759
- fields: {
4760
- orderId: { key: true, list: false },
4761
- orderDate: { title: 'Date', type: 'date' },
4762
- amount: { title: 'Amount', type: 'number' }
4763
- }
4764
- },
4765
- childTableColumnsVisible: true,
4766
-
4767
- });
4768
-
4769
- // Or dynamic child table
4770
- childTable: async function(parentRecord) {
4771
- return {
4772
- title: `Orders for ${parentRecord.name}`,
4773
- actions: {
4774
- listAction: `/api/users/${parentRecord.id}/orders`
4775
- },
4776
- fields: {
4777
- // Dynamic fields based on parent
4778
- }
4779
- };
4780
- }
4781
-
4782
- // function for select options
4783
- fields: {
4784
- assignee: {
4785
- title: 'Assigned To',
4786
- type: 'select',
4787
- options: async function(params) {
4788
- // params contains dependsOnValue, dependsOnField, etc.
4789
- const department = params.dependsOnValue;
4790
- const response = await fetch(`/api/users?department=${department}`);
4791
- return response.json();
4792
- },
4793
- dependsOn: 'department'
4794
- }
4795
- }
4796
-
4797
- // child table:
4798
- phoneNumbers: {
4799
- title: 'Phones',
4800
- display: (data) => {
4801
- const img = document.createElement('img');
4802
- img.className = 'child-opener-image';
4803
- img.src = '/Content/images/Misc/phone.png';
4804
- img.title = 'Edit phone numbers';
4805
- img.style.cursor = 'pointer';
4806
-
4807
- parentRow = img.closest('tr');
4808
- img.addEventListener('click', () => {
4809
- e.stopPropagation();
4810
- if (parentRow.childRow) {
4811
- myTable.closeChildTable(parentRow);
4812
- } else {
4813
- myTable.openChildTable( parentRow, {
4814
- title: `${data.record.Name} - Phone numbers`,
4815
- actions: {
4816
- listAction: `/PagingPerson/PhoneList?PersonId=${data.record.PersonId}`,
4817
- deleteAction: '/PagingPerson/DeletePhone',
4818
- updateAction: '/PagingPerson/UpdatePhone',
4819
- createAction: `/PagingPerson/CreatePhone?PersonId=${data.record.PersonId}`
4820
- },
4821
- fields: {
4822
- PhoneId: { key: true },
4823
- Number: { title: 'Number', type: 'text' },
4824
- Type: { title: 'Type', options: { 0: 'Home', 1: 'Work', 2: 'Mobile' } }
4825
- }
4826
- }, (childTable) => {
4827
- console.log('Child table created');
4828
- };
4829
- }
4830
- });
4831
- img.addEventListener('click', (e) => {
4832
- e.stopPropagation();
4833
- if (parentRow.childRow) {
4834
- myTable.closeChildTable(parentRow);
4835
- } else {
4836
- myTable.openChildTable(parentRow, childOptions);
4837
- }
4838
- });
4839
-
4840
- return img;
4841
- }
4842
- }
4843
-
4844
- // Clear specific options cache
4845
- table.clearOptionsCache('/api/countries');
4846
-
4847
- table.load();
4848
- */
4849
-
4850
- window.FTable = FTable;
4851
-
4852
4725
  export default FTable;