@carbonorm/carbonreact 4.0.10 → 4.0.12

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": "4.0.10",
4
+ "version": "4.0.12",
5
5
  "browser": "dist/index.umd.js",
6
6
  "module": "dist/index.esm.js",
7
7
  "main": "dist/index.cjs.js",
@@ -1,16 +1,16 @@
1
- import {clearCache} from "@carbonorm/carbonnode";
1
+ import { clearCache } from "@carbonorm/carbonnode";
2
2
  import changed from "hoc/changed";
3
- import {GlobalHistory} from "hoc/GlobalHistory";
3
+ import { GlobalHistory } from "hoc/GlobalHistory";
4
4
  import hexToRgb from "hoc/hexToRgb";
5
- import {Component, Context, createContext, ReactElement, ReactNode} from 'react';
6
- import {ToastContainer} from 'react-toastify';
5
+ import { Component, Context, createContext, ReactElement, ReactNode } from 'react';
6
+ import { ToastContainer } from 'react-toastify';
7
7
  import 'react-toastify/dist/ReactToastify.min.css';
8
8
  import BackendThrowable from 'components/Errors/BackendThrowable';
9
9
  import Nest from 'components/Nest/Nest';
10
- import {initialRestfulObjectsState, iRestfulObjectArrayTypes} from "variables/C6";
11
- import CarbonWebSocket, {iCarbonWebSocketProps} from "./components/WebSocket/CarbonWebSocket";
12
- import updateRestfulObjectArrays, {iUpdateRestfulObjectArrays} from "./hoc/updateRestfulObjectArrays";
13
- import deleteRestfulObjectArrays, {iDeleteRestfulObjectArrays} from "./hoc/deleteRestfulObjectArrays";
10
+ import { initialRestfulObjectsState, iRestfulObjectArrayTypes } from "variables/C6";
11
+ import CarbonWebSocket, { iCarbonWebSocketProps } from "./components/WebSocket/CarbonWebSocket";
12
+ import updateRestfulObjectArrays, { iUpdateRestfulObjectArrays } from "./hoc/updateRestfulObjectArrays";
13
+ import deleteRestfulObjectArrays, { iDeleteRestfulObjectArrays } from "./hoc/deleteRestfulObjectArrays";
14
14
 
15
15
 
16
16
  export type tStatefulApiData<T extends {
@@ -46,23 +46,20 @@ export function isJsonString(str: string) {
46
46
  return false;
47
47
  }
48
48
  return true;
49
-
50
49
  }
51
50
 
