@lowentry/react-redux 1.16.1 → 2.0.2
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/LICENSE +23 -0
- package/dist/index.d.ts +453 -0
- package/dist/index.js +1456 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -70
- package/api-extractor.json +0 -43
- package/index.d.ts +0 -3
- package/index.js +0 -1497
- package/index.js.map +0 -1
- package/src/LeRed.jsx +0 -1522
- package/src/index.js +0 -1
- package/tsconfig.d.ts +0 -1
- package/tsconfig.json +0 -39
package/src/LeRed.jsx
DELETED
|
@@ -1,1522 +0,0 @@
|
|
|
1
|
-
import * as RTK from '@reduxjs/toolkit';
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
import * as ReactDOM from 'react-dom';
|
|
4
|
-
import * as ReactRedux from 'react-redux';
|
|
5
|
-
import * as ReduxSaga from 'redux-saga';
|
|
6
|
-
import * as ReduxSagaEffects from 'redux-saga/effects';
|
|
7
|
-
import {LeUtils, ISSET, ARRAY, STRING, INT_LAX_ANY, IS_OBJECT, IS_ARRAY} from '@lowentry/utils';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export const LeRed = (() =>
|
|
11
|
-
{
|
|
12
|
-
/** @type {Object} */
|
|
13
|
-
const LeRed = {};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
try
|
|
17
|
-
{
|
|
18
|
-
const set = (key, value, ignoreOverrides = false) =>
|
|
19
|
-
{
|
|
20
|
-
const keyFirstChar = key.charAt(0);
|
|
21
|
-
if(keyFirstChar === keyFirstChar.toLowerCase() && (keyFirstChar !== keyFirstChar.toUpperCase()))
|
|
22
|
-
{
|
|
23
|
-
if((key === 'default') || (key === 'version'))
|
|
24
|
-
{
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
if((key === 'set') || (key === 'setAll'))
|
|
28
|
-
{
|
|
29
|
-
console.error('tried to override LeRed["' + key + '"], which isn\'t allowed, to:');
|
|
30
|
-
console.error(value);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
if((ignoreOverrides !== true) && (key in LeRed))
|
|
34
|
-
{
|
|
35
|
-
console.warn('LeRed["' + key + '"] was overwritten, from:');
|
|
36
|
-
console.warn(LeRed[key]);
|
|
37
|
-
console.warn('to:');
|
|
38
|
-
console.warn(value);
|
|
39
|
-
}
|
|
40
|
-
LeRed[key] = value;
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
const setAll = (obj, ignoreOverrides = false, optionalSkipHasOwnPropertyCheck = true) =>
|
|
44
|
-
{
|
|
45
|
-
LeUtils.each(obj, (value, key) =>
|
|
46
|
-
{
|
|
47
|
-
set(key, value, ignoreOverrides);
|
|
48
|
-
}, optionalSkipHasOwnPropertyCheck);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
LeRed.set = (key, value) => set(key, value, true);
|
|
52
|
-
LeRed.setAll = (obj, optionalSkipHasOwnPropertyCheck = true) => setAll(obj, true, optionalSkipHasOwnPropertyCheck);
|
|
53
|
-
|
|
54
|
-
setAll(ReactDOM);
|
|
55
|
-
setAll(ReduxSaga);
|
|
56
|
-
setAll({effects:{...ReduxSagaEffects}});
|
|
57
|
-
setAll(RTK);
|
|
58
|
-
setAll(React);
|
|
59
|
-
setAll(ReactRedux);
|
|
60
|
-
|
|
61
|
-
LeRed.effects.delayFrames = function* (frames = 1)
|
|
62
|
-
{
|
|
63
|
-
yield LeRed.effects.call(() =>
|
|
64
|
-
{
|
|
65
|
-
return new Promise((resolve, reject) =>
|
|
66
|
-
{
|
|
67
|
-
try
|
|
68
|
-
{
|
|
69
|
-
LeUtils.setAnimationFrameTimeout(resolve, frames);
|
|
70
|
-
}
|
|
71
|
-
catch(e)
|
|
72
|
-
{
|
|
73
|
-
reject(e);
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
LeRed.effects.interval = function* (callback, intervalMs)
|
|
80
|
-
{
|
|
81
|
-
let channel = LeRed.eventChannel((emitter) =>
|
|
82
|
-
{
|
|
83
|
-
const interval = setInterval(() =>
|
|
84
|
-
{
|
|
85
|
-
emitter({});
|
|
86
|
-
}, intervalMs);
|
|
87
|
-
return () =>
|
|
88
|
-
{
|
|
89
|
-
clearInterval(interval);
|
|
90
|
-
};
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const stop = () =>
|
|
94
|
-
{
|
|
95
|
-
try
|
|
96
|
-
{
|
|
97
|
-
if(channel !== null)
|
|
98
|
-
{
|
|
99
|
-
channel.close();
|
|
100
|
-
channel = null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
catch(e)
|
|
104
|
-
{
|
|
105
|
-
console.error(e);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
while(channel !== null)
|
|
110
|
-
{
|
|
111
|
-
try
|
|
112
|
-
{
|
|
113
|
-
yield LeRed.effects.take(channel);
|
|
114
|
-
yield callback(stop);
|
|
115
|
-
}
|
|
116
|
-
catch(e)
|
|
117
|
-
{
|
|
118
|
-
console.error(e);
|
|
119
|
-
}
|
|
120
|
-
finally
|
|
121
|
-
{
|
|
122
|
-
try
|
|
123
|
-
{
|
|
124
|
-
if(yield LeRed.effects.cancelled())
|
|
125
|
-
{
|
|
126
|
-
channel.close();
|
|
127
|
-
channel = null;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
catch(e)
|
|
131
|
-
{
|
|
132
|
-
console.error(e);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
catch(e)
|
|
139
|
-
{
|
|
140
|
-
console.error(e);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const fixEqualsComparator = (equalsComparator, errorMessage) =>
|
|
145
|
-
{
|
|
146
|
-
if(ISSET(equalsComparator))
|
|
147
|
-
{
|
|
148
|
-
if(typeof equalsComparator !== 'function')
|
|
149
|
-
{
|
|
150
|
-
console.error(errorMessage);
|
|
151
|
-
console.error(equalsComparator);
|
|
152
|
-
return LeUtils.equals;
|
|
153
|
-
}
|
|
154
|
-
return equalsComparator;
|
|
155
|
-
}
|
|
156
|
-
return LeUtils.equals;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const useCompareMemoize = (value, equalsComparator) =>
|
|
160
|
-
{
|
|
161
|
-
const ref = React.useRef();
|
|
162
|
-
if(!equalsComparator(value, ref.current))
|
|
163
|
-
{
|
|
164
|
-
ref.current = value;
|
|
165
|
-
}
|
|
166
|
-
return ref.current;
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
LeRed.configureStore = (storeData) =>
|
|
171
|
-
{
|
|
172
|
-
if(storeData.__lowentry_store__ === true)
|
|
173
|
-
{
|
|
174
|
-
return storeData;
|
|
175
|
-
}
|
|
176
|
-
if(ISSET(storeData.slices))
|
|
177
|
-
{
|
|
178
|
-
storeData.reducer = storeData.slices;
|
|
179
|
-
delete storeData.slices;
|
|
180
|
-
}
|
|
181
|
-
let sagaListeners = [];
|
|
182
|
-
if(ISSET(storeData.reducer))
|
|
183
|
-
{
|
|
184
|
-
let slices = storeData.reducer;
|
|
185
|
-
if(typeof slices === 'object')
|
|
186
|
-
{
|
|
187
|
-
if(slices.name || slices.__lowentry_unfinished_slice)
|
|
188
|
-
{
|
|
189
|
-
slices = [slices];
|
|
190
|
-
}
|
|
191
|
-
else
|
|
192
|
-
{
|
|
193
|
-
let slicesArray = [];
|
|
194
|
-
LeUtils.each(slices, (slice, index) =>
|
|
195
|
-
{
|
|
196
|
-
if(!slice.name)
|
|
197
|
-
{
|
|
198
|
-
slice.name = index;
|
|
199
|
-
}
|
|
200
|
-
slicesArray.push(slice);
|
|
201
|
-
});
|
|
202
|
-
slices = slicesArray;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
slices = ARRAY(slices);
|
|
206
|
-
|
|
207
|
-
let initialState = {};
|
|
208
|
-
let reducerArrays = {};
|
|
209
|
-
LeUtils.each(slices, (slice, index) =>
|
|
210
|
-
{
|
|
211
|
-
if(!slice.name)
|
|
212
|
-
{
|
|
213
|
-
slice.name = 'slice_' + index;
|
|
214
|
-
}
|
|
215
|
-
if(slice.__lowentry_unfinished_slice)
|
|
216
|
-
{
|
|
217
|
-
delete slice.__lowentry_unfinished_slice;
|
|
218
|
-
slice = LeRed.createSlice(slice);
|
|
219
|
-
}
|
|
220
|
-
initialState[slice.name] = ((typeof slice.state === 'function') ? slice.state() : slice.state);
|
|
221
|
-
LeUtils.each(slice.reducers, (reducer, reducerName) =>
|
|
222
|
-
{
|
|
223
|
-
const fullReducerName = reducerName.startsWith('lowentrystore/') ? reducerName.substring('lowentrystore/'.length) : (slice.name + '/' + reducerName);
|
|
224
|
-
if(typeof reducerArrays[fullReducerName] === 'undefined')
|
|
225
|
-
{
|
|
226
|
-
reducerArrays[fullReducerName] = [];
|
|
227
|
-
}
|
|
228
|
-
reducerArrays[fullReducerName].push(reducer);
|
|
229
|
-
});
|
|
230
|
-
LeUtils.each(slice.sagaListeners, (sagaListener, reducerName) =>
|
|
231
|
-
{
|
|
232
|
-
LeUtils.each(LeUtils.flattenArray(sagaListener), (listener) =>
|
|
233
|
-
{
|
|
234
|
-
try
|
|
235
|
-
{
|
|
236
|
-
sagaListeners.push(listener());
|
|
237
|
-
}
|
|
238
|
-
catch(e)
|
|
239
|
-
{
|
|
240
|
-
console.error('an error was thrown by your saga code, in slice "' + slice.name + '", action "' + reducerName + '":');
|
|
241
|
-
console.error(e);
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
/** @type {Object|*} */
|
|
248
|
-
let reducers = {};
|
|
249
|
-
LeUtils.each(reducerArrays, (reducerArray, reducerName) =>
|
|
250
|
-
{
|
|
251
|
-
reducerArray = LeUtils.flattenArray(reducerArray);
|
|
252
|
-
if(reducerArray.length <= 0)
|
|
253
|
-
{
|
|
254
|
-
reducers[reducerName] = reducerArray[0];
|
|
255
|
-
}
|
|
256
|
-
else
|
|
257
|
-
{
|
|
258
|
-
reducers[reducerName] = (...args) =>
|
|
259
|
-
{
|
|
260
|
-
LeUtils.each(reducerArray, (reducer) =>
|
|
261
|
-
{
|
|
262
|
-
try
|
|
263
|
-
{
|
|
264
|
-
reducer(...args);
|
|
265
|
-
}
|
|
266
|
-
catch(e)
|
|
267
|
-
{
|
|
268
|
-
console.error(e);
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
storeData.reducer = RTK.createSlice({
|
|
276
|
-
name: 'lowentrystore',
|
|
277
|
-
initialState:initialState,
|
|
278
|
-
reducers: reducers,
|
|
279
|
-
}).reducer;
|
|
280
|
-
}
|
|
281
|
-
if(ISSET(storeData.state))
|
|
282
|
-
{
|
|
283
|
-
storeData.preloadedState = storeData.state;
|
|
284
|
-
delete storeData.state;
|
|
285
|
-
}
|
|
286
|
-
if(ISSET(storeData.preloadedState))
|
|
287
|
-
{
|
|
288
|
-
storeData.preloadedState = {reducer:storeData.preloadedState};
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
let middleware = ARRAY(storeData.middleware);
|
|
292
|
-
let sagaMiddleware = null;
|
|
293
|
-
if(sagaListeners.length > 0)
|
|
294
|
-
{
|
|
295
|
-
sagaMiddleware = ReduxSaga.default();
|
|
296
|
-
middleware.push(sagaMiddleware);
|
|
297
|
-
}
|
|
298
|
-
storeData.middleware = (getDefaultMiddleware) => getDefaultMiddleware().concat(...middleware);
|
|
299
|
-
|
|
300
|
-
/** @type {RTK.EnhancedStore|*} */
|
|
301
|
-
let store = RTK.configureStore(storeData);
|
|
302
|
-
store.__lowentry_store__ = true;
|
|
303
|
-
store.state = () => store.getState().reducer;
|
|
304
|
-
|
|
305
|
-
const dispatch = store.dispatch;
|
|
306
|
-
// noinspection JSValidateTypes
|
|
307
|
-
store.dispatch = (action) =>
|
|
308
|
-
{
|
|
309
|
-
action.__lowentry_dispatch__ = true;
|
|
310
|
-
if(STRING(action.type).startsWith('lowentrystore/lowentryaction/'))
|
|
311
|
-
{
|
|
312
|
-
action.__lowentry_dispatch_result__ = [];
|
|
313
|
-
}
|
|
314
|
-
else
|
|
315
|
-
{
|
|
316
|
-
delete action.__lowentry_dispatch_result__;
|
|
317
|
-
}
|
|
318
|
-
dispatch(action);
|
|
319
|
-
const result = action.__lowentry_dispatch_result__;
|
|
320
|
-
delete action.__lowentry_dispatch_result__;
|
|
321
|
-
delete action.__lowentry_dispatch__;
|
|
322
|
-
return result;
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
if(sagaMiddleware !== null)
|
|
326
|
-
{
|
|
327
|
-
sagaMiddleware.run(function* ()
|
|
328
|
-
{
|
|
329
|
-
yield ReduxSagaEffects.all(sagaListeners);
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
return store;
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
LeRed.createAction = (id) =>
|
|
336
|
-
{
|
|
337
|
-
return RTK.createAction('lowentrystore/lowentryaction/' + id);
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
LeRed.createSelector = (selectorsGenerator) =>
|
|
341
|
-
{
|
|
342
|
-
return function(stateOfSlice)
|
|
343
|
-
{
|
|
344
|
-
const state = this;
|
|
345
|
-
const selectors = selectorsGenerator.apply(state, [stateOfSlice]);
|
|
346
|
-
let selectorArgs = [];
|
|
347
|
-
|
|
348
|
-
for(let i = 0; i < selectors.length - 1; i++)
|
|
349
|
-
{
|
|
350
|
-
let selectorsEntry = selectors[i];
|
|
351
|
-
if(typeof selectorsEntry === 'function')
|
|
352
|
-
{
|
|
353
|
-
selectorsEntry = selectorsEntry.apply(state, [state]);
|
|
354
|
-
}
|
|
355
|
-
selectorArgs.push(selectorsEntry);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
let finalSelector = selectors[selectors.length - 1];
|
|
359
|
-
if(typeof finalSelector === 'function')
|
|
360
|
-
{
|
|
361
|
-
finalSelector = finalSelector.apply(state, selectorArgs);
|
|
362
|
-
}
|
|
363
|
-
return finalSelector;
|
|
364
|
-
};
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
LeRed.createCachedSelector = (selectorsGenerator, equalsComparator) =>
|
|
368
|
-
{
|
|
369
|
-
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.createCachedSelector() was given an invalid comparator:');
|
|
370
|
-
if(equalsComparator === false)
|
|
371
|
-
{
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
let previousSelectorArgs = null;
|
|
375
|
-
let previousFinalSelectorResult = null;
|
|
376
|
-
return function(stateOfSlice)
|
|
377
|
-
{
|
|
378
|
-
const state = this;
|
|
379
|
-
const selectors = selectorsGenerator.apply(state, [stateOfSlice]);
|
|
380
|
-
let selectorArgs = [];
|
|
381
|
-
|
|
382
|
-
for(let i = 0; i < selectors.length - 1; i++)
|
|
383
|
-
{
|
|
384
|
-
let selectorsEntry = selectors[i];
|
|
385
|
-
if(typeof selectorsEntry === 'function')
|
|
386
|
-
{
|
|
387
|
-
selectorsEntry = selectorsEntry.apply(state, [state]);
|
|
388
|
-
}
|
|
389
|
-
selectorArgs.push(selectorsEntry);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
let finalSelector = selectors[selectors.length - 1];
|
|
393
|
-
if(typeof finalSelector === 'function')
|
|
394
|
-
{
|
|
395
|
-
if(equalsComparator(previousSelectorArgs, selectorArgs))
|
|
396
|
-
{
|
|
397
|
-
finalSelector = previousFinalSelectorResult;
|
|
398
|
-
}
|
|
399
|
-
else
|
|
400
|
-
{
|
|
401
|
-
finalSelector = finalSelector.apply(state, selectorArgs);
|
|
402
|
-
previousSelectorArgs = selectorArgs;
|
|
403
|
-
previousFinalSelectorResult = finalSelector;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
return finalSelector;
|
|
407
|
-
};
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
LeRed.createSlice = (slice) =>
|
|
411
|
-
{
|
|
412
|
-
if(Array.isArray(slice))
|
|
413
|
-
{
|
|
414
|
-
const e = new Error('the given slice is an array (instead of an object)');
|
|
415
|
-
console.error('an error was thrown by your LeRed.createSlice(...) code:');
|
|
416
|
-
console.error(e);
|
|
417
|
-
throw e;
|
|
418
|
-
}
|
|
419
|
-
if(slice.name)
|
|
420
|
-
{
|
|
421
|
-
let actions = {};
|
|
422
|
-
let reducers = {};
|
|
423
|
-
let sagas = {};
|
|
424
|
-
let sagaListeners = {};
|
|
425
|
-
LeUtils.each(slice.actions, (reducer, reducerNames) =>
|
|
426
|
-
{
|
|
427
|
-
LeUtils.each(reducerNames.split(','), (reducerName) =>
|
|
428
|
-
{
|
|
429
|
-
if(reducerName.length <= 0)
|
|
430
|
-
{
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
const reducerAction = RTK.createAction((reducerName.startsWith('lowentrystore/') ? '' : ('lowentrystore/' + slice.name + '/')) + reducerName);
|
|
434
|
-
actions[reducerName] = reducerAction;
|
|
435
|
-
LeUtils.each(LeUtils.flattenArray(reducer), (reducer) =>
|
|
436
|
-
{
|
|
437
|
-
if(LeUtils.isGeneratorFunction(reducer))
|
|
438
|
-
{
|
|
439
|
-
const sagaListener = function* ()
|
|
440
|
-
{
|
|
441
|
-
yield ReduxSagaEffects.takeEvery(reducerAction, function* (/** @type {RTK.Action|*} */ action)
|
|
442
|
-
{
|
|
443
|
-
/** @type {((value:*)=>void)|null|*} */
|
|
444
|
-
let promiseResolve = null;
|
|
445
|
-
/** @type {((reason:*)=>void)|null|*} */
|
|
446
|
-
let promiseReject = null;
|
|
447
|
-
try
|
|
448
|
-
{
|
|
449
|
-
if(action.__lowentry_dispatch__ === true)
|
|
450
|
-
{
|
|
451
|
-
const promise = new Promise((resolve, reject) =>
|
|
452
|
-
{
|
|
453
|
-
promiseResolve = resolve;
|
|
454
|
-
promiseReject = reject;
|
|
455
|
-
});
|
|
456
|
-
if(Array.isArray(action.__lowentry_dispatch_result__))
|
|
457
|
-
{
|
|
458
|
-
if(typeof promise !== 'undefined')
|
|
459
|
-
{
|
|
460
|
-
action.__lowentry_dispatch_result__.push(promise);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
else
|
|
464
|
-
{
|
|
465
|
-
action.__lowentry_dispatch_result__ = promise;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
const result = yield reducer.apply(slice, [action.payload]);
|
|
470
|
-
if(promiseResolve !== null)
|
|
471
|
-
{
|
|
472
|
-
promiseResolve(result);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
catch(e)
|
|
476
|
-
{
|
|
477
|
-
console.error('an error was thrown by your LeRed.createSlice(...) code, by slice "' + slice.name + '", action "' + reducerName + '":');
|
|
478
|
-
console.error(e);
|
|
479
|
-
if(promiseReject !== null)
|
|
480
|
-
{
|
|
481
|
-
try
|
|
482
|
-
{
|
|
483
|
-
promiseReject(e);
|
|
484
|
-
}
|
|
485
|
-
catch(e2)
|
|
486
|
-
{
|
|
487
|
-
console.error(e2);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
});
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
if(ISSET(sagas[reducerName]))
|
|
495
|
-
{
|
|
496
|
-
sagas[reducerName].push(reducer);
|
|
497
|
-
}
|
|
498
|
-
else
|
|
499
|
-
{
|
|
500
|
-
sagas[reducerName] = [reducer];
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
if(ISSET(sagaListeners[reducerName]))
|
|
504
|
-
{
|
|
505
|
-
sagaListeners[reducerName].push(sagaListener);
|
|
506
|
-
}
|
|
507
|
-
else
|
|
508
|
-
{
|
|
509
|
-
sagaListeners[reducerName] = [sagaListener];
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
else
|
|
513
|
-
{
|
|
514
|
-
const reducerFunction = (state, action) =>
|
|
515
|
-
{
|
|
516
|
-
try
|
|
517
|
-
{
|
|
518
|
-
const result = reducer.apply(state, [state[slice.name], action.payload]);
|
|
519
|
-
if(action.__lowentry_dispatch__ === true)
|
|
520
|
-
{
|
|
521
|
-
if(Array.isArray(action.__lowentry_dispatch_result__))
|
|
522
|
-
{
|
|
523
|
-
if(typeof result !== 'undefined')
|
|
524
|
-
{
|
|
525
|
-
action.__lowentry_dispatch_result__.push(result);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
else
|
|
529
|
-
{
|
|
530
|
-
action.__lowentry_dispatch_result__ = result;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
catch(e)
|
|
535
|
-
{
|
|
536
|
-
console.error('an error was thrown by your LeRed.createSlice(...) code, by slice "' + slice.name + '", action "' + reducerName + '":');
|
|
537
|
-
console.error(e);
|
|
538
|
-
}
|
|
539
|
-
};
|
|
540
|
-
|
|
541
|
-
if(ISSET(reducers[reducerName]))
|
|
542
|
-
{
|
|
543
|
-
reducers[reducerName].push(reducerFunction);
|
|
544
|
-
}
|
|
545
|
-
else
|
|
546
|
-
{
|
|
547
|
-
reducers[reducerName] = [reducerFunction];
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
});
|
|
551
|
-
});
|
|
552
|
-
});
|
|
553
|
-
slice.actions = actions;
|
|
554
|
-
slice.reducers = reducers;
|
|
555
|
-
slice.sagas = sagas;
|
|
556
|
-
slice.sagaListeners = sagaListeners;
|
|
557
|
-
|
|
558
|
-
let selectors = {};
|
|
559
|
-
LeUtils.each(slice.selectors, (selector, selectorName) =>
|
|
560
|
-
{
|
|
561
|
-
selectors[selectorName] = (state) =>
|
|
562
|
-
{
|
|
563
|
-
try
|
|
564
|
-
{
|
|
565
|
-
return selector.apply(state, [state[slice.name]]);
|
|
566
|
-
}
|
|
567
|
-
catch(e)
|
|
568
|
-
{
|
|
569
|
-
console.error('an error was thrown by your LeRed.createSlice(...) code, by slice "' + slice.name + '", selector "' + selectorName + '":');
|
|
570
|
-
console.error(e);
|
|
571
|
-
throw e;
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
});
|
|
575
|
-
slice.selectors = selectors;
|
|
576
|
-
|
|
577
|
-
let getters = {};
|
|
578
|
-
LeUtils.each(slice.getters, (getter, getterName) =>
|
|
579
|
-
{
|
|
580
|
-
getters[getterName] = (...params) =>
|
|
581
|
-
{
|
|
582
|
-
try
|
|
583
|
-
{
|
|
584
|
-
const selector = getter(...params);
|
|
585
|
-
return (state) =>
|
|
586
|
-
{
|
|
587
|
-
try
|
|
588
|
-
{
|
|
589
|
-
return selector.apply(state, [state[slice.name]]);
|
|
590
|
-
}
|
|
591
|
-
catch(e)
|
|
592
|
-
{
|
|
593
|
-
console.error('an error was thrown by your LeRed.createSlice(...) code, by slice "' + slice.name + '", getter "' + getterName + '":');
|
|
594
|
-
console.error(e);
|
|
595
|
-
throw e;
|
|
596
|
-
}
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
catch(e)
|
|
600
|
-
{
|
|
601
|
-
console.error('an error was thrown by your LeRed.createSlice(...) code, by slice "' + slice.name + '", getter "' + getterName + '":');
|
|
602
|
-
console.error(e);
|
|
603
|
-
throw e;
|
|
604
|
-
}
|
|
605
|
-
};
|
|
606
|
-
});
|
|
607
|
-
slice.getters = getters;
|
|
608
|
-
return slice;
|
|
609
|
-
}
|
|
610
|
-
else
|
|
611
|
-
{
|
|
612
|
-
slice.__lowentry_unfinished_slice = true;
|
|
613
|
-
return slice;
|
|
614
|
-
}
|
|
615
|
-
};
|
|
616
|
-
|
|
617
|
-
LeRed.createFastSlice = (slice) =>
|
|
618
|
-
{
|
|
619
|
-
if(Array.isArray(slice))
|
|
620
|
-
{
|
|
621
|
-
const e = new Error('the given slice is an array (instead of an object)');
|
|
622
|
-
console.error('an error was thrown by your LeRed.createFastSlice(...) code:');
|
|
623
|
-
console.error(e);
|
|
624
|
-
throw e;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
let actions = {};
|
|
628
|
-
LeUtils.each(slice.actions, (reducer, reducerName) =>
|
|
629
|
-
{
|
|
630
|
-
actions[reducerName] = (...params) => reducer.apply(slice, [slice.state, ...params]);
|
|
631
|
-
});
|
|
632
|
-
slice.actions = actions;
|
|
633
|
-
|
|
634
|
-
let selectors = {};
|
|
635
|
-
LeUtils.each(slice.selectors, (selector, selectorName) =>
|
|
636
|
-
{
|
|
637
|
-
selectors[selectorName] = () => selector.apply(slice, [slice.state]);
|
|
638
|
-
});
|
|
639
|
-
slice.selectors = new Proxy(selectors, {
|
|
640
|
-
get:(target, key) => (key in target) ? target[key]() : undefined,
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
let getters = {};
|
|
644
|
-
LeUtils.each(slice.getters, (selector, selectorName) =>
|
|
645
|
-
{
|
|
646
|
-
getters[selectorName] = (...params) => selector.apply(slice, [slice.state, ...params]);
|
|
647
|
-
});
|
|
648
|
-
slice.getters = getters;
|
|
649
|
-
return slice;
|
|
650
|
-
};
|
|
651
|
-
|
|
652
|
-
LeRed.current = (obj) =>
|
|
653
|
-
{
|
|
654
|
-
try
|
|
655
|
-
{
|
|
656
|
-
return RTK.current(obj);
|
|
657
|
-
}
|
|
658
|
-
catch(e)
|
|
659
|
-
{
|
|
660
|
-
}
|
|
661
|
-
return obj;
|
|
662
|
-
};
|
|
663
|
-
|
|
664
|
-
LeRed.useConfigureStore = (storeData) =>
|
|
665
|
-
{
|
|
666
|
-
return LeRed.useMemo(() => LeRed.configureStore(storeData), [storeData]);
|
|
667
|
-
};
|
|
668
|
-
|
|
669
|
-
LeRed.useSelector = (selector, equalsComparator) =>
|
|
670
|
-
{
|
|
671
|
-
if(typeof selector !== 'function')
|
|
672
|
-
{
|
|
673
|
-
console.error('LeRed.useSelector() was given an invalid selector:');
|
|
674
|
-
console.error(selector);
|
|
675
|
-
selector = () =>
|
|
676
|
-
{
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.useSelector() was given an invalid comparator:');
|
|
680
|
-
return ReactRedux.useSelector(selector, equalsComparator);
|
|
681
|
-
};
|
|
682
|
-
|
|
683
|
-
LeRed.useLayoutEffect = (callable, comparingValues, equalsComparator) =>
|
|
684
|
-
{
|
|
685
|
-
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.useLayoutEffect() was given an invalid comparator:');
|
|
686
|
-
comparingValues = ARRAY(comparingValues);
|
|
687
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
688
|
-
comparingValues = comparingValues.map(value => useCompareMemoize(value, equalsComparator));
|
|
689
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
690
|
-
return React.useLayoutEffect(callable, comparingValues);
|
|
691
|
-
};
|
|
692
|
-
|
|
693
|
-
LeRed.useEffect = (callable, comparingValues, equalsComparator) =>
|
|
694
|
-
{
|
|
695
|
-
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.useEffect() was given an invalid comparator:');
|
|
696
|
-
comparingValues = ARRAY(comparingValues);
|
|
697
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
698
|
-
comparingValues = comparingValues.map(value => useCompareMemoize(value, equalsComparator));
|
|
699
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
700
|
-
return React.useEffect(callable, comparingValues);
|
|
701
|
-
};
|
|
702
|
-
|
|
703
|
-
LeRed.useEffectInterval = (callable, comparingValues, intervalMs, fireImmediately, equalsComparator) =>
|
|
704
|
-
{
|
|
705
|
-
return LeRed.useEffect(() => LeUtils.setInterval(callable, intervalMs, fireImmediately).remove, [comparingValues, equalsComparator]);
|
|
706
|
-
};
|
|
707
|
-
|
|
708
|
-
LeRed.useEffectAnimationFrameInterval = (callable, comparingValues, intervalFrames, fireImmediately, equalsComparator) =>
|
|
709
|
-
{
|
|
710
|
-
return LeRed.useEffect(() => LeUtils.setAnimationFrameInterval(callable, intervalFrames, fireImmediately).remove, [comparingValues, equalsComparator]);
|
|
711
|
-
};
|
|
712
|
-
|
|
713
|
-
LeRed.useEffectGenerator = (callable, comparingValues, intervalMs, fireImmediately, equalsComparator) =>
|
|
714
|
-
{
|
|
715
|
-
return LeRed.useEffect(() =>
|
|
716
|
-
{
|
|
717
|
-
let stop = false;
|
|
718
|
-
|
|
719
|
-
(async () =>
|
|
720
|
-
{
|
|
721
|
-
for(const promise of callable())
|
|
722
|
-
{
|
|
723
|
-
if(stop)
|
|
724
|
-
{
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
await promise;
|
|
728
|
-
if(stop)
|
|
729
|
-
{
|
|
730
|
-
return;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
})();
|
|
734
|
-
|
|
735
|
-
return () =>
|
|
736
|
-
{
|
|
737
|
-
stop = true;
|
|
738
|
-
};
|
|
739
|
-
}, [comparingValues, equalsComparator]);
|
|
740
|
-
};
|
|
741
|
-
|
|
742
|
-
LeRed.useEffectGeneratorLoop = (callable, comparingValues, intervalMs, fireImmediately, equalsComparator) =>
|
|
743
|
-
{
|
|
744
|
-
return LeRed.useEffect(() =>
|
|
745
|
-
{
|
|
746
|
-
let stop = false;
|
|
747
|
-
|
|
748
|
-
(async () =>
|
|
749
|
-
{
|
|
750
|
-
while(!stop)
|
|
751
|
-
{
|
|
752
|
-
for(const promise of callable())
|
|
753
|
-
{
|
|
754
|
-
if(stop)
|
|
755
|
-
{
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
|
-
await promise;
|
|
759
|
-
if(stop)
|
|
760
|
-
{
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
})();
|
|
766
|
-
|
|
767
|
-
return () =>
|
|
768
|
-
{
|
|
769
|
-
stop = true;
|
|
770
|
-
};
|
|
771
|
-
}, [comparingValues, equalsComparator]);
|
|
772
|
-
};
|
|
773
|
-
|
|
774
|
-
LeRed.useEffectShutdown = (callable, comparingValues, equalsComparator) =>
|
|
775
|
-
{
|
|
776
|
-
return LeRed.useEffect(() =>
|
|
777
|
-
{
|
|
778
|
-
const run = () =>
|
|
779
|
-
{
|
|
780
|
-
callable();
|
|
781
|
-
};
|
|
782
|
-
|
|
783
|
-
globalThis?.addEventListener?.('beforeunload', run, {capture:true});
|
|
784
|
-
return () =>
|
|
785
|
-
{
|
|
786
|
-
globalThis?.removeEventListener?.('beforeunload', run, {capture:true});
|
|
787
|
-
run();
|
|
788
|
-
};
|
|
789
|
-
}, [comparingValues, equalsComparator]);
|
|
790
|
-
};
|
|
791
|
-
|
|
792
|
-
LeRed.useEffectPageFocusLost = (callable, comparingValues, equalsComparator) =>
|
|
793
|
-
{
|
|
794
|
-
const events = ['pagehide', 'freeze', 'blur', 'visibilitychange'];
|
|
795
|
-
return LeRed.useEffect(() =>
|
|
796
|
-
{
|
|
797
|
-
const run = () =>
|
|
798
|
-
{
|
|
799
|
-
if((globalThis?.document?.visibilityState !== 'hidden') && globalThis?.document?.hasFocus?.())
|
|
800
|
-
{
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
callable();
|
|
804
|
-
};
|
|
805
|
-
|
|
806
|
-
events.forEach(type =>
|
|
807
|
-
{
|
|
808
|
-
globalThis?.addEventListener?.(type, run, {capture:true});
|
|
809
|
-
});
|
|
810
|
-
return () =>
|
|
811
|
-
{
|
|
812
|
-
events.forEach(type =>
|
|
813
|
-
{
|
|
814
|
-
globalThis?.removeEventListener?.(type, run, {capture:true});
|
|
815
|
-
});
|
|
816
|
-
};
|
|
817
|
-
}, [comparingValues, equalsComparator]);
|
|
818
|
-
};
|
|
819
|
-
|
|
820
|
-
LeRed.memo = (component, equalsComparator) =>
|
|
821
|
-
{
|
|
822
|
-
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.memo() was given an invalid comparator:');
|
|
823
|
-
return React.memo(component, equalsComparator);
|
|
824
|
-
};
|
|
825
|
-
|
|
826
|
-
LeRed.useMemo = (callable, comparingValues, equalsComparator) =>
|
|
827
|
-
{
|
|
828
|
-
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.useMemo() was given an invalid comparator:');
|
|
829
|
-
comparingValues = ARRAY(comparingValues);
|
|
830
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
831
|
-
comparingValues = comparingValues.map(value => useCompareMemoize(value, equalsComparator));
|
|
832
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
833
|
-
return React.useMemo(callable, comparingValues);
|
|
834
|
-
};
|
|
835
|
-
|
|
836
|
-
LeRed.useCallback = (callable, comparingValues, equalsComparator) =>
|
|
837
|
-
{
|
|
838
|
-
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.useCallback() was given an invalid comparator:');
|
|
839
|
-
comparingValues = ARRAY(comparingValues);
|
|
840
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
841
|
-
comparingValues = comparingValues.map(value => useCompareMemoize(value, equalsComparator));
|
|
842
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
843
|
-
return React.useCallback(callable, comparingValues);
|
|
844
|
-
};
|
|
845
|
-
|
|
846
|
-
LeRed.usePrevious = (value, initialValue) =>
|
|
847
|
-
{
|
|
848
|
-
const ref = LeRed.useRef(initialValue);
|
|
849
|
-
LeRed.useEffect(() =>
|
|
850
|
-
{
|
|
851
|
-
ref.current = value;
|
|
852
|
-
}, [value]);
|
|
853
|
-
return ref.current;
|
|
854
|
-
};
|
|
855
|
-
|
|
856
|
-
LeRed.useFont = (font) =>
|
|
857
|
-
{
|
|
858
|
-
font = '12px ' + STRING(font).trim();
|
|
859
|
-
const [hasFont, setHasFont] = LeRed.useState(false);
|
|
860
|
-
LeRed.useEffect(() =>
|
|
861
|
-
{
|
|
862
|
-
if(!hasFont)
|
|
863
|
-
{
|
|
864
|
-
if(!globalThis?.document?.fonts?.check)
|
|
865
|
-
{
|
|
866
|
-
setHasFont(true);
|
|
867
|
-
return;
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
const handler = setInterval(() =>
|
|
871
|
-
{
|
|
872
|
-
try
|
|
873
|
-
{
|
|
874
|
-
if(globalThis.document.fonts.check(font))
|
|
875
|
-
{
|
|
876
|
-
clearInterval(handler);
|
|
877
|
-
setHasFont(true);
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
catch(e)
|
|
881
|
-
{
|
|
882
|
-
console.error(e);
|
|
883
|
-
clearInterval(handler);
|
|
884
|
-
setHasFont(true);
|
|
885
|
-
}
|
|
886
|
-
}, 30);
|
|
887
|
-
}
|
|
888
|
-
}, [hasFont]);
|
|
889
|
-
return hasFont;
|
|
890
|
-
};
|
|
891
|
-
|
|
892
|
-
/**
|
|
893
|
-
* Adds a <script> tag to the <head> of the document.
|
|
894
|
-
* Only for development and testing purposes.
|
|
895
|
-
*
|
|
896
|
-
* @param {string} url The URL of the js file to include.
|
|
897
|
-
* @param {Object} props Additional props of the <script> tag.
|
|
898
|
-
*/
|
|
899
|
-
LeRed.useScript = (url, props = {}) =>
|
|
900
|
-
{
|
|
901
|
-
return LeRed.useEffect(() =>
|
|
902
|
-
{
|
|
903
|
-
if(!globalThis?.document?.createElement || !globalThis?.document?.head?.appendChild || !globalThis?.document?.head?.removeChild)
|
|
904
|
-
{
|
|
905
|
-
return;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
const script = globalThis.document.createElement('script');
|
|
909
|
-
script.type = 'text/javascript';
|
|
910
|
-
script.src = url;
|
|
911
|
-
|
|
912
|
-
LeUtils.each(props, (value, key) =>
|
|
913
|
-
{
|
|
914
|
-
script.setAttribute(key, value);
|
|
915
|
-
});
|
|
916
|
-
|
|
917
|
-
globalThis.document.head.appendChild(script);
|
|
918
|
-
return () => globalThis.document.head.removeChild(script);
|
|
919
|
-
}, [url, props]);
|
|
920
|
-
};
|
|
921
|
-
|
|
922
|
-
LeRed.mergeRefs = (...refs) =>
|
|
923
|
-
{
|
|
924
|
-
refs = LeUtils.flattenArray(refs);
|
|
925
|
-
if(!refs)
|
|
926
|
-
{
|
|
927
|
-
return null;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
let newRefs = [];
|
|
931
|
-
LeUtils.each(refs, (ref) =>
|
|
932
|
-
{
|
|
933
|
-
if(ref)
|
|
934
|
-
{
|
|
935
|
-
newRefs.push(ref);
|
|
936
|
-
}
|
|
937
|
-
});
|
|
938
|
-
refs = newRefs;
|
|
939
|
-
|
|
940
|
-
if(refs.length <= 0)
|
|
941
|
-
{
|
|
942
|
-
return null;
|
|
943
|
-
}
|
|
944
|
-
if(refs.length === 1)
|
|
945
|
-
{
|
|
946
|
-
return refs[0];
|
|
947
|
-
}
|
|
948
|
-
return (inst) =>
|
|
949
|
-
{
|
|
950
|
-
LeUtils.each(refs, (ref) =>
|
|
951
|
-
{
|
|
952
|
-
try
|
|
953
|
-
{
|
|
954
|
-
if(typeof ref === 'function')
|
|
955
|
-
{
|
|
956
|
-
ref(inst);
|
|
957
|
-
}
|
|
958
|
-
else if(ref)
|
|
959
|
-
{
|
|
960
|
-
ref.current = inst;
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
catch(e)
|
|
964
|
-
{
|
|
965
|
-
console.error(e);
|
|
966
|
-
}
|
|
967
|
-
});
|
|
968
|
-
};
|
|
969
|
-
};
|
|
970
|
-
|
|
971
|
-
LeRed.useTriggerable = (event) =>
|
|
972
|
-
{
|
|
973
|
-
const [[value, uniqueId], setValue] = LeRed.useState([null, LeUtils.uniqueId()]);
|
|
974
|
-
|
|
975
|
-
LeRed.useEffect(() =>
|
|
976
|
-
{
|
|
977
|
-
if(!globalThis?.addEventListener || !globalThis?.removeEventListener)
|
|
978
|
-
{
|
|
979
|
-
return;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
const callback = (e) =>
|
|
983
|
-
{
|
|
984
|
-
setValue([e?.detail, LeUtils.uniqueId()]);
|
|
985
|
-
};
|
|
986
|
-
|
|
987
|
-
const eventName = 'lowentrytriggerable_' + event;
|
|
988
|
-
globalThis.addEventListener(eventName, callback);
|
|
989
|
-
return () => globalThis.removeEventListener(eventName, callback);
|
|
990
|
-
}, [event]);
|
|
991
|
-
|
|
992
|
-
return value;
|
|
993
|
-
};
|
|
994
|
-
|
|
995
|
-
LeRed.trigger = (event, value) =>
|
|
996
|
-
{
|
|
997
|
-
if(!globalThis?.dispatchEvent || !globalThis?.CustomEvent)
|
|
998
|
-
{
|
|
999
|
-
return;
|
|
1000
|
-
}
|
|
1001
|
-
const eventName = 'lowentrytriggerable_' + event;
|
|
1002
|
-
globalThis.dispatchEvent(new globalThis.CustomEvent(eventName, {detail:value}));
|
|
1003
|
-
};
|
|
1004
|
-
|
|
1005
|
-
/**
|
|
1006
|
-
* A useState() hook that automatically resets to the defaultValue after the given duration.
|
|
1007
|
-
*
|
|
1008
|
-
* Example:
|
|
1009
|
-
*
|
|
1010
|
-
* ```js
|
|
1011
|
-
* const [value, setValue] = LeRed.useTempState(true, 2000);
|
|
1012
|
-
* // somewhere in your code:
|
|
1013
|
-
* setValue(false); // value is now false, after 2 seconds it will be reset to true
|
|
1014
|
-
* ```
|
|
1015
|
-
*
|
|
1016
|
-
* Repeated calls cause the timer to reset, meaning each set value will always remain for the full given duration.
|
|
1017
|
-
*/
|
|
1018
|
-
LeRed.useTempState = (defaultValue, duration) =>
|
|
1019
|
-
{
|
|
1020
|
-
const [value, setValue] = LeRed.useState(defaultValue);
|
|
1021
|
-
const timeoutHandle = LeRed.useRef(null);
|
|
1022
|
-
|
|
1023
|
-
return [value, (newValue) =>
|
|
1024
|
-
{
|
|
1025
|
-
if(timeoutHandle.current)
|
|
1026
|
-
{
|
|
1027
|
-
clearTimeout(timeoutHandle.current);
|
|
1028
|
-
}
|
|
1029
|
-
setValue(newValue);
|
|
1030
|
-
timeoutHandle.current = setTimeout(() =>
|
|
1031
|
-
{
|
|
1032
|
-
timeoutHandle.current = null;
|
|
1033
|
-
setValue(defaultValue);
|
|
1034
|
-
}, duration);
|
|
1035
|
-
}];
|
|
1036
|
-
};
|
|
1037
|
-
|
|
1038
|
-
/**
|
|
1039
|
-
* Allows you to listen to the browser history events (forwards, backwards) and execute a callback on those events.
|
|
1040
|
-
*
|
|
1041
|
-
* You pass 2 functions to it (the callbacks), and it also provides 2 functions (for manually going forwards and backwards).
|
|
1042
|
-
*
|
|
1043
|
-
* Usage:
|
|
1044
|
-
*
|
|
1045
|
-
* ```js
|
|
1046
|
-
* const [goForwards, goBackwards] = LeRed.useHistory(() => console.log('has gone forwards'), () => console.log('has gone backwards'));
|
|
1047
|
-
* ```
|
|
1048
|
-
*/
|
|
1049
|
-
LeRed.useHistory = (() =>
|
|
1050
|
-
{
|
|
1051
|
-
let historyStateListeners = [];
|
|
1052
|
-
|
|
1053
|
-
globalThis?.addEventListener?.('popstate', () =>
|
|
1054
|
-
{
|
|
1055
|
-
historyStateListeners.pop()?.callback();
|
|
1056
|
-
});
|
|
1057
|
-
|
|
1058
|
-
const addListener = (callback) =>
|
|
1059
|
-
{
|
|
1060
|
-
const id = LeUtils.uniqueId();
|
|
1061
|
-
historyStateListeners.push({id, callback});
|
|
1062
|
-
return id;
|
|
1063
|
-
};
|
|
1064
|
-
|
|
1065
|
-
const removeListener = (id) =>
|
|
1066
|
-
{
|
|
1067
|
-
if(!id)
|
|
1068
|
-
{
|
|
1069
|
-
return;
|
|
1070
|
-
}
|
|
1071
|
-
historyStateListeners = historyStateListeners.filter(listener => (listener.id !== id));
|
|
1072
|
-
};
|
|
1073
|
-
|
|
1074
|
-
return (onForward, onBack) =>
|
|
1075
|
-
{
|
|
1076
|
-
const remaining = LeRed.useRef(0);
|
|
1077
|
-
const id = LeRed.useRef(null);
|
|
1078
|
-
|
|
1079
|
-
const goBack = LeRed.useCallback(() =>
|
|
1080
|
-
{
|
|
1081
|
-
if(remaining.current <= 0)
|
|
1082
|
-
{
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
remaining.current--;
|
|
1086
|
-
if(remaining.current === 0)
|
|
1087
|
-
{
|
|
1088
|
-
if(id.current)
|
|
1089
|
-
{
|
|
1090
|
-
removeListener(id.current);
|
|
1091
|
-
}
|
|
1092
|
-
id.current = null;
|
|
1093
|
-
}
|
|
1094
|
-
onBack();
|
|
1095
|
-
}, [onBack]);
|
|
1096
|
-
|
|
1097
|
-
return [
|
|
1098
|
-
() => /** do **/
|
|
1099
|
-
{
|
|
1100
|
-
LeRed.navigate('#');
|
|
1101
|
-
remaining.current++;
|
|
1102
|
-
if(remaining.current === 1)
|
|
1103
|
-
{
|
|
1104
|
-
if(id.current)
|
|
1105
|
-
{
|
|
1106
|
-
removeListener(id.current);
|
|
1107
|
-
}
|
|
1108
|
-
id.current = addListener(goBack);
|
|
1109
|
-
}
|
|
1110
|
-
onForward();
|
|
1111
|
-
},
|
|
1112
|
-
|
|
1113
|
-
() => /** undo **/
|
|
1114
|
-
{
|
|
1115
|
-
if(remaining.current > 0)
|
|
1116
|
-
{
|
|
1117
|
-
LeRed.navigate(-1);
|
|
1118
|
-
}
|
|
1119
|
-
},
|
|
1120
|
-
];
|
|
1121
|
-
};
|
|
1122
|
-
})();
|
|
1123
|
-
|
|
1124
|
-
/**
|
|
1125
|
-
* Similar to {@link LeRed.useHistory}, but this is specifically for toggling a boolean state between true and false. For example, for a modal, which you'd like to be closed when the user goes back in history.
|
|
1126
|
-
*
|
|
1127
|
-
* Example:
|
|
1128
|
-
*
|
|
1129
|
-
* ```js
|
|
1130
|
-
* const [isModalOpen, openModal, closeModal] = LeRed.useHistoryState(false); // you'd open it programmatically using openModal(), afterwards, if the user goes back in history, it will close again
|
|
1131
|
-
* ```
|
|
1132
|
-
*
|
|
1133
|
-
* or, if you'd like it to be true by default:
|
|
1134
|
-
*
|
|
1135
|
-
* ```js
|
|
1136
|
-
* const [isModalOpen, openModal, closeModal] = LeRed.useHistoryState(true); // you'd close it programmatically using closeModal(), afterwards, if the user goes back in history, it will open again
|
|
1137
|
-
* ```
|
|
1138
|
-
*/
|
|
1139
|
-
LeRed.useHistoryState = (initialState) =>
|
|
1140
|
-
{
|
|
1141
|
-
const [state, setState] = LeRed.useState(!!initialState);
|
|
1142
|
-
const [forwards, backwards] = LeRed.useHistory(() => setState(!initialState), () => setState(!!initialState));
|
|
1143
|
-
if(!!initialState)
|
|
1144
|
-
{
|
|
1145
|
-
return [state, backwards, forwards];
|
|
1146
|
-
}
|
|
1147
|
-
return [state, forwards, backwards];
|
|
1148
|
-
};
|
|
1149
|
-
|
|
1150
|
-
/**
|
|
1151
|
-
* Allows you to listen to the hash of the URL (window.location.hash).
|
|
1152
|
-
*
|
|
1153
|
-
* The hash can be useful than the query, as changing the hash will not cause the page to reload. Plus there's a good way to listen to hash changes, using the `hashchange` event, which is lacking for the query.
|
|
1154
|
-
*
|
|
1155
|
-
* Example:
|
|
1156
|
-
*
|
|
1157
|
-
* ```js
|
|
1158
|
-
* const [hashParams, hashString] = LeRed.useUrlHashParams();
|
|
1159
|
-
* ```
|
|
1160
|
-
*/
|
|
1161
|
-
LeRed.useUrlHashParams = (() =>
|
|
1162
|
-
{
|
|
1163
|
-
const getHashString = () => (globalThis?.location?.hash?.trim()?.replace(/^#/, '') ?? '');
|
|
1164
|
-
const parseHashParams = (hashString) => Object.fromEntries(new URLSearchParams(hashString));
|
|
1165
|
-
|
|
1166
|
-
/**
|
|
1167
|
-
* @returns {[hashParams:URLSearchParams, hashString:string]}
|
|
1168
|
-
*/
|
|
1169
|
-
return () =>
|
|
1170
|
-
{
|
|
1171
|
-
const [hashString, setHashString] = LeRed.useState(getHashString);
|
|
1172
|
-
const hashParams = LeRed.useMemo(() => parseHashParams(hashString), [hashString]);
|
|
1173
|
-
|
|
1174
|
-
LeRed.useEffect(() =>
|
|
1175
|
-
{
|
|
1176
|
-
const onUrlChanged = () =>
|
|
1177
|
-
{
|
|
1178
|
-
setHashString(getHashString());
|
|
1179
|
-
};
|
|
1180
|
-
|
|
1181
|
-
globalThis?.addEventListener?.('hashchange', onUrlChanged);
|
|
1182
|
-
return () =>
|
|
1183
|
-
{
|
|
1184
|
-
globalThis?.removeEventListener?.('hashchange', onUrlChanged);
|
|
1185
|
-
};
|
|
1186
|
-
}, []);
|
|
1187
|
-
|
|
1188
|
-
return [hashParams, hashString];
|
|
1189
|
-
};
|
|
1190
|
-
})();
|
|
1191
|
-
|
|
1192
|
-
/**
|
|
1193
|
-
* Allows you to easily create an <pre><img></pre> url and onError handler that will automatically retry loading the image if it fails.
|
|
1194
|
-
*/
|
|
1195
|
-
LeRed.useRetryingImageUrl = (url, options) =>
|
|
1196
|
-
{
|
|
1197
|
-
url = STRING(url);
|
|
1198
|
-
const urlHasQ = url.includes('?');
|
|
1199
|
-
|
|
1200
|
-
const [imageUrl, setImageUrl] = LeRed.useState(url);
|
|
1201
|
-
const retries = LeRed.useRef(0);
|
|
1202
|
-
const timeout = LeRed.useRef({remove:() => undefined});
|
|
1203
|
-
|
|
1204
|
-
LeRed.useEffect(() =>
|
|
1205
|
-
{
|
|
1206
|
-
timeout.current.remove();
|
|
1207
|
-
retries.current = 0;
|
|
1208
|
-
setImageUrl(url);
|
|
1209
|
-
}, [url]);
|
|
1210
|
-
|
|
1211
|
-
const onImageLoadError = LeRed.useCallback(() =>
|
|
1212
|
-
{
|
|
1213
|
-
if(retries.current < INT_LAX_ANY(options?.retries, 30))
|
|
1214
|
-
{
|
|
1215
|
-
const defaultDelay = 100 + (50 * retries.current);
|
|
1216
|
-
timeout.current.remove();
|
|
1217
|
-
timeout.current = LeUtils.setTimeout(() =>
|
|
1218
|
-
{
|
|
1219
|
-
setImageUrl(url + (urlHasQ ? '&' : '?') + (options?.queryParam || 'lowentryretryingimgversion') + '=' + (retries.current++));
|
|
1220
|
-
}, (typeof options?.delay === 'function') ? INT_LAX_ANY(options?.delay(retries.current), defaultDelay) : (INT_LAX_ANY(options?.delay, defaultDelay)));
|
|
1221
|
-
}
|
|
1222
|
-
}, [url, options]);
|
|
1223
|
-
|
|
1224
|
-
const onImageLoadErrorIgnored = LeRed.useCallback(() =>
|
|
1225
|
-
{
|
|
1226
|
-
}, []);
|
|
1227
|
-
|
|
1228
|
-
if(!url)
|
|
1229
|
-
{
|
|
1230
|
-
return [url, onImageLoadErrorIgnored];
|
|
1231
|
-
}
|
|
1232
|
-
return [imageUrl, onImageLoadError];
|
|
1233
|
-
};
|
|
1234
|
-
|
|
1235
|
-
/**
|
|
1236
|
-
* Allows you to easily convert promises to react hooks.
|
|
1237
|
-
*
|
|
1238
|
-
* The given callable should return promises. The returned promises can be an array, an object, or even a single promise. The returned data of this hook will match the promises it has operated on.
|
|
1239
|
-
*
|
|
1240
|
-
* The given comparingValues can be anything, this is used to detect whether the given promises have changed or not, and so whether new promises have to be generated and executed again.
|
|
1241
|
-
*/
|
|
1242
|
-
LeRed.usePromises = (callable, comparingValues) =>
|
|
1243
|
-
{
|
|
1244
|
-
const comparingValuesClone = LeUtils.clone(comparingValues);
|
|
1245
|
-
const comparingValuesRef = LeRed.useRef(comparingValuesClone);
|
|
1246
|
-
const latestComparingValuesRef = LeRed.useRef();
|
|
1247
|
-
latestComparingValuesRef.current = comparingValuesClone;
|
|
1248
|
-
|
|
1249
|
-
const [data, setData] = LeRed.useState(null);
|
|
1250
|
-
const [loading, setLoading] = LeRed.useState(true);
|
|
1251
|
-
const [error, setError] = LeRed.useState(null);
|
|
1252
|
-
|
|
1253
|
-
LeRed.useEffect(() =>
|
|
1254
|
-
{
|
|
1255
|
-
setLoading(true);
|
|
1256
|
-
setData(null);
|
|
1257
|
-
setError(null);
|
|
1258
|
-
|
|
1259
|
-
try
|
|
1260
|
-
{
|
|
1261
|
-
const promises = callable();
|
|
1262
|
-
|
|
1263
|
-
const promisesIsObject = IS_OBJECT(promises) && (typeof promises?.then !== 'function');
|
|
1264
|
-
const promisesIsArray = IS_ARRAY(promises);
|
|
1265
|
-
|
|
1266
|
-
let promisesKeyed = [];
|
|
1267
|
-
if(promisesIsObject || promisesIsArray)
|
|
1268
|
-
{
|
|
1269
|
-
LeUtils.each(promises, (promise, key) =>
|
|
1270
|
-
{
|
|
1271
|
-
promisesKeyed.push({promise, key});
|
|
1272
|
-
});
|
|
1273
|
-
}
|
|
1274
|
-
else
|
|
1275
|
-
{
|
|
1276
|
-
promisesKeyed.push({promise:promises, key:undefined});
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
let wrappedPromises = [];
|
|
1280
|
-
LeUtils.each(promisesKeyed, ({promise, key}) =>
|
|
1281
|
-
{
|
|
1282
|
-
wrappedPromises.push(promise
|
|
1283
|
-
.then(async result => ({result, key})));
|
|
1284
|
-
});
|
|
1285
|
-
|
|
1286
|
-
Promise.all(wrappedPromises)
|
|
1287
|
-
.then(resultObjects =>
|
|
1288
|
-
{
|
|
1289
|
-
if(promisesIsObject)
|
|
1290
|
-
{
|
|
1291
|
-
let results = {};
|
|
1292
|
-
LeUtils.each(resultObjects, ({result, key}) =>
|
|
1293
|
-
{
|
|
1294
|
-
results[key] = result;
|
|
1295
|
-
});
|
|
1296
|
-
return results;
|
|
1297
|
-
}
|
|
1298
|
-
else if(promisesIsArray)
|
|
1299
|
-
{
|
|
1300
|
-
let results = [];
|
|
1301
|
-
LeUtils.each(resultObjects, ({result, key}) =>
|
|
1302
|
-
{
|
|
1303
|
-
results[key] = result;
|
|
1304
|
-
});
|
|
1305
|
-
return results;
|
|
1306
|
-
}
|
|
1307
|
-
return resultObjects.pop()?.result;
|
|
1308
|
-
})
|
|
1309
|
-
.then(results =>
|
|
1310
|
-
{
|
|
1311
|
-
if(!LeUtils.equals(latestComparingValuesRef.current, comparingValuesClone))
|
|
1312
|
-
{
|
|
1313
|
-
// canceled
|
|
1314
|
-
return;
|
|
1315
|
-
}
|
|
1316
|
-
comparingValuesRef.current = comparingValuesClone;
|
|
1317
|
-
setLoading(false);
|
|
1318
|
-
setData(results);
|
|
1319
|
-
setError(null);
|
|
1320
|
-
})
|
|
1321
|
-
.catch(error =>
|
|
1322
|
-
{
|
|
1323
|
-
if(!LeUtils.equals(latestComparingValuesRef.current, comparingValuesClone))
|
|
1324
|
-
{
|
|
1325
|
-
// canceled
|
|
1326
|
-
return;
|
|
1327
|
-
}
|
|
1328
|
-
comparingValuesRef.current = comparingValuesClone;
|
|
1329
|
-
setLoading(false);
|
|
1330
|
-
setData(null);
|
|
1331
|
-
setError(LeUtils.purgeErrorMessage(error));
|
|
1332
|
-
});
|
|
1333
|
-
|
|
1334
|
-
return () =>
|
|
1335
|
-
{
|
|
1336
|
-
LeUtils.each(wrappedPromises, promise =>
|
|
1337
|
-
{
|
|
1338
|
-
try
|
|
1339
|
-
{
|
|
1340
|
-
promise?.cancel?.();
|
|
1341
|
-
}
|
|
1342
|
-
catch(e)
|
|
1343
|
-
{
|
|
1344
|
-
console.error('Failed to cancel the given promise:', e);
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
try
|
|
1348
|
-
{
|
|
1349
|
-
promise?.remove?.();
|
|
1350
|
-
}
|
|
1351
|
-
catch(e)
|
|
1352
|
-
{
|
|
1353
|
-
console.error('Failed to remove the given promise:', e);
|
|
1354
|
-
}
|
|
1355
|
-
});
|
|
1356
|
-
};
|
|
1357
|
-
}
|
|
1358
|
-
catch(error)
|
|
1359
|
-
{
|
|
1360
|
-
LeUtils.setAnimationFrameTimeout(() =>
|
|
1361
|
-
{
|
|
1362
|
-
if(!LeUtils.equals(latestComparingValuesRef.current, comparingValuesClone))
|
|
1363
|
-
{
|
|
1364
|
-
// canceled
|
|
1365
|
-
return;
|
|
1366
|
-
}
|
|
1367
|
-
comparingValuesRef.current = comparingValuesClone;
|
|
1368
|
-
setLoading(false);
|
|
1369
|
-
setData(null);
|
|
1370
|
-
setError(LeUtils.purgeErrorMessage(error));
|
|
1371
|
-
});
|
|
1372
|
-
}
|
|
1373
|
-
}, [comparingValuesClone]);
|
|
1374
|
-
|
|
1375
|
-
if(!LeUtils.equals(comparingValuesRef.current, comparingValuesClone))
|
|
1376
|
-
{
|
|
1377
|
-
return [null, true, null];
|
|
1378
|
-
}
|
|
1379
|
-
return [data, loading, error];
|
|
1380
|
-
};
|
|
1381
|
-
|
|
1382
|
-
/**
|
|
1383
|
-
* Allows you to easily obtain external data.
|
|
1384
|
-
*/
|
|
1385
|
-
LeRed.useExternal = (url, options, responseFunction) =>
|
|
1386
|
-
{
|
|
1387
|
-
return LeRed.usePromises(() =>
|
|
1388
|
-
{
|
|
1389
|
-
const createFetch = (urlString) => LeUtils.fetch(STRING(urlString), {retries:3, ...(options ?? {})})
|
|
1390
|
-
.then(async response =>
|
|
1391
|
-
{
|
|
1392
|
-
const data = await responseFunction(response);
|
|
1393
|
-
if(typeof options?.verify === 'function')
|
|
1394
|
-
{
|
|
1395
|
-
await options.verify(data, response);
|
|
1396
|
-
}
|
|
1397
|
-
return data;
|
|
1398
|
-
});
|
|
1399
|
-
|
|
1400
|
-
if(IS_OBJECT(url))
|
|
1401
|
-
{
|
|
1402
|
-
let promises = {};
|
|
1403
|
-
LeUtils.each(url, (urlString, key) =>
|
|
1404
|
-
{
|
|
1405
|
-
promises[key] = createFetch(urlString);
|
|
1406
|
-
});
|
|
1407
|
-
return promises;
|
|
1408
|
-
}
|
|
1409
|
-
if(IS_ARRAY(url))
|
|
1410
|
-
{
|
|
1411
|
-
let promises = [];
|
|
1412
|
-
LeUtils.each(url, urlString =>
|
|
1413
|
-
{
|
|
1414
|
-
promises.push(createFetch(urlString));
|
|
1415
|
-
});
|
|
1416
|
-
return promises;
|
|
1417
|
-
}
|
|
1418
|
-
return createFetch(url);
|
|
1419
|
-
}, [url, options, responseFunction]);
|
|
1420
|
-
};
|
|
1421
|
-
|
|
1422
|
-
/**
|
|
1423
|
-
* Allows you to easily obtain external JSON data.
|
|
1424
|
-
*/
|
|
1425
|
-
LeRed.useExternalJson = (url, options) =>
|
|
1426
|
-
{
|
|
1427
|
-
const responseFunction = LeRed.useCallback(response => response.json(), []);
|
|
1428
|
-
return LeRed.useExternal(url, options, responseFunction);
|
|
1429
|
-
};
|
|
1430
|
-
|
|
1431
|
-
/**
|
|
1432
|
-
* Allows you to easily obtain external Blob data.
|
|
1433
|
-
*/
|
|
1434
|
-
LeRed.useExternalBlob = (url, options) =>
|
|
1435
|
-
{
|
|
1436
|
-
const responseFunction = LeRed.useCallback(response => response.blob(), []);
|
|
1437
|
-
return LeRed.useExternal(url, options, responseFunction);
|
|
1438
|
-
};
|
|
1439
|
-
|
|
1440
|
-
/**
|
|
1441
|
-
* Allows you to easily obtain external ArrayBuffer data.
|
|
1442
|
-
*/
|
|
1443
|
-
LeRed.useExternalArrayBuffer = (url, options) =>
|
|
1444
|
-
{
|
|
1445
|
-
const responseFunction = LeRed.useCallback(response => response.arrayBuffer(), []);
|
|
1446
|
-
return LeRed.useExternal(url, options, responseFunction);
|
|
1447
|
-
};
|
|
1448
|
-
|
|
1449
|
-
/**
|
|
1450
|
-
* Allows you to easily obtain external string data.
|
|
1451
|
-
*/
|
|
1452
|
-
LeRed.useExternalString = (url, options) =>
|
|
1453
|
-
{
|
|
1454
|
-
const responseFunction = LeRed.useCallback(response => response.text(), []);
|
|
1455
|
-
return LeRed.useExternal(url, options, responseFunction);
|
|
1456
|
-
};
|
|
1457
|
-
|
|
1458
|
-
/**
|
|
1459
|
-
* Allows you to easily obtain external form data.
|
|
1460
|
-
*/
|
|
1461
|
-
LeRed.useExternalFormData = (url, options) =>
|
|
1462
|
-
{
|
|
1463
|
-
const responseFunction = LeRed.useCallback(response => response.formData(), []);
|
|
1464
|
-
return LeRed.useExternal(url, options, responseFunction);
|
|
1465
|
-
};
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
LeRed.Root = LeRed.memo(({store, children, ...other}) =>
|
|
1469
|
-
{
|
|
1470
|
-
if(ISSET(store))
|
|
1471
|
-
{
|
|
1472
|
-
store = LeRed.configureStore(store);
|
|
1473
|
-
return (<ReactRedux.Provider store={store} children={children} {...other}/>);
|
|
1474
|
-
}
|
|
1475
|
-
return children;
|
|
1476
|
-
});
|
|
1477
|
-
|
|
1478
|
-
LeRed.PreloadComponent = (load) =>
|
|
1479
|
-
{
|
|
1480
|
-
if(typeof window !== 'undefined')
|
|
1481
|
-
{
|
|
1482
|
-
const promise = load(); // in the browser, start loading already, before it's being rendered in React
|
|
1483
|
-
return () => promise;
|
|
1484
|
-
}
|
|
1485
|
-
return load;
|
|
1486
|
-
};
|
|
1487
|
-
|
|
1488
|
-
LeRed.LoadComponent = LeRed.memo(({loading, load, ...other}) =>
|
|
1489
|
-
{
|
|
1490
|
-
const [Component, setComponent] = LeRed.useState(loading ?? null);
|
|
1491
|
-
|
|
1492
|
-
LeRed.useEffect(() =>
|
|
1493
|
-
{
|
|
1494
|
-
(async () =>
|
|
1495
|
-
{
|
|
1496
|
-
const LoadedComponent = (typeof load === 'function') ? await load() : await load;
|
|
1497
|
-
if(!LoadedComponent)
|
|
1498
|
-
{
|
|
1499
|
-
setComponent(null);
|
|
1500
|
-
return;
|
|
1501
|
-
}
|
|
1502
|
-
setComponent(<LoadedComponent {...other}/>);
|
|
1503
|
-
})();
|
|
1504
|
-
}, []);
|
|
1505
|
-
|
|
1506
|
-
return Component;
|
|
1507
|
-
});
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
if(typeof Proxy === 'undefined')
|
|
1511
|
-
{
|
|
1512
|
-
return LeRed;
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
return new Proxy(LeRed, {
|
|
1516
|
-
set:(target, key, value) =>
|
|
1517
|
-
{
|
|
1518
|
-
LeRed.set(key, value);
|
|
1519
|
-
return true;
|
|
1520
|
-
},
|
|
1521
|
-
});
|
|
1522
|
-
})();
|