@liedekef/ftable 1.1.1 → 1.1.4

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/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
1
+ = 1.1.3 (2025/08/03) =
2
+ * support fdatepicker
3
+
4
+ = 1.1.2 (2025/08/01) =
5
+ * support defaultDateLocale
6
+
1
7
  = 1.1.1 (2025/08/01) =
2
8
  * Correct build and include esm
3
9
 
package/ftable.esm.js CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  // Modern fTable - Vanilla JS Refactor
3
3
 
4
- const JTABLE_DEFAULT_MESSAGES = {
4
+ const FTABLE_DEFAULT_MESSAGES = {
5
5
  serverCommunicationError: 'An error occurred while communicating to the server.',
6
6
  loadingMessage: 'Loading records...',
7
7
  noDataAvailable: 'No data available!',
@@ -14,7 +14,7 @@ const JTABLE_DEFAULT_MESSAGES = {
14
14
  cancel: 'Cancel',
15
15
  deleteText: 'Delete',
16
16
  deleting: 'Deleting',
17
- error: 'Error',
17
+ error: 'An error has occured',
18
18
  close: 'Close',
19
19
  cannotLoadOptionsFor: 'Cannot load options for field {0}!',
20
20
  pagingInfo: 'Showing {0}-{1} of {2}',
@@ -904,6 +904,10 @@ class FTableFormBuilder {
904
904
  case 'file':
905
905
  input = this.createFileInput(fieldName, field, value);
906
906
  break;
907
+ case 'date':
908
+ case 'datetime-local':
909
+ input = this.createDateInput(fieldName, field, value);
910
+ break;
907
911
  default:
908
912
  input = this.createTypedInput(fieldName, field, value);
909
913
  }
@@ -948,14 +952,65 @@ class FTableFormBuilder {
948
952
  return container;
949
953
  }
950
954
 
955
+ createDateInput(fieldName, field, value) {
956
+ // Check if FDatepicker is available
957
+ if (typeof FDatepicker !== 'undefined') {
958
+ const dateFormat = field.dateFormat || this.options.defaultDateFormat;
959
+
960
+ const container = document.createElement('div');
961
+ // Create hidden input
962
+ const hiddenInput = Object.assign(document.createElement('input'), {
963
+ id: 'real-' + fieldName,
964
+ type: 'hidden',
965
+ value: value || '',
966
+ name: fieldName
967
+ });
968
+ // Create visible input
969
+ const visibleInput = Object.assign(document.createElement('input'), {
970
+ className: field.inputClass || 'datepicker-input',
971
+ id: 'Edit-' + fieldName,
972
+ type: 'text',
973
+ 'data-date': 'alt-' + fieldName,
974
+ value: value || '',
975
+ readOnly: true
976
+ });
977
+
978
+ if (value) {
979
+ hiddenInput.value = value;
980
+ visibleInput.dataset.date = value;
981
+ }
982
+
983
+ // Set any additional attributes
984
+ if (field.inputAttributes) {
985
+ Object.keys(field.inputAttributes).forEach(key => {
986
+ visibleInput.setAttribute(key, field.inputAttributes[key]);
987
+ });
988
+ }
989
+
990
+ // Append both inputs
991
+ container.appendChild(hiddenInput);
992
+ container.appendChild(visibleInput);
993
+
994
+ // Apply FDatepicker
995
+ const picker = new FDatepicker(visibleInput, {
996
+ format: dateFormat,
997
+ altField: 'real-' + fieldName,
998
+ altFormat: 'Y-m-d'
999
+ });
1000
+
1001
+ return container;
1002
+ } else {
1003
+ return createTypedInput(fieldName, field, value);
1004
+ }
1005
+ }
1006
+
951
1007
  createTypedInput(fieldName, field, value) {
952
1008
  const inputType = field.type || 'text';
953
1009
  const attributes = {
954
1010
  type: inputType,
955
1011
  id: `Edit-${fieldName}`,
956
1012
  placeholder: field.placeholder || '',
957
- value: value || '',
958
- class: field.inputClass || ''
1013
+ value: value || ''
959
1014
  };
960
1015
 
961
1016
  // extra check for name and multiple
@@ -975,22 +1030,10 @@ class FTableFormBuilder {
975
1030
  }
976
1031
  attributes.name = name;
977
1032
 
978
- // Handle required attribute
979
- if (field.required) {
980
- attributes.required = 'required';
981
- }
982
-
983
- // Handle readonly attribute
984
- if (field.readonly) {
985
- attributes.readonly = 'readonly';
986
- }
987
-
988
- // Handle disabled attribute
989
- if (field.disabled) {
990
- attributes.disabled = 'disabled';
991
- }
992
-
993
- const input = FTableDOMHelper.create('input', { attributes });
1033
+ const input = FTableDOMHelper.create('input', {
1034
+ className: field.inputClass || '',
1035
+ attributes: attributes
1036
+ });
994
1037
 
995
1038
  // Prevent form submit on Enter, trigger change instead
996
1039
  input.addEventListener('keypress', (e) => {
@@ -1237,18 +1280,6 @@ class FTableFormBuilder {
1237
1280
  return wrapper;
1238
1281
  }
1239
1282
 
1240
- createDateInput(fieldName, field, value) {
1241
- return FTableDOMHelper.create('input', {
1242
- attributes: {
1243
- type: 'date',
1244
- name: fieldName,
1245
- id: `Edit-${fieldName}`,
1246
- class: field.inputClass || '',
1247
- value: value || ''
1248
- }
1249
- });
1250
- }
1251
-
1252
1283
  populateSelectOptions(select, options, selectedValue) {
1253
1284
  select.innerHTML = ''; // Clear existing options
1254
1285
 
@@ -1331,6 +1362,7 @@ class FTable extends FTableEventEmitter {
1331
1362
  }
1332
1363
 
1333
1364
  this.options = this.mergeOptions(options);
1365
+ this.verifyOptions();
1334
1366
  this.logger = new FTableLogger(this.options.logLevel);
1335
1367
  this.userPrefs = new FTableUserPreferences('', this.options.saveUserPreferencesMethod);
1336
1368
  this.formBuilder = new FTableFormBuilder(this.options, this);
@@ -1365,15 +1397,18 @@ class FTable extends FTableEventEmitter {
1365
1397
  fields: {},
1366
1398
  animationsEnabled: true,
1367
1399
  loadingAnimationDelay: 1000,
1368
- defaultDateFormat: 'yyyy-mm-dd',
1400
+ defaultDateLocale: 'en',
1401
+ defaultDateFormat: 'm/d/Y',
1369
1402
  saveUserPreferences: true,
1370
1403
  saveUserPreferencesMethod: 'localStorage',
1371
1404
  defaultSorting: '',
1405
+ tableReset: false,
1372
1406
 
1373
1407
  // Paging
1374
1408
  paging: false,
1375
1409
  pageList: 'normal',
1376
1410
  pageSize: 10,
1411
+ pageSizes: [10, 25, 50, 100],
1377
1412
  gotoPageArea: 'combobox',
1378
1413
 
1379
1414
  // Sorting
@@ -1397,7 +1432,7 @@ class FTable extends FTableEventEmitter {
1397
1432
  listCache: 30000, // or listCache: 30000 (duration in ms)
1398
1433
 
1399
1434
  // Messages
1400
- messages: { ...JTABLE_DEFAULT_MESSAGES } // Safe copy
1435
+ messages: { ...FTABLE_DEFAULT_MESSAGES } // Safe copy
1401
1436
  };
1402
1437
 
1403
1438
  return this.deepMerge(defaults, options);
@@ -1417,9 +1452,15 @@ class FTable extends FTableEventEmitter {
1417
1452
  return result;
1418
1453
  }
1419
1454
 
1455
+ verifyOptions() {
1456
+ if (this.options.pageSize && !this.options.pageSizes.includes(this.options.pageSize)) {
1457
+ this.options.pageSize = this.options.pageSizes[0];
1458
+ }
1459
+ }
1460
+
1420
1461
  // Public
1421
1462
  static setMessages(customMessages) {
1422
- Object.assign(JTABLE_DEFAULT_MESSAGES, customMessages);
1463
+ Object.assign(FTABLE_DEFAULT_MESSAGES, customMessages);
1423
1464
  }
1424
1465
 
1425
1466
  init() {
@@ -1886,16 +1927,49 @@ class FTable extends FTableEventEmitter {
1886
1927
  if (!field.type && field.options) {
1887
1928
  field.type = 'select';
1888
1929
  }
1930
+ const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
1889
1931
 
1890
1932
  switch (field.type) {
1891
1933
  case 'date':
1892
- input = FTableDOMHelper.create('input', {
1893
- attributes: {
1894
- type: 'date',
1895
- 'data-field-name': fieldName,
1896
- class: 'ftable-toolbarsearch'
1897
- }
1898
- });
1934
+ case 'datetime-local':
1935
+ if (typeof FDatepicker !== 'undefined') {
1936
+ const dateFormat = field.dateFormat || this.options.defaultDateFormat;
1937
+ input = document.createElement('div');
1938
+ // Create hidden input
1939
+ const hiddenInput = Object.assign(document.createElement('input'), {
1940
+ id: 'ftable-toolbarsearch-extra-' + fieldName,
1941
+ type: 'hidden',
1942
+ name: fieldName,
1943
+ className: 'ftable-toolbarsearch-extra'
1944
+ });
1945
+ // Create visible input
1946
+ const visibleInput = Object.assign(document.createElement('input'), {
1947
+ className: 'ftable-toolbarsearch',
1948
+ id: 'ftable-toolbarsearch-' + fieldName,
1949
+ type: 'text',
1950
+ readOnly: true
1951
+ });
1952
+ // Append both inputs
1953
+ input.appendChild(hiddenInput);
1954
+ input.appendChild(visibleInput);
1955
+
1956
+ // Apply FDatepicker
1957
+ const picker = new FDatepicker(visibleInput, {
1958
+ format: dateFormat,
1959
+ altField: 'ftable-toolbarsearch-extra-' + fieldName,
1960
+ altFormat: 'Y-m-d'
1961
+ });
1962
+
1963
+ } else {
1964
+ input = FTableDOMHelper.create('input', {
1965
+ className: 'ftable-toolbarsearch',
1966
+ attributes: {
1967
+ type: 'date',
1968
+ 'data-field-name': fieldName,
1969
+ id: fieldSearchName,
1970
+ }
1971
+ });
1972
+ }
1899
1973
  break;
1900
1974
 
1901
1975
  case 'checkbox':
@@ -1903,10 +1977,11 @@ class FTable extends FTableEventEmitter {
1903
1977
  input = await this.createSelectForSearch(fieldName, field, true);
1904
1978
  } else {
1905
1979
  input = FTableDOMHelper.create('input', {
1980
+ className: 'ftable-toolbarsearch',
1906
1981
  attributes: {
1907
1982
  type: 'text',
1908
1983
  'data-field-name': fieldName,
1909
- class: 'ftable-toolbarsearch',
1984
+ id: fieldSearchName,
1910
1985
  placeholder: 'Search...'
1911
1986
  }
1912
1987
  });
@@ -1918,10 +1993,11 @@ class FTable extends FTableEventEmitter {
1918
1993
  input = await this.createSelectForSearch(fieldName, field, false);
1919
1994
  } else {
1920
1995
  input = FTableDOMHelper.create('input', {
1996
+ className: 'ftable-toolbarsearch',
1921
1997
  attributes: {
1922
1998
  type: 'text',
1923
1999
  'data-field-name': fieldName,
1924
- class: 'ftable-toolbarsearch',
2000
+ id: fieldSearchName,
1925
2001
  placeholder: 'Search...'
1926
2002
  }
1927
2003
  });
@@ -1930,10 +2006,11 @@ class FTable extends FTableEventEmitter {
1930
2006
 
1931
2007
  default:
1932
2008
  input = FTableDOMHelper.create('input', {
2009
+ className: 'ftable-toolbarsearch',
1933
2010
  attributes: {
1934
2011
  type: 'text',
1935
2012
  'data-field-name': fieldName,
1936
- class: 'ftable-toolbarsearch',
2013
+ id: fieldSearchName,
1937
2014
  placeholder: 'Search...'
1938
2015
  }
1939
2016
  });
@@ -1986,9 +2063,11 @@ class FTable extends FTableEventEmitter {
1986
2063
  }
1987
2064
 
1988
2065
  async createSelectForSearch(fieldName, field, isCheckboxValues) {
2066
+ const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
1989
2067
  const select = FTableDOMHelper.create('select', {
1990
2068
  attributes: {
1991
2069
  'data-field-name': fieldName,
2070
+ id: fieldSearchName,
1992
2071
  class: 'ftable-toolbarsearch'
1993
2072
  }
1994
2073
  });
@@ -2212,7 +2291,7 @@ class FTable extends FTableEventEmitter {
2212
2291
  if (Array.isArray(state.sorting)) {
2213
2292
  this.state.sorting = state.sorting;
2214
2293
  }
2215
- if (state.pageSize) {
2294
+ if (state.pageSize && this.options.pageSizes.includes(state.pageSize)) {
2216
2295
  this.state.pageSize = state.pageSize;
2217
2296
  }
2218
2297
  } catch (error) {
@@ -2353,7 +2432,7 @@ class FTable extends FTableEventEmitter {
2353
2432
  createInfoModal() {
2354
2433
  this.modals.info = new JtableModal({
2355
2434
  parent: this.elements.mainContainer,
2356
- title: this.options.messages.error,
2435
+ title: '',
2357
2436
  className: 'ftable-info-modal',
2358
2437
  buttons: [
2359
2438
  {
@@ -2382,9 +2461,6 @@ class FTable extends FTableEventEmitter {
2382
2461
  this.createToolbarButtons();
2383
2462
  this.createCustomToolbarItems();
2384
2463
 
2385
- // Handle window unload
2386
- this.handlePageUnload();
2387
-
2388
2464
  // Keyboard shortcuts
2389
2465
  this.bindKeyboardEvents();
2390
2466
 
@@ -2671,20 +2747,6 @@ class FTable extends FTableEventEmitter {
2671
2747
  });
2672
2748
  }
2673
2749
 
2674
- handlePageUnload() {
2675
- let unloadingPage = false;
2676
-
2677
- window.addEventListener('beforeunload', () => {
2678
- unloadingPage = true;
2679
- });
2680
-
2681
- window.addEventListener('unload', () => {
2682
- unloadingPage = false;
2683
- });
2684
-
2685
- this.unloadingPage = () => unloadingPage;
2686
- }
2687
-
2688
2750
  bindKeyboardEvents() {
2689
2751
  if (this.options.selecting) {
2690
2752
  this.shiftKeyDown = false;
@@ -3012,7 +3074,19 @@ class FTable extends FTableEventEmitter {
3012
3074
  }
3013
3075
 
3014
3076
  if (field.type === 'date' && value) {
3015
- return this.formatDate(value, field.dateFormat);
3077
+ if (typeof FDatepicker !== 'undefined') {
3078
+ return FDatepicker.formatDate(this._parseDate(value), field.dateFormat || this.options.defaultDateFormat);
3079
+ } else {
3080
+ return this.formatDate(value, field.dateLocale || this.options.defaultDateLocale || 'en' );
3081
+ }
3082
+ }
3083
+
3084
+ if (field.type === 'datetime-local' && value) {
3085
+ if (typeof FDatepicker !== 'undefined') {
3086
+ return FDatepicker.formatDate(this._parseDate(value), field.dateFormat || this.options.defaultDateFormat);
3087
+ } else {
3088
+ return this.formatDateTime(value, field.dateLocale || this.options.defaultDateLocale || 'en' );
3089
+ }
3016
3090
  }
3017
3091
 
3018
3092
  if (field.type === 'checkbox') {
@@ -3027,18 +3101,53 @@ class FTable extends FTableEventEmitter {
3027
3101
  return value || '';
3028
3102
  }
3029
3103
 
3104
+ _parseDate(dateString) {
3105
+ if (dateString.includes('Date')) { // Format: /Date(1320259705710)/
3106
+ return new Date(
3107
+ parseInt(dateString.substr(6), 10)
3108
+ );
3109
+ } else if (dateString.length == 10) { // Format: 2011-01-01
3110
+ return new Date(
3111
+ parseInt(dateString.substr(0, 4), 10),
3112
+ parseInt(dateString.substr(5, 2), 10) - 1,
3113
+ parseInt(dateString.substr(8, 2), 10)
3114
+ );
3115
+ } else if (dateString.length == 19) { // Format: 2011-01-01 20:32:42
3116
+ return new Date(
3117
+ parseInt(dateString.substr(0, 4), 10),
3118
+ parseInt(dateString.substr(5, 2), 10) - 1,
3119
+ parseInt(dateString.substr(8, 2), 10),
3120
+ parseInt(dateString.substr(11, 2), 10),
3121
+ parseInt(dateString.substr(14, 2), 10),
3122
+ parseInt(dateString.substr(17, 2), 10)
3123
+ );
3124
+ } else {
3125
+ return new Date(dateString);
3126
+ }
3127
+ }
3128
+
3030
3129
  formatDate(dateValue, format) {
3031
3130
  if (!dateValue) return '';
3032
-
3033
- const date = new Date(dateValue);
3034
- if (isNaN(date.getTime())) return dateValue;
3035
3131
 
3036
- // Simple date formatting - could be enhanced with a proper date library
3037
- const year = date.getFullYear();
3038
- const month = String(date.getMonth() + 1).padStart(2, '0');
3039
- const day = String(date.getDate()).padStart(2, '0');
3040
-
3041
- return `${year}-${month}-${day}`;
3132
+ const date = this._parseDate(dateValue);
3133
+ try {
3134
+ if (isNaN(date.getTime())) return dateValue;
3135
+ return date.toLocaleDateString(format,{ year: "numeric", month: "2-digit", day: "2-digit" });
3136
+ } catch {
3137
+ return dateValue;
3138
+ }
3139
+ }
3140
+
3141
+ formatDateTime(dateValue, format) {
3142
+ if (!dateValue) return '';
3143
+
3144
+ const date = this._parseDate(dateValue);
3145
+ try {
3146
+ if (isNaN(date.getTime())) return dateValue;
3147
+ return date.toLocaleString(format);
3148
+ } catch {
3149
+ return dateValue;
3150
+ }
3042
3151
  }
3043
3152
 
3044
3153
  getCheckboxText(fieldName, value) {
@@ -4060,55 +4169,6 @@ class FTable extends FTableEventEmitter {
4060
4169
  });
4061
4170
  }
4062
4171
 
4063
- // Data validation
4064
- validateRecord(record, operation = 'create') {
4065
- const errors = [];
4066
-
4067
- Object.entries(this.options.fields).forEach(([fieldName, field]) => {
4068
- const value = record[fieldName];
4069
-
4070
- // Required field validation
4071
- if (field.required && (!value || value.toString().trim() === '')) {
4072
- errors.push(`${field.title || fieldName} is required`);
4073
- }
4074
-
4075
- // Type validation
4076
- if (value && field.validate && typeof field.validate === 'function') {
4077
- const validationResult = field.validate(value, record);
4078
- if (validationResult !== true) {
4079
- errors.push(validationResult || `${field.title || fieldName} is invalid`);
4080
- }
4081
- }
4082
-
4083
- // Built-in type validations
4084
- if (value) {
4085
- switch (field.type) {
4086
- case 'email':
4087
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
4088
- if (!emailRegex.test(value)) {
4089
- errors.push(`${field.title || fieldName} must be a valid email`);
4090
- }
4091
- break;
4092
- case 'number':
4093
- if (isNaN(value)) {
4094
- errors.push(`${field.title || fieldName} must be a number`);
4095
- }
4096
- break;
4097
- case 'date':
4098
- if (isNaN(new Date(value).getTime())) {
4099
- errors.push(`${field.title || fieldName} must be a valid date`);
4100
- }
4101
- break;
4102
- }
4103
- }
4104
- });
4105
-
4106
- return {
4107
- isValid: errors.length === 0,
4108
- errors
4109
- };
4110
- }
4111
-
4112
4172
  // Advanced search functionality
4113
4173
  enableSearch(options = {}) {
4114
4174
  const searchOptions = {