@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.umd.js CHANGED
@@ -1,7 +1,12 @@
1
- /* UMD wrapper will go here */
2
- // Modern fTable - Vanilla JS Refactor
3
1
 
4
- 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 = {
5
10
  serverCommunicationError: 'An error occurred while communicating to the server.',
6
11
  loadingMessage: 'Loading records...',
7
12
  noDataAvailable: 'No data available!',
@@ -904,6 +909,10 @@ class FTableFormBuilder {
904
909
  case 'file':
905
910
  input = this.createFileInput(fieldName, field, value);
906
911
  break;
912
+ case 'date':
913
+ case 'datetime-local':
914
+ input = this.createDateInput(fieldName, field, value);
915
+ break;
907
916
  default:
908
917
  input = this.createTypedInput(fieldName, field, value);
909
918
  }
@@ -948,14 +957,65 @@ class FTableFormBuilder {
948
957
  return container;
949
958
  }
950
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
+
951
1012
  createTypedInput(fieldName, field, value) {
952
1013
  const inputType = field.type || 'text';
953
1014
  const attributes = {
954
1015
  type: inputType,
955
1016
  id: `Edit-${fieldName}`,
956
1017
  placeholder: field.placeholder || '',
957
- value: value || '',
958
- class: field.inputClass || ''
1018
+ value: value || ''
959
1019
  };
960
1020
 
961
1021
  // extra check for name and multiple
@@ -975,22 +1035,10 @@ class FTableFormBuilder {
975
1035
  }
976
1036
  attributes.name = name;
977
1037
 
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 });
1038
+ const input = FTableDOMHelper.create('input', {
1039
+ className: field.inputClass || '',
1040
+ attributes: attributes
1041
+ });
994
1042
 
995
1043
  // Prevent form submit on Enter, trigger change instead
