1 | import { useState, useEffect, useCallback, useRef } from 'react';
|
2 |
|
3 | function getData(dataObject, filter) {
|
4 | const {
|
5 | data
|
6 | } = window.af;
|
7 | const dataHandler = new data.DataProviderHandler({
|
8 | dataSourceId: dataObject.getDataSourceId(),
|
9 | timeout: 30000
|
10 | });
|
11 | const fields = dataObject.getFields();
|
12 | return new Promise((resolve, reject) => {
|
13 | const filterData = {
|
14 | filterString: "",
|
15 | whereClause: typeof filter === "string" ? filter : "",
|
16 | whereObject: typeof filter === "object" ? filter : null
|
17 | };
|
18 | dataHandler.retrieve(filterData, function (error, data) {
|
19 | if (error !== null) {
|
20 | reject(error);
|
21 | } else {
|
22 | const records = [];
|
23 |
|
24 | for (let item of data) {
|
25 | const record = {};
|
26 |
|
27 | for (let i = 0; i < item.length; i++) {
|
28 | record[fields[i].name] = item[i];
|
29 | }
|
30 |
|
31 | records.push(record);
|
32 | }
|
33 |
|
34 | resolve(records);
|
35 | }
|
36 | });
|
37 | });
|
38 | }
|
39 |
|
40 | function useCurrentIndex(dataObject) {
|
41 | const [index, setIndex] = useState(dataObject.getCurrentIndex());
|
42 | useEffect(() => {
|
43 | dataObject.attachEvent("onCurrentIndexChanged", setIndex);
|
44 | setIndex(dataObject.getCurrentIndex());
|
45 | return () => dataObject.detachEvent("onCurrentIndexChanged", setIndex);
|
46 | }, [dataObject]);
|
47 | return index;
|
48 | }
|
49 |
|
50 |
|
51 | var dataUpdateEvents = ["onFieldChanged", "onRecordCreated", "onRecordDeleted", "onRecordRefreshed", "onAfterSave", "onCancelEdit", "onDataLoaded", "onPartialDataLoaded"];
|
52 |
|
53 | function useCurrentRow(dataObject) {
|
54 | const [record, setRecord] = useState(dataObject.currentRow() || {});
|
55 | const updateRecord = useCallback(() => setRecord(dataObject.currentRow()), [dataObject]);
|
56 | useEffect(() => {
|
57 | const recordUpdateEvents = ["onCurrentIndexChanged", ...dataUpdateEvents];
|
58 | recordUpdateEvents.forEach(event => dataObject.attachEvent(event, updateRecord));
|
59 | updateRecord();
|
60 | return () => recordUpdateEvents.forEach(event => dataObject.detachEvent(event, updateRecord));
|
61 | }, [dataObject, updateRecord]);
|
62 | return record;
|
63 | }
|
64 |
|
65 | function useData(dataObject) {
|
66 | const [data, setData] = useState(dataObject.getData());
|
67 | const updateData = useCallback(() => {
|
68 | const data = dataObject.getData();
|
69 |
|
70 | const idx = dataObject.getCurrentIndex();
|
71 |
|
72 | if (idx >= 0) {
|
73 | data[idx] = dataObject.currentRow();
|
74 | }
|
75 |
|
76 | setData(data);
|
77 | }, [dataObject]);
|
78 | useEffect(() => {
|
79 | dataUpdateEvents.forEach(event => dataObject.attachEvent(event, updateData));
|
80 | updateData();
|
81 | return () => dataUpdateEvents.forEach(event => dataObject.detachEvent(event, updateData));
|
82 | }, [dataObject, updateData]);
|
83 | return data;
|
84 | }
|
85 |
|
86 | function useDataLength(dataObject) {
|
87 | const [length, setLength] = useState(() => dataObject.getDataLength());
|
88 | useEffect(() => {
|
89 | function update() {
|
90 | setLength(dataObject.getDataLength());
|
91 | }
|
92 |
|
93 | dataObject.attachEvent("onRecordCreated", update);
|
94 | dataObject.attachEvent("onRecordDeleted", update);
|
95 | dataObject.attachEvent("onDataLoaded", update);
|
96 | dataObject.attachEvent("onPartialDataLoaded", update);
|
97 | update();
|
98 | return () => {
|
99 | dataObject.detachEvent("onRecordCreated", update);
|
100 | dataObject.detachEvent("onRecordDeleted", update);
|
101 | dataObject.detachEvent("onDataLoaded", update);
|
102 | dataObject.detachEvent("onPartialDataLoaded", update);
|
103 | };
|
104 | }, [dataObject]);
|
105 | return length;
|
106 | }
|
107 |
|
108 | var isArray = Array.isArray;
|
109 | var keyList = Object.keys;
|
110 | var hasProp = Object.prototype.hasOwnProperty;
|
111 |
|
112 | var fastDeepEqual = function equal(a, b) {
|
113 | if (a === b) return true;
|
114 |
|
115 | if (a && b && typeof a == 'object' && typeof b == 'object') {
|
116 | var arrA = isArray(a)
|
117 | , arrB = isArray(b)
|
118 | , i
|
119 | , length
|
120 | , key;
|
121 |
|
122 | if (arrA && arrB) {
|
123 | length = a.length;
|
124 | if (length != b.length) return false;
|
125 | for (i = length; i-- !== 0;)
|
126 | if (!equal(a[i], b[i])) return false;
|
127 | return true;
|
128 | }
|
129 |
|
130 | if (arrA != arrB) return false;
|
131 |
|
132 | var dateA = a instanceof Date
|
133 | , dateB = b instanceof Date;
|
134 | if (dateA != dateB) return false;
|
135 | if (dateA && dateB) return a.getTime() == b.getTime();
|
136 |
|
137 | var regexpA = a instanceof RegExp
|
138 | , regexpB = b instanceof RegExp;
|
139 | if (regexpA != regexpB) return false;
|
140 | if (regexpA && regexpB) return a.toString() == b.toString();
|
141 |
|
142 | var keys = keyList(a);
|
143 | length = keys.length;
|
144 |
|
145 | if (length !== keyList(b).length)
|
146 | return false;
|
147 |
|
148 | for (i = length; i-- !== 0;)
|
149 | if (!hasProp.call(b, keys[i])) return false;
|
150 |
|
151 | for (i = length; i-- !== 0;) {
|
152 | key = keys[i];
|
153 | if (!equal(a[key], b[key])) return false;
|
154 | }
|
155 |
|
156 | return true;
|
157 | }
|
158 |
|
159 | return a!==a && b!==b;
|
160 | };
|
161 |
|
162 | function useFilter(dataObject, filter) {
|
163 | let type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "filterString";
|
164 | useEffect(() => {
|
165 | const current = dataObject.getParameter(type);
|
166 |
|
167 | if (filter !== false) {
|
168 | if (!fastDeepEqual(current, filter) || !dataObject.isDataLoaded() && !dataObject.isDataLoading()) {
|
169 | dataObject.setParameter(type, filter);
|
170 | dataObject.refreshDataSource();
|
171 | }
|
172 | }
|
173 | }, [dataObject, filter, type]);
|
174 | }
|
175 |
|
176 | function useDataWithFilter(dataObject, filter) {
|
177 | let type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "filterString";
|
178 | const data = useData(dataObject);
|
179 | useFilter(dataObject, filter, type);
|
180 | return data;
|
181 | }
|
182 |
|
183 | function useDirty(dataObject) {
|
184 | const [isDirty, setDirty] = useState(dataObject.isDirty() || false);
|
185 | useEffect(() => {
|
186 | dataObject.attachEvent("onDirtyChanged", setDirty);
|
187 | setDirty(dataObject.isDirty());
|
188 | return () => {
|
189 | dataObject.detachEvent("onDirtyChanged", setDirty);
|
190 | };
|
191 | }, [dataObject]);
|
192 | return isDirty;
|
193 | }
|
194 |
|
195 | function useError(dataObject) {
|
196 | const [loadError, setError] = useState(null);
|
197 | useEffect(() => {
|
198 | dataObject.attachEvent("onDataLoadFailed", setError);
|
199 | return () => {
|
200 | dataObject.detachEvent("onDataLoadFailed", setError);
|
201 | };
|
202 | }, [dataObject]);
|
203 | return loadError;
|
204 | }
|
205 |
|
206 | function useFetchData(dataObject, filter) {
|
207 | const [data, setData] = useState([]);
|
208 | const [shouldUpdate, setShouldUpdate] = useState(false);
|
209 | const [isLoading, setIsLoading] = useState(filter !== false);
|
210 | const refresh = useCallback(() => {
|
211 | setShouldUpdate(shouldUpdate => !shouldUpdate);
|
212 | }, []);
|
213 | const refreshRows = useCallback(function (filter) {
|
214 | let idField = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "PrimKey";
|
215 | getData(dataObject, filter).then(records => {
|
216 | let newData = [...data];
|
217 |
|
218 | for (let record of records) {
|
219 | for (let i = 0; i < newData.length; i++) {
|
220 | const current = newData[i];
|
221 |
|
222 | if (current[idField] === record[idField]) {
|
223 | newData[i] = record;
|
224 | }
|
225 | }
|
226 | }
|
227 |
|
228 | setData(newData);
|
229 | });
|
230 | }, [dataObject, data]);
|
231 | useEffect(() => {
|
232 | let isCancelled = false;
|
233 |
|
234 | if (filter !== false) {
|
235 | setIsLoading(true);
|
236 | getData(dataObject, filter).then(data => {
|
237 | if (!isCancelled) {
|
238 | if (data.length > 0) {
|
239 | setData(data);
|
240 | } else {
|
241 | setData([]);
|
242 | }
|
243 |
|
244 | setIsLoading(false);
|
245 | }
|
246 | });
|
247 | }
|
248 |
|
249 | return () => isCancelled = true;
|
250 | }, [dataObject, filter, shouldUpdate]);
|
251 | return {
|
252 | data,
|
253 | refresh,
|
254 | refreshRows,
|
255 | isLoading
|
256 | };
|
257 | }
|
258 |
|
259 | function useFetchRecord(dataObject, filter) {
|
260 | const {
|
261 | isLoading,
|
262 | data,
|
263 | refresh
|
264 | } = useFetchData(dataObject, filter);
|
265 | const record = data.length > 0 ? data[0] : {};
|
266 | return {
|
267 | record,
|
268 | refresh,
|
269 | isLoading
|
270 | };
|
271 | }
|
272 |
|
273 | function useLoading(dataObject) {
|
274 | const [isLoading, setLoading] = useState(dataObject.isDataLoading());
|
275 |
|
276 | function setIsLoading() {
|
277 | setLoading(true);
|
278 | }
|
279 |
|
280 | function setIsNotLoading() {
|
281 | setLoading(false);
|
282 | }
|
283 |
|
284 | useEffect(() => {
|
285 | dataObject.attachEvent("onBeforeLoad", setIsLoading);
|
286 | dataObject.attachEvent("onDataLoaded", setIsNotLoading);
|
287 | dataObject.attachEvent("onDataLoadFailed", setIsNotLoading);
|
288 | dataObject.attachEvent("onPartialDataLoaded", setIsNotLoading);
|
289 | setLoading(dataObject.isDataLoading());
|
290 | return () => {
|
291 | dataObject.detachEvent("onBeforeLoad", setIsLoading);
|
292 | dataObject.detachEvent("onDataLoaded", setIsNotLoading);
|
293 | dataObject.detachEvent("onDataLoadFailed", setIsNotLoading);
|
294 | dataObject.detachEvent("onPartialDataLoaded", setIsNotLoading);
|
295 | };
|
296 | }, [dataObject]);
|
297 | return isLoading;
|
298 | }
|
299 |
|
300 | function getCurrentData(dataObject) {
|
301 | return dataObject.getPagingComponent().getCurrentData() || [];
|
302 | }
|
303 |
|
304 | function usePagedData(dataObject) {
|
305 | const [currentData, setCurrentData] = useState(() => getCurrentData(dataObject));
|
306 | useEffect(() => {
|
307 | function updateData() {
|
308 | setCurrentData([...getCurrentData(dataObject)]);
|
309 | }
|
310 |
|
311 | dataUpdateEvents.forEach(event => dataObject.attachEvent(event, updateData));
|
312 | const pagingComponent = dataObject.getPagingComponent();
|
313 | pagingComponent.attach("on", "pageChange", updateData);
|
314 | pagingComponent.attach("on", "pageRefresh", updateData);
|
315 | updateData();
|
316 | return () => {
|
317 | dataUpdateEvents.forEach(event => dataObject.detachEvent(event, updateData));
|
318 | pagingComponent.detach("on", "pageChange", updateData);
|
319 | pagingComponent.detach("on", "pageRefresh", updateData);
|
320 | };
|
321 | }, [dataObject]);
|
322 | return currentData;
|
323 | }
|
324 |
|
325 | function usePagedDataWithFilter(dataObject, filter) {
|
326 | let type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "filterString";
|
327 | const data = usePagedData(dataObject);
|
328 | useFilter(dataObject, filter, type);
|
329 | return data;
|
330 | }
|
331 |
|
332 | function usePaging(dataObject) {
|
333 | const [page, setPage] = useState(() => dataObject.getPagingComponent().getCurrentPage());
|
334 | const [pageCount, setPageCount] = useState(() => dataObject.getPagingComponent().getPageCount());
|
335 | useEffect(() => {
|
336 | const paging = dataObject.getPagingComponent();
|
337 |
|
338 | const updateCurrentPage = () => setPage(paging.getCurrentPage());
|
339 |
|
340 | const updatePageCount = () => setPageCount(paging.getPageCount());
|
341 |
|
342 | paging.attach("on", "pageChange", updateCurrentPage);
|
343 | paging.attach("on", "pageCountChange", updatePageCount);
|
344 | return () => {
|
345 | paging.detach("on", "pageChange", updateCurrentPage);
|
346 | paging.detach("on", "pageCountChange", updatePageCount);
|
347 | };
|
348 | }, [dataObject]);
|
349 | return {
|
350 | page,
|
351 | pageCount,
|
352 | changePage: dataObject.getPagingComponent().changePage
|
353 | };
|
354 | }
|
355 |
|
356 | function useParameter(dataObject, parameter) {
|
357 | const [value, setValue] = useState(() => dataObject.getParameter(parameter));
|
358 | useEffect(() => {
|
359 | function update() {
|
360 | setValue(dataObject.getParameter(parameter));
|
361 | }
|
362 |
|
363 | dataObject.attachEvent("onParameterUpdated", update);
|
364 | update();
|
365 | return () => {
|
366 | dataObject.detachEvent("onParameterUpdated", update);
|
367 | };
|
368 | }, [dataObject, parameter]);
|
369 | return value;
|
370 | }
|
371 |
|
372 | function usePermissions(dataObject) {
|
373 | const [allowDelete, setAllowDelete] = useState(dataObject.isDeleteAllowed());
|
374 | const [allowInsert, setAllowInsert] = useState(dataObject.isInsertAllowed());
|
375 | const [allowUpdate, setAllowUpdate] = useState(dataObject.isUpdateAllowed());
|
376 | useEffect(() => {
|
377 | const updateAllowDelete = () => setAllowDelete(dataObject.isDeleteAllowed());
|
378 |
|
379 | const updateAllowInsert = () => setAllowInsert(dataObject.isInsertAllowed());
|
380 |
|
381 | const updateAllowUpdate = () => setAllowUpdate(dataObject.isUpdateAllowed());
|
382 |
|
383 | dataObject.attachEvent("onAllowDeleteChanged", updateAllowDelete);
|
384 | dataObject.attachEvent("onAllowInsertChanged", updateAllowInsert);
|
385 | dataObject.attachEvent("onAllowUpdateChanged", updateAllowUpdate);
|
386 | updateAllowDelete();
|
387 | updateAllowInsert();
|
388 | updateAllowUpdate();
|
389 | return () => {
|
390 | dataObject.detachEvent("onAllowDeleteChanged", updateAllowDelete);
|
391 | dataObject.detachEvent("onAllowInsertChanged", updateAllowInsert);
|
392 | dataObject.detachEvent("onAllowUpdateChanged", updateAllowUpdate);
|
393 | };
|
394 | }, [dataObject]);
|
395 | return {
|
396 | allowDelete,
|
397 | allowInsert,
|
398 | allowUpdate
|
399 | };
|
400 | }
|
401 |
|
402 | function useDeepCompareMemoize(value) {
|
403 | const ref = useRef();
|
404 |
|
405 | if (!fastDeepEqual(value, ref.current)) {
|
406 | ref.current = value;
|
407 | }
|
408 |
|
409 | return ref.current;
|
410 | }
|
411 |
|
412 | function useDeepCompareEffect(callback, dependencies) {
|
413 | useEffect(callback, useDeepCompareMemoize(dependencies));
|
414 | }
|
415 | function useProcedure(procedure, params) {
|
416 | const [data, setData] = useState([]);
|
417 | const [error, setError] = useState(null);
|
418 | const [isExecuting, setIsExecuting] = useState(false);
|
419 | useDeepCompareEffect(() => {
|
420 | let isAborted = false;
|
421 |
|
422 | if (params && procedure) {
|
423 | setIsExecuting(true);
|
424 | procedure.execute(params, (err, data) => {
|
425 | if (!isAborted) {
|
426 | setIsExecuting(false);
|
427 |
|
428 | if (err) {
|
429 | setError(err);
|
430 | setData([]);
|
431 | } else {
|
432 | setData(data);
|
433 | setError(null);
|
434 | }
|
435 | }
|
436 | });
|
437 | }
|
438 |
|
439 | return () => {
|
440 | isAborted = true;
|
441 | };
|
442 | }, [procedure, params]);
|
443 | return {
|
444 | data,
|
445 | error,
|
446 | isExecuting
|
447 | };
|
448 | }
|
449 |
|
450 | function useStatus(dataObject) {
|
451 | const [isSaving, setIsSaving] = useState(false);
|
452 | const [isDeleting, setIsDeleting] = useState(false);
|
453 |
|
454 | function setSaving() {
|
455 | setIsSaving(true);
|
456 | }
|
457 |
|
458 | function setNotSaving() {
|
459 | setIsSaving(false);
|
460 | }
|
461 |
|
462 | function setDeleting() {
|
463 | setIsDeleting(true);
|
464 | }
|
465 |
|
466 | function setNotDeleting() {
|
467 | setIsDeleting(false);
|
468 | }
|
469 |
|
470 | useEffect(() => {
|
471 | dataObject.attachEvent("onBeforeSave", setSaving);
|
472 | dataObject.attachEvent("onAfterSave", setNotSaving);
|
473 | dataObject.attachEvent("onSaveFailed", setNotSaving);
|
474 | dataObject.attachEvent("onRecordDeleting", setDeleting);
|
475 | dataObject.attachEvent("onRecordDeleted", setNotDeleting);
|
476 | return () => {
|
477 | dataObject.detachEvent("onBeforeSave", setSaving);
|
478 | dataObject.detachEvent("onAfterSave", setNotSaving);
|
479 | dataObject.detachEvent("onSaveFailed", setNotSaving);
|
480 | dataObject.detachEvent("onRecordDeleting", setDeleting);
|
481 | dataObject.detachEvent("onRecordDeleted", setNotDeleting);
|
482 | };
|
483 | }, [dataObject]);
|
484 | return {
|
485 | isDeleting,
|
486 | isSaving
|
487 | };
|
488 | }
|
489 |
|
490 | export { getData, useCurrentIndex, useCurrentRow, useData, useDataLength, useDataWithFilter, useDirty, useError, useFetchData, useFetchRecord, useFilter, useLoading, usePagedData, usePagedDataWithFilter, usePaging, useParameter, usePermissions, useProcedure, useStatus };
|