52
51
  abstract class CarbonReact<P = {}, S extends iCarbonReactState = iCarbonReactState> extends Component<{
53
52
  children?: ReactNode | ReactNode[],
54
53
  instanceId?: string,
55
- websocket?: Omit<iCarbonWebSocketProps<P,S>, "instance"> | false
54
+ websocket?: Omit<iCarbonWebSocketProps<P, S>, "instance"> | false
56
55
  } & P, S> {
57
56
 
58
57
  private static persistentStateMap = new Map<string, { [key: string]: any; }>();
58
+ private static activeInstances = new Map<string, CarbonReact<any, any>>();
59
59
 
60
- // Context is for functional components to access the state of this class efficiently
61
60
  context: Context<S & iCarbonReactState> = createContext(this.state);
62
-
63
61
  protected target: typeof CarbonReact;
64
62
 
65
- // @link https://stackoverflow.com/questions/55029032/what-is-typescripts-thistype-used-for
66
63
  protected static _instance: ThisType<CarbonReact<any, any>>;
67
64
 
68
65
  static getInstance<T extends CarbonReact<any, any>>(): T {
@@ -75,7 +72,6 @@ abstract class CarbonReact<P = {}, S extends iCarbonReactState = iCarbonReactSta
75
72
  this._instance = instance;
76
73
  }
77
74
 
78
- // these are public but the class is abstract
79
75
  public updateRestfulObjectArrays = <ObjectType extends { [key: string]: any; } = {}>
80
76
  (rest: Omit<iUpdateRestfulObjectArrays<ObjectType, S, P>, "instance">) => updateRestfulObjectArrays<ObjectType, S, P>({
81
77
  instance: this,
@@ -90,70 +86,57 @@ abstract class CarbonReact<P = {}, S extends iCarbonReactState = iCarbonReactSta
90
86
 
91
87
  static lastLocation = window.location.pathname;
92
88
 
93
- // @link https://github.com/welldone-software/why-did-you-render
94
- // noinspection JSUnusedGlobalSymbols
95
89
  static whyDidYouRender = true;
96
90
 
97
-
98
91
  protected constructor(props: {
99
92
  children?: ReactNode | ReactNode[];
100
93
  shouldStatePersist?: boolean | undefined;
101
- websocket?: boolean | iCarbonWebSocketProps<P,S> | undefined;
94
+ websocket?: boolean | iCarbonWebSocketProps<P, S> | undefined;
95
+ instanceId?: string; // Optional instanceId from props
102
96
  } & P) {
103
-
104
97
  super(props);
105
98
 
106
- this.target = new.target;
99
+ const target = new.target as typeof CarbonReact;
100
+ const identifier = props.instanceId || target.name;
107
101
 
108
- console.log('CarbonORM TSX CONSTRUCTOR');
102
+ if (CarbonReact.activeInstances.has(identifier)) {
103
+ throw new Error(`Instance with ID ${identifier} already exists! CarbonReact extended classes can only be referenced once in DOM with the same identifier.`);
104
+ }
109
105
 
110
- // this is the magic that allows each class that's extends this to have a static instance - a singleton pattern
111
- // new.target is a meta-property introduced in ES6 that references the constructor that was directly invoked with the new keyword.
112
- Object.assign(new.target, {
113
- _instance: this
114
- })
106
+ CarbonReact.activeInstances.set(identifier, this);
115
107
 
116
- if (this.props.instanceId && CarbonReact.persistentStateMap.has(this.props.instanceId)) {
108
+ this.target = target;
109
+ console.log('CarbonORM TSX CONSTRUCTOR');
117
110
 
118
- this.state = CarbonReact.persistentStateMap.get(this.props.instanceId) as S & iCarbonReactState;
111
+ Object.assign(target, {
112
+ _instance: this
113
+ });
119
114
 
115
+ if (CarbonReact.persistentStateMap.has(identifier)) {
116
+ this.state = CarbonReact.persistentStateMap.get(identifier) as S & iCarbonReactState;
120
117
  } else {
121
-
122
- // This should only ever be done here, when the full state is being trashed.
123
- // todo - does this suck in context of multiple instances?
124
118
  clearCache({
125
119
  ignoreWarning: true
126
120
  });
127
-
128
121
  this.state = initialCarbonReactState as unknown as S & iCarbonReactState;
129
-
130
122
  }
131
123
 
132
- /** We can think of our app as having one state; this state.
133
- * Long-term, I'd like us to store this state to local storage and only load updates on reload...
134
- * Class based components are far easier to manage state in local storage and pass state down to children.
135
- * Children, if not faced with a local storage or other complexity should be a functional component. Functional
136
- * components' tend to be shorter syntactically and bonus points if it's stateless.
137
- **/
138
-
124
+ // Save the initial state to the persistent state map with the identifier
125
+ CarbonReact.persistentStateMap.set(identifier, this.state);
139
126
  }
140
127
 
141
-
142
128
  shouldComponentUpdate(
143
129
  nextProps: Readonly<P>,
144
130
  nextState: Readonly<S>,
145
131
  _nextContext: any): boolean {
146
132
 
147
- if (this.props.instanceId) {
148
- CarbonReact.persistentStateMap.set(this.props.instanceId, nextState);
149
- }
133
+ const identifier = this.props.instanceId || (this.constructor as typeof CarbonReact).name;
134
+ CarbonReact.persistentStateMap.set(identifier, nextState);
150
135
 
151
136
  changed(this.constructor.name + ' (C6Api)', 'props', this.props, nextProps);
152
-
153
137
  changed(this.constructor.name + ' (C6Api)', 'state', this.state, nextState);
154
138
 
155
- return true
156
-
139
+ return true;
157
140
  }
158
141
 
159
142
  componentDidUpdate(_prevProps: Readonly<P>, _prevState: Readonly<S>, _snapshot?: any) {
@@ -168,39 +151,39 @@ abstract class CarbonReact<P = {}, S extends iCarbonReactState = iCarbonReactSta
168
151
  }
169
152
 
170
153
  render(): ReactElement {
171
-
172
154
  console.log('CarbonORM TSX RENDER');
173
155
 
174
156
  const colorHex = '#' + Math.random().toString(16).slice(-6);
175
157
 
176
158
  console.log('%c color (' + colorHex + ')', 'color: ' + colorHex);
177
159
 
178
- const nest = <Nest position={'fixed'} backgroundColor={''} color={hexToRgb(colorHex)} count={100}/>;
160
+ const nest = <Nest position={'fixed'} backgroundColor={''} color={hexToRgb(colorHex)} count={100} />;
179
161
 
180
162
  if (this.state.backendThrowable.length > 0) {
181
-
182
163
  return <>
183
164
  {nest}
184
- <BackendThrowable instance={this}/>
165
+ <BackendThrowable instance={this} />
185
166
  </>;
186
-
187
167
  }
188
168
 
189
169
  const Context = this.context.Provider;
190
170
 
191
171
  return <>
192
- <GlobalHistory/>
172
+ <GlobalHistory />
193
173
  {this.props.websocket &&
194
- <CarbonWebSocket<P,S> {...(false !== this.props.websocket ? this.props.websocket : {})}
195
- instance={this}/>}
174
+ <CarbonWebSocket<P, S> {...(false !== this.props.websocket ? this.props.websocket : {})}
175
+ instance={this} />}
196
176
  <Context value={this.state}>
197
177
  {this.props.children}
198
178
  </Context>
199
- <ToastContainer/>
179
+ <ToastContainer />
200
180
  </>;
201
-
202
181
  }
203
182
 
183
+ componentWillUnmount() {
184
+ const identifier = this.props.instanceId || (this.constructor as typeof CarbonReact).name;
185
+ CarbonReact.activeInstances.delete(identifier);
186
+ }
204
187
  }
205
188
 
206
189
  export default CarbonReact;
@@ -18,10 +18,10 @@ export interface iAlertButtonOptions {
18
18
  color: "default" | "primary" | "secondary" | "inherit" | "danger" | "info" | "success" | "warning" | undefined,
19
19
  }
20
20
 
21
- export interface iAlert<P,S extends iCarbonReactState> {
21
+ export interface iAlert<P, S extends iCarbonReactState> {
22
22
  title: string,
23
23
  text: string,
24
- instance: CarbonReact<P,S>,
24
+ instance: CarbonReact<P, S>,
25
25
  component?: ReactNode,
26
26
  icon?: "warning" | "error" | "success" | "info" | "question" | null,
27
27
  buttons?: (iAlertButtonOptions)[] | undefined, //['No thanks!', 'Yes, Delete it'],
@@ -41,13 +41,11 @@ export function addAlert<P, S extends iCarbonReactState>(props: iAlert<P, S>) {
41
41
  }));
42
42
  }
43
43
 
44
- export default function Alert<P,S extends iCarbonReactState>({
45
- instance
46
- }: { instance: CarbonReact<P,S> }) {
44
+ export default function Alert<P, S extends iCarbonReactState>({instance}: { instance: CarbonReact<P, S> }) {
47
45
 
48
46
  const {alertsWaiting, backendThrowable} = instance.state
49
47
 
50
- let alert: iAlert<P,S> | undefined = undefined;
48
+ let alert: iAlert<P, S> | undefined = undefined;
51
49
 
52
50
  const alertWaiting = alertsWaiting.length + backendThrowable.length
53
51