@liedekef/ftable 1.1.1 → 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
@@ -6,7 +6,7 @@
6
6
  }(this, (function () {
7
7
  // Modern fTable - Vanilla JS Refactor
8
8
 
9
- const JTABLE_DEFAULT_MESSAGES = {
9
+ const FTABLE_DEFAULT_MESSAGES = {
10
10
  serverCommunicationError: 'An error occurred while communicating to the server.',
11
11
  loadingMessage: 'Loading records...',
12
12
  noDataAvailable: 'No data available!',
@@ -909,6 +909,10 @@ class FTableFormBuilder {
909
909
  case 'file':
910
910
  input = this.createFileInput(fieldName, field, value);
911
911
  break;
912
+ case 'date':
913
+ case 'datetime-local':
914
+ input = this.createDateInput(fieldName, field, value);
915
+ break;
912
916
  default:
913
917
  input = this.createTypedInput(fieldName, field, value);
914
918
  }
@@ -953,14 +957,65 @@ class FTableFormBuilder {
953
957
  return container;
954
958
  }
955
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
+
956
1012
  createTypedInput(fieldName, field, value) {
957
1013
  const inputType = field.type || 'text';
958
1014
  const attributes = {
959
1015
  type: inputType,
960
1016
  id: `Edit-${fieldName}`,
961
1017
  placeholder: field.placeholder || '',
962
- value: value || '',
963
- class: field.inputClass || ''
1018
+ value: value || ''
964
1019
  };
965
1020
 
966
1021
  // extra check for name and multiple
@@ -980,22 +1035,10 @@ class FTableFormBuilder {
980
1035
  }
981
1036
  attributes.name = name;
982
1037
 
983
- // Handle required attribute
984
- if (field.required) {
985
- attributes.required = 'required';
986
- }
987
-
988
- // Handle readonly attribute
989
- if (field.readonly) {
990
- attributes.readonly = 'readonly';
991
- }
992
-
993
- // Handle disabled attribute
994
- if (field.disabled) {
995
- attributes.disabled = 'disabled';
996
- }
997
-
998
- const input = FTableDOMHelper.create('input', { attributes });
1038
+ const input = FTableDOMHelper.create('input', {
1039
+ className: field.inputClass || '',
1040
+ attributes: attributes
1041
+ });
999
1042
 
1000
1043
  // Prevent form submit on Enter, trigger change instead
1001
1044
  input.addEventListener('keypress', (e) => {
@@ -1242,18 +1285,6 @@ class FTableFormBuilder {
1242
1285
  return wrapper;
1243
1286
  }
1244
1287
 
1245
- createDateInput(fieldName, field, value) {
1246
- return FTableDOMHelper.create('input', {
1247
- attributes: {
1248
- type: 'date',
1249
- name: fieldName,
1250
- id: `Edit-${fieldName}`,
1251
- class: field.inputClass || '',
1252
- value: value || ''
1253
- }
1254
- });
1255
- }
1256
-
1257
1288
  populateSelectOptions(select, options, selectedValue) {
1258
1289
  select.innerHTML = ''; // Clear existing options
1259
1290
 
@@ -1336,6 +1367,7 @@ class FTable extends FTableEventEmitter {
1336
1367
  }
1337
1368
 
1338
1369
  this.options = this.mergeOptions(options);
1370
+ this.verifyOptions();
1339
1371
  this.logger = new FTableLogger(this.options.logLevel);
1340
1372
  this.userPrefs = new FTableUserPreferences('', this.options.saveUserPreferencesMethod);
1341
1373
  this.formBuilder = new FTableFormBuilder(this.options, this);
@@ -1370,15 +1402,18 @@ class FTable extends FTableEventEmitter {
1370
1402
  fields: {},
1371
1403
  animationsEnabled: true,
1372
1404
  loadingAnimationDelay: 1000,
1373
- defaultDateFormat: 'yyyy-mm-dd',
1405
+ defaultDateLocale: 'en',
1406
+ defaultDateFormat: 'm/d/Y',
1374
1407
  saveUserPreferences: true,
1375
1408
  saveUserPreferencesMethod: 'localStorage',
1376
1409
  defaultSorting: '',
1410
+ tableReset: false,
1377
1411
 
1378
1412
  // Paging
1379
1413
  paging: false,
1380
1414
  pageList: 'normal',
1381
1415
  pageSize: 10,
1416
+ pageSizes: [10, 25, 50, 100],
1382
1417
  gotoPageArea: 'combobox',
1383
1418
 
1384
1419
  // Sorting
@@ -1402,7 +1437,7 @@ class FTable extends FTableEventEmitter {
1402
1437
  listCache: 30000, // or listCache: 30000 (duration in ms)
1403
1438
 
1404
1439
  // Messages
1405
- messages: { ...JTABLE_DEFAULT_MESSAGES } // Safe copy
1440
+ messages: { ...FTABLE_DEFAULT_MESSAGES } // Safe copy
1406
1441
  };
1407
1442
 
1408
1443
  return this.deepMerge(defaults, options);
@@ -1422,9 +1457,15 @@ class FTable extends FTableEventEmitter {
1422
1457
  return result;
1423
1458
  }
1424
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
+
1425
1466
  // Public
1426
1467
  static setMessages(customMessages) {
1427
- Object.assign(JTABLE_DEFAULT_MESSAGES, customMessages);
1468
+ Object.assign(FTABLE_DEFAULT_MESSAGES, customMessages);
1428
1469
  }
1429
1470
 
1430
1471
  init() {
@@ -1891,16 +1932,49 @@ class FTable extends FTableEventEmitter {
1891
1932
  if (!field.type && field.options) {
1892
1933
  field.type = 'select';
1893
1934
  }
1935
+ const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
1894
1936
 
1895
1937
  switch (field.type) {
1896
1938
  case 'date':
1897
- input = FTableDOMHelper.create('input', {
1898
- attributes: {
1899
- type: 'date',
1900
- 'data-field-name': fieldName,
1901
- class: 'ftable-toolbarsearch'
1902
- }
1903
- });
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
+ }
1904
1978
  break;
1905
1979
 
1906
1980
  case 'checkbox':
@@ -1908,10 +1982,11 @@ class FTable extends FTableEventEmitter {
1908
1982
  input = await this.createSelectForSearch(fieldName, field, true);
1909
1983
  } else {
1910
1984
  input = FTableDOMHelper.create('input', {
1985
+ className: 'ftable-toolbarsearch',
1911
1986
  attributes: {
1912
1987
  type: 'text',
1913
1988
  'data-field-name': fieldName,
1914
- class: 'ftable-toolbarsearch',
1989
+ id: fieldSearchName,
1915
1990
  placeholder: 'Search...'
1916
1991
  }
1917
1992
  });
@@ -1923,10 +1998,11 @@ class FTable extends FTableEventEmitter {
1923
1998
  input = await this.createSelectForSearch(fieldName, field, false);
1924
1999
  } else {
1925
2000
  input = FTableDOMHelper.create('input', {
2001
+ className: 'ftable-toolbarsearch',
1926
2002
  attributes: {
1927
2003
  type: 'text',
1928
2004
  'data-field-name': fieldName,
1929
- class: 'ftable-toolbarsearch',
2005
+ id: fieldSearchName,
1930
2006
  placeholder: 'Search...'
1931
2007
  }
1932
2008
  });
@@ -1935,10 +2011,11 @@ class FTable extends FTableEventEmitter {
1935
2011
 
1936
2012
  default:
1937
2013
  input = FTableDOMHelper.create('input', {
2014
+ className: 'ftable-toolbarsearch',
1938
2015
  attributes: {
1939
2016
  type: 'text',
1940
2017
  'data-field-name': fieldName,
1941
- class: 'ftable-toolbarsearch',
2018
+ id: fieldSearchName,
1942
2019
  placeholder: 'Search...'
1943
2020
  }
1944
2021
  });
@@ -1991,9 +2068,11 @@ class FTable extends FTableEventEmitter {
1991
2068
  }
1992
2069
 
1993
2070
  async createSelectForSearch(fieldName, field, isCheckboxValues) {
2071
+ const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
1994
2072
  const select = FTableDOMHelper.create('select', {
1995
2073
  attributes: {
1996
2074
  'data-field-name': fieldName,
2075
+ id: fieldSearchName,
1997
2076
  class: 'ftable-toolbarsearch'
1998
2077
  }
1999
2078
  });
@@ -2217,7 +2296,7 @@ class FTable extends FTableEventEmitter {
2217
2296
  if (Array.isArray(state.sorting)) {
2218
2297
  this.state.sorting = state.sorting;
2219
2298
  }
2220
- if (state.pageSize) {
2299
+ if (state.pageSize && this.options.pageSizes.includes(state.pageSize)) {
2221
2300
  this.state.pageSize = state.pageSize;
2222
2301
  }
2223
2302
  } catch (error) {
@@ -2387,9 +2466,6 @@ class FTable extends FTableEventEmitter {
2387
2466
  this.createToolbarButtons();
2388
2467
  this.createCustomToolbarItems();
2389
2468
 
2390
- // Handle window unload
2391
- this.handlePageUnload();
2392
-
2393
2469
  // Keyboard shortcuts
2394
2470
  this.bindKeyboardEvents();
2395
2471
 
@@ -2676,20 +2752,6 @@ class FTable extends FTableEventEmitter {
2676
2752
  });
2677
2753
  }
2678
2754
 
2679
- handlePageUnload() {
2680
- let unloadingPage = false;
2681
-
2682
- window.addEventListener('beforeunload', () => {
2683
- unloadingPage = true;
2684
- });
2685
-
2686
- window.addEventListener('unload', () => {
2687
- unloadingPage = false;
2688
- });
2689
-
2690
- this.unloadingPage = () => unloadingPage;
2691
- }
2692
-
2693
2755
  bindKeyboardEvents() {
2694
2756
  if (this.options.selecting) {
2695
2757
  this.shiftKeyDown = false;
@@ -3017,7 +3079,19 @@ class FTable extends FTableEventEmitter {
3017
3079
  }
3018
3080
 
3019
3081
  if (field.type === 'date' && value) {
3020
- 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
+ }
3021
3095
  }
3022
3096
 
3023
3097
  if (field.type === 'checkbox') {
@@ -3032,18 +3106,53 @@ class FTable extends FTableEventEmitter {
3032
3106
  return value || '';
3033
3107
  }
3034
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
+
3035
3134
  formatDate(dateValue, format) {
3036
3135
  if (!dateValue) return '';
3037
-
3038
- const date = new Date(dateValue);
3039
- if (isNaN(date.getTime())) return dateValue;
3040
3136
 
3041
- // Simple date formatting - could be enhanced with a proper date library
3042
- const year = date.getFullYear();
3043
- const month = String(date.getMonth() + 1).padStart(2, '0');
3044
- const day = String(date.getDate()).padStart(2, '0');
3045
-
3046
- 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
+ }
3047
3156
  }
3048
3157
 
3049
3158
  getCheckboxText(fieldName, value) {
@@ -4065,55 +4174,6 @@ class FTable extends FTableEventEmitter {
4065
4174
  });
4066
4175
  }
4067
4176
 
4068
- // Data validation
4069
- validateRecord(record, operation = 'create') {
4070
- const errors = [];
4071
-
4072
- Object.entries(this.options.fields).forEach(([fieldName, field]) => {
4073
- const value = record[fieldName];
4074
-
4075
- // Required field validation
4076
- if (field.required && (!value || value.toString().trim() === '')) {
4077
- errors.push(`${field.title || fieldName} is required`);
4078
- }
4079
-
4080
- // Type validation
4081
- if (value && field.validate && typeof field.validate === 'function') {
4082
- const validationResult = field.validate(value, record);
4083
- if (validationResult !== true) {
4084
- errors.push(validationResult || `${field.title || fieldName} is invalid`);
4085
- }
4086
- }
4087
-
4088
- // Built-in type validations
4089
- if (value) {
4090
- switch (field.type) {
4091
- case 'email':
4092
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4093
- if (!emailRegex.test(value)) {
4094
- errors.push(`${field.title || fieldName} must be a valid email`);
4095
- }
4096
- break;
4097
- case 'number':
4098
- if (isNaN(value)) {
4099
- errors.push(`${field.title || fieldName} must be a number`);
4100
- }
4101
- break;
4102
- case 'date':
4103
- if (isNaN(new Date(value).getTime())) {
4104
- errors.push(`${field.title || fieldName} must be a valid date`);
4105
- }
4106
- break;
4107
- }
4108
- }
4109
- });
4110
-
4111
- return {
4112
- isValid: errors.length === 0,
4113
- errors
4114
- };
4115
- }
4116
-
4117
4177
  // Advanced search functionality
4118
4178
  enableSearch(options = {}) {
4119
4179
  const searchOptions = {