@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.
Files changed (2) hide show
  1. package/LeRed.js +195 -24
  2. 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
- LeRed.Root = LeRed.memo(({store, children}) =>
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 React.createElement(ReactRedux.Provider, {store}, children);
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
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowentry/react-redux",
3
- "version": "1.4.3",
3
+ "version": "1.5.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Provides utilities for React and Redux.",