@carbonorm/carbonreact 3.6.1 → 4.0.0
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/README.md +1 -1
- package/dist/CarbonReact.d.ts +31 -60
- package/dist/components/Alert/Alert.d.ts +5 -1
- package/dist/components/Errors/BackendThrowable.d.ts +4 -1
- package/dist/components/WebSocket/CarbonWebSocket.d.ts +3 -1
- package/dist/hoc/KeysMatching.d.ts +1 -1
- package/dist/hoc/SubsetMatching.d.ts +4 -0
- package/dist/hoc/deleteRestfulObjectArrays.d.ts +17 -3
- package/dist/hoc/updateRestfulObjectArrays.d.ts +25 -11
- package/dist/index.cjs.js +172 -179
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +172 -179
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/CarbonReact.tsx +54 -31
- package/src/components/Alert/Alert.tsx +32 -29
- package/src/components/Errors/BackendThrowable.tsx +11 -8
- package/src/components/WebSocket/CarbonWebSocket.tsx +21 -15
- package/src/hoc/KeysMatching.ts +4 -2
- package/src/hoc/SubsetMatching.ts +6 -0
- package/src/hoc/deleteRestfulObjectArrays.tsx +51 -52
- package/src/hoc/updateRestfulObjectArrays.tsx +97 -97
- package/src/index.ts +1 -0
- package/src/variables/C6.tsx +1 -1
- package/dist/variables/C6.d.ts +0 -842
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export { default as CarbonWebSocket } from "./components/WebSocket/CarbonWebSock
|
|
|
28
28
|
export * from "./components/WebSocket/CarbonWebSocket";
|
|
29
29
|
export * from "./hoc/GlobalHistory";
|
|
30
30
|
export * from "./hoc/KeysMatching";
|
|
31
|
+
export * from "./hoc/SubsetMatching";
|
|
31
32
|
export { default as addValidSQL } from "./hoc/addValidSQL";
|
|
32
33
|
export * from "./hoc/addValidSQL";
|
|
33
34
|
export { default as changed } from "./hoc/changed";
|
package/dist/index.esm.js
CHANGED
|
@@ -1409,11 +1409,11 @@ function hexToRgb (hex) {
|
|
|
1409
1409
|
|
|
1410
1410
|
var styles = {"maintenance-hero":"C5KSPNF","maintenanceHero":"C5KSPNF","httpStatusCode":"NhyRQts","centeredContainer":"nA4Uno6","errorTextGeneral":"yJAgaUs","errorKeys":"SRIoY4m","error-values":"xQIsoNw","errorValues":"xQIsoNw","error-pre":"C6eWpJ3","errorPre":"C6eWpJ3","line-number":"NFRyo7M","lineNumber":"NFRyo7M","cl":"vR2jbfr","slide":"OO2C-Wp"};
|
|
1411
1411
|
|
|
1412
|
-
var BackendThrowable = () => {
|
|
1413
|
-
const
|
|
1414
|
-
const currentThrowable =
|
|
1415
|
-
console.log([
|
|
1416
|
-
return jsxRuntime_2("div", { className: styles.maintenanceHero, children: [jsxRuntime_1("h1", { className: styles.httpStatusCode, children: currentThrowable?.status || 500 }), jsxRuntime_1(OutsideClickHandler, { onOutsideClick: () =>
|
|
1412
|
+
var BackendThrowable = (props) => {
|
|
1413
|
+
const { instance } = props;
|
|
1414
|
+
const currentThrowable = instance.state.backendThrowable[0];
|
|
1415
|
+
console.log([instance.state.backendThrowable, currentThrowable]);
|
|
1416
|
+
return jsxRuntime_2("div", { className: styles.maintenanceHero, children: [jsxRuntime_1("h1", { className: styles.httpStatusCode, children: currentThrowable?.status || 500 }), jsxRuntime_1(OutsideClickHandler, { onOutsideClick: () => instance.setState(currentState => ({ backendThrowable: currentState.backendThrowable.slice(1) })), children: jsxRuntime_1("div", { className: styles.centeredContainer, children: Object.keys(currentThrowable).map((key, index) => {
|
|
1417
1417
|
const valueIsString = typeof currentThrowable[key] === 'string';
|
|
1418
1418
|
const valueIsCode = 'THROWN NEAR' === key;
|
|
1419
1419
|
return jsxRuntime_2("div", { children: [jsxRuntime_2("div", { className: styles.errorTextGeneral, children: [" > ", jsxRuntime_1("span", { className: styles.errorKeys, children: key }), ":", valueIsString
|
|
@@ -3922,14 +3922,14 @@ function Popup({ open = true, handleClose, children, maxWidth, }) {
|
|
|
3922
3922
|
const isProduction = window.location.host.split(".")[0] === "www";
|
|
3923
3923
|
|
|
3924
3924
|
function addAlert(props) {
|
|
3925
|
-
|
|
3925
|
+
props.instance.setState(previousState => ({
|
|
3926
3926
|
alertsWaiting: previousState.alertsWaiting.length === 0
|
|
3927
3927
|
? [props]
|
|
3928
3928
|
: [...previousState.alertsWaiting, props]
|
|
3929
3929
|
}));
|
|
3930
3930
|
}
|
|
3931
|
-
function Alert() {
|
|
3932
|
-
const { alertsWaiting, backendThrowable } =
|
|
3931
|
+
function Alert({ instance }) {
|
|
3932
|
+
const { alertsWaiting, backendThrowable } = instance.state;
|
|
3933
3933
|
let alert = undefined;
|
|
3934
3934
|
const alertWaiting = alertsWaiting.length + backendThrowable.length;
|
|
3935
3935
|
if (backendThrowable.length !== 0) {
|
|
@@ -3944,8 +3944,9 @@ function Alert() {
|
|
|
3944
3944
|
color: 'primary',
|
|
3945
3945
|
});
|
|
3946
3946
|
}
|
|
3947
|
-
const backendThrowable =
|
|
3947
|
+
const backendThrowable = instance.state.backendThrowable[0];
|
|
3948
3948
|
alert = {
|
|
3949
|
+
instance: instance,
|
|
3949
3950
|
title: "Oh no! An issue occurred!",
|
|
3950
3951
|
text: backendThrowable?.['DropInGaming\\PHP\\Errors\\DropException'] ?? 'An unknown issue occurred. Please try again.',
|
|
3951
3952
|
timeout: 0,
|
|
@@ -3956,7 +3957,7 @@ function Alert() {
|
|
|
3956
3957
|
backendThrowable: backendThrowable,
|
|
3957
3958
|
then: (value) => {
|
|
3958
3959
|
if (value === 'Expand') {
|
|
3959
|
-
|
|
3960
|
+
instance.setState(previousState => {
|
|
3960
3961
|
let backendThrowable = previousState.backendThrowable.pop();
|
|
3961
3962
|
if (backendThrowable === undefined) {
|
|
3962
3963
|
return {
|
|
@@ -3970,7 +3971,7 @@ function Alert() {
|
|
|
3970
3971
|
});
|
|
3971
3972
|
}
|
|
3972
3973
|
else {
|
|
3973
|
-
|
|
3974
|
+
instance.setState(previousState => ({
|
|
3974
3975
|
backendThrowable: previousState.backendThrowable.slice(1)
|
|
3975
3976
|
}));
|
|
3976
3977
|
}
|
|
@@ -3984,15 +3985,14 @@ function Alert() {
|
|
|
3984
3985
|
return null;
|
|
3985
3986
|
}
|
|
3986
3987
|
const timeout = alert?.timeout || 15000;
|
|
3987
|
-
const
|
|
3988
|
-
const dig = getStyles();
|
|
3988
|
+
const styles = getStyles();
|
|
3989
3989
|
let cancelTimeout = null;
|
|
3990
3990
|
const handleClose = () => {
|
|
3991
3991
|
if (null !== cancelTimeout) {
|
|
3992
3992
|
clearTimeout(cancelTimeout);
|
|
3993
3993
|
}
|
|
3994
3994
|
if (alert?.backendThrowable === undefined) {
|
|
3995
|
-
|
|
3995
|
+
instance.setState(previousState => ({
|
|
3996
3996
|
alertsWaiting: previousState.alertsWaiting.slice(1)
|
|
3997
3997
|
}));
|
|
3998
3998
|
}
|
|
@@ -4006,25 +4006,26 @@ function Alert() {
|
|
|
4006
4006
|
handleClose();
|
|
4007
4007
|
}, timeout);
|
|
4008
4008
|
}
|
|
4009
|
-
return jsxRuntime_1(Popup, { handleClose: handleClose, children: jsxRuntime_2("div", { className: classNames("model-content",
|
|
4009
|
+
return jsxRuntime_1(Popup, { handleClose: handleClose, children: jsxRuntime_2("div", { className: classNames("model-content", styles.rounded0, styles.border0), style: {
|
|
4010
4010
|
maxWidth: '75vw',
|
|
4011
4011
|
maxHeight: '75vh',
|
|
4012
|
-
}, children: [jsxRuntime_2("div", { className: classNames(
|
|
4012
|
+
}, children: [jsxRuntime_2("div", { className: classNames(styles.modalHeader, styles.rounded0, styles.border0, {
|
|
4013
4013
|
// icon?: "warning" | "error" | "success" | "info" | "question"
|
|
4014
|
-
[
|
|
4015
|
-
[
|
|
4016
|
-
[
|
|
4017
|
-
[
|
|
4018
|
-
[
|
|
4019
|
-
}), children: [jsxRuntime_2("h3", { className: classNames(
|
|
4020
|
-
jsxRuntime_2("div", { className: classNames(
|
|
4021
|
-
|
|
4014
|
+
[styles.bg_primary]: "info" === alert.icon || alert.icon === undefined || alert.icon === null,
|
|
4015
|
+
[styles.bg_success]: "success" === alert.icon,
|
|
4016
|
+
[styles.bg_warning]: "warning" === alert.icon,
|
|
4017
|
+
[styles.bg_danger]: "error" === alert.icon, // TODO - change to red
|
|
4018
|
+
[styles.bgPrimary]: "question" === alert.icon,
|
|
4019
|
+
}), children: [jsxRuntime_2("h3", { className: classNames(styles.modalTitle, styles.textDark), id: "staticBackdropLabel", children: ["#", alertWaiting, " ", alert.title] }), jsxRuntime_1("div", { onClick: handleClose, children: jsxRuntime_1(FontAwesomeIcon, { icon: faClose, size: 'xl' }) })] }), jsxRuntime_1("div", { className: classNames(styles.modalBody, styles.border0, styles.textWhite), children: jsxRuntime_2("div", { className: styles.textCenter, children: [alert.text, alert.component] }) }), undefined !== alert.buttons &&
|
|
4020
|
+
jsxRuntime_2("div", { className: classNames(styles.modalFooter, styles.border0, styles.rounded0), children: [alert.footerText &&
|
|
4021
|
+
jsxRuntime_1("div", { className: classNames(styles.textCenter, styles.textWhite), children: alert.footerText }), alert.buttons?.map((button, index) => {
|
|
4022
|
+
return jsxRuntime_1("button", { className: classNames(styles.btn, styles.btnLg, {
|
|
4022
4023
|
// todo - color: "default" | "primary" | "secondary" | "inherit" | "danger" | "info" | "success" | "warning" | undefined,
|
|
4023
|
-
[
|
|
4024
|
-
[
|
|
4025
|
-
[
|
|
4026
|
-
[
|
|
4027
|
-
}, "btn-Yes",
|
|
4024
|
+
[styles.bg_success]: "success" === button.color,
|
|
4025
|
+
[styles.bg_danger]: "danger" === button.color,
|
|
4026
|
+
[styles.bg_primary]: "primary" === button.color,
|
|
4027
|
+
[styles.bg_warning]: "warning" === button.color,
|
|
4028
|
+
}, "btn-Yes", styles.rounded0), onClick: () => {
|
|
4028
4029
|
handleClose();
|
|
4029
4030
|
alert?.then?.(button.value ?? button.text);
|
|
4030
4031
|
}, children: button.text }, index);
|
|
@@ -4062,14 +4063,16 @@ const useEffectOnce = (effect) => {
|
|
|
4062
4063
|
* @function connect
|
|
4063
4064
|
* This function establishes a connection with the websocket and also ensures constant reconnection if connection closes
|
|
4064
4065
|
**/
|
|
4065
|
-
function initiateWebsocket(
|
|
4066
|
-
|
|
4066
|
+
function initiateWebsocket(props) {
|
|
4067
|
+
let { instance, TABLES = undefined, WsLiveUpdates = undefined, url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' + window.location.host + '/carbonorm/websocket', timeoutSeconds = 250, heartbeatSeconds = 60 } = props;
|
|
4068
|
+
const { websocket } = instance.state;
|
|
4067
4069
|
if (!("WebSocket" in window)) {
|
|
4068
4070
|
// todo - store that this has been shown in the state
|
|
4069
4071
|
addAlert({
|
|
4070
4072
|
title: 'Browser does not support websockets, live updates will fail. You may need to refresh the page to see the newest content.',
|
|
4071
4073
|
text: 'Please use a modern browser.',
|
|
4072
4074
|
icon: 'warning',
|
|
4075
|
+
instance
|
|
4073
4076
|
});
|
|
4074
4077
|
}
|
|
4075
4078
|
if (false === (undefined === websocket || null === websocket)) {
|
|
@@ -4078,14 +4081,14 @@ function initiateWebsocket({ TABLES = undefined, WsLiveUpdates = undefined, url
|
|
|
4078
4081
|
let connectInterval;
|
|
4079
4082
|
const connection = new WebSocket(url);
|
|
4080
4083
|
console.log("Connecting websocket url", url);
|
|
4081
|
-
|
|
4084
|
+
instance.setState({
|
|
4082
4085
|
websocket: connection
|
|
4083
4086
|
}, () => {
|
|
4084
4087
|
connection.onopen = () => {
|
|
4085
4088
|
console.log('WebSocket Client Connected To :: ' + url);
|
|
4086
4089
|
clearTimeout(connectInterval); // clear Interval on open of websocket connection
|
|
4087
4090
|
function heartbeat() {
|
|
4088
|
-
const { websocket } =
|
|
4091
|
+
const { websocket } = instance.state;
|
|
4089
4092
|
if (!websocket)
|
|
4090
4093
|
return;
|
|
4091
4094
|
if (websocket.readyState !== 1)
|
|
@@ -4100,7 +4103,7 @@ function initiateWebsocket({ TABLES = undefined, WsLiveUpdates = undefined, url
|
|
|
4100
4103
|
if (message.data === 'pong') {
|
|
4101
4104
|
return;
|
|
4102
4105
|
}
|
|
4103
|
-
|
|
4106
|
+
instance.setState((prevState) => ({
|
|
4104
4107
|
websocketEvents: prevState.websocketEvents.concat(message),
|
|
4105
4108
|
websocketData: prevState.websocketData.concat(parsedData), // JSON.parse no good - base64?
|
|
4106
4109
|
}), () => {
|
|
@@ -4124,7 +4127,7 @@ function initiateWebsocket({ TABLES = undefined, WsLiveUpdates = undefined, url
|
|
|
4124
4127
|
}
|
|
4125
4128
|
console.log('todo - going to impl REST', TABLE_NAME, METHOD, REQUEST_PRIMARY_KEY, parsedData?.REST);
|
|
4126
4129
|
const TABLE_NAME_SHORT = TABLE_NAME.substring(TABLE_PREFIX.length);
|
|
4127
|
-
const currentCache =
|
|
4130
|
+
const currentCache = instance.state[TABLE_NAME_SHORT];
|
|
4128
4131
|
// just because we have a websocket update, doesn't mean we need the update
|
|
4129
4132
|
// check to see if the primary key is in the current cache
|
|
4130
4133
|
const c6Table = TABLES[TABLE_NAME_SHORT] ?? null;
|
|
@@ -4161,7 +4164,7 @@ function initiateWebsocket({ TABLES = undefined, WsLiveUpdates = undefined, url
|
|
|
4161
4164
|
}
|
|
4162
4165
|
});
|
|
4163
4166
|
};
|
|
4164
|
-
window.addEventListener("focus", () => initiateWebsocket());
|
|
4167
|
+
window.addEventListener("focus", () => initiateWebsocket(props));
|
|
4165
4168
|
// websocket onclose event listener
|
|
4166
4169
|
connection.addEventListener('close', event => {
|
|
4167
4170
|
let reason;
|
|
@@ -4170,7 +4173,7 @@ function initiateWebsocket({ TABLES = undefined, WsLiveUpdates = undefined, url
|
|
|
4170
4173
|
const retrySeconds = Math.min(5000, (timeoutSeconds + timeoutSeconds) * 1000);
|
|
4171
4174
|
timeoutSeconds = retrySeconds;
|
|
4172
4175
|
console.log(`WebSocket reconnect will be attempted in ${retrySeconds} second(s).`);
|
|
4173
|
-
connectInterval = setTimeout(() => initiateWebsocket(), retrySeconds);
|
|
4176
|
+
connectInterval = setTimeout(() => initiateWebsocket(props), retrySeconds);
|
|
4174
4177
|
};
|
|
4175
4178
|
// See https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1
|
|
4176
4179
|
switch (event.code) {
|
|
@@ -4234,6 +4237,106 @@ function CarbonWebSocket (props) {
|
|
|
4234
4237
|
return null;
|
|
4235
4238
|
}
|
|
4236
4239
|
|
|
4240
|
+
var eUpdateInsertMethod;
|
|
4241
|
+
(function (eUpdateInsertMethod) {
|
|
4242
|
+
eUpdateInsertMethod[eUpdateInsertMethod["REPLACE"] = 0] = "REPLACE";
|
|
4243
|
+
eUpdateInsertMethod[eUpdateInsertMethod["FIRST"] = 1] = "FIRST";
|
|
4244
|
+
eUpdateInsertMethod[eUpdateInsertMethod["LAST"] = 2] = "LAST";
|
|
4245
|
+
})(eUpdateInsertMethod || (eUpdateInsertMethod = {}));
|
|
4246
|
+
/**
|
|
4247
|
+
* Updates or inserts objects in a stateful array, merging new data with existing objects.
|
|
4248
|
+
* @param instance - The React component instance.
|
|
4249
|
+
* @param dataOrCallback - Array of objects or a callback function returning an array of objects.
|
|
4250
|
+
* @param stateKey - The key in the state where the data array is stored.
|
|
4251
|
+
* @param uniqueObjectId - The unique identifier(s) for objects, typically the primary key of the table.
|
|
4252
|
+
* @param insertUpdateOrder - The order in which new data should be inserted/updated.
|
|
4253
|
+
* @param callback - Optional callback function to run after state update.
|
|
4254
|
+
*/
|
|
4255
|
+
function updateRestfulObjectArrays({ instance, dataOrCallback, stateKey, uniqueObjectId, insertUpdateOrder = eUpdateInsertMethod.LAST, callback, }) {
|
|
4256
|
+
const uniqueObjectIds = Array.isArray(uniqueObjectId) ? uniqueObjectId : [uniqueObjectId];
|
|
4257
|
+
instance.setState((previousBootstrapState, props) => {
|
|
4258
|
+
let newOrReplacementData = [];
|
|
4259
|
+
if (Array.isArray(dataOrCallback)) {
|
|
4260
|
+
newOrReplacementData = dataOrCallback;
|
|
4261
|
+
}
|
|
4262
|
+
else if (typeof dataOrCallback === "function") {
|
|
4263
|
+
newOrReplacementData = dataOrCallback(previousBootstrapState, props);
|
|
4264
|
+
}
|
|
4265
|
+
else {
|
|
4266
|
+
throw new Error("The dataOrCallback parameter must be an array or function");
|
|
4267
|
+
}
|
|
4268
|
+
if (newOrReplacementData === null) {
|
|
4269
|
+
return null;
|
|
4270
|
+
}
|
|
4271
|
+
const findUniqueObjectIds = (item, value) => {
|
|
4272
|
+
return uniqueObjectIds.every((id) => item[id] === value[id]);
|
|
4273
|
+
};
|
|
4274
|
+
const previousStateProperty = previousBootstrapState[stateKey] ?? [];
|
|
4275
|
+
let updatedData = newOrReplacementData.map((value) => {
|
|
4276
|
+
const existingObject = previousStateProperty?.find((item) => findUniqueObjectIds(item, value)) || {};
|
|
4277
|
+
return { ...existingObject, ...value };
|
|
4278
|
+
});
|
|
4279
|
+
const filterOutUpdated = (array) => {
|
|
4280
|
+
return array?.filter((item) => !updatedData.some((value) => findUniqueObjectIds(item, value))) ?? [];
|
|
4281
|
+
};
|
|
4282
|
+
let newState = {};
|
|
4283
|
+
switch (insertUpdateOrder) {
|
|
4284
|
+
case eUpdateInsertMethod.LAST:
|
|
4285
|
+
newState[stateKey] = [
|
|
4286
|
+
...filterOutUpdated(previousStateProperty),
|
|
4287
|
+
...updatedData,
|
|
4288
|
+
];
|
|
4289
|
+
break;
|
|
4290
|
+
case eUpdateInsertMethod.FIRST:
|
|
4291
|
+
newState[stateKey] = [
|
|
4292
|
+
...updatedData,
|
|
4293
|
+
...filterOutUpdated(previousStateProperty),
|
|
4294
|
+
];
|
|
4295
|
+
break;
|
|
4296
|
+
case eUpdateInsertMethod.REPLACE:
|
|
4297
|
+
newState[stateKey] = [
|
|
4298
|
+
...(previousStateProperty?.map((oldObject) => {
|
|
4299
|
+
const index = updatedData.findIndex((item) => findUniqueObjectIds(item, oldObject));
|
|
4300
|
+
if (index !== -1) {
|
|
4301
|
+
return updatedData.splice(index, 1)[0];
|
|
4302
|
+
}
|
|
4303
|
+
return oldObject;
|
|
4304
|
+
}) ?? []),
|
|
4305
|
+
...updatedData,
|
|
4306
|
+
];
|
|
4307
|
+
break;
|
|
4308
|
+
default:
|
|
4309
|
+
throw new Error("The insertUpdateOrder (eUpdateInsertMethod) was not implemented");
|
|
4310
|
+
}
|
|
4311
|
+
return newState;
|
|
4312
|
+
}, callback);
|
|
4313
|
+
}
|
|
4314
|
+
|
|
4315
|
+
function deleteRestfulObjectArrays({ instance, dataOrCallback, stateKey, uniqueObjectId, callback }) {
|
|
4316
|
+
const uniqueObjectIds = Array.isArray(uniqueObjectId) ? uniqueObjectId : [uniqueObjectId];
|
|
4317
|
+
instance.setState((previousBootstrapState, props) => {
|
|
4318
|
+
let newOrReplacementData = [];
|
|
4319
|
+
if (Array.isArray(dataOrCallback)) {
|
|
4320
|
+
newOrReplacementData = dataOrCallback;
|
|
4321
|
+
}
|
|
4322
|
+
else if (typeof dataOrCallback === 'function') {
|
|
4323
|
+
const callbackReturn = dataOrCallback(previousBootstrapState, props);
|
|
4324
|
+
if (callbackReturn === null) {
|
|
4325
|
+
return null; // No updates needed (noop)
|
|
4326
|
+
}
|
|
4327
|
+
newOrReplacementData = callbackReturn;
|
|
4328
|
+
}
|
|
4329
|
+
else {
|
|
4330
|
+
throw new Error('The dataOrCallback parameter was not an array or function');
|
|
4331
|
+
}
|
|
4332
|
+
const previousStateProperty = previousBootstrapState[stateKey];
|
|
4333
|
+
const updatedStateProperty = previousStateProperty?.filter(item => !newOrReplacementData.some(value => uniqueObjectIds.every(uniqueId => value[uniqueId] === item[uniqueId]))) ?? [];
|
|
4334
|
+
return {
|
|
4335
|
+
[stateKey]: updatedStateProperty
|
|
4336
|
+
};
|
|
4337
|
+
}, callback);
|
|
4338
|
+
}
|
|
4339
|
+
|
|
4237
4340
|
const initialRequiredCarbonORMState = {
|
|
4238
4341
|
alertsWaiting: [],
|
|
4239
4342
|
backendThrowable: [],
|
|
@@ -4254,28 +4357,45 @@ function isJsonString(str) {
|
|
|
4254
4357
|
}
|
|
4255
4358
|
return true;
|
|
4256
4359
|
}
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
static instance;
|
|
4360
|
+
const persistentStateMap = new Map();
|
|
4361
|
+
class CarbonReact extends Component {
|
|
4260
4362
|
context = createContext(this.state);
|
|
4261
|
-
|
|
4363
|
+
// Private static member
|
|
4364
|
+
// we actually implement this in the constructor todo - test this
|
|
4365
|
+
static instance;
|
|
4366
|
+
target;
|
|
4367
|
+
updateRestfulObjectArrays = (rest) => updateRestfulObjectArrays({
|
|
4368
|
+
instance: this,
|
|
4369
|
+
...rest
|
|
4370
|
+
});
|
|
4371
|
+
deleteRestfulObjectArrays = (rest) => deleteRestfulObjectArrays({
|
|
4372
|
+
instance: this,
|
|
4373
|
+
...rest
|
|
4374
|
+
});
|
|
4262
4375
|
static lastLocation = window.location.pathname;
|
|
4263
4376
|
// @link https://github.com/welldone-software/why-did-you-render
|
|
4264
4377
|
// noinspection JSUnusedGlobalSymbols
|
|
4265
4378
|
static whyDidYouRender = true;
|
|
4266
4379
|
constructor(props) {
|
|
4267
4380
|
super(props);
|
|
4381
|
+
this.target = new.target;
|
|
4268
4382
|
console.log('CarbonORM TSX CONSTRUCTOR');
|
|
4269
|
-
|
|
4270
|
-
|
|
4383
|
+
// this is the magic that allows each class that's extends this to have a static instance - a singleton pattern
|
|
4384
|
+
// new.target is a meta-property introduced in ES6 that references the constructor that was directly invoked with the new keyword.
|
|
4385
|
+
Object.assign(new.target, {
|
|
4386
|
+
instance: this
|
|
4387
|
+
});
|
|
4388
|
+
if (this.props.instanceId && persistentStateMap.has(this.props.instanceId)) {
|
|
4389
|
+
this.state = persistentStateMap.get(this.props.instanceId);
|
|
4271
4390
|
}
|
|
4272
4391
|
else {
|
|
4392
|
+
// This should only ever be done here, when the full state is being trashed.
|
|
4393
|
+
// todo - does this suck in context of multiple instances?
|
|
4394
|
+
clearCache({
|
|
4395
|
+
ignoreWarning: true
|
|
4396
|
+
});
|
|
4273
4397
|
this.state = initialCarbonReactState;
|
|
4274
4398
|
}
|
|
4275
|
-
// This should only ever be done here, when the full state is being trashed.
|
|
4276
|
-
clearCache({
|
|
4277
|
-
ignoreWarning: true
|
|
4278
|
-
});
|
|
4279
4399
|
/** We can think of our app as having one state; this state.
|
|
4280
4400
|
* Long-term, I'd like us to store this state to local storage and only load updates on reload...
|
|
4281
4401
|
* Class based components are far easier to manage state in local storage and pass state down to children.
|
|
@@ -4283,15 +4403,9 @@ const CarbonReact = class extends Component {
|
|
|
4283
4403
|
* components' tend to be shorter syntactically and bonus points if it's stateless.
|
|
4284
4404
|
**/
|
|
4285
4405
|
}
|
|
4286
|
-
static getState() {
|
|
4287
|
-
return CarbonReact.instance.state;
|
|
4288
|
-
}
|
|
4289
4406
|
shouldComponentUpdate(nextProps, nextState, _nextContext) {
|
|
4290
|
-
if (this.props.
|
|
4291
|
-
|
|
4292
|
-
}
|
|
4293
|
-
else {
|
|
4294
|
-
CarbonReact.persistentState = nextState;
|
|
4407
|
+
if (this.props.instanceId) {
|
|
4408
|
+
persistentStateMap.set(this.props.instanceId, nextState);
|
|
4295
4409
|
}
|
|
4296
4410
|
changed(this.constructor.name + ' (C6Api)', 'props', this.props, nextProps);
|
|
4297
4411
|
changed(this.constructor.name + ' (C6Api)', 'state', this.state, nextState);
|
|
@@ -4313,13 +4427,13 @@ const CarbonReact = class extends Component {
|
|
|
4313
4427
|
console.log('%c color (' + colorHex + ')', 'color: ' + colorHex);
|
|
4314
4428
|
const nest = jsxRuntime_1(Nest, { position: 'fixed', backgroundColor: '', color: hexToRgb(colorHex), count: 100 });
|
|
4315
4429
|
if (this.state.backendThrowable.length > 0) {
|
|
4316
|
-
return jsxRuntime_2(jsxRuntime_3, { children: [nest, jsxRuntime_1(BackendThrowable, {})] });
|
|
4430
|
+
return jsxRuntime_2(jsxRuntime_3, { children: [nest, jsxRuntime_1(BackendThrowable, { instance: CarbonReact.instance })] });
|
|
4317
4431
|
}
|
|
4318
4432
|
const Context = this.context.Provider;
|
|
4319
4433
|
return jsxRuntime_2(jsxRuntime_3, { children: [jsxRuntime_1(GlobalHistory, {}), this.props.websocket &&
|
|
4320
|
-
jsxRuntime_1(CarbonWebSocket, { ...(true === this.props.websocket ? {} : this.props.websocket) }), jsxRuntime_1(Context, { value: this.state, children: this.props.children }), jsxRuntime_1(ToastContainer, {})] });
|
|
4434
|
+
jsxRuntime_1(CarbonWebSocket, { ...(true === this.props.websocket ? {} : this.props.websocket), instance: CarbonReact.instance }), jsxRuntime_1(Context, { value: this.state, children: this.props.children }), jsxRuntime_1(ToastContainer, {})] });
|
|
4321
4435
|
}
|
|
4322
|
-
}
|
|
4436
|
+
}
|
|
4323
4437
|
|
|
4324
4438
|
var getStatefulObjectWithWhere = ({ request }) => {
|
|
4325
4439
|
console.log('request', request);
|
|
@@ -4376,44 +4490,6 @@ function addValidSQL$1 (sql) {
|
|
|
4376
4490
|
addValidSQL.push({ [expect.getState().currentTestName.replaceAll(" ", "_").toLowerCase()]: sql });
|
|
4377
4491
|
}
|
|
4378
4492
|
|
|
4379
|
-
//ObjectType, UniqueIdType extends keyof ObjectType
|
|
4380
|
-
// @link https://www.typescriptlang.org/docs/handbook/2/mapped-types.html
|
|
4381
|
-
function deleteRestfulObjectArrays(dataOrCallback, stateKey, uniqueObjectId, callback) {
|
|
4382
|
-
const uniqueObjectIds = uniqueObjectId instanceof Array ? uniqueObjectId : [uniqueObjectId];
|
|
4383
|
-
return CarbonReact.instance.setState((previousBootstrapState, props) => {
|
|
4384
|
-
let newOrReplacementData = [];
|
|
4385
|
-
if (dataOrCallback instanceof Array) {
|
|
4386
|
-
newOrReplacementData = dataOrCallback;
|
|
4387
|
-
}
|
|
4388
|
-
else if (dataOrCallback instanceof Function) {
|
|
4389
|
-
let callbackReturn = dataOrCallback(previousBootstrapState, props);
|
|
4390
|
-
if (null === callbackReturn) {
|
|
4391
|
-
return;
|
|
4392
|
-
}
|
|
4393
|
-
newOrReplacementData = callbackReturn;
|
|
4394
|
-
}
|
|
4395
|
-
else {
|
|
4396
|
-
throw Error('The dataOrCallback parameter was not an array or function');
|
|
4397
|
-
}
|
|
4398
|
-
const previousStateProperty = previousBootstrapState[stateKey];
|
|
4399
|
-
return {
|
|
4400
|
-
[stateKey]: [
|
|
4401
|
-
...previousStateProperty?.filter(item => false === (newOrReplacementData?.find(value => {
|
|
4402
|
-
let isMatch = true;
|
|
4403
|
-
uniqueObjectIds.find(uniqueObjectId => {
|
|
4404
|
-
if (value[uniqueObjectId] !== item[uniqueObjectId]) {
|
|
4405
|
-
isMatch = false;
|
|
4406
|
-
return true;
|
|
4407
|
-
}
|
|
4408
|
-
return false;
|
|
4409
|
-
});
|
|
4410
|
-
return isMatch;
|
|
4411
|
-
}) || false)) || []
|
|
4412
|
-
]
|
|
4413
|
-
};
|
|
4414
|
-
}, callback);
|
|
4415
|
-
}
|
|
4416
|
-
|
|
4417
4493
|
// @link https://stackoverflow.com/questions/31721250/how-to-target-edge-browser-with-javascript
|
|
4418
4494
|
// Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) \
|
|
4419
4495
|
// Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.26
|
|
@@ -4553,89 +4629,6 @@ function setupTests ({ sqlDirectory = './logs/rest/', logsDirectory = './logs/te
|
|
|
4553
4629
|
}, 65000);
|
|
4554
4630
|
}
|
|
4555
4631
|
|
|
4556
|
-
var eUpdateInsertMethod;
|
|
4557
|
-
(function (eUpdateInsertMethod) {
|
|
4558
|
-
eUpdateInsertMethod[eUpdateInsertMethod["REPLACE"] = 0] = "REPLACE";
|
|
4559
|
-
eUpdateInsertMethod[eUpdateInsertMethod["FIRST"] = 1] = "FIRST";
|
|
4560
|
-
eUpdateInsertMethod[eUpdateInsertMethod["LAST"] = 2] = "LAST";
|
|
4561
|
-
})(eUpdateInsertMethod || (eUpdateInsertMethod = {}));
|
|
4562
|
-
/**
|
|
4563
|
-
*
|
|
4564
|
-
* merged with existing objects.uniqueObjectId || {}.
|
|
4565
|
-
* @param dataOrCallback
|
|
4566
|
-
* @param uniqueObjectId - the uniqueObjectId of the object to update; typically the primary key of the table.
|
|
4567
|
-
* @param stateKey -
|
|
4568
|
-
* @param insertUpdateOrder
|
|
4569
|
-
* @param callback - if you want to do something with the updated state, you can pass a callback here. Run as the second
|
|
4570
|
-
* parameter of setState.
|
|
4571
|
-
*/
|
|
4572
|
-
function updateRestfulObjectArrays(dataOrCallback, stateKey, uniqueObjectId, insertUpdateOrder = eUpdateInsertMethod.LAST, callback) {
|
|
4573
|
-
const uniqueObjectIds = uniqueObjectId instanceof Array ? uniqueObjectId : [uniqueObjectId];
|
|
4574
|
-
const bootstrap = CarbonReact.instance;
|
|
4575
|
-
return bootstrap.setState((previousBootstrapState, props) => {
|
|
4576
|
-
let newOrReplacementData = [];
|
|
4577
|
-
if (dataOrCallback instanceof Array) {
|
|
4578
|
-
newOrReplacementData = dataOrCallback;
|
|
4579
|
-
}
|
|
4580
|
-
else if (dataOrCallback instanceof Function) {
|
|
4581
|
-
newOrReplacementData = dataOrCallback(previousBootstrapState, props);
|
|
4582
|
-
}
|
|
4583
|
-
else {
|
|
4584
|
-
throw Error('The dataOrCallback parameter was not an array or function');
|
|
4585
|
-
}
|
|
4586
|
-
const findUniqueObjectIds = (item, value) => {
|
|
4587
|
-
let isMatch = true;
|
|
4588
|
-
uniqueObjectIds.find(uniqueObjectId => {
|
|
4589
|
-
if (value[uniqueObjectId] !== item[uniqueObjectId]) {
|
|
4590
|
-
isMatch = false;
|
|
4591
|
-
return true;
|
|
4592
|
-
}
|
|
4593
|
-
return false;
|
|
4594
|
-
});
|
|
4595
|
-
return isMatch;
|
|
4596
|
-
};
|
|
4597
|
-
const previousStateProperty = previousBootstrapState[stateKey];
|
|
4598
|
-
let updatedData = newOrReplacementData?.map(value => {
|
|
4599
|
-
return {
|
|
4600
|
-
...previousStateProperty?.find(previousValue => findUniqueObjectIds(previousValue, value)) || {},
|
|
4601
|
-
...value
|
|
4602
|
-
};
|
|
4603
|
-
}) ?? [];
|
|
4604
|
-
switch (insertUpdateOrder) {
|
|
4605
|
-
default:
|
|
4606
|
-
throw Error('The insertUpdateOrder (eUpdateInsertMethod) was not implemented');
|
|
4607
|
-
case eUpdateInsertMethod.LAST:
|
|
4608
|
-
return {
|
|
4609
|
-
[stateKey]: null === newOrReplacementData ? null : [
|
|
4610
|
-
...updatedData,
|
|
4611
|
-
...(previousStateProperty?.filter(item => false === (updatedData?.find(value => findUniqueObjectIds(item, value)) || false)) ?? [])
|
|
4612
|
-
]
|
|
4613
|
-
};
|
|
4614
|
-
case eUpdateInsertMethod.FIRST:
|
|
4615
|
-
return {
|
|
4616
|
-
[stateKey]: null === newOrReplacementData ? null : [
|
|
4617
|
-
...(previousStateProperty?.filter(item => false === (updatedData?.find(value => findUniqueObjectIds(item, value)) || false)) ?? []),
|
|
4618
|
-
...updatedData,
|
|
4619
|
-
]
|
|
4620
|
-
};
|
|
4621
|
-
case eUpdateInsertMethod.REPLACE: {
|
|
4622
|
-
return {
|
|
4623
|
-
[stateKey]: [
|
|
4624
|
-
...(previousStateProperty?.map(oldObject => {
|
|
4625
|
-
const index = updatedData.findIndex(item => findUniqueObjectIds(item, oldObject));
|
|
4626
|
-
if (-1 === index) {
|
|
4627
|
-
return oldObject;
|
|
4628
|
-
}
|
|
4629
|
-
return updatedData.splice(index, 1).pop();
|
|
4630
|
-
}) ?? []),
|
|
4631
|
-
...updatedData
|
|
4632
|
-
]
|
|
4633
|
-
};
|
|
4634
|
-
}
|
|
4635
|
-
}
|
|
4636
|
-
}, callback);
|
|
4637
|
-
}
|
|
4638
|
-
|
|
4639
4632
|
// @link https://stackoverflow.com/questions/6735414/php-data-uri-to-file
|
|
4640
4633
|
// @link https://www.tutorialspoint.com/convert-image-to-data-uri-with-javascript
|
|
4641
4634
|
async function toDataURL(src, fileType, callback) {
|