@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/CHANGES.md CHANGED
@@ -1,3 +1,12 @@
1
+ = 1.1.3 (2025/08/03) =
2
+ * support fdatepicker
3
+
4
+ = 1.1.2 (2025/08/01) =
5
+ * support defaultDateLocale
6
+
7
+ = 1.1.1 (2025/08/01) =
8
+ * Correct build and include esm
9
+
1
10
  = 1.1.0 (2025/08/01) =
2
11
  * Make sure all missing options are added and doc wiki is more up to date
3
12
 
package/ftable.esm.js CHANGED
@@ -1,6 +1,7 @@
1
+
1
2
  // Modern fTable - Vanilla JS Refactor
2
3
 
3
- const JTABLE_DEFAULT_MESSAGES = {
4
+ const FTABLE_DEFAULT_MESSAGES = {
4
5
  serverCommunicationError: 'An error occurred while communicating to the server.',
5
6
  loadingMessage: 'Loading records...',
6
7
  noDataAvailable: 'No data available!',
@@ -903,6 +904,10 @@ class FTableFormBuilder {
903
904
  case 'file':
904
905
  input = this.createFileInput(fieldName, field, value);
905
906
  break;
907
+ case 'date':
908
+ case 'datetime-local':
909
+ input = this.createDateInput(fieldName, field, value);
910
+ break;
906
911
  default:
907
912
  input = this.createTypedInput(fieldName, field, value);
908
913
  }
@@ -947,14 +952,65 @@ class FTableFormBuilder {
947
952
  return container;
948
953
  }
949
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
+
950
1007
  createTypedInput(fieldName, field, value) {
951
1008
  const inputType = field.type || 'text';
952
1009
  const attributes = {
953
1010
  type: inputType,
954
1011
  id: `Edit-${fieldName}`,
955
1012
  placeholder: field.placeholder || '',
956
- value: value || '',
957
- class: field.inputClass || ''
1013
+ value: value || ''
958
1014
  };
959
1015
 
960
1016
  // extra check for name and multiple
@@ -974,22 +1030,10 @@ class FTableFormBuilder {
974
1030
  }
975
1031
  attributes.name = name;
976
1032
 
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 });
1033
+ const input = FTableDOMHelper.create('input', {
1034
+ className: field.inputClass || '',
1035
+ attributes: attributes
1036
+ });
993
1037
 
994
1038
  // Prevent form submit on Enter, trigger change instead
995
1039
  input.addEventListener('keypress', (e) => {
@@ -1236,18 +1280,6 @@ class FTableFormBuilder {
1236
1280
  return wrapper;
1237
1281
  }
1238
1282
 
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
1283
  populateSelectOptions(select, options, selectedValue) {
1252
1284
  select.innerHTML = ''; // Clear existing options
1253
1285
 
@@ -1330,6 +1362,7 @@ class FTable extends FTableEventEmitter {
1330
1362
  }
1331
1363
 
1332
1364
  this.options = this.mergeOptions(options);
1365
+ this.verifyOptions();
1333
1366
  this.logger = new FTableLogger(this.options.logLevel);
1334
1367
  this.userPrefs = new FTableUserPreferences('', this.options.saveUserPreferencesMethod);
1335
1368
  this.formBuilder = new FTableFormBuilder(this.options, this);
@@ -1364,15 +1397,18 @@ class FTable extends FTableEventEmitter {
1364
1397
  fields: {},
1365
1398
  animationsEnabled: true,
1366
1399
  loadingAnimationDelay: 1000,
1367
- defaultDateFormat: 'yyyy-mm-dd',
1400
+ defaultDateLocale: 'en',
1401
+ defaultDateFormat: 'm/d/Y',
1368
1402
  saveUserPreferences: true,
1369
1403
  saveUserPreferencesMethod: 'localStorage',
1370
1404
  defaultSorting: '',
1405
+ tableReset: false,
1371
1406
 
1372
1407
  // Paging
1373
1408
  paging: false,
1374
1409
  pageList: 'normal',
1375
1410
  pageSize: 10,
1411
+ pageSizes: [10, 25, 50, 100],
1376
1412
  gotoPageArea: 'combobox',
1377
1413
 
1378
1414
  // Sorting
@@ -1396,7 +1432,7 @@ class FTable extends FTableEventEmitter {
1396
1432
  listCache: 30000, // or listCache: 30000 (duration in ms)
1397
1433
 
1398
1434
  // Messages
1399
- messages: { ...JTABLE_DEFAULT_MESSAGES } // Safe copy
1435
+ messages: { ...FTABLE_DEFAULT_MESSAGES } // Safe copy
1400
1436
  };
1401
1437
 
1402
1438
  return this.deepMerge(defaults, options);
@@ -1416,9 +1452,15 @@ class FTable extends FTableEventEmitter {
1416
1452
  return result;
1417
1453
  }
1418
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
+
1419
1461
  // Public
1420
1462
  static setMessages(customMessages) {
1421
- Object.assign(JTABLE_DEFAULT_MESSAGES, customMessages);
1463
+ Object.assign(FTABLE_DEFAULT_MESSAGES, customMessages);
1422
1464
  }
1423
1465
 
1424
1466
  init() {
@@ -1885,16 +1927,49 @@ class FTable extends FTableEventEmitter {
1885
1927
  if (!field.type && field.options) {
1886
1928
  field.type = 'select';
1887
1929
  }
1930
+ const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
1888
1931
 
1889
1932
  switch (field.type) {
1890
1933
  case 'date':
1891
- input = FTableDOMHelper.create('input', {
1892
- attributes: {
1893
- type: 'date',
1894
- 'data-field-name': fieldName,
1895
- class: 'ftable-toolbarsearch'
1896
- }
1897
- });
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
+ }
1898
1973
  break;
1899
1974
 
1900
1975
  case 'checkbox':
@@ -1902,10 +1977,11 @@ class FTable extends FTableEventEmitter {
1902
1977
  input = await this.createSelectForSearch(fieldName, field, true);
1903
1978
  } else {
1904
1979
  input = FTableDOMHelper.create('input', {
1980
+ className: 'ftable-toolbarsearch',
1905
1981
  attributes: {
1906
1982
  type: 'text',
1907
1983
  'data-field-name': fieldName,
1908
- class: 'ftable-toolbarsearch',
1984
+ id: fieldSearchName,
1909
1985
  placeholder: 'Search...'
1910
1986
  }
1911
1987
  });
@@ -1917,10 +1993,11 @@ class FTable extends FTableEventEmitter {
1917
1993
  input = await this.createSelectForSearch(fieldName, field, false);
1918
1994
  } else {
1919
1995
  input = FTableDOMHelper.create('input', {
1996
+ className: 'ftable-toolbarsearch',
1920
1997
  attributes: {
1921
1998
  type: 'text',
1922
1999
  'data-field-name': fieldName,
1923
- class: 'ftable-toolbarsearch',
2000
+ id: fieldSearchName,
1924
2001
  placeholder: 'Search...'
1925
2002
  }
1926
2003
  });
@@ -1929,10 +2006,11 @@ class FTable extends FTableEventEmitter {
1929
2006
 
1930
2007
  default:
1931
2008
  input = FTableDOMHelper.create('input', {
2009
+ className: 'ftable-toolbarsearch',
1932
2010
  attributes: {
1933
2011
  type: 'text',
1934
2012
  'data-field-name': fieldName,
1935
- class: 'ftable-toolbarsearch',
2013
+ id: fieldSearchName,
1936
2014
  placeholder: 'Search...'
1937
2015
  }
1938
2016
  });
@@ -1985,9 +2063,11 @@ class FTable extends FTableEventEmitter {
1985
2063
  }
1986
2064
 
1987
2065
  async createSelectForSearch(fieldName, field, isCheckboxValues) {
2066
+ const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
1988
2067
  const select = FTableDOMHelper.create('select', {
1989
2068
  attributes: {
1990
2069
  'data-field-name': fieldName,
2070
+ id: fieldSearchName,
1991
2071
  class: 'ftable-toolbarsearch'
1992
2072
  }
1993
2073
  });
@@ -2211,7 +2291,7 @@ class FTable extends FTableEventEmitter {
2211
2291
  if (Array.isArray(state.sorting)) {
2212
2292
  this.state.sorting = state.sorting;
2213
2293
  }
2214
- if (state.pageSize) {
2294
+ if (state.pageSize && this.options.pageSizes.includes(state.pageSize)) {
2215
2295
  this.state.pageSize = state.pageSize;
2216
2296
  }
2217
2297
  } catch (error) {
@@ -2381,9 +2461,6 @@ class FTable extends FTableEventEmitter {
2381
2461
  this.createToolbarButtons();
2382
2462
  this.createCustomToolbarItems();
2383
2463
 
2384
- // Handle window unload
2385
- this.handlePageUnload();
2386
-
2387
2464
  // Keyboard shortcuts
2388
2465
  this.bindKeyboardEvents();
2389
2466
 
@@ -2670,20 +2747,6 @@ class FTable extends FTableEventEmitter {
2670
2747
  });
2671
2748
  }
2672
2749
 
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
2750
  bindKeyboardEvents() {
2688
2751
  if (this.options.selecting) {
2689
2752
  this.shiftKeyDown = false;
@@ -3011,7 +3074,19 @@ class FTable extends FTableEventEmitter {
3011
3074
  }
3012
3075
 
3013
3076
  if (field.type === 'date' && value) {
3014
- 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
+ }
3015
3090
  }
3016
3091
 
3017
3092
  if (field.type === 'checkbox') {
@@ -3026,18 +3101,53 @@ class FTable extends FTableEventEmitter {
3026
3101
  return value || '';
3027
3102
  }
3028
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
+
3029
3129
  formatDate(dateValue, format) {
3030
3130
  if (!dateValue) return '';
3031
-
3032
- const date = new Date(dateValue);
3033
- if (isNaN(date.getTime())) return dateValue;
3034
3131
 
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}`;
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
+ }
3041
3151
  }
3042
3152
 
3043
3153
  getCheckboxText(fieldName, value) {
@@ -4059,55 +4169,6 @@ class FTable extends FTableEventEmitter {
4059
4169
  });
4060
4170
  }
4061
4171
 
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
4172
  // Advanced search functionality
4112
4173
  enableSearch(options = {}) {
4113
4174
  const searchOptions = {
@@ -4783,3 +4844,5 @@ table.load();
4783
4844
  */
4784
4845
 
4785
4846
  window.FTable = FTable;
4847
+
4848
+ export default FTable;