UNPKG

14.6 kBJavaScriptView Raw
1import { useState, useEffect, useCallback, useRef } from 'react';
2
3function 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
40function 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// A resusable array containing all data object events that should trigger an update
51var dataUpdateEvents = ["onFieldChanged", "onRecordCreated", "onRecordDeleted", "onRecordRefreshed", "onAfterSave", "onCancelEdit", "onDataLoaded", "onPartialDataLoaded"];
52
53function 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
65function useData(dataObject) {
66 const [data, setData] = useState(dataObject.getData());
67 const updateData = useCallback(() => {
68 const data = dataObject.getData(); // If current row is dirty, getData will still return the saved record
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
86function 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
108var isArray = Array.isArray;
109var keyList = Object.keys;
110var hasProp = Object.prototype.hasOwnProperty;
111
112var 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
162function 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
176function 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
183function 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
195function 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
206function 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
259function 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
273function 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
300function getCurrentData(dataObject) {
301 return dataObject.getPagingComponent().getCurrentData() || [];
302}
303
304function 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
325function 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
332function 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
356function 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
372function 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
402function 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
412function useDeepCompareEffect(callback, dependencies) {
413 useEffect(callback, useDeepCompareMemoize(dependencies));
414}
415function 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 }; // eslint-disable-next-line react-hooks/exhaustive-deps
442 }, [procedure, params]);
443 return {
444 data,
445 error,
446 isExecuting
447 };
448}
449
450function 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
490export { getData, useCurrentIndex, useCurrentRow, useData, useDataLength, useDataWithFilter, useDirty, useError, useFetchData, useFetchRecord, useFilter, useLoading, usePagedData, usePagedDataWithFilter, usePaging, useParameter, usePermissions, useProcedure, useStatus };