@lowentry/react-redux 1.4.3 → 1.5.1
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 +195 -24
- package/package.json +1 -1
package/LeRed.js
CHANGED
|
@@ -5,7 +5,7 @@ import * as ReactRedux from 'react-redux';
|
|
|
5
5
|
import * as ReduxSaga from 'redux-saga';
|
|
6
6
|
import * as ReduxSagaEffects from 'redux-saga/effects';
|
|
7
7
|
import FastDeepEqualReact from 'fast-deep-equal/react';
|
|
8
|
-
import {LeUtils, ISSET, ARRAY, STRING} from '@lowentry/utils';
|
|
8
|
+
import {LeUtils, ISSET, ARRAY, STRING, INT_LAX} from '@lowentry/utils';
|
|
9
9
|
|
|
10
10
|
export const LeRed = (() =>
|
|
11
11
|
{
|
|
@@ -77,7 +77,6 @@ export const LeRed = (() =>
|
|
|
77
77
|
|
|
78
78
|
LeRed.effects.interval = function* (callback, intervalMs)
|
|
79
79
|
{
|
|
80
|
-
// noinspection JSUnresolvedReference
|
|
81
80
|
let channel = LeRed.eventChannel((emitter) =>
|
|
82
81
|
{
|
|
83
82
|
const interval = setInterval(() =>
|
|
@@ -121,7 +120,6 @@ export const LeRed = (() =>
|
|
|
121
120
|
{
|
|
122
121
|
try
|
|
123
122
|
{
|
|
124
|
-
// noinspection JSUnresolvedReference
|
|
125
123
|
if(yield LeRed.effects.cancelled())
|
|
126
124
|
{
|
|
127
125
|
channel.close();
|
|
@@ -168,33 +166,15 @@ export const LeRed = (() =>
|
|
|
168
166
|
};
|
|
169
167
|
|
|
170
168
|
|
|
171
|
-
LeRed.createRootElement = (elementClass, storeData) =>
|
|
172
|
-
{
|
|
173
|
-
if(ISSET(storeData))
|
|
174
|
-
{
|
|
175
|
-
storeData = LeRed.configureStore(storeData);
|
|
176
|
-
return React.createElement(ReactRedux.Provider, {store:storeData}, React.createElement(elementClass));
|
|
177
|
-
}
|
|
178
|
-
return React.createElement(elementClass);
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
LeRed.createElement = (elementClass, props = null, ...children) =>
|
|
182
|
-
{
|
|
183
|
-
return React.createElement(elementClass, props, ...children);
|
|
184
|
-
};
|
|
185
|
-
|
|
186
169
|
LeRed.configureStore = (storeData) =>
|
|
187
170
|
{
|
|
188
171
|
if(storeData.__lowentry_store__ === true)
|
|
189
172
|
{
|
|
190
173
|
return storeData;
|
|
191
174
|
}
|
|
192
|
-
// noinspection JSUnresolvedReference
|
|
193
175
|
if(ISSET(storeData.slices))
|
|
194
176
|
{
|
|
195
|
-
// noinspection JSUnresolvedReference
|
|
196
177
|
storeData.reducer = storeData.slices;
|
|
197
|
-
// noinspection JSUnresolvedReference
|
|
198
178
|
delete storeData.slices;
|
|
199
179
|
}
|
|
200
180
|
let sagaListeners = [];
|
|
@@ -455,7 +435,6 @@ export const LeRed = (() =>
|
|
|
455
435
|
{
|
|
456
436
|
const sagaListener = function* ()
|
|
457
437
|
{
|
|
458
|
-
// noinspection JSUnresolvedReference
|
|
459
438
|
yield ReduxSagaEffects.takeEvery(reducerAction, function* (action)
|
|
460
439
|
{
|
|
461
440
|
let promiseResolve = null;
|
|
@@ -1012,17 +991,209 @@ export const LeRed = (() =>
|
|
|
1012
991
|
window.dispatchEvent(new CustomEvent(eventName, {detail:value}));
|
|
1013
992
|
};
|
|
1014
993
|
|
|
994
|
+
/**
|
|
995
|
+
* A useState() hook that automatically resets to the defaultValue after the given duration.
|
|
996
|
+
*
|
|
997
|
+
* Example:
|
|
998
|
+
*
|
|
999
|
+
* ```js
|
|
1000
|
+
* const [value, setValue] = LeRed.useTempState(true, 2000);
|
|
1001
|
+
* // somewhere in your code:
|
|
1002
|
+
* setValue(false); // value is now false, after 2 seconds it will be reset to true
|
|
1003
|
+
* ```
|
|
1004
|
+
*
|
|
1005
|
+
* Repeated calls cause the timer to reset, meaning each set value will always remain for the full given duration.
|
|
1006
|
+
*/
|
|
1007
|
+
LeRed.useTempState = (defaultValue, duration) =>
|
|
1008
|
+
{
|
|
1009
|
+
const [value, setValue] = LeRed.useState(defaultValue);
|
|
1010
|
+
const timeoutHandle = LeRed.useRef(null);
|
|
1011
|
+
|
|
1012
|
+
return [value, (newValue) =>
|
|
1013
|
+
{
|
|
1014
|
+
if(timeoutHandle.current)
|
|
1015
|
+
{
|
|
1016
|
+
clearTimeout(timeoutHandle.current);
|
|
1017
|
+
}
|
|
1018
|
+
setValue(newValue);
|
|
1019
|
+
timeoutHandle.current = setTimeout(() =>
|
|
1020
|
+
{
|
|
1021
|
+
timeoutHandle.current = null;
|
|
1022
|
+
setValue(defaultValue);
|
|
1023
|
+
}, duration);
|
|
1024
|
+
}];
|
|
1025
|
+
};
|
|
1015
1026
|
|
|
1016
|
-
|
|
1027
|
+
/**
|
|
1028
|
+
* Allows you to listen to the browser history events (forwards, backwards) and execute a callback on those events.
|
|
1029
|
+
*
|
|
1030
|
+
* You pass 2 functions to it (the callbacks), and it also provides 2 functions (for manually going forwards and backwards).
|
|
1031
|
+
*
|
|
1032
|
+
* Usage:
|
|
1033
|
+
*
|
|
1034
|
+
* ```js
|
|
1035
|
+
* const [goForwards, goBackwards] = LeRed.useHistory(() => console.log('has gone forwards'), () => console.log('has gone backwards'));
|
|
1036
|
+
* ```
|
|
1037
|
+
*/
|
|
1038
|
+
LeRed.useHistory = (() =>
|
|
1039
|
+
{
|
|
1040
|
+
let historyStateListeners = [];
|
|
1041
|
+
|
|
1042
|
+
if(typeof window !== 'undefined')
|
|
1043
|
+
{
|
|
1044
|
+
window.addEventListener('popstate', () =>
|
|
1045
|
+
{
|
|
1046
|
+
historyStateListeners.pop()?.callback();
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
const addListener = (callback) =>
|
|
1051
|
+
{
|
|
1052
|
+
const id = LeUtils.uniqueId();
|
|
1053
|
+
historyStateListeners.push({id, callback});
|
|
1054
|
+
return id;
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
const removeListener = (id) =>
|
|
1058
|
+
{
|
|
1059
|
+
if(!id)
|
|
1060
|
+
{
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
historyStateListeners = historyStateListeners.filter(listener => (listener.id !== id));
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
return (onForward, onBack) =>
|
|
1067
|
+
{
|
|
1068
|
+
const remaining = LeRed.useRef(0);
|
|
1069
|
+
const id = LeRed.useRef(null);
|
|
1070
|
+
|
|
1071
|
+
const goBack = LeRed.useCallback(() =>
|
|
1072
|
+
{
|
|
1073
|
+
if(remaining.current <= 0)
|
|
1074
|
+
{
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
remaining.current--;
|
|
1078
|
+
if(remaining.current === 0)
|
|
1079
|
+
{
|
|
1080
|
+
if(id.current)
|
|
1081
|
+
{
|
|
1082
|
+
removeListener(id.current);
|
|
1083
|
+
}
|
|
1084
|
+
id.current = null;
|
|
1085
|
+
}
|
|
1086
|
+
onBack();
|
|
1087
|
+
}, [onBack]);
|
|
1088
|
+
|
|
1089
|
+
return [
|
|
1090
|
+
() => /** do **/
|
|
1091
|
+
{
|
|
1092
|
+
LeRed.navigate('#');
|
|
1093
|
+
remaining.current++;
|
|
1094
|
+
if(remaining.current === 1)
|
|
1095
|
+
{
|
|
1096
|
+
if(id.current)
|
|
1097
|
+
{
|
|
1098
|
+
removeListener(id.current);
|
|
1099
|
+
}
|
|
1100
|
+
id.current = addListener(goBack);
|
|
1101
|
+
}
|
|
1102
|
+
onForward();
|
|
1103
|
+
},
|
|
1104
|
+
|
|
1105
|
+
() => /** undo **/
|
|
1106
|
+
{
|
|
1107
|
+
if(remaining.current > 0)
|
|
1108
|
+
{
|
|
1109
|
+
LeRed.navigate(-1);
|
|
1110
|
+
}
|
|
1111
|
+
},
|
|
1112
|
+
];
|
|
1113
|
+
};
|
|
1114
|
+
})();
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* 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.
|
|
1118
|
+
*
|
|
1119
|
+
* Example:
|
|
1120
|
+
*
|
|
1121
|
+
* ```js
|
|
1122
|
+
* 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
|
|
1123
|
+
* ```
|
|
1124
|
+
*
|
|
1125
|
+
* or, if you'd like it to be true by default:
|
|
1126
|
+
*
|
|
1127
|
+
* ```js
|
|
1128
|
+
* 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
|
|
1129
|
+
* ```
|
|
1130
|
+
*/
|
|
1131
|
+
LeRed.useHistoryState = (initialState) =>
|
|
1132
|
+
{
|
|
1133
|
+
const [state, setState] = LeRed.useState(!!initialState);
|
|
1134
|
+
const [forwards, backwards] = LeRed.useHistory(() => setState(!initialState), () => setState(!!initialState));
|
|
1135
|
+
if(!!initialState)
|
|
1136
|
+
{
|
|
1137
|
+
return [state, backwards, forwards];
|
|
1138
|
+
}
|
|
1139
|
+
return [state, forwards, backwards];
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
LeRed.Root = LeRed.memo(({store, children, ...other}) =>
|
|
1017
1144
|
{
|
|
1018
1145
|
if(ISSET(store))
|
|
1019
1146
|
{
|
|
1020
1147
|
store = LeRed.configureStore(store);
|
|
1021
|
-
return
|
|
1148
|
+
return (<ReactRedux.Provider store={store} {...other}>{children}</ReactRedux.Provider>);
|
|
1022
1149
|
}
|
|
1023
1150
|
return children;
|
|
1024
1151
|
});
|
|
1025
1152
|
|
|
1153
|
+
LeRed.PreloadComponent = (load) =>
|
|
1154
|
+
{
|
|
1155
|
+
if(typeof window !== 'undefined')
|
|
1156
|
+
{
|
|
1157
|
+
const promise = load(); // start loading already, before it's being rendered in React
|
|
1158
|
+
return () => promise;
|
|
1159
|
+
}
|
|
1160
|
+
return load;
|
|
1161
|
+
};
|
|
1162
|
+
|
|
1163
|
+
LeRed.LoadComponent = LeRed.memo(({loading, load, ...other}) =>
|
|
1164
|
+
{
|
|
1165
|
+
const [Component, setComponent] = LeRed.useState(loading ?? null);
|
|
1166
|
+
|
|
1167
|
+
LeRed.useEffect(() =>
|
|
1168
|
+
{
|
|
1169
|
+
(async () =>
|
|
1170
|
+
{
|
|
1171
|
+
const LoadedComponent = (typeof load === 'function') ? await load() : await load;
|
|
1172
|
+
if(!LoadedComponent)
|
|
1173
|
+
{
|
|
1174
|
+
setComponent(null);
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
setComponent(<LoadedComponent {...other}/>);
|
|
1178
|
+
})();
|
|
1179
|
+
}, []);
|
|
1180
|
+
|
|
1181
|
+
return Component;
|
|
1182
|
+
});
|
|
1183
|
+
|
|
1184
|
+
LeRed.InitiallyInvisible = LeRed.memo(({frames, transition, style, children, opacityKey, ...other}) =>
|
|
1185
|
+
{
|
|
1186
|
+
const [opacity, setOpacity] = LeRed.useState(0);
|
|
1187
|
+
|
|
1188
|
+
LeRed.useEffect(() =>
|
|
1189
|
+
{
|
|
1190
|
+
setOpacity(0);
|
|
1191
|
+
return LeUtils.setAnimationFrameTimeout(() => setOpacity(1), Math.max((opacityKey ? 2 : 0), INT_LAX(frames))).remove;
|
|
1192
|
+
}, [opacityKey]);
|
|
1193
|
+
|
|
1194
|
+
return (<div style={{width:'100%', height:'100%', opacity, transition:(((opacity > 0) && transition) ? ('opacity ' + transition) : 'none'), ...(style ?? {})}} {...other}>{children}</div>);
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1026
1197
|
|
|
1027
1198
|
if(typeof Proxy === 'undefined')
|
|
1028
1199
|
{
|