@liedekef/ftable 1.1.0 → 1.1.3

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.js CHANGED
@@ -1,6 +1,12 @@
1
- // Modern fTable - Vanilla JS Refactor
2
1
 
3
- const JTABLE_DEFAULT_MESSAGES = {
2
+ (function (global, factory) {
3
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4
+ typeof define === 'function' && define.amd ? define(factory) :
5
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.FTable = factory());
6
+ }(this, (function () {
7
+ // Modern fTable - Vanilla JS Refactor
8
+
9
+ const FTABLE_DEFAULT_MESSAGES = {
4
10
  serverCommunicationError: 'An error occurred while communicating to the server.',
5
11
  loadingMessage: 'Loading records...',
6
12
  noDataAvailable: 'No data available!',
@@ -903,6 +909,10 @@ class FTableFormBuilder {
903
909
  case 'file':
904
910
  input = this.createFileInput(fieldName, field, value);
905
911
  break;
912
+ case 'date':
913
+ case 'datetime-local':
914
+ input = this.createDateInput(fieldName, field, value);
915
+ break;
906
916
  default:
907
917
  input = this.createTypedInput(fieldName, field, value);
908
918
  }
@@ -947,14 +957,65 @@ class FTableFormBuilder {
947
957
  return container;
948
958
  }
949
959
 
960
+ createDateInput(fieldName, field, value) {
961
+ // Check if FDatepicker is available
962
+ if (typeof FDatepicker !== 'undefined') {
963
+ const dateFormat = field.dateFormat || this.options.defaultDateFormat;
964
+
965
+ const container = document.createElement('div');
966
+ // Create hidden input
967
+ const hiddenInput = Object.assign(document.createElement('input'), {
968
+ id: 'real-' + fieldName,
969
+ type: 'hidden',
970
+ value: value || '',
971
+ name: fieldName
972
+ });
973
+ // Create visible input
974
+ const visibleInput = Object.assign(document.createElement('input'), {
975
+ className: field.inputClass || 'datepicker-input',
976
+ id: 'Edit-' + fieldName,
977
+ type: 'text',
978
+ 'data-date': 'alt-' + fieldName,
979
+ value: value || '',
980
+ readOnly: true
981
+ });
982
+
983
+ if (value) {
984
+ hiddenInput.value = value;
985
+ visibleInput.dataset.date = value;
986
+ }
987
+
988
+ // Set any additional attributes
989
+ if (field.inputAttributes) {
990
+ Object.keys(field.inputAttributes).forEach(key => {
991
+ visibleInput.setAttribute(key, field.inputAttributes[key]);
992
+ });
993
+ }
994
+
995
+ // Append both inputs
996
+ container.appendChild(hiddenInput);
997
+ container.appendChild(visibleInput);
998
+
999
+ // Apply FDatepicker
1000
+ const picker = new FDatepicker(visibleInput, {
1001
+ format: dateFormat,
1002
+ altField: 'real-' + fieldName,
1003
+ altFormat: 'Y-m-d'
1004
+ });
1005
+
1006
+ return container;
1007
+ } else {
1008
+ return createTypedInput(fieldName, field, value);
1009
+ }
1010
+ }
1011
+
950
1012
  createTypedInput(fieldName, field, value) {
951
1013
  const inputType = field.type || 'text';
952
1014
  const attributes = {
953
1015
  type: inputType,
954
1016
  id: `Edit-${fieldName}`,
955
1017
  placeholder: field.placeholder || '',
956
- value: value || '',
957
- class: field.inputClass || ''
1018
+ value: value || ''
958
1019
  };
959
1020
 
960
1021
  // extra check for name and multiple
@@ -974,22 +1035,10 @@ class FTableFormBuilder {
974
1035
  }
975
1036
  attributes.name = name;
976
1037
 
977
- // Handle required attribute
978
- if (field.required) {
979
- attributes.required = 'required';
980
- }
981
-
982
- // Handle readonly attribute
983
- if (field.readonly) {
984
- attributes.readonly = 'readonly';
985
- }
986
-
987
- // Handle disabled attribute
988
- if (field.disabled) {
989
- attributes.disabled = 'disabled';
990
- }
991
-
992
- const input = FTableDOMHelper.create('input', { attributes });
1038
+ const input = FTableDOMHelper.create('input', {
1039
+ className: field.inputClass || '',
1040
+ attributes: attributes
1041
+ });
993
1042
 
994
1043
  // Prevent form submit on Enter, trigger change instead
995
1044
  input.addEventListener('keypress', (e) => {
@@ -1236,18 +1285,6 @@ class FTableFormBuilder {
1236
1285
  return wrapper;
1237
1286
  }
1238
1287
 
1239
- createDateInput(fieldName, field, value) {
1240
- return FTableDOMHelper.create('input', {
1241
- attributes: {
1242
- type: 'date',
1243
- name: fieldName,
1244
- id: `Edit-${fieldName}`,
1245
- class: field.inputClass || '',
1246
- value: value || ''
1247
- }
1248
- });
1249
- }
1250
-
1251
1288
  populateSelectOptions(select, options, selectedValue) {
1252
1289
  select.innerHTML = ''; // Clear existing options
1253
1290
 
@@ -1330,6 +1367,7 @@ class FTable extends FTableEventEmitter {
1330
1367
  }
1331
1368
 
1332
1369
  this.options = this.mergeOptions(options);
1370
+ this.verifyOptions();
1333
1371
  this.logger = new FTableLogger(this.options.logLevel);
1334
1372
  this.userPrefs = new FTableUserPreferences('', this.options.saveUserPreferencesMethod);
1335
1373
  this.formBuilder = new FTableFormBuilder(this.options, this);
@@ -1364,15 +1402,18 @@ class FTable extends FTableEventEmitter {
1364
1402
  fields: {},
1365
1403
  animationsEnabled: true,
1366
1404
  loadingAnimationDelay: 1000,
1367
- defaultDateFormat: 'yyyy-mm-dd',
1405
+ defaultDateLocale: 'en',
1406
+ defaultDateFormat: 'm/d/Y',
1368
1407
  saveUserPreferences: true,
1369
1408
  saveUserPreferencesMethod: 'localStorage',
1370
1409
  defaultSorting: '',
1410
+ tableReset: false,
1371
1411
 
1372
1412
  // Paging
1373
1413
  paging: false,
1374
1414
  pageList: 'normal',
1375
1415
  pageSize: 10,
1416
+ pageSizes: [10, 25, 50, 100],
1376
1417
  gotoPageArea: 'combobox',
1377
1418
 
1378
1419
  // Sorting
@@ -1396,7 +1437,7 @@ class FTable extends FTableEventEmitter {
1396
1437
  listCache: 30000, // or listCache: 30000 (duration in ms)
1397
1438
 
1398
1439
  // Messages
1399
- messages: { ...JTABLE_DEFAULT_MESSAGES } // Safe copy
1440
+ messages: { ...FTABLE_DEFAULT_MESSAGES } // Safe copy
1400
1441
  };
1401
1442
 
1402
1443
  return this.deepMerge(defaults, options);
@@ -1416,9 +1457,15 @@ class FTable extends FTableEventEmitter {
1416
1457
  return result;
1417
1458
  }
1418
1459
 
1460
+ verifyOptions() {
1461
+ if (this.options.pageSize && !this.options.pageSizes.includes(this.options.pageSize)) {
1462
+ this.options.pageSize = this.options.pageSizes[0];
1463
+ }
1464
+ }
1465
+
1419
1466
  // Public
1420
1467
  static setMessages(customMessages) {
1421
- Object.assign(JTABLE_DEFAULT_MESSAGES, customMessages);
1468
+ Object.assign(FTABLE_DEFAULT_MESSAGES, customMessages);
1422
1469
  }
1423
1470
 
1424
1471
  init() {
@@ -1885,16 +1932,49 @@ class FTable extends FTableEventEmitter {
1885
1932
  if (!field.type && field.options) {
1886
1933
  field.type = 'select';
1887
1934
  }
1935
+ const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
1888
1936
 
1889
1937
  switch (field.type) {
1890
1938
  case 'date':
1891
- input = FTableDOMHelper.create('input', {
1892
- attributes: {
1893
- type: 'date',
1894
- 'data-field-name': fieldName,
1895
- class: 'ftable-toolbarsearch'
1896
- }
1897
- });
1939
+ case 'datetime-local':
1940
+ if (typeof FDatepicker !== 'undefined') {
1941
+ const dateFormat = field.dateFormat || this.options.defaultDateFormat;
1942
+ input = document.createElement('div');
1943
+ // Create hidden input
1944
+ const hiddenInput = Object.assign(document.createElement('input'), {
1945
+ id: 'ftable-toolbarsearch-extra-' + fieldName,
1946
+ type: 'hidden',
1947
+ name: fieldName,
1948
+ className: 'ftable-toolbarsearch-extra'
1949
+ });
1950
+ // Create visible input
1951
+ const visibleInput = Object.assign(document.createElement('input'), {
1952
+ className: 'ftable-toolbarsearch',
1953
+ id: 'ftable-toolbarsearch-' + fieldName,
1954
+ type: 'text',
1955
+ readOnly: true
1956
+ });
1957
+ // Append both inputs
1958
+ input.appendChild(hiddenInput);
1959
+ input.appendChild(visibleInput);
1960
+
1961
+ // Apply FDatepicker
1962
+ const picker = new FDatepicker(visibleInput, {
1963
+ format: dateFormat,
1964
+ altField: 'ftable-toolbarsearch-extra-' + fieldName,
1965
+ altFormat: 'Y-m-d'
1966
+ });
1967
+
1968
+ } else {
1969
+ input = FTableDOMHelper.create('input', {
1970
+ className: 'ftable-toolbarsearch',
1971
+ attributes: {
1972
+ type: 'date',
1973
+ 'data-field-name': fieldName,
1974
+ id: fieldSearchName,
1975
+ }
1976
+ });
1977
+ }
1898
1978
  break;
1899
1979
 
1900
1980
  case 'checkbox':
@@ -1902,10 +1982,11 @@ class FTable extends FTableEventEmitter {
1902
1982
  input = await this.createSelectForSearch(fieldName, field, true);
1903
1983
  } else {
1904
1984
  input = FTableDOMHelper.create('input', {
1985
+ className: 'ftable-toolbarsearch',
1905
1986
  attributes: {
1906
1987
  type: 'text',
1907
1988
  'data-field-name': fieldName,
1908
- class: 'ftable-toolbarsearch',
1989
+ id: fieldSearchName,
1909
1990
  placeholder: 'Search...'
1910
1991
  }
1911
1992
  });
@@ -1917,10 +1998,11 @@ class FTable extends FTableEventEmitter {
1917
1998
  input = await this.createSelectForSearch(fieldName, field, false);
1918
1999
  } else {
1919
2000
  input = FTableDOMHelper.create('input', {
2001
+ className: 'ftable-toolbarsearch',
1920
2002
  attributes: {
1921
2003
  type: 'text',
1922
2004
  'data-field-name': fieldName,
1923
- class: 'ftable-toolbarsearch',
2005
+ id: fieldSearchName,
1924
2006
  placeholder: 'Search...'
1925
2007
  }
1926
2008
  });
@@ -1929,10 +2011,11 @@ class FTable extends FTableEventEmitter {
1929
2011
 
1930
2012
  default:
1931
2013
  input = FTableDOMHelper.create('input', {
2014
+ className: 'ftable-toolbarsearch',
1932
2015
  attributes: {
1933
2016
  type: 'text',
1934
2017
  'data-field-name': fieldName,
1935
- class: 'ftable-toolbarsearch',
2018
+ id: fieldSearchName,
1936
2019
  placeholder: 'Search...'
1937
2020
  }
1938
2021
  });
@@ -1985,9 +2068,11 @@ class FTable extends FTableEventEmitter {
1985
2068
  }
1986
2069
 
1987
2070
  async createSelectForSearch(fieldName, field, isCheckboxValues) {
2071
+ const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
1988
2072
  const select = FTableDOMHelper.create('select', {
1989
2073
  attributes: {
1990
2074
  'data-field-name': fieldName,
2075
+ id: fieldSearchName,
1991
2076
  class: 'ftable-toolbarsearch'
1992
2077
  }
1993
2078
  });
@@ -2211,7 +2296,7 @@ class FTable extends FTableEventEmitter {
2211
2296
  if (Array.isArray(state.sorting)) {
2212
2297
  this.state.sorting = state.sorting;
2213
2298
  }
2214
- if (state.pageSize) {
2299
+ if (state.pageSize && this.options.pageSizes.includes(state.pageSize)) {
2215
2300
  this.state.pageSize = state.pageSize;
2216
2301
  }
2217
2302
  } catch (error) {
@@ -2381,9 +2466,6 @@ class FTable extends FTableEventEmitter {
2381
2466
  this.createToolbarButtons();
2382
2467
  this.createCustomToolbarItems();
2383
2468
 
2384
- // Handle window unload
2385
- this.handlePageUnload();
2386
-
2387
2469
  // Keyboard shortcuts
2388
2470
  this.bindKeyboardEvents();
2389
2471
 
@@ -2670,20 +2752,6 @@ class FTable extends FTableEventEmitter {
2670
2752
  });
2671
2753
  }
2672
2754
 
2673
- handlePageUnload() {
2674
- let unloadingPage = false;
2675
-
2676
- window.addEventListener('beforeunload', () => {
2677
- unloadingPage = true;
2678
- });
2679
-
2680
- window.addEventListener('unload', () => {
2681
- unloadingPage = false;
2682
- });
2683
-
2684
- this.unloadingPage = () => unloadingPage;
2685
- }
2686
-
2687
2755
  bindKeyboardEvents() {
2688
2756
  if (this.options.selecting) {
2689
2757
  this.shiftKeyDown = false;
@@ -3011,7 +3079,19 @@ class FTable extends FTableEventEmitter {
3011
3079
  }
3012
3080
 
3013
3081
  if (field.type === 'date' && value) {
3014
- return this.formatDate(value, field.dateFormat);
3082
+ if (typeof FDatepicker !== 'undefined') {
3083
+ return FDatepicker.formatDate(this._parseDate(value), field.dateFormat || this.options.defaultDateFormat);
3084
+ } else {
3085
+ return this.formatDate(value, field.dateLocale || this.options.defaultDateLocale || 'en' );
3086
+ }
3087
+ }
3088
+
3089
+ if (field.type === 'datetime-local' && value) {
3090
+ if (typeof FDatepicker !== 'undefined') {
3091
+ return FDatepicker.formatDate(this._parseDate(value), field.dateFormat || this.options.defaultDateFormat);
3092
+ } else {
3093
+ return this.formatDateTime(value, field.dateLocale || this.options.defaultDateLocale || 'en' );
3094
+ }
3015
3095
  }
3016
3096
 
3017
3097
  if (field.type === 'checkbox') {
@@ -3026,18 +3106,53 @@ class FTable extends FTableEventEmitter {
3026
3106
  return value || '';
3027
3107
  }
3028
3108
 
3109
+ _parseDate(dateString) {
3110
+ if (dateString.includes('Date')) { // Format: /Date(1320259705710)/
3111
+ return new Date(
3112
+ parseInt(dateString.substr(6), 10)
3113
+ );
3114
+ } else if (dateString.length == 10) { // Format: 2011-01-01
3115
+ return new Date(
3116
+ parseInt(dateString.substr(0, 4), 10),
3117
+ parseInt(dateString.substr(5, 2), 10) - 1,
3118
+ parseInt(dateString.substr(8, 2), 10)
3119
+ );
3120
+ } else if (dateString.length == 19) { // Format: 2011-01-01 20:32:42
3121
+ return new Date(
3122
+ parseInt(dateString.substr(0, 4), 10),
3123
+ parseInt(dateString.substr(5, 2), 10) - 1,
3124
+ parseInt(dateString.substr(8, 2), 10),
3125
+ parseInt(dateString.substr(11, 2), 10),
3126
+ parseInt(dateString.substr(14, 2), 10),
3127
+ parseInt(dateString.substr(17, 2), 10)
3128
+ );
3129
+ } else {
3130
+ return new Date(dateString);
3131
+ }
3132
+ }
3133
+
3029
3134
  formatDate(dateValue, format) {
3030
3135
  if (!dateValue) return '';
3031
-
3032
- const date = new Date(dateValue);
3033
- if (isNaN(date.getTime())) return dateValue;
3034
3136
 
3035
- // Simple date formatting - could be enhanced with a proper date library
3036
- const year = date.getFullYear();
3037
- const month = String(date.getMonth() + 1).padStart(2, '0');
3038
- const day = String(date.getDate()).padStart(2, '0');
3039
-
3040
- return `${year}-${month}-${day}`;
3137
+ const date = this._parseDate(dateValue);
3138
+ try {
3139
+ if (isNaN(date.getTime())) return dateValue;
3140
+ return date.toLocaleDateString(format,{ year: "numeric", month: "2-digit", day: "2-digit" });
3141
+ } catch {
3142
+ return dateValue;
3143
+ }
3144
+ }
3145
+
3146
+ formatDateTime(dateValue, format) {
3147
+ if (!dateValue) return '';
3148
+
3149
+ const date = this._parseDate(dateValue);
3150
+ try {
3151
+ if (isNaN(date.getTime())) return dateValue;
3152
+ return date.toLocaleString(format);
3153
+ } catch {
3154
+ return dateValue;
3155
+ }
3041
3156
  }
3042
3157
 
3043
3158
  getCheckboxText(fieldName, value) {
@@ -4059,55 +4174,6 @@ class FTable extends FTableEventEmitter {
4059
4174
  });
4060
4175
  }
4061
4176
 
4062
- // Data validation
4063
- validateRecord(record, operation = 'create') {
4064
- const errors = [];
4065
-
4066
- Object.entries(this.options.fields).forEach(([fieldName, field]) => {
4067
- const value = record[fieldName];
4068
-
4069
- // Required field validation
4070
- if (field.required && (!value || value.toString().trim() === '')) {
4071
- errors.push(`${field.title || fieldName} is required`);
4072
- }
4073
-
4074
- // Type validation
4075
- if (value && field.validate && typeof field.validate === 'function') {
4076
- const validationResult = field.validate(value, record);
4077
- if (validationResult !== true) {
4078
- errors.push(validationResult || `${field.title || fieldName} is invalid`);
4079
- }
4080
- }
4081
-
4082
- // Built-in type validations
4083
- if (value) {
4084
- switch (field.type) {
4085
- case 'email':
4086
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4087
- if (!emailRegex.test(value)) {
4088
- errors.push(`${field.title || fieldName} must be a valid email`);
4089
- }
4090
- break;
4091
- case 'number':
4092
- if (isNaN(value)) {
4093
- errors.push(`${field.title || fieldName} must be a number`);
4094
- }
4095
- break;
4096
- case 'date':
4097
- if (isNaN(new Date(value).getTime())) {
4098
- errors.push(`${field.title || fieldName} must be a valid date`);
4099
- }
4100
- break;
4101
- }
4102
- }
4103
- });
4104
-
4105
- return {
4106
- isValid: errors.length === 0,
4107
- errors
4108
- };
4109
- }
4110
-
4111
4177
  // Advanced search functionality
4112
4178
  enableSearch(options = {}) {
4113
4179
  const searchOptions = {
@@ -4783,3 +4849,6 @@ table.load();
4783
4849
  */
4784
4850
 
4785
4851
  window.FTable = FTable;
4852
+
4853
+ return FTable;
4854
+ })));