@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.
- package/ftable.esm.js +66 -193
- package/ftable.js +67 -194
- package/ftable.min.js +3 -3
- package/ftable.umd.js +67 -194
- package/package.json +1 -1
- package/themes/basic/ftable_basic.css +35 -25
- package/themes/basic/ftable_basic.min.css +1 -1
- package/themes/ftable_theme_base.less +49 -35
- package/themes/lightcolor/blue/ftable.css +43 -33
- package/themes/lightcolor/blue/ftable.min.css +1 -1
- package/themes/lightcolor/ftable_lightcolor_base.less +6 -5
- package/themes/lightcolor/gray/ftable.css +43 -33
- package/themes/lightcolor/gray/ftable.min.css +1 -1
- package/themes/lightcolor/green/ftable.css +43 -33
- package/themes/lightcolor/green/ftable.min.css +1 -1
- package/themes/lightcolor/orange/ftable.css +43 -33
- package/themes/lightcolor/orange/ftable.min.css +1 -1
- package/themes/lightcolor/red/ftable.css +43 -33
- package/themes/lightcolor/red/ftable.min.css +1 -1
- package/themes/metro/blue/ftable.css +35 -25
- package/themes/metro/blue/ftable.min.css +1 -1
- package/themes/metro/brown/ftable.css +35 -25
- package/themes/metro/brown/ftable.min.css +1 -1
- package/themes/metro/crimson/ftable.css +35 -25
- package/themes/metro/crimson/ftable.min.css +1 -1
- package/themes/metro/darkgray/ftable.css +35 -25
- package/themes/metro/darkgray/ftable.min.css +1 -1
- package/themes/metro/darkorange/ftable.css +35 -25
- package/themes/metro/darkorange/ftable.min.css +1 -1
- package/themes/metro/green/ftable.css +35 -25
- package/themes/metro/green/ftable.min.css +1 -1
- package/themes/metro/lightgray/ftable.css +35 -25
- package/themes/metro/lightgray/ftable.min.css +1 -1
- package/themes/metro/pink/ftable.css +35 -25
- package/themes/metro/pink/ftable.min.css +1 -1
- package/themes/metro/purple/ftable.css +35 -25
- package/themes/metro/purple/ftable.min.css +1 -1
- package/themes/metro/red/ftable.css +35 -25
- 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 =
|
|
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-
|
|
1222
|
+
className: 'ftable-yesno-check-wrapper'
|
|
1232
1223
|
});
|
|
1233
1224
|
|
|
1234
1225
|
const isChecked = [1, '1', true, 'true'].includes(value);
|
|
1235
1226
|
|
|
1236
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
2616
|
-
const menuWidth = 200; // Approximate menu width
|
|
2617
|
-
const menuHeight = this.columnList.length * 30 + 20; // Approximate height
|
|
2613
|
+
const self = this;
|
|
2618
2614
|
|
|
2619
|
-
|
|
2620
|
-
let
|
|
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
|
-
//
|
|
2623
|
-
|
|
2624
|
-
left = Math.max(0, containerRect.width - menuWidth);
|
|
2625
|
-
}
|
|
2619
|
+
// Define minimum width
|
|
2620
|
+
const minWidth = 100;
|
|
2626
2621
|
|
|
2627
|
-
|
|
2628
|
-
|
|
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
|
-
|
|
2632
|
-
|
|
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 =
|
|
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 =
|
|
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;
|