996
1044
  input.addEventListener('keypress', (e) => {
@@ -1237,18 +1285,6 @@ class FTableFormBuilder {
1237
1285
  return wrapper;
1238
1286
  }
1239
1287
 
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
1288
  populateSelectOptions(select, options, selectedValue) {
1253
1289
  select.innerHTML = ''; // Clear existing options
1254
1290
 
@@ -1331,6 +1367,7 @@ class FTable extends FTableEventEmitter {
1331
1367
  }
1332
1368
 
1333
1369
  this.options = this.mergeOptions(options);
1370
+ this.verifyOptions();
1334
1371
  this.logger = new FTableLogger(this.options.logLevel);
1335
1372
  this.userPrefs = new FTableUserPreferences('', this.options.saveUserPreferencesMethod);
1336
1373
  this.formBuilder = new FTableFormBuilder(this.options, this);
@@ -1365,15 +1402,18 @@ class FTable extends FTableEventEmitter {
1365
1402
  fields: {},
1366
1403
  animationsEnabled: true,
1367
1404
  loadingAnimationDelay: 1000,
1368
- defaultDateFormat: 'yyyy-mm-dd',
1405
+ defaultDateLocale: 'en',
1406
+ defaultDateFormat: 'm/d/Y',
1369
1407
  saveUserPreferences: true,
1370
1408
  saveUserPreferencesMethod: 'localStorage',
1371
1409
  defaultSorting: '',
1410
+ tableReset: false,
1372
1411
 
1373
1412
  // Paging
1374
1413
  paging: false,
1375
1414
  pageList: 'normal',
1376
1415
  pageSize: 10,
1416
+ pageSizes: [10, 25, 50, 100],
1377
1417
  gotoPageArea: 'combobox',
1378
1418
 
1379
1419
  // Sorting
@@ -1397,7 +1437,7 @@ class FTable extends FTableEventEmitter {
1397
1437
  listCache: 30000, // or listCache: 30000 (duration in ms)
1398
1438
 
1399
1439
  // Messages
1400
- messages: { ...JTABLE_DEFAULT_MESSAGES } // Safe copy
1440
+ messages: { ...FTABLE_DEFAULT_MESSAGES } // Safe copy
1401
1441
  };
1402
1442
 
1403
1443
  return this.deepMerge(defaults, options);
@@ -1417,9 +1457,15 @@ class FTable extends FTableEventEmitter {
1417
1457
  return result;
1418
1458
  }
1419
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
+
1420
1466
  // Public
1421
1467
  static setMessages(customMessages) {
1422
- Object.assign(JTABLE_DEFAULT_MESSAGES, customMessages);
1468
+ Object.assign(FTABLE_DEFAULT_MESSAGES, customMessages);
1423
1469
  }
1424
1470
 
1425
1471
  init() {
@@ -1886,16 +1932,49 @@ class FTable extends FTableEventEmitter {
1886
1932
  if (!field.type && field.options) {
1887
1933
  field.type = 'select';
1888
1934
  }
1935
+ const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
1889
1936
 
1890
1937
  switch (field.type) {
1891
1938
  case 'date':
1892
- input = FTableDOMHelper.create('input', {
1893
- attributes: {
1894
- type: 'date',
1895
- 'data-field-name': fieldName,
1896
- class: 'ftable-toolbarsearch'
1897
- }
1898
- });
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
+ }
1899
1978
  break;
1900
1979
 
1901
1980
  case 'checkbox':
@@ -1903,10 +1982,11 @@ class FTable extends FTableEventEmitter {
1903
1982
  input = await this.createSelectForSearch(fieldName, field, true);
1904
1983
  } else {
1905
1984
  input = FTableDOMHelper.create('input', {
1985
+ className: 'ftable-toolbarsearch',
1906
1986
  attributes: {
1907
1987
  type: 'text',
1908
1988
  'data-field-name': fieldName,
1909
- class: 'ftable-toolbarsearch',
1989
+ id: fieldSearchName,
1910
1990
  placeholder: 'Search...'
1911
1991
  }
1912
1992
  });
@@ -1918,10 +1998,11 @@ class FTable extends FTableEventEmitter {
1918
1998
  input = await this.createSelectForSearch(fieldName, field, false);
1919
1999
  } else {
1920
2000
  input = FTableDOMHelper.create('input', {
2001
+ className: 'ftable-toolbarsearch',
1921
2002
  attributes: {
1922
2003
  type: 'text',
1923
2004
  'data-field-name': fieldName,
1924
- class: 'ftable-toolbarsearch',
2005
+ id: fieldSearchName,
1925
2006
  placeholder: 'Search...'
1926
2007
  }
1927
2008
  });
@@ -1930,10 +2011,11 @@ class FTable extends FTableEventEmitter {
1930
2011
 
1931
2012
  default:
1932
2013
  input = FTableDOMHelper.create('input', {
2014
+ className: 'ftable-toolbarsearch',
1933
2015
  attributes: {
1934
2016
  type: 'text',
1935
2017
  'data-field-name': fieldName,
1936
- class: 'ftable-toolbarsearch',
2018
+ id: fieldSearchName,
1937
2019
  placeholder: 'Search...'
1938
2020
  }
1939
2021
  });
@@ -1986,9 +2068,11 @@ class FTable extends FTableEventEmitter {
1986
2068
  }
1987
2069
 
1988
2070
  async createSelectForSearch(fieldName, field, isCheckboxValues) {
2071
+ const fieldSearchName = 'ftable-toolbarsearch-' + fieldName;
1989
2072
  const select = FTableDOMHelper.create('select', {
1990
2073
  attributes: {
1991
2074
  'data-field-name': fieldName,
2075
+ id: fieldSearchName,
1992
2076
  class: 'ftable-toolbarsearch'
1993
2077
  }
1994
2078
  });
@@ -2212,7 +2296,7 @@ class FTable extends FTableEventEmitter {
2212
2296
  if (Array.isArray(state.sorting)) {
2213
2297
  this.state.sorting = state.sorting;
2214
2298
  }
2215
- if (state.pageSize) {
2299
+ if (state.pageSize && this.options.pageSizes.includes(state.pageSize)) {
2216
2300
  this.state.pageSize = state.pageSize;
2217
2301
  }
2218
2302
  } catch (error) {
@@ -2382,9 +2466,6 @@ class FTable extends FTableEventEmitter {
2382
2466
  this.createToolbarButtons();
2383
2467
  this.createCustomToolbarItems();
2384
2468
 
2385
- // Handle window unload
2386
- this.handlePageUnload();
2387
-
2388
2469
  // Keyboard shortcuts
2389
2470
  this.bindKeyboardEvents();
2390
2471
 
@@ -2671,20 +2752,6 @@ class FTable extends FTableEventEmitter {
2671
2752
  });
2672
2753
  }
2673
2754
 
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
2755
  bindKeyboardEvents() {
2689
2756
  if (this.options.selecting) {
2690
2757
  this.shiftKeyDown = false;
@@ -3012,7 +3079,19 @@ class FTable extends FTableEventEmitter {
3012
3079
  }
3013
3080
 
3014
3081
  if (field.type === 'date' && value) {
3015
- 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
+ }
3016
3095
  }
3017
3096
 
3018
3097
  if (field.type === 'checkbox') {
@@ -3027,18 +3106,53 @@ class FTable extends FTableEventEmitter {
3027
3106
  return value || '';
3028
3107
  }
3029
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
+
3030
3134
  formatDate(dateValue, format) {
3031
3135
  if (!dateValue) return '';
3032
-
3033
- const date = new Date(dateValue);
3034
- if (isNaN(date.getTime())) return dateValue;
3035
3136
 
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}`;
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
+ }
3042
3156
  }
3043
3157
 
3044
3158
  getCheckboxText(fieldName, value) {
@@ -4060,55 +4174,6 @@ class FTable extends FTableEventEmitter {
4060
4174
  });
4061
4175
  }
4062
4176
 
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
4177
  // Advanced search functionality
4113
4178
  enableSearch(options = {}) {
4114
4179
  const searchOptions = {
@@ -4784,3 +4849,6 @@ table.load();
4784
4849
  */
4785
4850
 
4786
4851
  window.FTable = FTable;
4852
+
4853
+ return FTable;
4854
+ })));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liedekef/ftable",
3
- "version": "1.1.0",
3
+ "version": "1.1.3",
4
4
  "description": "Modern, lightweight, jQuery-free CRUD table for dynamic AJAX-powered tables.",
5
5
  "main": "ftable.js",
6
6
  "module": "ftable.esm.js",
@@ -15,9 +15,8 @@
15
15
  "README.md"
16
16
  ],
17
17
  "scripts": {
18
- "build": "npm run build:esm && npm run build:umd && npm run build:min",
19
- "build:esm": "cp ftable.js ftable.esm.js",
20
- "build:umd": "echo '/* UMD wrapper will go here */' > ftable.umd.js && cat ftable.js >> ftable.umd.js",
18
+ "build": "npm run build:esmumd && npm run build:min",
19
+ "build:esmumd": "node build.mjs",
21
20
  "build:min": "uglifyjs ftable.js -o ftable.min.js --compress --mangle",
22
21
  "prepublishOnly": "npm run build"
23
22
  },