@carbonorm/carbonreact 3.4.7 → 3.5.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 +225 -8
- package/dist/CarbonReact.d.ts +8 -3
- package/dist/components/WebSocket/CarbonWebSocket.d.ts +3 -2
- package/dist/index.cjs.js +66 -19
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +67 -20
- package/dist/index.esm.js.map +1 -1
- package/package.json +6 -5
- package/src/CarbonReact.tsx +27 -11
- package/src/components/Popup/Popup.tsx +1 -8
- package/src/components/WebSocket/CarbonWebSocket.tsx +88 -9
package/package.json
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carbonorm/carbonreact",
|
|
3
|
-
"
|
|
3
|
+
"license": "MIT",
|
|
4
|
+
"version": "3.5.0",
|
|
4
5
|
"browser": "dist/index.umd.js",
|
|
5
6
|
"module": "dist/index.esm.js",
|
|
6
7
|
"main": "dist/index.cjs.js",
|
|
7
8
|
"types": "dist/index.d.ts",
|
|
8
9
|
"type": "module",
|
|
9
10
|
"dependencies": {
|
|
10
|
-
"@carbonorm/carbonnode": "^1.
|
|
11
|
+
"@carbonorm/carbonnode": "^1.4.0",
|
|
11
12
|
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
|
12
13
|
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
|
13
14
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
|
15
|
+
"@testing-library/react": "^14.0.0",
|
|
14
16
|
"ansi-colors": "^4.1.3",
|
|
15
17
|
"axios": "^1.4.0",
|
|
16
18
|
"bootstrap": "^5.3.1",
|
|
17
19
|
"classnames": "^2.3.2",
|
|
18
20
|
"heic2any": "^0.0.4",
|
|
21
|
+
"jest": "^29.5.0",
|
|
22
|
+
"jest-config": "^29.5.0",
|
|
19
23
|
"qs": "^6.11.1",
|
|
20
24
|
"react": "^18.2.0",
|
|
21
25
|
"react-dom": "^18.2.0",
|
|
@@ -34,7 +38,6 @@
|
|
|
34
38
|
"@rollup/plugin-commonjs": "^11.0.1",
|
|
35
39
|
"@rollup/plugin-node-resolve": "^7.0.0",
|
|
36
40
|
"@rollup/plugin-typescript": "^11.1.2",
|
|
37
|
-
"@testing-library/react": "^14.0.0",
|
|
38
41
|
"@types/jest": "^29.5.4",
|
|
39
42
|
"@types/ms": "^0.7.31",
|
|
40
43
|
"@types/node": "^18.17.14",
|
|
@@ -46,8 +49,6 @@
|
|
|
46
49
|
"classnames": "^2.3.2",
|
|
47
50
|
"css-loader": "^6.8.1",
|
|
48
51
|
"deepmerge": "^4.3.1",
|
|
49
|
-
"jest": "^29.5.0",
|
|
50
|
-
"jest-config": "^29.5.0",
|
|
51
52
|
"livereload": "^0.9.3",
|
|
52
53
|
"postcss": "^8.4.27",
|
|
53
54
|
"postcss-nested": "^6.0.1",
|
package/src/CarbonReact.tsx
CHANGED
|
@@ -3,7 +3,6 @@ import changed from "hoc/changed";
|
|
|
3
3
|
import {GlobalHistory} from "hoc/GlobalHistory";
|
|
4
4
|
import hexToRgb from "hoc/hexToRgb";
|
|
5
5
|
import {Component, ReactNode} from 'react';
|
|
6
|
-
import {BrowserRouter} from 'react-router-dom';
|
|
7
6
|
import {ToastContainer} from 'react-toastify';
|
|
8
7
|
import 'react-toastify/dist/ReactToastify.min.css';
|
|
9
8
|
import BackendThrowable from 'components/Errors/BackendThrowable';
|
|
@@ -12,7 +11,6 @@ import {initialRestfulObjectsState, iRestfulObjectArrayTypes} from "variables/C6
|
|
|
12
11
|
import CarbonWebSocket from "./components/WebSocket/CarbonWebSocket";
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
|
|
16
14
|
// our central container, single page application is best with the DigApi
|
|
17
15
|
export interface iCarbonReactState {
|
|
18
16
|
alertsWaiting: any[],
|
|
@@ -44,18 +42,17 @@ export function isJsonString(str) {
|
|
|
44
42
|
return true;
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
const CarbonReact= class
|
|
45
|
+
const CarbonReact = class<P = {}, S = {}> extends Component<{
|
|
48
46
|
children?: ReactNode | ReactNode[],
|
|
47
|
+
shouldStatePersist?: boolean,
|
|
49
48
|
} & P, S & iCarbonReactState> {
|
|
50
49
|
|
|
51
50
|
static instance: Component<{
|
|
52
51
|
children?: ReactNode | ReactNode[],
|
|
53
52
|
} & any, any & iCarbonReactState>;
|
|
54
53
|
|
|
54
|
+
static persistentState?: iCarbonReactState = undefined
|
|
55
55
|
static lastLocation = window.location.pathname;
|
|
56
|
-
static websocketUrl = (window.location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.host + ':8888/ws';
|
|
57
|
-
static websocketTimeoutSeconds : number = 250;
|
|
58
|
-
static websocketHeartbeatSeconds : number = 250;
|
|
59
56
|
|
|
60
57
|
// @link https://github.com/welldone-software/why-did-you-render
|
|
61
58
|
// noinspection JSUnusedGlobalSymbols
|
|
@@ -65,7 +62,15 @@ const CarbonReact= class <P = {}, S = {}> extends Component<{
|
|
|
65
62
|
|
|
66
63
|
super(props);
|
|
67
64
|
|
|
68
|
-
|
|
65
|
+
if (CarbonReact.persistentState !== undefined && this.props.shouldStatePersist !== false) {
|
|
66
|
+
|
|
67
|
+
this.state = CarbonReact.persistentState as S & iCarbonReactState;
|
|
68
|
+
|
|
69
|
+
} else {
|
|
70
|
+
|
|
71
|
+
this.state = initialCarbonReactState as unknown as S & iCarbonReactState;
|
|
72
|
+
|
|
73
|
+
}
|
|
69
74
|
|
|
70
75
|
// This should only ever be done here, when the full state is being trashed.
|
|
71
76
|
clearCache({
|
|
@@ -81,7 +86,7 @@ const CarbonReact= class <P = {}, S = {}> extends Component<{
|
|
|
81
86
|
|
|
82
87
|
}
|
|
83
88
|
|
|
84
|
-
static getState<S>()
|
|
89
|
+
static getState<S>(): S {
|
|
85
90
|
return CarbonReact.instance.state;
|
|
86
91
|
}
|
|
87
92
|
|
|
@@ -90,7 +95,18 @@ const CarbonReact= class <P = {}, S = {}> extends Component<{
|
|
|
90
95
|
nextState: Readonly<iCarbonReactState>,
|
|
91
96
|
_nextContext: any): boolean {
|
|
92
97
|
|
|
98
|
+
if (this.props.shouldStatePersist === false) {
|
|
99
|
+
|
|
100
|
+
CarbonReact.persistentState = undefined;
|
|
101
|
+
|
|
102
|
+
} else {
|
|
103
|
+
|
|
104
|
+
CarbonReact.persistentState = nextState;
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
|
|
93
108
|
changed(this.constructor.name + ' (DigApi)', 'props', this.props, nextProps);
|
|
109
|
+
|
|
94
110
|
changed(this.constructor.name + ' (DigApi)', 'state', this.state, nextState);
|
|
95
111
|
|
|
96
112
|
return true
|
|
@@ -127,12 +143,12 @@ const CarbonReact= class <P = {}, S = {}> extends Component<{
|
|
|
127
143
|
|
|
128
144
|
}
|
|
129
145
|
|
|
130
|
-
return
|
|
146
|
+
return <>
|
|
131
147
|
<GlobalHistory/>
|
|
132
|
-
<CarbonWebSocket
|
|
148
|
+
<CarbonWebSocket/>
|
|
133
149
|
{this.props.children}
|
|
134
150
|
<ToastContainer/>
|
|
135
|
-
|
|
151
|
+
</>;
|
|
136
152
|
|
|
137
153
|
}
|
|
138
154
|
|
|
@@ -20,16 +20,9 @@ export default function Popup({
|
|
|
20
20
|
maxWidth,
|
|
21
21
|
}: PropsWithChildren<iPopupProperties>) : ReactElement {
|
|
22
22
|
|
|
23
|
-
if (false === open) {
|
|
24
|
-
|
|
25
|
-
// @link https://legacy.reactjs.org/docs/conditional-rendering.html#preventing-component-from-rendering
|
|
26
|
-
return <></>;
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
23
|
const dig = getStyles()
|
|
31
24
|
|
|
32
|
-
return <div className={classNames(dig.modal, dig.fade, dig.show, dig.dBlock)}
|
|
25
|
+
return <div className={classNames(dig.modal, dig.fade, { [dig.show]: open}, dig.dBlock)}
|
|
33
26
|
style={{backgroundColor: "rgba(0,0,0,0.8)"}}
|
|
34
27
|
id="exampleModalCenter"
|
|
35
28
|
tabIndex={-1} aria-labelledby="exampleModalCenterTitle"
|
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
import CarbonReact, {isJsonString} from "CarbonReact";
|
|
2
2
|
import {addAlert} from "../Alert/Alert";
|
|
3
3
|
import {useEffectOnce} from "../../api/hoc/useEffectOnce";
|
|
4
|
-
import {tC6Tables} from "@carbonorm/carbonnode";
|
|
4
|
+
import {tC6Tables, tWsLiveUpdate} from "@carbonorm/carbonnode";
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
export interface iCarbonWebSocketProps {
|
|
8
8
|
url?: string,
|
|
9
9
|
timeoutSeconds?: number,
|
|
10
10
|
heartbeatSeconds?: number,
|
|
11
|
-
TABLES?: tC6Tables
|
|
11
|
+
TABLES?: tC6Tables,
|
|
12
|
+
WsLiveUpdates?: tWsLiveUpdate,
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @function connect
|
|
16
17
|
* This function establishes a connection with the websocket and also ensures constant reconnection if connection closes
|
|
17
18
|
**/
|
|
18
|
-
export function initiateWebsocket({
|
|
19
|
+
export function initiateWebsocket({
|
|
20
|
+
TABLES = undefined,
|
|
21
|
+
WsLiveUpdates = undefined,
|
|
19
22
|
url = 'ws://localhost:8080/ws',
|
|
20
23
|
timeoutSeconds = 250,
|
|
21
24
|
heartbeatSeconds = 60
|
|
22
|
-
}: iCarbonWebSocketProps = {}) {
|
|
25
|
+
}: iCarbonWebSocketProps = {}) {
|
|
23
26
|
|
|
24
27
|
const {websocket} = CarbonReact.instance.state;
|
|
25
28
|
|
|
@@ -79,19 +82,95 @@ export function initiateWebsocket({TABLES = undefined,
|
|
|
79
82
|
|
|
80
83
|
const parsedData = isJsonString(message?.data) ? JSON.parse(message?.data) : message?.data;
|
|
81
84
|
|
|
85
|
+
if (message.data === 'pong') {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
82
89
|
CarbonReact.instance.setState((prevState: Readonly<any>) => ({
|
|
83
90
|
websocketEvents: prevState.websocketEvents.concat(message),
|
|
84
91
|
websocketData: prevState.websocketData.concat(parsedData), // JSON.parse no good - base64?
|
|
85
|
-
}))
|
|
92
|
+
}), () => {
|
|
93
|
+
|
|
94
|
+
if (undefined === TABLES) {
|
|
95
|
+
|
|
96
|
+
console.log('WebSocket updates without the TABLES property passed will not automatically update the state.')
|
|
97
|
+
|
|
98
|
+
return;
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (undefined === WsLiveUpdates) {
|
|
103
|
+
|
|
104
|
+
console.log('WebSocket updates without the WsLiveUpdates property passed will not automatically update the state.')
|
|
105
|
+
|
|
106
|
+
return;
|
|
107
|
+
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (parsedData?.REST) {
|
|
111
|
+
|
|
112
|
+
const TABLE_NAME: string = parsedData?.REST?.TABLE_NAME;
|
|
113
|
+
|
|
114
|
+
const TABLE_PREFIX: string = parsedData?.REST?.TABLE_PREFIX;
|
|
115
|
+
|
|
116
|
+
const METHOD: string = parsedData?.REST?.METHOD;
|
|
117
|
+
|
|
118
|
+
const REQUEST: { [key:string]: any } = parsedData?.REST?.REQUEST;
|
|
119
|
+
|
|
120
|
+
const REQUEST_PRIMARY_KEY: {
|
|
121
|
+
[key: string]: string
|
|
122
|
+
} = parsedData?.REST?.REQUEST_PRIMARY_KEY ?? null;
|
|
123
|
+
|
|
124
|
+
if (null === REQUEST_PRIMARY_KEY) {
|
|
125
|
+
|
|
126
|
+
console.log('WebSocket updates without a primary key are not yet supported.')
|
|
127
|
+
|
|
128
|
+
return;
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log('todo - going to impl REST', TABLE_NAME, METHOD, REQUEST_PRIMARY_KEY, parsedData?.REST)
|
|
133
|
+
|
|
134
|
+
const TABLE_NAME_SHORT = TABLE_NAME.substring(TABLE_PREFIX.length);
|
|
135
|
+
|
|
136
|
+
const currentCache: [] = CarbonReact.instance.state[TABLE_NAME_SHORT]
|
|
137
|
+
|
|
138
|
+
// just because we have a websocket update, doesn't mean we need the update
|
|
139
|
+
// check to see if the primary key is in the current cache
|
|
140
|
+
const c6Table = TABLES[TABLE_NAME_SHORT] ?? null;
|
|
141
|
+
|
|
142
|
+
if (null === c6Table) {
|
|
143
|
+
|
|
144
|
+
console.error('WebSocket update could not find (' + TABLE_NAME_SHORT + ') in the TABLES property passed.', TABLES)
|
|
145
|
+
|
|
146
|
+
return;
|
|
147
|
+
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const primaryKeyKeys = Object.keys(REQUEST_PRIMARY_KEY)
|
|
151
|
+
|
|
152
|
+
const elementsToUpdate = currentCache.filter((row: any) => {
|
|
153
|
+
for (const element of primaryKeyKeys) {
|
|
154
|
+
if (REQUEST_PRIMARY_KEY[element] !== row[element]) {
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return true
|
|
159
|
+
})
|
|
86
160
|
|
|
87
|
-
console.info('todo - going to impl TABLES', TABLES)
|
|
88
161
|
|
|
89
|
-
|
|
162
|
+
const updatedElements = elementsToUpdate.map((row: any) => {
|
|
163
|
+
return {
|
|
164
|
+
...row,
|
|
165
|
+
...REQUEST
|
|
166
|
+
}
|
|
167
|
+
})
|
|
90
168
|
|
|
91
|
-
|
|
169
|
+
WsLiveUpdates[TABLE_NAME_SHORT][METHOD]({}, updatedElements)
|
|
92
170
|
|
|
171
|
+
}
|
|
93
172
|
|
|
94
|
-
}
|
|
173
|
+
});
|
|
95
174
|
|
|
96
175
|
};
|
|
97
176
|
|