vgapp 1.2.5 → 1.3.0
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/README.md +131 -20
- package/app/modules/base-module.js +111 -17
- package/app/modules/vgalert/js/vgalert.js +3 -3
- package/app/modules/vgdynamictable/index.js +5 -0
- package/app/modules/vgdynamictable/js/editable.js +438 -0
- package/app/modules/vgdynamictable/js/expandable.js +248 -0
- package/app/modules/vgdynamictable/js/filters.js +450 -0
- package/app/modules/vgdynamictable/js/fixed.js +566 -0
- package/app/modules/vgdynamictable/js/options.js +646 -0
- package/app/modules/vgdynamictable/js/pagination.js +623 -0
- package/app/modules/vgdynamictable/js/search.js +82 -0
- package/app/modules/vgdynamictable/js/skeleton.js +136 -0
- package/app/modules/vgdynamictable/js/sortable.js +442 -0
- package/app/modules/vgdynamictable/js/summary-footer.js +284 -0
- package/app/modules/vgdynamictable/js/table-remote.js +821 -0
- package/app/modules/vgdynamictable/js/table-state.js +243 -0
- package/app/modules/vgdynamictable/js/table-url-state.js +444 -0
- package/app/modules/vgdynamictable/js/utils/common.js +48 -0
- package/app/modules/vgdynamictable/js/vgdynamictable.js +3829 -0
- package/app/modules/vgdynamictable/js/viewport.js +322 -0
- package/app/modules/vgdynamictable/readme.md +251 -0
- package/app/modules/vgdynamictable/scss/_actions.scss +193 -0
- package/app/modules/vgdynamictable/scss/_pagination.scss +183 -0
- package/app/modules/vgdynamictable/scss/_skeleton.scss +60 -0
- package/app/modules/vgdynamictable/scss/_sortable.scss +63 -0
- package/app/modules/vgdynamictable/scss/_table.scss +157 -0
- package/app/modules/vgdynamictable/scss/_variables.scss +130 -0
- package/app/modules/vgdynamictable/scss/vgdynamictable.scss +267 -0
- package/app/modules/vgrangeslider/index.js +3 -0
- package/app/modules/vgrangeslider/js/skins.js +222 -0
- package/app/modules/vgrangeslider/js/vgrangeslider.js +704 -0
- package/app/modules/vgrangeslider/readme.md +523 -0
- package/app/modules/vgrangeslider/scss/_variables.scss +53 -0
- package/app/modules/vgrangeslider/scss/vgrangeslider.scss +240 -0
- package/app/utils/js/components/ajax.js +116 -7
- package/app/vgapp.js +107 -0
- package/build/vgapp.css +2 -3246
- package/build/vgapp.css.map +1 -1
- package/build/vgapp.js +3 -30
- package/build/vgapp.js.LICENSE.txt +1 -0
- package/build/vgapp.js.map +1 -1
- package/index.js +15 -8
- package/index.scss +6 -0
- package/package.json +21 -1
- package/.gitattributes +0 -1
- package/CHANGELOG.md +0 -369
- package/agents.md +0 -14
- package/webpack.config.js +0 -63
|
@@ -0,0 +1,821 @@
|
|
|
1
|
+
const tableRemoteMethods = {
|
|
2
|
+
_getRemoteRouteRequestConfig(route = '') {
|
|
3
|
+
const requestOptions = this._params.request || {};
|
|
4
|
+
const attrMethod = this._element.getAttribute('data-request-method');
|
|
5
|
+
const attrCredentials = this._element.getAttribute('data-request-credentials');
|
|
6
|
+
const attrParams = this._element.getAttribute('data-request-params');
|
|
7
|
+
let paramsFromAttr = {};
|
|
8
|
+
|
|
9
|
+
if (attrParams !== null && String(attrParams).trim() !== '') {
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(attrParams);
|
|
12
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
13
|
+
paramsFromAttr = parsed;
|
|
14
|
+
}
|
|
15
|
+
} catch (error) {
|
|
16
|
+
paramsFromAttr = {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
route: route || this._getRemoteDataRoute(),
|
|
22
|
+
method: attrMethod !== null ? attrMethod : requestOptions.method,
|
|
23
|
+
credentials: attrCredentials !== null ? attrCredentials : requestOptions.credentials,
|
|
24
|
+
headers: requestOptions.headers && typeof requestOptions.headers === 'object'
|
|
25
|
+
? Object.assign({}, requestOptions.headers)
|
|
26
|
+
: { 'Accept': 'application/json' },
|
|
27
|
+
baseParams: Object.assign(
|
|
28
|
+
{},
|
|
29
|
+
requestOptions.params && typeof requestOptions.params === 'object' ? requestOptions.params : {},
|
|
30
|
+
paramsFromAttr
|
|
31
|
+
),
|
|
32
|
+
responseType: 'json',
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
_isRemoteCacheEnabled() {
|
|
37
|
+
const requestOptions = this._params.request || {};
|
|
38
|
+
const requestCacheOptions = requestOptions.cache && typeof requestOptions.cache === 'object'
|
|
39
|
+
? requestOptions.cache
|
|
40
|
+
: {};
|
|
41
|
+
const attr = this._element.getAttribute('data-request-cache-enable');
|
|
42
|
+
if (attr !== null) {
|
|
43
|
+
return this._isTruthy(attr);
|
|
44
|
+
}
|
|
45
|
+
const fromOptions = requestCacheOptions.enable;
|
|
46
|
+
if (fromOptions === undefined || fromOptions === null) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return Boolean(fromOptions);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
_getRemoteCacheTtlMs() {
|
|
53
|
+
const requestOptions = this._params.request || {};
|
|
54
|
+
const requestCacheOptions = requestOptions.cache && typeof requestOptions.cache === 'object'
|
|
55
|
+
? requestOptions.cache
|
|
56
|
+
: {};
|
|
57
|
+
const attr = this._element.getAttribute('data-request-cache-ttl');
|
|
58
|
+
const raw = attr !== null ? attr : requestCacheOptions.ttl;
|
|
59
|
+
const parsed = Number.parseInt(raw, 10);
|
|
60
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 30000;
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
_getRemoteCacheMaxEntries() {
|
|
64
|
+
const requestOptions = this._params.request || {};
|
|
65
|
+
const requestCacheOptions = requestOptions.cache && typeof requestOptions.cache === 'object'
|
|
66
|
+
? requestOptions.cache
|
|
67
|
+
: {};
|
|
68
|
+
const attr = this._element.getAttribute('data-request-cache-max');
|
|
69
|
+
const raw = attr !== null ? attr : requestCacheOptions.max;
|
|
70
|
+
const parsed = Number.parseInt(raw, 10);
|
|
71
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 30;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
_getRemoteCacheKey(endpoint, params = {}) {
|
|
75
|
+
const keys = Object.keys(params || {}).sort();
|
|
76
|
+
const query = keys.map((key) => {
|
|
77
|
+
const value = params[key];
|
|
78
|
+
const normalized = Array.isArray(value)
|
|
79
|
+
? value.join(',')
|
|
80
|
+
: String(value === undefined || value === null ? '' : value);
|
|
81
|
+
return `${encodeURIComponent(key)}=${encodeURIComponent(normalized)}`;
|
|
82
|
+
}).join('&');
|
|
83
|
+
return `${String(endpoint || '').trim()}?${query}`;
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
_readRemoteCache(endpoint, params = {}) {
|
|
87
|
+
if (!this._isRemoteCacheEnabled()) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
if (!this._remotePageCache || typeof this._remotePageCache.get !== 'function') {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const key = this._getRemoteCacheKey(endpoint, params);
|
|
94
|
+
const entry = this._remotePageCache.get(key);
|
|
95
|
+
if (!entry || typeof entry !== 'object') {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const ttlMs = this._getRemoteCacheTtlMs();
|
|
99
|
+
const age = Date.now() - Number(entry.ts || 0);
|
|
100
|
+
if (!Number.isFinite(age) || age < 0 || age > ttlMs) {
|
|
101
|
+
this._remotePageCache.delete(key);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
entry.lastUsedAt = Date.now();
|
|
105
|
+
return entry.response;
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
_writeRemoteCache(endpoint, params = {}, response) {
|
|
109
|
+
if (!this._isRemoteCacheEnabled()) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!this._remotePageCache || typeof this._remotePageCache.set !== 'function') {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const key = this._getRemoteCacheKey(endpoint, params);
|
|
116
|
+
this._remotePageCache.set(key, {
|
|
117
|
+
ts: Date.now(),
|
|
118
|
+
lastUsedAt: Date.now(),
|
|
119
|
+
response,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const maxEntries = this._getRemoteCacheMaxEntries();
|
|
123
|
+
if (this._remotePageCache.size <= maxEntries) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const entries = Array.from(this._remotePageCache.entries());
|
|
127
|
+
entries.sort((a, b) => Number((a[1] && a[1].lastUsedAt) || 0) - Number((b[1] && b[1].lastUsedAt) || 0));
|
|
128
|
+
while (this._remotePageCache.size > maxEntries && entries.length) {
|
|
129
|
+
const next = entries.shift();
|
|
130
|
+
if (next) {
|
|
131
|
+
this._remotePageCache.delete(next[0]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
_applyRemoteResponse(response, safePage, safePerPage, userOnChange = null, options = {}) {
|
|
137
|
+
const rows = this._extractRemoteRows(response);
|
|
138
|
+
const meta = this._extractRemoteMeta(response);
|
|
139
|
+
const fromCache = Boolean(options.fromCache);
|
|
140
|
+
const requestContext = options.requestContext && typeof options.requestContext === 'object'
|
|
141
|
+
? options.requestContext
|
|
142
|
+
: null;
|
|
143
|
+
const requestId = String(
|
|
144
|
+
(meta && (meta.request_id || meta.requestId))
|
|
145
|
+
|| (requestContext && requestContext.requestId)
|
|
146
|
+
|| ''
|
|
147
|
+
).trim();
|
|
148
|
+
this._lastRemoteMeta = meta;
|
|
149
|
+
this._renderSummary(meta);
|
|
150
|
+
|
|
151
|
+
const viewRowsHtml = this._extractRemoteViewRowsHtml(response);
|
|
152
|
+
const didRenderView = this._renderRemoteViewIfConfigured(viewRowsHtml, rows);
|
|
153
|
+
if (!didRenderView) {
|
|
154
|
+
this._renderRemoteRows(rows);
|
|
155
|
+
}
|
|
156
|
+
this._renderFooterFromRows(rows, meta);
|
|
157
|
+
this._updatePanHintVisibility();
|
|
158
|
+
this._refreshStickyAndFixedLayout();
|
|
159
|
+
|
|
160
|
+
const nextPage = this._normalizePositiveInt(meta.page, safePage);
|
|
161
|
+
const nextPerPage = this._clampPerPage(meta.per_page, safePerPage);
|
|
162
|
+
const totalPages = this._normalizePositiveInt(meta.pages, 1);
|
|
163
|
+
this._pageState = { page: nextPage, perPage: nextPerPage };
|
|
164
|
+
this._storePage(nextPage);
|
|
165
|
+
this._storePerPage(nextPerPage);
|
|
166
|
+
|
|
167
|
+
const nextSort = typeof meta.sort === 'string' ? meta.sort.trim() : this._sortState.field;
|
|
168
|
+
const nextDir = meta && typeof meta.dir === 'string' ? meta.dir : this._sortState.dir;
|
|
169
|
+
if (nextSort) {
|
|
170
|
+
const metaSorts = this._parseSortsFromStrings(nextSort, nextDir);
|
|
171
|
+
this._sortState = Object.assign({}, this._sortState, {
|
|
172
|
+
field: metaSorts[0] ? metaSorts[0].field : nextSort,
|
|
173
|
+
dir: metaSorts[0] ? metaSorts[0].dir : this._normalizeSortDir(nextDir),
|
|
174
|
+
sorts: metaSorts,
|
|
175
|
+
});
|
|
176
|
+
if (this._sortable) {
|
|
177
|
+
this._sortable.setState({
|
|
178
|
+
sorts: this._sortState.sorts,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (this._pagination) {
|
|
184
|
+
this._pagination.setMeta({
|
|
185
|
+
page: nextPage,
|
|
186
|
+
perPage: nextPerPage,
|
|
187
|
+
totalPages,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (typeof userOnChange === 'function') {
|
|
192
|
+
userOnChange({
|
|
193
|
+
page: nextPage,
|
|
194
|
+
perPage: nextPerPage,
|
|
195
|
+
totalPages,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
this._syncUrlState();
|
|
199
|
+
const received = {
|
|
200
|
+
rows: Array.isArray(rows) ? rows.slice() : [],
|
|
201
|
+
total: this._normalizePositiveInt(meta && meta.total, Array.isArray(rows) ? rows.length : 0),
|
|
202
|
+
serverMeta: Object.assign({}, meta || {}),
|
|
203
|
+
raw: response,
|
|
204
|
+
};
|
|
205
|
+
const sent = requestContext && requestContext.sent
|
|
206
|
+
? Object.assign({}, requestContext.sent)
|
|
207
|
+
: {};
|
|
208
|
+
const requestTrigger = requestContext && typeof requestContext.trigger === 'string'
|
|
209
|
+
? String(requestContext.trigger).toLowerCase().trim()
|
|
210
|
+
: '';
|
|
211
|
+
const requestFiltersState = requestContext && requestContext.filtersState && typeof requestContext.filtersState === 'object'
|
|
212
|
+
? this._normalizeFiltersEventState(requestContext.filtersState)
|
|
213
|
+
: null;
|
|
214
|
+
if (requestTrigger === 'filterschange' && this._isFiltersEmitFullContextEnabled()) {
|
|
215
|
+
const fallbackState = this._normalizeFiltersEventState({
|
|
216
|
+
filters: sent.filters || {},
|
|
217
|
+
params: sent.params || {},
|
|
218
|
+
});
|
|
219
|
+
const eventState = requestFiltersState || fallbackState;
|
|
220
|
+
const isSearched = this._isSearchActiveFromParams(sent.params || {});
|
|
221
|
+
this._emitAction('filterschange', {
|
|
222
|
+
filters: Object.assign({}, eventState.filters || {}),
|
|
223
|
+
params: Object.assign({}, eventState.params || {}),
|
|
224
|
+
fields: Array.isArray(eventState.fields) ? eventState.fields.slice() : [],
|
|
225
|
+
meta: Object.assign({}, eventState.meta || {}),
|
|
226
|
+
phase: 'response',
|
|
227
|
+
isFiltered: this._isFilteredState(eventState) || isSearched,
|
|
228
|
+
isSearched,
|
|
229
|
+
requestId,
|
|
230
|
+
fromCache,
|
|
231
|
+
sent,
|
|
232
|
+
received,
|
|
233
|
+
response,
|
|
234
|
+
serverMeta: Object.assign({}, received.serverMeta || {}),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
this._emitAction('requestsuccess', {
|
|
238
|
+
requestId,
|
|
239
|
+
mode: 'remote',
|
|
240
|
+
responsemode: this._getRemoteResponseMode(),
|
|
241
|
+
sent,
|
|
242
|
+
received,
|
|
243
|
+
fromCache,
|
|
244
|
+
});
|
|
245
|
+
this._emitAction('dataloaded', {
|
|
246
|
+
requestId,
|
|
247
|
+
page: nextPage,
|
|
248
|
+
perPage: nextPerPage,
|
|
249
|
+
totalPages,
|
|
250
|
+
rows,
|
|
251
|
+
view: viewRowsHtml,
|
|
252
|
+
meta,
|
|
253
|
+
fromCache,
|
|
254
|
+
sent,
|
|
255
|
+
received,
|
|
256
|
+
});
|
|
257
|
+
this._emitAction('afterrender', {
|
|
258
|
+
requestId,
|
|
259
|
+
renderedCount: Array.isArray(rows) ? rows.length : 0,
|
|
260
|
+
total: received.total,
|
|
261
|
+
sent,
|
|
262
|
+
received,
|
|
263
|
+
});
|
|
264
|
+
this._emitAction('statesync', {
|
|
265
|
+
requestId,
|
|
266
|
+
state: {
|
|
267
|
+
page: nextPage,
|
|
268
|
+
perPage: nextPerPage,
|
|
269
|
+
sorts: this._getNormalizedSorts(this._sortState.sorts),
|
|
270
|
+
filters: Object.assign({}, this._activeFilters || {}),
|
|
271
|
+
params: Object.assign({}, this._remoteParams || {}),
|
|
272
|
+
},
|
|
273
|
+
sent,
|
|
274
|
+
received,
|
|
275
|
+
});
|
|
276
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
277
|
+
this._emitAction('emptyresult', {
|
|
278
|
+
requestId,
|
|
279
|
+
reason: 'no_matches',
|
|
280
|
+
sent,
|
|
281
|
+
received,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
_buildRemoteRequestParams(page, perPage) {
|
|
287
|
+
const sortOptions = this._params.sortable || {};
|
|
288
|
+
const requestParams = {
|
|
289
|
+
page,
|
|
290
|
+
per_page: perPage,
|
|
291
|
+
};
|
|
292
|
+
Object.assign(requestParams, this._remoteParams);
|
|
293
|
+
|
|
294
|
+
const responseMode = this._getRemoteResponseMode();
|
|
295
|
+
if (responseMode !== 'data') {
|
|
296
|
+
const viewParam = this._getRemoteViewParamName();
|
|
297
|
+
const viewValue = this._getRemoteViewParamValue();
|
|
298
|
+
if (viewParam && viewValue && !Object.prototype.hasOwnProperty.call(requestParams, viewParam)) {
|
|
299
|
+
requestParams[viewParam] = viewValue;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const fieldsParam = this._getRemoteFieldsParamName();
|
|
303
|
+
if (fieldsParam && this._fields.length && !Object.prototype.hasOwnProperty.call(requestParams, fieldsParam)) {
|
|
304
|
+
requestParams[fieldsParam] = this._fields.join(',');
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (sortOptions.enable && this._sortState.field) {
|
|
309
|
+
const remoteFieldParam = 'sort';
|
|
310
|
+
const remoteDirParam = 'dir';
|
|
311
|
+
const sorts = this._getNormalizedSorts(this._sortState.sorts);
|
|
312
|
+
if (sorts.length > 0) {
|
|
313
|
+
requestParams[remoteFieldParam] = sorts.map((item) => item.field).join(',');
|
|
314
|
+
requestParams[remoteDirParam] = sorts.map((item) => item.dir).join(',');
|
|
315
|
+
} else {
|
|
316
|
+
requestParams[remoteFieldParam] = this._sortState.field;
|
|
317
|
+
requestParams[remoteDirParam] = this._sortState.dir;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return requestParams;
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
exportRemote(format = 'csv', options = {}) {
|
|
325
|
+
if (!this._isRemote) {
|
|
326
|
+
return '';
|
|
327
|
+
}
|
|
328
|
+
const requestOptions = this._params.request || {};
|
|
329
|
+
|
|
330
|
+
const normalizedFormat = String(format || 'csv').toLowerCase().trim() === 'xlsx'
|
|
331
|
+
? 'xlsx'
|
|
332
|
+
: 'csv';
|
|
333
|
+
const safePage = this._normalizePositiveInt(
|
|
334
|
+
options.page !== undefined ? options.page : this._pageState.page,
|
|
335
|
+
1
|
|
336
|
+
);
|
|
337
|
+
const safePerPage = this._clampPerPage(
|
|
338
|
+
options.perPage !== undefined ? options.perPage : this._pageState.perPage,
|
|
339
|
+
this._getInitialPerPage()
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
const requestParams = this._buildRemoteRequestParams(safePage, safePerPage);
|
|
343
|
+
requestParams.format = normalizedFormat;
|
|
344
|
+
const mappedRequestParams = this._mapRemoteRequestParams(requestParams);
|
|
345
|
+
const endpoint = String(
|
|
346
|
+
options.endpoint
|
|
347
|
+
|| (requestOptions.export && requestOptions.export.route)
|
|
348
|
+
|| this._element.getAttribute('data-request-export-route')
|
|
349
|
+
|| this._getRemoteDataRoute()
|
|
350
|
+
).trim();
|
|
351
|
+
const exportEndpoint = endpoint || this._getRemoteDataRoute();
|
|
352
|
+
const exportUrl = this._buildRouteUrl(
|
|
353
|
+
mappedRequestParams,
|
|
354
|
+
exportEndpoint,
|
|
355
|
+
this._getRemoteRouteRequestConfig(exportEndpoint)
|
|
356
|
+
).toString();
|
|
357
|
+
if (options.open !== false && typeof window !== 'undefined' && typeof window.open === 'function') {
|
|
358
|
+
window.open(exportUrl, '_blank', 'noopener');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
this._emitAction('export', {
|
|
362
|
+
format: normalizedFormat,
|
|
363
|
+
page: safePage,
|
|
364
|
+
perPage: safePerPage,
|
|
365
|
+
endpoint: exportEndpoint,
|
|
366
|
+
url: exportUrl,
|
|
367
|
+
});
|
|
368
|
+
return exportUrl;
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
async _loadRemotePage(page, perPage, userOnChange = null, requestMeta = null) {
|
|
372
|
+
if (!this._isRemote) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const safePage = this._normalizePositiveInt(page, 1);
|
|
377
|
+
const safePerPage = this._clampPerPage(perPage, this._getInitialPerPage());
|
|
378
|
+
const requestToken = ++this._requestToken;
|
|
379
|
+
const requestId = `req-${Date.now()}-${requestToken}`;
|
|
380
|
+
const requestParams = this._buildRemoteRequestParams(safePage, safePerPage);
|
|
381
|
+
const mappedRequestParams = this._mapRemoteRequestParams(requestParams);
|
|
382
|
+
const endpoint = this._getRemoteDataRoute();
|
|
383
|
+
const filtersOptions = this._params.filters || {};
|
|
384
|
+
const transport = filtersOptions.transport && typeof filtersOptions.transport === 'object'
|
|
385
|
+
? filtersOptions.transport
|
|
386
|
+
: {};
|
|
387
|
+
if (transport.includeMeta !== false) {
|
|
388
|
+
mappedRequestParams.request_id = requestId;
|
|
389
|
+
mappedRequestParams.responsemode = this._getRemoteResponseMode();
|
|
390
|
+
}
|
|
391
|
+
const sent = {
|
|
392
|
+
filters: Object.assign({}, this._activeFilters || {}),
|
|
393
|
+
sort: this._getNormalizedSorts(this._sortState.sorts || []),
|
|
394
|
+
page: safePage,
|
|
395
|
+
perPage: safePerPage,
|
|
396
|
+
params: Object.assign({}, this._remoteParams || {}),
|
|
397
|
+
urlState: this._collectUrlState(),
|
|
398
|
+
requestPayload: Object.assign({}, mappedRequestParams),
|
|
399
|
+
endpoint,
|
|
400
|
+
};
|
|
401
|
+
const trigger = requestMeta && typeof requestMeta.trigger === 'string'
|
|
402
|
+
? String(requestMeta.trigger).toLowerCase().trim()
|
|
403
|
+
: '';
|
|
404
|
+
const filtersState = requestMeta && requestMeta.filtersState && typeof requestMeta.filtersState === 'object'
|
|
405
|
+
? this._normalizeFiltersEventState(requestMeta.filtersState)
|
|
406
|
+
: null;
|
|
407
|
+
const requestContext = {
|
|
408
|
+
requestId,
|
|
409
|
+
sent,
|
|
410
|
+
trigger,
|
|
411
|
+
filtersState,
|
|
412
|
+
};
|
|
413
|
+
this._activeRemoteRequestId = requestId;
|
|
414
|
+
const cachedResponse = this._readRemoteCache(endpoint, mappedRequestParams);
|
|
415
|
+
if (trigger === 'filterschange' && this._isFiltersEmitFullContextEnabled()) {
|
|
416
|
+
const eventState = filtersState || this._normalizeFiltersEventState({
|
|
417
|
+
filters: sent.filters || {},
|
|
418
|
+
params: sent.params || {},
|
|
419
|
+
});
|
|
420
|
+
const isSearched = this._isSearchActiveFromParams(sent.params || {});
|
|
421
|
+
this._emitAction('filterschange', {
|
|
422
|
+
filters: Object.assign({}, eventState.filters || {}),
|
|
423
|
+
params: Object.assign({}, eventState.params || {}),
|
|
424
|
+
fields: Array.isArray(eventState.fields) ? eventState.fields.slice() : [],
|
|
425
|
+
meta: Object.assign({}, eventState.meta || {}),
|
|
426
|
+
phase: 'request',
|
|
427
|
+
isFiltered: this._isFilteredState(eventState) || isSearched,
|
|
428
|
+
isSearched,
|
|
429
|
+
requestId,
|
|
430
|
+
fromCache: Boolean(cachedResponse),
|
|
431
|
+
sent,
|
|
432
|
+
table: null,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
if (this._requestAbortController && typeof this._requestAbortController.abort === 'function') {
|
|
436
|
+
this._requestAbortController.abort();
|
|
437
|
+
}
|
|
438
|
+
const requestAbortController = cachedResponse ? null : (typeof AbortController === 'function'
|
|
439
|
+
? new AbortController()
|
|
440
|
+
: null);
|
|
441
|
+
this._requestAbortController = requestAbortController;
|
|
442
|
+
const requestStartedAt = Date.now();
|
|
443
|
+
this._emitAction('beforeload', {
|
|
444
|
+
requestId,
|
|
445
|
+
page: safePage,
|
|
446
|
+
perPage: safePerPage,
|
|
447
|
+
params: Object.assign({}, this._remoteParams),
|
|
448
|
+
sent,
|
|
449
|
+
});
|
|
450
|
+
if (!cachedResponse) {
|
|
451
|
+
this._renderLoadingSkeleton();
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
const response = cachedResponse || await this._route(Object.assign(
|
|
456
|
+
{},
|
|
457
|
+
this._getRemoteRouteRequestConfig(endpoint),
|
|
458
|
+
{
|
|
459
|
+
route: endpoint,
|
|
460
|
+
method: 'GET',
|
|
461
|
+
params: mappedRequestParams,
|
|
462
|
+
signal: requestAbortController ? requestAbortController.signal : undefined,
|
|
463
|
+
responseType: 'json',
|
|
464
|
+
}
|
|
465
|
+
));
|
|
466
|
+
if (!cachedResponse) {
|
|
467
|
+
await this._waitMinLoadingDelay(requestStartedAt);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (requestToken !== this._requestToken) {
|
|
471
|
+
this._emitAction('staleresponse', {
|
|
472
|
+
requestId,
|
|
473
|
+
activeRequestId: String(this._activeRemoteRequestId || ''),
|
|
474
|
+
sent,
|
|
475
|
+
});
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (!cachedResponse) {
|
|
480
|
+
this._writeRemoteCache(endpoint, mappedRequestParams, response);
|
|
481
|
+
}
|
|
482
|
+
this._applyRemoteResponse(response, safePage, safePerPage, userOnChange, {
|
|
483
|
+
fromCache: Boolean(cachedResponse),
|
|
484
|
+
requestContext,
|
|
485
|
+
});
|
|
486
|
+
} catch (error) {
|
|
487
|
+
if (error && error.name === 'AbortError') {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
await this._waitMinLoadingDelay(requestStartedAt);
|
|
491
|
+
if (requestToken !== this._requestToken) {
|
|
492
|
+
this._emitAction('staleresponse', {
|
|
493
|
+
requestId,
|
|
494
|
+
activeRequestId: String(this._activeRemoteRequestId || ''),
|
|
495
|
+
sent,
|
|
496
|
+
});
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
this._renderSummary({ total: 0 });
|
|
500
|
+
this._clearFooter();
|
|
501
|
+
const errorMessage = this._getTableMessage('stateError', 'Failed to load data.');
|
|
502
|
+
if (typeof console !== 'undefined' && typeof console.error === 'function') {
|
|
503
|
+
console.error('VGDynamicTable: remote data load failed', error);
|
|
504
|
+
}
|
|
505
|
+
this._renderStateRow(errorMessage, 'error');
|
|
506
|
+
this._refreshStickyAndFixedLayout();
|
|
507
|
+
this._emitAction('error', {
|
|
508
|
+
requestId,
|
|
509
|
+
page: safePage,
|
|
510
|
+
perPage: safePerPage,
|
|
511
|
+
error,
|
|
512
|
+
message: errorMessage,
|
|
513
|
+
sent,
|
|
514
|
+
});
|
|
515
|
+
this._emitAction('requesterror', {
|
|
516
|
+
requestId,
|
|
517
|
+
mode: 'remote',
|
|
518
|
+
responsemode: this._getRemoteResponseMode(),
|
|
519
|
+
sent,
|
|
520
|
+
error: {
|
|
521
|
+
message: String((error && error.message) || errorMessage),
|
|
522
|
+
code: String((error && error.code) || 'REQUEST_FAILED'),
|
|
523
|
+
status: error && error.status ? error.status : 0,
|
|
524
|
+
details: error && Array.isArray(error.details) ? error.details.slice() : [],
|
|
525
|
+
},
|
|
526
|
+
});
|
|
527
|
+
if (requestContext && String(requestContext.trigger || '').toLowerCase().trim() === 'filterschange' && this._isFiltersEmitFullContextEnabled()) {
|
|
528
|
+
const eventState = requestContext.filtersState && typeof requestContext.filtersState === 'object'
|
|
529
|
+
? this._normalizeFiltersEventState(requestContext.filtersState)
|
|
530
|
+
: this._normalizeFiltersEventState({
|
|
531
|
+
filters: sent.filters || {},
|
|
532
|
+
params: sent.params || {},
|
|
533
|
+
});
|
|
534
|
+
const isSearched = this._isSearchActiveFromParams(sent.params || {});
|
|
535
|
+
this._emitAction('filterschange', {
|
|
536
|
+
filters: Object.assign({}, eventState.filters || {}),
|
|
537
|
+
params: Object.assign({}, eventState.params || {}),
|
|
538
|
+
fields: Array.isArray(eventState.fields) ? eventState.fields.slice() : [],
|
|
539
|
+
meta: Object.assign({}, eventState.meta || {}),
|
|
540
|
+
phase: 'error',
|
|
541
|
+
isFiltered: this._isFilteredState(eventState) || isSearched,
|
|
542
|
+
isSearched,
|
|
543
|
+
requestId,
|
|
544
|
+
sent,
|
|
545
|
+
error: {
|
|
546
|
+
message: String((error && error.message) || errorMessage),
|
|
547
|
+
code: String((error && error.code) || 'REQUEST_FAILED'),
|
|
548
|
+
status: error && error.status ? error.status : 0,
|
|
549
|
+
details: error && Array.isArray(error.details) ? error.details.slice() : [],
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
} finally {
|
|
554
|
+
if (this._requestAbortController === requestAbortController) {
|
|
555
|
+
this._requestAbortController = null;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
_getRemoteResponseMode() {
|
|
561
|
+
const requestOptions = this._params.request || {};
|
|
562
|
+
const attrResponseMode = this._element.getAttribute('data-request-responsemode');
|
|
563
|
+
const raw = attrResponseMode !== null ? attrResponseMode : requestOptions.responsemode;
|
|
564
|
+
const normalized = String(raw || 'data').toLowerCase().trim();
|
|
565
|
+
if (normalized === 'view' || normalized === 'auto') {
|
|
566
|
+
return normalized;
|
|
567
|
+
}
|
|
568
|
+
return 'data';
|
|
569
|
+
},
|
|
570
|
+
|
|
571
|
+
_getRemoteViewParamName() {
|
|
572
|
+
const requestOptions = this._params.request || {};
|
|
573
|
+
const attr = this._element.getAttribute('data-request-viewparam');
|
|
574
|
+
const raw = attr !== null ? attr : requestOptions.viewparam;
|
|
575
|
+
const normalized = String(raw || '').trim();
|
|
576
|
+
return normalized || '';
|
|
577
|
+
},
|
|
578
|
+
|
|
579
|
+
_getRemoteViewParamValue() {
|
|
580
|
+
const requestOptions = this._params.request || {};
|
|
581
|
+
const attr = this._element.getAttribute('data-request-viewvalue');
|
|
582
|
+
const raw = attr !== null ? attr : requestOptions.viewvalue;
|
|
583
|
+
const normalized = String(raw || '').trim();
|
|
584
|
+
return normalized || '';
|
|
585
|
+
},
|
|
586
|
+
|
|
587
|
+
_getRemoteFieldsParamName() {
|
|
588
|
+
const requestOptions = this._params.request || {};
|
|
589
|
+
const attr = this._element.getAttribute('data-request-fieldsparam');
|
|
590
|
+
const raw = attr !== null ? attr : requestOptions.fieldsparam;
|
|
591
|
+
const normalized = String(raw || '').trim();
|
|
592
|
+
return normalized || '';
|
|
593
|
+
},
|
|
594
|
+
|
|
595
|
+
_getRemoteDataRoute() {
|
|
596
|
+
const filtersRoute = this._getFiltersRoute();
|
|
597
|
+
if (filtersRoute) {
|
|
598
|
+
return filtersRoute;
|
|
599
|
+
}
|
|
600
|
+
return this._getRoute();
|
|
601
|
+
},
|
|
602
|
+
|
|
603
|
+
_getRequestParamMap() {
|
|
604
|
+
const requestOptions = this._params.request || {};
|
|
605
|
+
const fromOptions = requestOptions.parammap;
|
|
606
|
+
if (fromOptions && typeof fromOptions === 'object' && !Array.isArray(fromOptions)) {
|
|
607
|
+
return fromOptions;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const attr = this._element.getAttribute('data-request-parammap') || '';
|
|
611
|
+
if (!attr) {
|
|
612
|
+
return {};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
try {
|
|
616
|
+
const parsed = JSON.parse(attr);
|
|
617
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
618
|
+
return parsed;
|
|
619
|
+
}
|
|
620
|
+
} catch (error) {
|
|
621
|
+
return {};
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return {};
|
|
625
|
+
},
|
|
626
|
+
|
|
627
|
+
_mapRemoteRequestParams(params = {}) {
|
|
628
|
+
const map = Object.assign(
|
|
629
|
+
{},
|
|
630
|
+
this._getAutoFilterRequestParamMap(params),
|
|
631
|
+
this._getRequestParamMap()
|
|
632
|
+
);
|
|
633
|
+
const result = {};
|
|
634
|
+
Object.keys(params || {}).forEach((key) => {
|
|
635
|
+
const target = Object.prototype.hasOwnProperty.call(map, key)
|
|
636
|
+
? String(map[key] || '').trim()
|
|
637
|
+
: key;
|
|
638
|
+
const nextKey = target || key;
|
|
639
|
+
result[nextKey] = params[key];
|
|
640
|
+
});
|
|
641
|
+
return result;
|
|
642
|
+
},
|
|
643
|
+
|
|
644
|
+
_getAutoFilterRequestParamMap(params = {}) {
|
|
645
|
+
const source = params && typeof params === 'object'
|
|
646
|
+
? params
|
|
647
|
+
: {};
|
|
648
|
+
const fields = typeof this._getFilterFieldKeys === 'function'
|
|
649
|
+
? this._getFilterFieldKeys()
|
|
650
|
+
: [];
|
|
651
|
+
if (!Array.isArray(fields) || !fields.length) {
|
|
652
|
+
return {};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const prefix = typeof this._getFiltersUrlPrefix === 'function'
|
|
656
|
+
? String(this._getFiltersUrlPrefix() || '').trim()
|
|
657
|
+
: '';
|
|
658
|
+
if (!prefix) {
|
|
659
|
+
return {};
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const autoMap = {};
|
|
663
|
+
fields.forEach((field) => {
|
|
664
|
+
const normalizedField = String(field || '').trim();
|
|
665
|
+
if (!normalizedField) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const mappedField = typeof this._prefixFilterParamKey === 'function'
|
|
669
|
+
? this._prefixFilterParamKey(normalizedField, prefix)
|
|
670
|
+
: (normalizedField.startsWith(prefix) ? normalizedField : `${prefix}${normalizedField}`);
|
|
671
|
+
|
|
672
|
+
if (Object.prototype.hasOwnProperty.call(source, normalizedField)) {
|
|
673
|
+
autoMap[normalizedField] = mappedField;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const operatorKey = `${normalizedField}_op`;
|
|
677
|
+
const mappedOperatorKey = typeof this._prefixFilterParamKey === 'function'
|
|
678
|
+
? this._prefixFilterParamKey(operatorKey, prefix)
|
|
679
|
+
: (operatorKey.startsWith(prefix) ? operatorKey : `${prefix}${operatorKey}`);
|
|
680
|
+
if (Object.prototype.hasOwnProperty.call(source, operatorKey)) {
|
|
681
|
+
autoMap[operatorKey] = mappedOperatorKey;
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
return autoMap;
|
|
685
|
+
},
|
|
686
|
+
|
|
687
|
+
_getRequestPath(type) {
|
|
688
|
+
const requestOptions = this._params.request || {};
|
|
689
|
+
if (type === 'data') {
|
|
690
|
+
const attrData = this._element.getAttribute('data-request-datapath');
|
|
691
|
+
const raw = attrData !== null ? attrData : requestOptions.datapath;
|
|
692
|
+
return String(raw || '').trim();
|
|
693
|
+
}
|
|
694
|
+
if (type === 'meta') {
|
|
695
|
+
const attrMeta = this._element.getAttribute('data-request-metapath');
|
|
696
|
+
const raw = attrMeta !== null ? attrMeta : requestOptions.metapath;
|
|
697
|
+
return String(raw || '').trim();
|
|
698
|
+
}
|
|
699
|
+
if (type === 'view') {
|
|
700
|
+
const attrView = this._element.getAttribute('data-request-viewpath');
|
|
701
|
+
const raw = attrView !== null ? attrView : requestOptions.viewpath;
|
|
702
|
+
return String(raw || '').trim();
|
|
703
|
+
}
|
|
704
|
+
return '';
|
|
705
|
+
},
|
|
706
|
+
|
|
707
|
+
_readPath(source, path) {
|
|
708
|
+
if (!source || typeof source !== 'object') {
|
|
709
|
+
return undefined;
|
|
710
|
+
}
|
|
711
|
+
const normalized = String(path || '').trim();
|
|
712
|
+
if (!normalized) {
|
|
713
|
+
return undefined;
|
|
714
|
+
}
|
|
715
|
+
const parts = normalized.split('.').map((item) => String(item || '').trim()).filter(Boolean);
|
|
716
|
+
if (!parts.length) {
|
|
717
|
+
return undefined;
|
|
718
|
+
}
|
|
719
|
+
let current = source;
|
|
720
|
+
for (let i = 0; i < parts.length; i += 1) {
|
|
721
|
+
if (!current || typeof current !== 'object' || !Object.prototype.hasOwnProperty.call(current, parts[i])) {
|
|
722
|
+
return undefined;
|
|
723
|
+
}
|
|
724
|
+
current = current[parts[i]];
|
|
725
|
+
}
|
|
726
|
+
return current;
|
|
727
|
+
},
|
|
728
|
+
|
|
729
|
+
_extractRemoteRows(response) {
|
|
730
|
+
const configured = this._readPath(response, this._getRequestPath('data'));
|
|
731
|
+
if (Array.isArray(configured)) {
|
|
732
|
+
return configured;
|
|
733
|
+
}
|
|
734
|
+
return Array.isArray(response && response.data) ? response.data : [];
|
|
735
|
+
},
|
|
736
|
+
|
|
737
|
+
_extractRemoteMeta(response) {
|
|
738
|
+
const configured = this._readPath(response, this._getRequestPath('meta'));
|
|
739
|
+
if (configured && typeof configured === 'object' && !Array.isArray(configured)) {
|
|
740
|
+
return configured;
|
|
741
|
+
}
|
|
742
|
+
return response && typeof response === 'object' && response.meta && typeof response.meta === 'object'
|
|
743
|
+
? response.meta
|
|
744
|
+
: {};
|
|
745
|
+
},
|
|
746
|
+
|
|
747
|
+
_extractRemoteViewRowsHtml(response) {
|
|
748
|
+
if (!response || typeof response !== 'object') {
|
|
749
|
+
return '';
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const configured = this._readPath(response, this._getRequestPath('view'));
|
|
753
|
+
if (typeof configured === 'string') {
|
|
754
|
+
return configured;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if (typeof response.view === 'string') {
|
|
758
|
+
return response.view;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
const view = response.view && typeof response.view === 'object' ? response.view : null;
|
|
762
|
+
if (view && typeof view.tbody === 'string') {
|
|
763
|
+
return view.tbody;
|
|
764
|
+
}
|
|
765
|
+
if (view && typeof view.rows === 'string') {
|
|
766
|
+
return view.rows;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (typeof response.tbody === 'string') {
|
|
770
|
+
return response.tbody;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
return '';
|
|
774
|
+
},
|
|
775
|
+
|
|
776
|
+
_renderRemoteViewIfConfigured(viewRowsHtml, rows) {
|
|
777
|
+
const mode = this._getRemoteResponseMode();
|
|
778
|
+
const canUseView = typeof viewRowsHtml === 'string' && viewRowsHtml.trim() !== '';
|
|
779
|
+
const shouldUseView = mode === 'view' || (mode === 'auto' && canUseView);
|
|
780
|
+
if (!shouldUseView) {
|
|
781
|
+
return false;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (!canUseView) {
|
|
785
|
+
if (Array.isArray(rows) && rows.length) {
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
const message = this._getTableMessage('stateEmpty', 'Ничего нет');
|
|
789
|
+
this._renderStateRow(message, 'empty');
|
|
790
|
+
return true;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const body = this._getBody();
|
|
794
|
+
body.innerHTML = viewRowsHtml;
|
|
795
|
+
if (!this._hasRenderableRowsInBody(body)) {
|
|
796
|
+
this._renderStateRow(this._getTableMessage('stateEmpty', 'Ничего нет'), 'empty');
|
|
797
|
+
return true;
|
|
798
|
+
}
|
|
799
|
+
this._setFixedColumnsSuppressed(false);
|
|
800
|
+
this._clearStateMode();
|
|
801
|
+
return true;
|
|
802
|
+
},
|
|
803
|
+
|
|
804
|
+
async _waitMinLoadingDelay(startedAt) {
|
|
805
|
+
const loadingOptions = this._params.loading || {};
|
|
806
|
+
const minDelayMs = Math.max(0, Number.parseInt(loadingOptions.minDelay, 10) || 0);
|
|
807
|
+
if (minDelayMs <= 0) {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
const elapsed = Date.now() - startedAt;
|
|
812
|
+
const remaining = minDelayMs - elapsed;
|
|
813
|
+
if (remaining <= 0) {
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
await new Promise((resolve) => setTimeout(resolve, remaining));
|
|
818
|
+
},
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
export default tableRemoteMethods;
|