@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/ftable.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
}(this, (function () {
|
|
7
7
|
// Modern fTable - Vanilla JS Refactor
|
|
8
8
|
|
|
9
|
-
const
|
|
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
|
-
|
|
984
|
-
|
|
985
|
-
attributes
|
|
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
|
-
|
|
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: { ...
|
|
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(
|
|
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
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
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 = {
|