@lowentry/react-redux 0.2.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/LeRed.js +977 -0
- package/LeTypes.js +76 -0
- package/LeUtils.js +890 -0
- package/README.md +121 -0
- package/bitbucket-pipelines.yml +19 -0
- package/index.js +5 -0
- package/package.json +35 -0
package/LeRed.js
ADDED
|
@@ -0,0 +1,977 @@
|
|
|
1
|
+
import * as FastDeepEqualReact from 'fast-deep-equal/react';
|
|
2
|
+
import * as RTK from '@reduxjs/toolkit';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as ReactDOM from 'react-dom';
|
|
5
|
+
import * as ReactRedux from 'react-redux';
|
|
6
|
+
import * as ReduxSaga from 'redux-saga';
|
|
7
|
+
import * as ReduxSagaEffects from 'redux-saga/effects';
|
|
8
|
+
import {ISSET, ARRAY, STRING} from './LeTypes.js';
|
|
9
|
+
import {LeUtils} from './LeUtils.js';
|
|
10
|
+
|
|
11
|
+
export const LeRed = (() =>
|
|
12
|
+
{
|
|
13
|
+
let LeRed = {
|
|
14
|
+
// for editor auto-complete >>
|
|
15
|
+
createTheme: () =>
|
|
16
|
+
{
|
|
17
|
+
},
|
|
18
|
+
useDispatch: () =>
|
|
19
|
+
{
|
|
20
|
+
},
|
|
21
|
+
useDrag: () =>
|
|
22
|
+
{
|
|
23
|
+
},
|
|
24
|
+
useDrop: () =>
|
|
25
|
+
{
|
|
26
|
+
},
|
|
27
|
+
useDragLayer:() =>
|
|
28
|
+
{
|
|
29
|
+
},
|
|
30
|
+
effects: {},
|
|
31
|
+
// for editor auto-complete <<
|
|
32
|
+
};
|
|
33
|
+
LeRed = {};
|
|
34
|
+
|
|
35
|
+
try
|
|
36
|
+
{
|
|
37
|
+
const set = (value, key, ignoreOverrides = false) =>
|
|
38
|
+
{
|
|
39
|
+
const keyFirstChar = key.charAt(0);
|
|
40
|
+
if(keyFirstChar === keyFirstChar.toLowerCase() && (keyFirstChar !== keyFirstChar.toUpperCase()))
|
|
41
|
+
{
|
|
42
|
+
if((key === 'default') || (key === 'version'))
|
|
43
|
+
{
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if((key === 'set') || (key === 'setAll'))
|
|
47
|
+
{
|
|
48
|
+
console.error('tried to override LeRed["' + key + '"], which isn\'t allowed, to:');
|
|
49
|
+
console.error(value);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if((ignoreOverrides !== true) && (key in LeRed))
|
|
53
|
+
{
|
|
54
|
+
console.warn('LeRed["' + key + '"] was overwritten, from:');
|
|
55
|
+
console.warn(LeRed[key]);
|
|
56
|
+
console.warn('to:');
|
|
57
|
+
console.warn(value);
|
|
58
|
+
}
|
|
59
|
+
LeRed[key] = value;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const setAll = (obj, ignoreOverrides = false, optionalSkipHasOwnPropertyCheck = true) =>
|
|
63
|
+
{
|
|
64
|
+
LeUtils.each(obj, (value, key) =>
|
|
65
|
+
{
|
|
66
|
+
set(value, key, ignoreOverrides);
|
|
67
|
+
}, optionalSkipHasOwnPropertyCheck);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
LeRed.set = (value, key) => set(value, key, true);
|
|
71
|
+
LeRed.setAll = (obj, optionalSkipHasOwnPropertyCheck = true) => setAll(obj, true, optionalSkipHasOwnPropertyCheck);
|
|
72
|
+
|
|
73
|
+
setAll(ReactDOM);
|
|
74
|
+
setAll(ReduxSaga);
|
|
75
|
+
setAll({effects:ReduxSagaEffects});
|
|
76
|
+
setAll(RTK);
|
|
77
|
+
setAll(React);
|
|
78
|
+
setAll(ReactRedux);
|
|
79
|
+
|
|
80
|
+
LeRed.effects.delayFrames = function* (frames = 1)
|
|
81
|
+
{
|
|
82
|
+
yield LeRed.effects.call(() =>
|
|
83
|
+
{
|
|
84
|
+
return new Promise((resolve, reject) =>
|
|
85
|
+
{
|
|
86
|
+
try
|
|
87
|
+
{
|
|
88
|
+
LeUtils.setAnimationFrameTimeout(resolve, frames);
|
|
89
|
+
}
|
|
90
|
+
catch(e)
|
|
91
|
+
{
|
|
92
|
+
reject(e);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
LeRed.effects.interval = function* (callback, intervalMs)
|
|
99
|
+
{
|
|
100
|
+
let channel = LeRed.eventChannel((emitter) =>
|
|
101
|
+
{
|
|
102
|
+
const interval = setInterval(() =>
|
|
103
|
+
{
|
|
104
|
+
emitter({});
|
|
105
|
+
}, intervalMs);
|
|
106
|
+
return () =>
|
|
107
|
+
{
|
|
108
|
+
clearInterval(interval);
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const stop = () =>
|
|
113
|
+
{
|
|
114
|
+
try
|
|
115
|
+
{
|
|
116
|
+
if(channel !== null)
|
|
117
|
+
{
|
|
118
|
+
channel.close();
|
|
119
|
+
channel = null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch(e)
|
|
123
|
+
{
|
|
124
|
+
console.error(e);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
while(channel !== null)
|
|
129
|
+
{
|
|
130
|
+
try
|
|
131
|
+
{
|
|
132
|
+
yield LeRed.effects.take(channel);
|
|
133
|
+
yield callback(stop);
|
|
134
|
+
}
|
|
135
|
+
catch(e)
|
|
136
|
+
{
|
|
137
|
+
console.error(e);
|
|
138
|
+
}
|
|
139
|
+
finally
|
|
140
|
+
{
|
|
141
|
+
try
|
|
142
|
+
{
|
|
143
|
+
if(yield LeRed.effects.cancelled())
|
|
144
|
+
{
|
|
145
|
+
channel.close();
|
|
146
|
+
channel = null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch(e)
|
|
150
|
+
{
|
|
151
|
+
console.error(e);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
catch(e)
|
|
158
|
+
{
|
|
159
|
+
console.error(e);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
const fixEqualsComparator = (equalsComparator, errorMessage) =>
|
|
164
|
+
{
|
|
165
|
+
if(ISSET(equalsComparator))
|
|
166
|
+
{
|
|
167
|
+
if(typeof equalsComparator !== 'function')
|
|
168
|
+
{
|
|
169
|
+
console.error(errorMessage);
|
|
170
|
+
console.error(equalsComparator);
|
|
171
|
+
return FastDeepEqualReact;
|
|
172
|
+
}
|
|
173
|
+
return equalsComparator;
|
|
174
|
+
}
|
|
175
|
+
return FastDeepEqualReact;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const useCompareMemoize = (value, equalsComparator) =>
|
|
179
|
+
{
|
|
180
|
+
const ref = React.useRef();
|
|
181
|
+
if(!equalsComparator(value, ref.current))
|
|
182
|
+
{
|
|
183
|
+
ref.current = value;
|
|
184
|
+
}
|
|
185
|
+
return ref.current;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
LeRed.Root = LeRed.memo(({store, children}) =>
|
|
190
|
+
{
|
|
191
|
+
return React.createElement(ReactRedux.Provider, {store}, children);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
LeRed.createRootElement = (elementClass, storeData) =>
|
|
195
|
+
{
|
|
196
|
+
if(ISSET(storeData))
|
|
197
|
+
{
|
|
198
|
+
storeData = LeRed.configureStore(storeData);
|
|
199
|
+
return React.createElement(ReactRedux.Provider, {store:storeData}, React.createElement(elementClass));
|
|
200
|
+
}
|
|
201
|
+
return React.createElement(elementClass);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
LeRed.createElement = (elementClass, props = null, ...children) =>
|
|
205
|
+
{
|
|
206
|
+
return React.createElement(elementClass, props, ...children);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
LeRed.configureStore = (storeData) =>
|
|
210
|
+
{
|
|
211
|
+
if(storeData.__lowentry_store__ === true)
|
|
212
|
+
{
|
|
213
|
+
return storeData;
|
|
214
|
+
}
|
|
215
|
+
if(ISSET(storeData.slices))
|
|
216
|
+
{
|
|
217
|
+
storeData.reducer = storeData.slices;
|
|
218
|
+
delete storeData.slices;
|
|
219
|
+
}
|
|
220
|
+
let sagaListeners = [];
|
|
221
|
+
if(ISSET(storeData.reducer))
|
|
222
|
+
{
|
|
223
|
+
let slices = storeData.reducer;
|
|
224
|
+
if(typeof slices === 'object')
|
|
225
|
+
{
|
|
226
|
+
if(slices.name || slices.__lowentry_unfinished_slice)
|
|
227
|
+
{
|
|
228
|
+
slices = [slices];
|
|
229
|
+
}
|
|
230
|
+
else
|
|
231
|
+
{
|
|
232
|
+
let slicesArray = [];
|
|
233
|
+
LeUtils.each(slices, (slice, index) =>
|
|
234
|
+
{
|
|
235
|
+
if(!slice.name)
|
|
236
|
+
{
|
|
237
|
+
slice.name = index;
|
|
238
|
+
}
|
|
239
|
+
slicesArray.push(slice);
|
|
240
|
+
});
|
|
241
|
+
slices = slicesArray;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
slices = ARRAY(slices);
|
|
245
|
+
|
|
246
|
+
let initialState = {};
|
|
247
|
+
let reducerArrays = {};
|
|
248
|
+
LeUtils.each(slices, (slice, index) =>
|
|
249
|
+
{
|
|
250
|
+
if(!slice.name)
|
|
251
|
+
{
|
|
252
|
+
slice.name = 'slice_' + index;
|
|
253
|
+
}
|
|
254
|
+
if(slice.__lowentry_unfinished_slice)
|
|
255
|
+
{
|
|
256
|
+
delete slice.__lowentry_unfinished_slice;
|
|
257
|
+
slice = LeRed.createSlice(slice);
|
|
258
|
+
}
|
|
259
|
+
initialState[slice.name] = ((typeof slice.state === 'function') ? slice.state() : slice.state);
|
|
260
|
+
LeUtils.each(slice.reducers, (reducer, reducerName) =>
|
|
261
|
+
{
|
|
262
|
+
const fullReducerName = reducerName.startsWith('lowentrystore/') ? reducerName.substring('lowentrystore/'.length) : (slice.name + '/' + reducerName);
|
|
263
|
+
if(typeof reducerArrays[fullReducerName] === 'undefined')
|
|
264
|
+
{
|
|
265
|
+
reducerArrays[fullReducerName] = [];
|
|
266
|
+
}
|
|
267
|
+
reducerArrays[fullReducerName].push(reducer);
|
|
268
|
+
});
|
|
269
|
+
LeUtils.each(slice.sagaListeners, (sagaListener, reducerName) =>
|
|
270
|
+
{
|
|
271
|
+
LeUtils.each(LeUtils.flattenArray(sagaListener), (listener) =>
|
|
272
|
+
{
|
|
273
|
+
try
|
|
274
|
+
{
|
|
275
|
+
sagaListeners.push(listener());
|
|
276
|
+
}
|
|
277
|
+
catch(e)
|
|
278
|
+
{
|
|
279
|
+
console.error('an error was thrown by your saga code, in slice "' + slice.name + '", action "' + reducerName + '":');
|
|
280
|
+
console.error(e);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
let reducers = {};
|
|
287
|
+
LeUtils.each(reducerArrays, (reducerArray, reducerName) =>
|
|
288
|
+
{
|
|
289
|
+
reducerArray = LeUtils.flattenArray(reducerArray);
|
|
290
|
+
if(reducerArray.length <= 0)
|
|
291
|
+
{
|
|
292
|
+
reducers[reducerName] = reducerArray[0];
|
|
293
|
+
}
|
|
294
|
+
else
|
|
295
|
+
{
|
|
296
|
+
reducers[reducerName] = (...args) =>
|
|
297
|
+
{
|
|
298
|
+
LeUtils.each(reducerArray, (reducer) =>
|
|
299
|
+
{
|
|
300
|
+
try
|
|
301
|
+
{
|
|
302
|
+
reducer(...args);
|
|
303
|
+
}
|
|
304
|
+
catch(e)
|
|
305
|
+
{
|
|
306
|
+
console.error(e);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
storeData.reducer = RTK.createSlice({
|
|
314
|
+
name: 'lowentrystore',
|
|
315
|
+
initialState:initialState,
|
|
316
|
+
reducers: reducers,
|
|
317
|
+
}).reducer;
|
|
318
|
+
}
|
|
319
|
+
if(ISSET(storeData.state))
|
|
320
|
+
{
|
|
321
|
+
storeData.preloadedState = storeData.state;
|
|
322
|
+
delete storeData.state;
|
|
323
|
+
}
|
|
324
|
+
if(ISSET(storeData.preloadedState))
|
|
325
|
+
{
|
|
326
|
+
storeData.preloadedState = {reducer:storeData.preloadedState};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
let middleware = ARRAY(storeData.middleware);
|
|
330
|
+
let sagaMiddleware = null;
|
|
331
|
+
if(sagaListeners.length > 0)
|
|
332
|
+
{
|
|
333
|
+
sagaMiddleware = ReduxSaga.default();
|
|
334
|
+
middleware.push(sagaMiddleware);
|
|
335
|
+
}
|
|
336
|
+
storeData.middleware = (getDefaultMiddleware) => getDefaultMiddleware().concat(...middleware);
|
|
337
|
+
|
|
338
|
+
let store = RTK.configureStore(storeData);
|
|
339
|
+
store.__lowentry_store__ = true;
|
|
340
|
+
store.state = () => store.getState().reducer;
|
|
341
|
+
|
|
342
|
+
const dispatch = store.dispatch;
|
|
343
|
+
// noinspection JSValidateTypes
|
|
344
|
+
store.dispatch = (action) =>
|
|
345
|
+
{
|
|
346
|
+
action.__lowentry_dispatch__ = true;
|
|
347
|
+
if(STRING(action.type).startsWith('lowentrystore/lowentryaction/'))
|
|
348
|
+
{
|
|
349
|
+
action.__lowentry_dispatch_result__ = [];
|
|
350
|
+
}
|
|
351
|
+
else
|
|
352
|
+
{
|
|
353
|
+
delete action.__lowentry_dispatch_result__;
|
|
354
|
+
}
|
|
355
|
+
dispatch(action);
|
|
356
|
+
const result = action.__lowentry_dispatch_result__;
|
|
357
|
+
delete action.__lowentry_dispatch_result__;
|
|
358
|
+
delete action.__lowentry_dispatch__;
|
|
359
|
+
return result;
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
if(sagaMiddleware !== null)
|
|
363
|
+
{
|
|
364
|
+
sagaMiddleware.run(function* ()
|
|
365
|
+
{
|
|
366
|
+
yield ReduxSagaEffects.all(sagaListeners);
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
return store;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
LeRed.createAction = (id) =>
|
|
373
|
+
{
|
|
374
|
+
return RTK.createAction('lowentrystore/lowentryaction/' + id);
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
LeRed.createSelector = (selectorsGenerator) =>
|
|
378
|
+
{
|
|
379
|
+
return function(stateOfSlice)
|
|
380
|
+
{
|
|
381
|
+
const state = this;
|
|
382
|
+
const selectors = selectorsGenerator.apply(state, [stateOfSlice]);
|
|
383
|
+
let selectorArgs = [];
|
|
384
|
+
|
|
385
|
+
for(let i = 0; i < selectors.length - 1; i++)
|
|
386
|
+
{
|
|
387
|
+
let selectorsEntry = selectors[i];
|
|
388
|
+
if(typeof selectorsEntry === 'function')
|
|
389
|
+
{
|
|
390
|
+
selectorsEntry = selectorsEntry.apply(state, [state]);
|
|
391
|
+
}
|
|
392
|
+
selectorArgs.push(selectorsEntry);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
let finalSelector = selectors[selectors.length - 1];
|
|
396
|
+
if(typeof finalSelector === 'function')
|
|
397
|
+
{
|
|
398
|
+
finalSelector = finalSelector.apply(state, selectorArgs);
|
|
399
|
+
}
|
|
400
|
+
return finalSelector;
|
|
401
|
+
};
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
LeRed.createCachedSelector = (selectorsGenerator, equalsComparator) =>
|
|
405
|
+
{
|
|
406
|
+
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.createCachedSelector() was given an invalid comparator:');
|
|
407
|
+
if(equalsComparator === false)
|
|
408
|
+
{
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
let previousSelectorArgs = null;
|
|
412
|
+
let previousFinalSelectorResult = null;
|
|
413
|
+
return function(stateOfSlice)
|
|
414
|
+
{
|
|
415
|
+
const state = this;
|
|
416
|
+
const selectors = selectorsGenerator.apply(state, [stateOfSlice]);
|
|
417
|
+
let selectorArgs = [];
|
|
418
|
+
|
|
419
|
+
for(let i = 0; i < selectors.length - 1; i++)
|
|
420
|
+
{
|
|
421
|
+
let selectorsEntry = selectors[i];
|
|
422
|
+
if(typeof selectorsEntry === 'function')
|
|
423
|
+
{
|
|
424
|
+
selectorsEntry = selectorsEntry.apply(state, [state]);
|
|
425
|
+
}
|
|
426
|
+
selectorArgs.push(selectorsEntry);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
let finalSelector = selectors[selectors.length - 1];
|
|
430
|
+
if(typeof finalSelector === 'function')
|
|
431
|
+
{
|
|
432
|
+
if(equalsComparator(previousSelectorArgs, selectorArgs))
|
|
433
|
+
{
|
|
434
|
+
finalSelector = previousFinalSelectorResult;
|
|
435
|
+
}
|
|
436
|
+
else
|
|
437
|
+
{
|
|
438
|
+
finalSelector = finalSelector.apply(state, selectorArgs);
|
|
439
|
+
previousSelectorArgs = selectorArgs;
|
|
440
|
+
previousFinalSelectorResult = finalSelector;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return finalSelector;
|
|
444
|
+
};
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
LeRed.createSlice = (slice) =>
|
|
448
|
+
{
|
|
449
|
+
if(Array.isArray(slice))
|
|
450
|
+
{
|
|
451
|
+
const e = new Error('the given slice is an array (instead of an object)');
|
|
452
|
+
console.error('an error was thrown by your LeRed.createSlice(...) code:');
|
|
453
|
+
console.error(e);
|
|
454
|
+
throw e;
|
|
455
|
+
}
|
|
456
|
+
if(slice.name)
|
|
457
|
+
{
|
|
458
|
+
let actions = {};
|
|
459
|
+
let reducers = {};
|
|
460
|
+
let sagas = {};
|
|
461
|
+
let sagaListeners = {};
|
|
462
|
+
LeUtils.each(slice.actions, (reducer, reducerNames) =>
|
|
463
|
+
{
|
|
464
|
+
LeUtils.each(reducerNames.split(','), (reducerName) =>
|
|
465
|
+
{
|
|
466
|
+
if(reducerName.length <= 0)
|
|
467
|
+
{
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
const reducerAction = RTK.createAction((reducerName.startsWith('lowentrystore/') ? '' : ('lowentrystore/' + slice.name + '/')) + reducerName);
|
|
471
|
+
actions[reducerName] = reducerAction;
|
|
472
|
+
LeUtils.each(LeUtils.flattenArray(reducer), (reducer) =>
|
|
473
|
+
{
|
|
474
|
+
if(LeUtils.isGeneratorFunction(reducer))
|
|
475
|
+
{
|
|
476
|
+
const sagaListener = function* ()
|
|
477
|
+
{
|
|
478
|
+
yield ReduxSagaEffects.takeEvery(reducerAction, function* (action)
|
|
479
|
+
{
|
|
480
|
+
let promiseResolve = null;
|
|
481
|
+
let promiseReject = null;
|
|
482
|
+
try
|
|
483
|
+
{
|
|
484
|
+
if(action.__lowentry_dispatch__ === true)
|
|
485
|
+
{
|
|
486
|
+
const promise = new Promise((resolve, reject) =>
|
|
487
|
+
{
|
|
488
|
+
promiseResolve = resolve;
|
|
489
|
+
promiseReject = reject;
|
|
490
|
+
});
|
|
491
|
+
if(Array.isArray(action.__lowentry_dispatch_result__))
|
|
492
|
+
{
|
|
493
|
+
if(typeof promise !== 'undefined')
|
|
494
|
+
{
|
|
495
|
+
action.__lowentry_dispatch_result__.push(promise);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
else
|
|
499
|
+
{
|
|
500
|
+
action.__lowentry_dispatch_result__ = promise;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const result = yield reducer.apply(slice, [action.payload]);
|
|
505
|
+
if(promiseResolve !== null)
|
|
506
|
+
{
|
|
507
|
+
promiseResolve(result);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
catch(e)
|
|
511
|
+
{
|
|
512
|
+
console.error('an error was thrown by your LeRed.createSlice(...) code, by slice "' + slice.name + '", action "' + reducerName + '":');
|
|
513
|
+
console.error(e);
|
|
514
|
+
if(promiseReject !== null)
|
|
515
|
+
{
|
|
516
|
+
try
|
|
517
|
+
{
|
|
518
|
+
promiseReject(e);
|
|
519
|
+
}
|
|
520
|
+
catch(e2)
|
|
521
|
+
{
|
|
522
|
+
console.error(e2);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
if(ISSET(sagas[reducerName]))
|
|
530
|
+
{
|
|
531
|
+
sagas[reducerName].push(reducer);
|
|
532
|
+
}
|
|
533
|
+
else
|
|
534
|
+
{
|
|
535
|
+
sagas[reducerName] = [reducer];
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if(ISSET(sagaListeners[reducerName]))
|
|
539
|
+
{
|
|
540
|
+
sagaListeners[reducerName].push(sagaListener);
|
|
541
|
+
}
|
|
542
|
+
else
|
|
543
|
+
{
|
|
544
|
+
sagaListeners[reducerName] = [sagaListener];
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
else
|
|
548
|
+
{
|
|
549
|
+
const reducerFunction = (state, action) =>
|
|
550
|
+
{
|
|
551
|
+
try
|
|
552
|
+
{
|
|
553
|
+
const result = reducer.apply(state, [state[slice.name], action.payload]);
|
|
554
|
+
if(action.__lowentry_dispatch__ === true)
|
|
555
|
+
{
|
|
556
|
+
if(Array.isArray(action.__lowentry_dispatch_result__))
|
|
557
|
+
{
|
|
558
|
+
if(typeof result !== 'undefined')
|
|
559
|
+
{
|
|
560
|
+
action.__lowentry_dispatch_result__.push(result);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
else
|
|
564
|
+
{
|
|
565
|
+
action.__lowentry_dispatch_result__ = result;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
catch(e)
|
|
570
|
+
{
|
|
571
|
+
console.error('an error was thrown by your LeRed.createSlice(...) code, by slice "' + slice.name + '", action "' + reducerName + '":');
|
|
572
|
+
console.error(e);
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
if(ISSET(reducers[reducerName]))
|
|
577
|
+
{
|
|
578
|
+
reducers[reducerName].push(reducerFunction);
|
|
579
|
+
}
|
|
580
|
+
else
|
|
581
|
+
{
|
|
582
|
+
reducers[reducerName] = [reducerFunction];
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
slice.actions = actions;
|
|
589
|
+
slice.reducers = reducers;
|
|
590
|
+
slice.sagas = sagas;
|
|
591
|
+
slice.sagaListeners = sagaListeners;
|
|
592
|
+
|
|
593
|
+
let selectors = {};
|
|
594
|
+
LeUtils.each(slice.selectors, (selector, selectorName) =>
|
|
595
|
+
{
|
|
596
|
+
selectors[selectorName] = (state) =>
|
|
597
|
+
{
|
|
598
|
+
try
|
|
599
|
+
{
|
|
600
|
+
return selector.apply(state, [state[slice.name]]);
|
|
601
|
+
}
|
|
602
|
+
catch(e)
|
|
603
|
+
{
|
|
604
|
+
console.error('an error was thrown by your LeRed.createSlice(...) code, by slice "' + slice.name + '", selector "' + selectorName + '":');
|
|
605
|
+
console.error(e);
|
|
606
|
+
throw e;
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
});
|
|
610
|
+
slice.selectors = selectors;
|
|
611
|
+
|
|
612
|
+
let getters = {};
|
|
613
|
+
LeUtils.each(slice.getters, (getter, getterName) =>
|
|
614
|
+
{
|
|
615
|
+
getters[getterName] = (...params) =>
|
|
616
|
+
{
|
|
617
|
+
try
|
|
618
|
+
{
|
|
619
|
+
const selector = getter.apply(window, [params]);
|
|
620
|
+
return (state) =>
|
|
621
|
+
{
|
|
622
|
+
try
|
|
623
|
+
{
|
|
624
|
+
return selector.apply(state, [state[slice.name]]);
|
|
625
|
+
}
|
|
626
|
+
catch(e)
|
|
627
|
+
{
|
|
628
|
+
console.error('an error was thrown by your LeRed.createSlice(...) code, by slice "' + slice.name + '", getter "' + getterName + '":');
|
|
629
|
+
console.error(e);
|
|
630
|
+
throw e;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
catch(e)
|
|
635
|
+
{
|
|
636
|
+
console.error('an error was thrown by your LeRed.createSlice(...) code, by slice "' + slice.name + '", getter "' + getterName + '":');
|
|
637
|
+
console.error(e);
|
|
638
|
+
throw e;
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
});
|
|
642
|
+
slice.getters = getters;
|
|
643
|
+
return slice;
|
|
644
|
+
}
|
|
645
|
+
else
|
|
646
|
+
{
|
|
647
|
+
slice.__lowentry_unfinished_slice = true;
|
|
648
|
+
return slice;
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
LeRed.createFastSlice = (slice) =>
|
|
653
|
+
{
|
|
654
|
+
if(Array.isArray(slice))
|
|
655
|
+
{
|
|
656
|
+
const e = new Error('the given slice is an array (instead of an object)');
|
|
657
|
+
console.error('an error was thrown by your LeRed.createFastSlice(...) code:');
|
|
658
|
+
console.error(e);
|
|
659
|
+
throw e;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
let actions = {};
|
|
663
|
+
LeUtils.each(slice.actions, (reducer, reducerName) =>
|
|
664
|
+
{
|
|
665
|
+
actions[reducerName] = (...params) => reducer.apply(slice, [slice.state, ...params]);
|
|
666
|
+
});
|
|
667
|
+
slice.actions = actions;
|
|
668
|
+
|
|
669
|
+
let selectors = {};
|
|
670
|
+
LeUtils.each(slice.selectors, (selector, selectorName) =>
|
|
671
|
+
{
|
|
672
|
+
selectors[selectorName] = () => selector.apply(slice, [slice.state]);
|
|
673
|
+
});
|
|
674
|
+
slice.selectors = new Proxy(selectors, {
|
|
675
|
+
get:(target, key) => (key in target) ? target[key]() : undefined,
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
let getters = {};
|
|
679
|
+
LeUtils.each(slice.getters, (selector, selectorName) =>
|
|
680
|
+
{
|
|
681
|
+
getters[selectorName] = (...params) => selector.apply(slice, [slice.state, ...params]);
|
|
682
|
+
});
|
|
683
|
+
slice.getters = getters;
|
|
684
|
+
return slice;
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
LeRed.current = (obj) =>
|
|
688
|
+
{
|
|
689
|
+
try
|
|
690
|
+
{
|
|
691
|
+
return RTK.current(obj);
|
|
692
|
+
}
|
|
693
|
+
catch(e)
|
|
694
|
+
{
|
|
695
|
+
}
|
|
696
|
+
return obj;
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
LeRed.useConfigureStore = (storeData) =>
|
|
700
|
+
{
|
|
701
|
+
return React.useMemo(() => LeRed.configureStore(storeData), [storeData]);
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
LeRed.useSelector = (selector, equalsComparator) =>
|
|
705
|
+
{
|
|
706
|
+
if(typeof selector !== 'function')
|
|
707
|
+
{
|
|
708
|
+
console.error('LeRed.useSelector() was given an invalid selector:');
|
|
709
|
+
console.error(selector);
|
|
710
|
+
selector = () =>
|
|
711
|
+
{
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.useSelector() was given an invalid comparator:');
|
|
715
|
+
return ReactRedux.useSelector(selector, equalsComparator);
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
LeRed.useEffect = (callable, comparingValues, equalsComparator) =>
|
|
719
|
+
{
|
|
720
|
+
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.useEffect() was given an invalid comparator:');
|
|
721
|
+
comparingValues = ARRAY(comparingValues);
|
|
722
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
723
|
+
comparingValues = comparingValues.map(value => useCompareMemoize(value, equalsComparator));
|
|
724
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
725
|
+
return React.useEffect(callable, comparingValues);
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
LeRed.useEffectInterval = (callable, comparingValues, intervalMs, fireImmediately, equalsComparator) =>
|
|
729
|
+
{
|
|
730
|
+
return LeRed.useEffect(() => LeUtils.setInterval(callable, intervalMs, fireImmediately).remove, [comparingValues, equalsComparator]);
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
LeRed.useEffectAnimationFrameInterval = (callable, comparingValues, intervalFrames, fireImmediately, equalsComparator) =>
|
|
734
|
+
{
|
|
735
|
+
return LeRed.useEffect(() => LeUtils.setAnimationFrameInterval(callable, intervalFrames, fireImmediately).remove, [comparingValues, equalsComparator]);
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
LeRed.useEffectGenerator = (callable, comparingValues, intervalMs, fireImmediately, equalsComparator) =>
|
|
739
|
+
{
|
|
740
|
+
return LeRed.useEffect(() =>
|
|
741
|
+
{
|
|
742
|
+
let stop = false;
|
|
743
|
+
|
|
744
|
+
(async () =>
|
|
745
|
+
{
|
|
746
|
+
for(const promise of callable())
|
|
747
|
+
{
|
|
748
|
+
if(stop)
|
|
749
|
+
{
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
await promise;
|
|
753
|
+
if(stop)
|
|
754
|
+
{
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
})();
|
|
759
|
+
|
|
760
|
+
return () =>
|
|
761
|
+
{
|
|
762
|
+
stop = true;
|
|
763
|
+
};
|
|
764
|
+
}, [comparingValues, equalsComparator]);
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
LeRed.useEffectGeneratorLoop = (callable, comparingValues, intervalMs, fireImmediately, equalsComparator) =>
|
|
768
|
+
{
|
|
769
|
+
return LeRed.useEffect(() =>
|
|
770
|
+
{
|
|
771
|
+
let stop = false;
|
|
772
|
+
|
|
773
|
+
(async () =>
|
|
774
|
+
{
|
|
775
|
+
while(!stop)
|
|
776
|
+
{
|
|
777
|
+
for(const promise of callable())
|
|
778
|
+
{
|
|
779
|
+
if(stop)
|
|
780
|
+
{
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
await promise;
|
|
784
|
+
if(stop)
|
|
785
|
+
{
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
})();
|
|
791
|
+
|
|
792
|
+
return () =>
|
|
793
|
+
{
|
|
794
|
+
stop = true;
|
|
795
|
+
};
|
|
796
|
+
}, [comparingValues, equalsComparator]);
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
LeRed.useEffectShutdown = (callable, comparingValues, equalsComparator) =>
|
|
800
|
+
{
|
|
801
|
+
return LeRed.useEffect(() =>
|
|
802
|
+
{
|
|
803
|
+
let stop = false;
|
|
804
|
+
|
|
805
|
+
let end;
|
|
806
|
+
|
|
807
|
+
const run = () =>
|
|
808
|
+
{
|
|
809
|
+
if(stop)
|
|
810
|
+
{
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
callable();
|
|
814
|
+
end();
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
end = () =>
|
|
818
|
+
{
|
|
819
|
+
stop = true;
|
|
820
|
+
window?.removeEventListener('beforeunload', run);
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
window?.addEventListener('beforeunload', run);
|
|
824
|
+
|
|
825
|
+
return run;
|
|
826
|
+
}, [comparingValues, equalsComparator]);
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
LeRed.memo = (component, equalsComparator) =>
|
|
830
|
+
{
|
|
831
|
+
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.memo() was given an invalid comparator:');
|
|
832
|
+
return React.memo(component, equalsComparator);
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
LeRed.useMemo = (callable, comparingValues, equalsComparator) =>
|
|
836
|
+
{
|
|
837
|
+
equalsComparator = fixEqualsComparator(equalsComparator, 'LeRed.useMemo() was given an invalid comparator:');
|
|
838
|
+
comparingValues = ARRAY(comparingValues);
|
|
839
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
840
|
+
comparingValues = comparingValues.map(value => useCompareMemoize(value, equalsComparator));
|
|
841
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
842
|
+
return React.useMemo(callable, comparingValues);
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
LeRed.usePrevious = (value, initialValue) =>
|
|
846
|
+
{
|
|
847
|
+
const ref = LeRed.useRef(initialValue);
|
|
848
|
+
LeRed.useEffect(() =>
|
|
849
|
+
{
|
|
850
|
+
ref.current = value;
|
|
851
|
+
}, [value]);
|
|
852
|
+
return ref.current;
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
LeRed.useFont = (font) =>
|
|
856
|
+
{
|
|
857
|
+
font = '12px ' + STRING(font).trim();
|
|
858
|
+
const [hasFont, setHasFont] = LeRed.useState(false);
|
|
859
|
+
LeRed.useEffect(() =>
|
|
860
|
+
{
|
|
861
|
+
if(!hasFont)
|
|
862
|
+
{
|
|
863
|
+
if(!ISSET(document?.fonts?.check))
|
|
864
|
+
{
|
|
865
|
+
setHasFont(true);
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const handler = setInterval(() =>
|
|
870
|
+
{
|
|
871
|
+
try
|
|
872
|
+
{
|
|
873
|
+
if(document.fonts.check(font))
|
|
874
|
+
{
|
|
875
|
+
clearInterval(handler);
|
|
876
|
+
setHasFont(true);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
catch(e)
|
|
880
|
+
{
|
|
881
|
+
console.error(e);
|
|
882
|
+
clearInterval(handler);
|
|
883
|
+
setHasFont(true);
|
|
884
|
+
}
|
|
885
|
+
}, 30);
|
|
886
|
+
}
|
|
887
|
+
}, [hasFont]);
|
|
888
|
+
return hasFont;
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* Adds a <script> tag to the <head> of the document.
|
|
893
|
+
* Only for development and testing purposes.
|
|
894
|
+
*
|
|
895
|
+
* @param {string} url The URL of the js file to include.
|
|
896
|
+
* @param {object} props Additional props of the <script> tag.
|
|
897
|
+
*/
|
|
898
|
+
LeRed.useScript = (url, props = {}) =>
|
|
899
|
+
{
|
|
900
|
+
return LeRed.useEffect(() =>
|
|
901
|
+
{
|
|
902
|
+
const script = document.createElement('script');
|
|
903
|
+
script.type = 'text/javascript';
|
|
904
|
+
script.src = url;
|
|
905
|
+
|
|
906
|
+
LeUtils.each(props, (value, key) =>
|
|
907
|
+
{
|
|
908
|
+
script.setAttribute(key, value);
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
document.head.appendChild(script);
|
|
912
|
+
return () => document.head.removeChild(script);
|
|
913
|
+
}, [url, props]);
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
LeRed.mergeRefs = (...refs) =>
|
|
917
|
+
{
|
|
918
|
+
refs = LeUtils.flattenArray(refs);
|
|
919
|
+
if(!refs)
|
|
920
|
+
{
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
let newRefs = [];
|
|
925
|
+
LeUtils.each(refs, (ref) =>
|
|
926
|
+
{
|
|
927
|
+
if(ref)
|
|
928
|
+
{
|
|
929
|
+
newRefs.push(ref);
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
refs = newRefs;
|
|
933
|
+
|
|
934
|
+
if(refs.length <= 0)
|
|
935
|
+
{
|
|
936
|
+
return null;
|
|
937
|
+
}
|
|
938
|
+
if(refs.length === 1)
|
|
939
|
+
{
|
|
940
|
+
return refs[0];
|
|
941
|
+
}
|
|
942
|
+
return (inst) =>
|
|
943
|
+
{
|
|
944
|
+
LeUtils.each(refs, (ref) =>
|
|
945
|
+
{
|
|
946
|
+
try
|
|
947
|
+
{
|
|
948
|
+
if(typeof ref === 'function')
|
|
949
|
+
{
|
|
950
|
+
ref(inst);
|
|
951
|
+
}
|
|
952
|
+
else if(ref)
|
|
953
|
+
{
|
|
954
|
+
ref.current = inst;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
catch(e)
|
|
958
|
+
{
|
|
959
|
+
console.error(e);
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
};
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
if(typeof Proxy === 'undefined')
|
|
966
|
+
{
|
|
967
|
+
return LeRed;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
return new Proxy(LeRed, {
|
|
971
|
+
set:(target, key, value) =>
|
|
972
|
+
{
|
|
973
|
+
LeRed.set(key, value);
|
|
974
|
+
return true;
|
|
975
|
+
},
|
|
976
|
+
});
|
|
977
|
+
})();
|