@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/CHANGES.md +6 -0
- package/ftable.esm.js +181 -121
- package/ftable.js +181 -121
- package/ftable.min.js +3 -3
- package/ftable.umd.js +181 -121
- package/package.json +1 -1
package/CHANGES.md
CHANGED
package/ftable.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
// Modern fTable - Vanilla JS Refactor
|
|
3
3
|
|
|
4
|
-
const
|
|
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!',
|
|
@@ -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
|
-
|
|
979
|
-
|
|
980
|
-
attributes
|
|
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
|
-
|
|
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: { ...
|
|
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(
|
|
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
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|
|
@@ -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
|
-
|
|
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
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
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 = {
|