@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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@carbonorm/carbonreact",
3
3
  "license": "MIT",
4
- "version": "3.6.1",
4
+ "version": "4.0.0",
5
5
  "browser": "dist/index.umd.js",
6
6
  "module": "dist/index.esm.js",
7
7
  "main": "dist/index.cjs.js",
@@ -9,8 +9,14 @@ import BackendThrowable from 'components/Errors/BackendThrowable';
9
9
  import Nest from 'components/Nest/Nest';
10
10
  import {initialRestfulObjectsState, iRestfulObjectArrayTypes} from "variables/C6";
11
11
  import CarbonWebSocket, {iCarbonWebSocketProps} from "./components/WebSocket/CarbonWebSocket";
12
+ import updateRestfulObjectArrays, {iUpdateRestfulObjectArrays} from "./hoc/updateRestfulObjectArrays";
13
+ import deleteRestfulObjectArrays, {iDeleteRestfulObjectArrays} from "./hoc/deleteRestfulObjectArrays";
12
14
 
13
15
 
16
+ export type tStatefulApiData<T extends {
17
+ [key: string]: any
18
+ } = {}> = T[] | undefined | null;
19
+
14
20
  // our central container, single page application
15
21
  export interface iCarbonReactState {
16
22
  alertsWaiting: any[],
@@ -42,22 +48,33 @@ export function isJsonString(str: string) {
42
48
  return true;
43
49
  }
44
50
 
51
+ const persistentStateMap = new Map<string, iCarbonReactState>();
45
52
 
46
- // Create a context
47
-
48
- const CarbonReact = class<P = {}, S = {}> extends Component<{
53
+ abstract class CarbonReact<P = {}, S extends { [key: string]: any; } = {}> extends Component<{
49
54
  children?: ReactNode | ReactNode[],
50
- shouldStatePersist?: boolean,
51
- websocket?: iCarbonWebSocketProps | boolean
55
+ instanceId?: string,
56
+ websocket?: Omit<iCarbonWebSocketProps, "instance"> | boolean
52
57
  } & P, S & iCarbonReactState> {
53
58
 
54
- static instance: Component<{
55
- children?: ReactNode | ReactNode[],
56
- } & any, any & iCarbonReactState>;
57
-
58
59
  context: Context<S & iCarbonReactState> = createContext(this.state);
59
60
 
60
- static persistentState?: iCarbonReactState = undefined;
61
+ // Private static member
62
+ // we actually implement this in the constructor todo - test this
63
+ protected static instance: CarbonReact;
64
+
65
+ protected target: typeof CarbonReact;
66
+
67
+ protected updateRestfulObjectArrays = <ObjectType extends { [key: string]: any; } = {}>
68
+ (rest: Omit<iUpdateRestfulObjectArrays<ObjectType, S, P>, "instance">) => updateRestfulObjectArrays<ObjectType, S, P>({
69
+ instance: this,
70
+ ...rest
71
+ });
72
+
73
+ protected deleteRestfulObjectArrays = <ObjectType extends { [key: string]: any } = {}>
74
+ (rest: Omit<iDeleteRestfulObjectArrays<ObjectType, S, P>, "instance">) => deleteRestfulObjectArrays<ObjectType, S, P>({
75
+ instance: this,
76
+ ...rest
77
+ });
61
78
 
62
79
  static lastLocation = window.location.pathname;
63
80
 
@@ -65,27 +82,41 @@ const CarbonReact = class<P = {}, S = {}> extends Component<{
65
82
  // noinspection JSUnusedGlobalSymbols
66
83
  static whyDidYouRender = true;
67
84
 
68
- constructor(props) {
85
+
86
+ protected constructor(props: {
87
+ children?: ReactNode | ReactNode[];
88
+ shouldStatePersist?: boolean | undefined;
89
+ websocket?: boolean | iCarbonWebSocketProps | undefined;
90
+ } & P) {
69
91
 
70
92
  super(props);
71
93
 
94
+ this.target = new.target;
95
+
72
96
  console.log('CarbonORM TSX CONSTRUCTOR');
73
97
 
74
- if (CarbonReact.persistentState !== undefined && this.props.shouldStatePersist !== false) {
98
+ // this is the magic that allows each class that's extends this to have a static instance - a singleton pattern
99
+ // new.target is a meta-property introduced in ES6 that references the constructor that was directly invoked with the new keyword.
100
+ Object.assign(new.target, {
101
+ instance: this
102
+ })
103
+
104
+ if (this.props.instanceId && persistentStateMap.has(this.props.instanceId)) {
75
105
 
76
- this.state = CarbonReact.persistentState as S & iCarbonReactState;
106
+ this.state = persistentStateMap.get(this.props.instanceId) as S & iCarbonReactState;
77
107
 
78
108
  } else {
79
109
 
110
+ // This should only ever be done here, when the full state is being trashed.
111
+ // todo - does this suck in context of multiple instances?
112
+ clearCache({
113
+ ignoreWarning: true
114
+ });
115
+
80
116
  this.state = initialCarbonReactState as unknown as S & iCarbonReactState;
81
117
 
82
118
  }
83
119
 
84
- // This should only ever be done here, when the full state is being trashed.
85
- clearCache({
86
- ignoreWarning: true
87
- });
88
-
89
120
  /** We can think of our app as having one state; this state.
90
121
  * Long-term, I'd like us to store this state to local storage and only load updates on reload...
91
122
  * Class based components are far easier to manage state in local storage and pass state down to children.
@@ -95,23 +126,14 @@ const CarbonReact = class<P = {}, S = {}> extends Component<{
95
126
 
96
127
  }
97
128
 
98
- static getState<S>(): S {
99
- return CarbonReact.instance.state;
100
- }
101
129
 
102
130
  shouldComponentUpdate(
103
131
  nextProps: Readonly<any>,
104
132
  nextState: Readonly<iCarbonReactState>,
105
133
  _nextContext: any): boolean {
106
134
 
107
- if (this.props.shouldStatePersist === false) {
108
-
109
- CarbonReact.persistentState = undefined;
110
-
111
- } else {
112
-
113
- CarbonReact.persistentState = nextState;
114
-
135
+ if (this.props.instanceId) {
136
+ persistentStateMap.set(this.props.instanceId, nextState);
115
137
  }
116
138
 
117
139
  changed(this.constructor.name + ' (C6Api)', 'props', this.props, nextProps);
@@ -147,7 +169,7 @@ const CarbonReact = class<P = {}, S = {}> extends Component<{
147
169
 
148
170
  return <>
149
171
  {nest}
150
- <BackendThrowable/>
172
+ <BackendThrowable instance={CarbonReact.instance}/>
151
173
  </>;
152
174
 
153
175
  }
@@ -157,7 +179,8 @@ const CarbonReact = class<P = {}, S = {}> extends Component<{
157
179
  return <>
158
180
  <GlobalHistory/>
159
181
  {this.props.websocket &&
160
- <CarbonWebSocket {...(true === this.props.websocket ? {} : this.props.websocket)} />}
182
+ <CarbonWebSocket {...(true === this.props.websocket ? {} : this.props.websocket)}
183
+ instance={CarbonReact.instance}/>}
161
184
  <Context value={this.state}>
162
185
  {this.props.children}
163
186
  </Context>
@@ -2,7 +2,7 @@ import classNames from "classnames";
2
2
  import CarbonReact from "CarbonReact";
3
3
  import {ReactNode} from "react";
4
4
  import Popup from "components/Popup/Popup";
5
- import getStyles from "hoc/getStyles";
5
+ import getStyles from "hoc/getStyles";
6
6
  import {faClose} from "@fortawesome/free-solid-svg-icons";
7
7
  import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
8
8
  import isProduction from "variables/isProduction";
@@ -21,6 +21,7 @@ export interface iAlertButtonOptions {
21
21
  export interface iAlert {
22
22
  title: string,
23
23
  text: string,
24
+ instance: CarbonReact,
24
25
  component?: ReactNode,
25
26
  icon?: "warning" | "error" | "success" | "info" | "question" | null,
26
27
  buttons?: (iAlertButtonOptions)[] | undefined, //['No thanks!', 'Yes, Delete it'],
@@ -34,7 +35,7 @@ export interface iAlert {
34
35
 
35
36
  export function addAlert(props: iAlert) {
36
37
 
37
- CarbonReact.instance.setState(previousState => ({
38
+ props.instance.setState(previousState => ({
38
39
  alertsWaiting: previousState.alertsWaiting.length === 0
39
40
  ? [props]
40
41
  : [...previousState.alertsWaiting, props]
@@ -42,9 +43,11 @@ export function addAlert(props: iAlert) {
42
43
 
43
44
  }
44
45
 
45
- export default function Alert() {
46
+ export default function Alert({
47
+ instance
48
+ }: { instance: CarbonReact }) {
46
49
 
47
- const {alertsWaiting, backendThrowable} = CarbonReact.instance.state
50
+ const {alertsWaiting, backendThrowable} = instance.state
48
51
 
49
52
  let alert: iAlert | undefined = undefined;
50
53
 
@@ -66,9 +69,10 @@ export default function Alert() {
66
69
  })
67
70
  }
68
71
 
69
- const backendThrowable = CarbonReact.instance.state.backendThrowable[0]
72
+ const backendThrowable = instance.state.backendThrowable[0]
70
73
 
71
74
  alert = {
75
+ instance: instance,
72
76
  title: "Oh no! An issue occurred!",
73
77
  text: backendThrowable?.['DropInGaming\\PHP\\Errors\\DropException'] ?? 'An unknown issue occurred. Please try again.',
74
78
  timeout: 0,
@@ -81,7 +85,7 @@ export default function Alert() {
81
85
 
82
86
  if (value === 'Expand') {
83
87
 
84
- CarbonReact.instance.setState(previousState => {
88
+ instance.setState(previousState => {
85
89
 
86
90
  let backendThrowable = previousState.backendThrowable.pop()
87
91
 
@@ -99,7 +103,7 @@ export default function Alert() {
99
103
  })
100
104
 
101
105
  } else {
102
- CarbonReact.instance.setState(previousState => ({
106
+ instance.setState(previousState => ({
103
107
  backendThrowable: previousState.backendThrowable.slice(1)
104
108
  }))
105
109
  }
@@ -120,9 +124,7 @@ export default function Alert() {
120
124
 
121
125
  const timeout = alert?.timeout || 15000
122
126
 
123
- const bootstrap = CarbonReact.instance
124
-
125
- const dig = getStyles()
127
+ const styles = getStyles()
126
128
 
127
129
  let cancelTimeout: any = null
128
130
 
@@ -132,7 +134,7 @@ export default function Alert() {
132
134
  }
133
135
 
134
136
  if (alert?.backendThrowable === undefined) {
135
- bootstrap.setState(previousState => ({
137
+ instance.setState(previousState => ({
136
138
  alertsWaiting: previousState.alertsWaiting.slice(1)
137
139
  }))
138
140
  }
@@ -153,19 +155,19 @@ export default function Alert() {
153
155
  }
154
156
 
155
157
  return <Popup handleClose={handleClose}>
156
- <div className={classNames("model-content", dig.rounded0, dig.border0)} style={{
158
+ <div className={classNames("model-content", styles.rounded0, styles.border0)} style={{
157
159
  maxWidth: '75vw',
158
160
  maxHeight: '75vh',
159
161
  }}>
160
- <div className={classNames(dig.modalHeader, dig.rounded0, dig.border0, {
162
+ <div className={classNames(styles.modalHeader, styles.rounded0, styles.border0, {
161
163
  // icon?: "warning" | "error" | "success" | "info" | "question"
162
- [dig.bg_primary]: "info" === alert.icon || alert.icon === undefined || alert.icon === null,
163
- [dig.bg_success]: "success" === alert.icon,
164
- [dig.bg_warning]: "warning" === alert.icon,
165
- [dig.bg_danger]: "error" === alert.icon, // TODO - change to red
166
- [dig.bgPrimary]: "question" === alert.icon,
164
+ [styles.bg_primary]: "info" === alert.icon || alert.icon === undefined || alert.icon === null,
165
+ [styles.bg_success]: "success" === alert.icon,
166
+ [styles.bg_warning]: "warning" === alert.icon,
167
+ [styles.bg_danger]: "error" === alert.icon, // TODO - change to red
168
+ [styles.bgPrimary]: "question" === alert.icon,
167
169
  })}>
168
- <h3 className={classNames(dig.modalTitle, dig.textDark)} id="staticBackdropLabel">
170
+ <h3 className={classNames(styles.modalTitle, styles.textDark)} id="staticBackdropLabel">
169
171
  #{alertWaiting} {alert.title}
170
172
  </h3>
171
173
  <div onClick={handleClose}>
@@ -174,26 +176,27 @@ export default function Alert() {
174
176
  size={'xl'}/>
175
177
  </div>
176
178
  </div>
177
- <div className={classNames(dig.modalBody, dig.border0, dig.textWhite)}>
178
- <div className={dig.textCenter}>
179
+ <div className={classNames(styles.modalBody, styles.border0, styles.textWhite)}>
180
+ <div className={styles.textCenter}>
179
181
  {alert.text}
180
182
  {alert.component}
181
183
  </div>
182
184
  </div>
183
185
  {undefined !== alert.buttons &&
184
- <div className={classNames(dig.modalFooter, dig.border0, dig.rounded0)}>
185
- {alert.footerText && <div className={classNames(dig.textCenter, dig.textWhite)}>{alert.footerText}</div>}
186
+ <div className={classNames(styles.modalFooter, styles.border0, styles.rounded0)}>
187
+ {alert.footerText &&
188
+ <div className={classNames(styles.textCenter, styles.textWhite)}>{alert.footerText}</div>}
186
189
 
187
190
  {alert.buttons?.map((button: iAlertButtonOptions, index: number) => {
188
191
 
189
192
  return <button key={index}
190
- className={classNames(dig.btn, dig.btnLg, {
193
+ className={classNames(styles.btn, styles.btnLg, {
191
194
  // todo - color: "default" | "primary" | "secondary" | "inherit" | "danger" | "info" | "success" | "warning" | undefined,
192
- [dig.bg_success]: "success" === button.color,
193
- [dig.bg_danger]: "danger" === button.color,
194
- [dig.bg_primary]: "primary" === button.color,
195
- [dig.bg_warning]: "warning" === button.color,
196
- }, "btn-Yes", dig.rounded0)}
195
+ [styles.bg_success]: "success" === button.color,
196
+ [styles.bg_danger]: "danger" === button.color,
197
+ [styles.bg_primary]: "primary" === button.color,
198
+ [styles.bg_warning]: "warning" === button.color,
199
+ }, "btn-Yes", styles.rounded0)}
197
200
  onClick={() => {
198
201
  handleClose()
199
202
  alert?.then?.(button.value ?? button.text)
@@ -3,18 +3,20 @@ import OutsideClickHandler from 'react-outside-click-handler';
3
3
  import CarbonReact from "../../CarbonReact";
4
4
  import {ReactElement} from "react";
5
5
 
6
- export default () : ReactElement => {
6
+ export default (props: {
7
+ instance: CarbonReact,
8
+ }): ReactElement => {
7
9
 
8
- const bootstrap = CarbonReact.instance;
10
+ const {instance} = props;
9
11
 
10
- const currentThrowable = bootstrap.state.backendThrowable[0];
12
+ const currentThrowable = instance.state.backendThrowable[0];
11
13
 
12
- console.log([bootstrap.state.backendThrowable, currentThrowable]);
14
+ console.log([instance.state.backendThrowable, currentThrowable]);
13
15
 
14
16
  return <div className={styles.maintenanceHero}>
15
17
  <h1 className={styles.httpStatusCode}>{currentThrowable?.status || 500}</h1>
16
18
  <OutsideClickHandler
17
- onOutsideClick={() => bootstrap.setState(currentState => ({ backendThrowable: currentState.backendThrowable.slice(1) }))}>
19
+ onOutsideClick={() => instance.setState(currentState => ({backendThrowable: currentState.backendThrowable.slice(1)}))}>
18
20
  <div className={styles.centeredContainer}>
19
21
  {Object.keys(currentThrowable).map((key, index) => {
20
22
 
@@ -26,14 +28,15 @@ export default () : ReactElement => {
26
28
  <div className={styles.errorTextGeneral}> &gt; <span className={styles.errorKeys}>{key}</span>:
27
29
  {valueIsString
28
30
  ? (valueIsCode ? <div
29
- style={{ backgroundColor: 'black', fontSize: 'xx-small' }}
30
- dangerouslySetInnerHTML={{ __html: currentThrowable[key] }} /> :
31
+ style={{backgroundColor: 'black', fontSize: 'xx-small'}}
32
+ dangerouslySetInnerHTML={{__html: currentThrowable[key]}}/> :
31
33
  <i className={styles.errorValues}>&quot;{currentThrowable[key]}&quot;</i>)
32
34
  : ''}
33
35
  </div>
34
36
  {valueIsString
35
37
  ? ''
36
- : <pre className={styles.errorPre}>{JSON.stringify(currentThrowable[key], undefined, 4)}</pre>}
38
+ : <pre
39
+ className={styles.errorPre}>{JSON.stringify(currentThrowable[key], undefined, 4)}</pre>}
37
40
  </div>;
38
41
  })}
39
42
  </div>
@@ -1,4 +1,4 @@
1
- import CarbonReact, {isJsonString} from "CarbonReact";
1
+ import CarbonReact, {isJsonString} from "CarbonReact";
2
2
  import {addAlert} from "../Alert/Alert";
3
3
  import {useEffectOnce} from "../../api/hoc/useEffectOnce";
4
4
  import {tC6Tables, tC6RestApi} from "@carbonorm/carbonnode";
@@ -8,6 +8,7 @@ export interface iCarbonWebSocketProps {
8
8
  url?: string,
9
9
  timeoutSeconds?: number,
10
10
  heartbeatSeconds?: number,
11
+ instance: CarbonReact,
11
12
  TABLES?: tC6Tables,
12
13
  WsLiveUpdates?: tC6RestApi,
13
14
  }
@@ -16,15 +17,19 @@ export interface iCarbonWebSocketProps {
16
17
  * @function connect
17
18
  * This function establishes a connection with the websocket and also ensures constant reconnection if connection closes
18
19
  **/
19
- export function initiateWebsocket({
20
- TABLES = undefined,
21
- WsLiveUpdates = undefined,
22
- url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' + window.location.host + '/carbonorm/websocket',
23
- timeoutSeconds = 250,
24
- heartbeatSeconds = 60
25
- }: iCarbonWebSocketProps = {}) {
20
+ export function initiateWebsocket(props: iCarbonWebSocketProps) {
21
+
22
+ let {
23
+ instance,
24
+ TABLES = undefined,
25
+ WsLiveUpdates = undefined,
26
+ url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' + window.location.host + '/carbonorm/websocket',
27
+ timeoutSeconds = 250,
28
+ heartbeatSeconds = 60
29
+ } = props;
30
+
31
+ const {websocket} = instance.state;
26
32
 
27
- const {websocket} = CarbonReact.instance.state;
28
33
 
29
34
  if (!("WebSocket" in window)) {
30
35
 
@@ -33,6 +38,7 @@ export function initiateWebsocket({
33
38
  title: 'Browser does not support websockets, live updates will fail. You may need to refresh the page to see the newest content.',
34
39
  text: 'Please use a modern browser.',
35
40
  icon: 'warning',
41
+ instance
36
42
  })
37
43
 
38
44
  }
@@ -50,7 +56,7 @@ export function initiateWebsocket({
50
56
 
51
57
  console.log("Connecting websocket url", url);
52
58
 
53
- CarbonReact.instance.setState({
59
+ instance.setState({
54
60
  websocket: connection
55
61
  }, () => {
56
62
 
@@ -62,7 +68,7 @@ export function initiateWebsocket({
62
68
 
63
69
  function heartbeat() {
64
70
 
65
- const {websocket} = CarbonReact.instance.state;
71
+ const {websocket} = instance.state;
66
72
 
67
73
  if (!websocket) return;
68
74
 
@@ -86,7 +92,7 @@ export function initiateWebsocket({
86
92
  return;
87
93
  }
88
94
 
89
- CarbonReact.instance.setState((prevState: Readonly<any>) => ({
95
+ instance.setState((prevState: Readonly<any>) => ({
90
96
  websocketEvents: prevState.websocketEvents.concat(message),
91
97
  websocketData: prevState.websocketData.concat(parsedData), // JSON.parse no good - base64?
92
98
  }), () => {
@@ -133,7 +139,7 @@ export function initiateWebsocket({
133
139
 
134
140
  const TABLE_NAME_SHORT = TABLE_NAME.substring(TABLE_PREFIX.length);
135
141
 
136
- const currentCache: [] = CarbonReact.instance.state[TABLE_NAME_SHORT]
142
+ const currentCache: [] = instance.state[TABLE_NAME_SHORT]
137
143
 
138
144
  // just because we have a websocket update, doesn't mean we need the update
139
145
  // check to see if the primary key is in the current cache
@@ -196,7 +202,7 @@ export function initiateWebsocket({
196
202
 
197
203
  };
198
204
 
199
- window.addEventListener("focus", () => initiateWebsocket());
205
+ window.addEventListener("focus", () => initiateWebsocket(props));
200
206
 
201
207
  // websocket onclose event listener
202
208
  connection.addEventListener('close', event => {
@@ -215,7 +221,7 @@ export function initiateWebsocket({
215
221
 
216
222
  console.log(`WebSocket reconnect will be attempted in ${retrySeconds} second(s).`)
217
223
 
218
- connectInterval = setTimeout(() => initiateWebsocket(), retrySeconds);
224
+ connectInterval = setTimeout(() => initiateWebsocket(props), retrySeconds);
219
225
 
220
226
  }
221
227
 
@@ -1,2 +1,4 @@
1
-
2
- export type KeysMatching<T, V> = {[K in keyof T]-?: T[K] extends V ? K : never}[keyof T];
1
+ // KeysMatching utility type
2
+ export type KeysMatching<T, V> = {
3
+ [K in keyof T]: T[K] extends V ? K : never
4
+ }[keyof T];
@@ -0,0 +1,6 @@
1
+ // Utility type to extract keys and corresponding values
2
+ import {KeysMatching} from "./KeysMatching";
3
+
4
+ export type SubsetMatching<T extends object, V> = {
5
+ [K in KeysMatching<T, V>]: T[K]
6
+ };
@@ -1,36 +1,54 @@
1
- import CarbonReact from "CarbonReact";
2
- import {tRestfulObjectArrayValues, tStatefulApiData} from "variables/C6";
3
- import {KeysMatching} from "./KeysMatching";
4
-
1
+ import CarbonReact, { iCarbonReactState, tStatefulApiData } from "CarbonReact";
2
+ import { KeysMatching } from "./KeysMatching";
3
+
4
+ export interface iDeleteRestfulObjectArrays<
5
+ ObjectType extends {
6
+ [key: string]: any
7
+ } = {},
8
+ S extends { [key: string]: any; } = CarbonReact['state'],
9
+ P = CarbonReact['props']
10
+ > {
11
+ instance: CarbonReact<P, S>,
12
+ dataOrCallback: ObjectType[] | ((state: Readonly<S>, props: Readonly<P>) => ObjectType[] | null),
13
+ stateKey: KeysMatching<S, tStatefulApiData<ObjectType>>,
14
+ uniqueObjectId: keyof ObjectType | (keyof ObjectType)[],
15
+ callback?: () => void
16
+ }
5
17
 
6
- //ObjectType, UniqueIdType extends keyof ObjectType
7
- // @link https://www.typescriptlang.org/docs/handbook/2/mapped-types.html
8
- export default function deleteRestfulObjectArrays<ObjectType = tRestfulObjectArrayValues, S = typeof CarbonReact.instance.state, P = typeof CarbonReact.instance.props>
9
- (dataOrCallback: ObjectType[] | (<K extends keyof S>(
10
- state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
11
- callback?: () => void
12
- ) => null|(ObjectType[])),
13
- stateKey: KeysMatching<S, tStatefulApiData<ObjectType>>,
14
- uniqueObjectId: (keyof ObjectType) | (keyof ObjectType)[],
15
- callback?: () => void): void {
18
+ export default function deleteRestfulObjectArrays<
19
+ ObjectType extends {
20
+ [key: string]: any
21
+ } = {},
22
+ S extends { [key: string]: any; } = CarbonReact['state'],
23
+ P = CarbonReact['props']
24
+ >({
25
+ instance,
26
+ dataOrCallback,
27
+ stateKey,
28
+ uniqueObjectId,
29
+ callback
30
+ }: iDeleteRestfulObjectArrays<ObjectType, S, P>): void {
16
31
 
17
- const uniqueObjectIds = uniqueObjectId instanceof Array ? uniqueObjectId : [uniqueObjectId];
32
+ const uniqueObjectIds = Array.isArray(uniqueObjectId) ? uniqueObjectId : [uniqueObjectId];
18
33
 
19
- return CarbonReact.instance.setState((previousBootstrapState, props) => {
34
+ instance.setState((
35
+ previousBootstrapState: Readonly<S & iCarbonReactState>,
36
+ props: Readonly<P>
37
+ ): Pick<S & iCarbonReactState, keyof S> | null => {
20
38
 
21
- let newOrReplacementData: ObjectType[] = [];
39
+ let newOrReplacementData: ObjectType[] = [];
22
40
 
23
- if (dataOrCallback instanceof Array) {
41
+ if (Array.isArray(dataOrCallback)) {
24
42
 
25
- newOrReplacementData = dataOrCallback
43
+ newOrReplacementData = dataOrCallback;
26
44
 
27
- } else if (dataOrCallback instanceof Function) {
45
+ } else if (typeof dataOrCallback === 'function') {
28
46
 
29
- let callbackReturn = dataOrCallback(previousBootstrapState, props);
47
+ const callbackReturn = dataOrCallback(previousBootstrapState, props);
30
48
 
31
- if (null === callbackReturn) {
49
+ if (callbackReturn === null) {
32
50
 
33
- return ;
51
+ return null; // No updates needed (noop)
34
52
 
35
53
  }
36
54
 
@@ -38,40 +56,21 @@ export default function deleteRestfulObjectArrays<ObjectType = tRestfulObjectArr
38
56
 
39
57
  } else {
40
58
 
41
- throw Error('The dataOrCallback parameter was not an array or function')
59
+ throw new Error('The dataOrCallback parameter was not an array or function');
42
60
 
43
61
  }
44
62
 
45
- const previousStateProperty : ObjectType[] = previousBootstrapState[stateKey];
46
-
47
- return {
48
- [stateKey]: [
49
-
50
- ...previousStateProperty?.filter(item => false === (newOrReplacementData?.find(value => {
51
-
52
- let isMatch = true;
53
-
54
- uniqueObjectIds.find(uniqueObjectId => {
55
-
56
- if (value[uniqueObjectId] !== item[uniqueObjectId]) {
57
-
58
- isMatch = false;
63
+ const previousStateProperty: tStatefulApiData<ObjectType> = previousBootstrapState[stateKey] as tStatefulApiData<ObjectType>;
59
64
 
60
- return true;
65
+ const updatedStateProperty = previousStateProperty?.filter(item =>
66
+ !newOrReplacementData.some(value =>
67
+ uniqueObjectIds.every(uniqueId => value[uniqueId] === item[uniqueId])
68
+ )
69
+ ) ?? [];
61
70
 
62
- }
63
-
64
- return false;
65
-
66
- })
67
-
68
- return isMatch;
69
-
70
- }) || false)) || []
71
+ return {
72
+ [stateKey]: updatedStateProperty
73
+ } as Pick<S & iCarbonReactState, keyof S>;
71
74
 
72
- ]
73
- }
74
75
  }, callback);
75
-
76
76
  }
77
-