plug-code 1.1.16 → 1.2.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/LICENSE +1 -1
- package/package.json +1 -1
- package/src/core/plcAPI.tsx +87 -37
- package/src/core/plcPipeline.tsx +10 -4
- package/src/helpers/core.ts +10 -0
- package/src/types/pipeline.ts +1 -1
- package/types/plug-code.d.ts +13 -7
package/LICENSE
CHANGED
package/package.json
CHANGED
package/src/core/plcAPI.tsx
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { produce } from "immer";
|
|
2
2
|
import type { ObjectType } from "../types/general";
|
|
3
|
-
import { useEffect, useState } from "react";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { PlcPipeline } from "./plcPipeline";
|
|
5
5
|
import type { PlcStore } from "./plcStore";
|
|
6
6
|
import type { CommandFn, transformerType } from "../types/api";
|
|
7
7
|
|
|
8
8
|
import type { ChannelRegistry, CommandRegistry, SlotRegistry } from "../types/registry";
|
|
9
|
+
import { isEqual } from "../helpers/core";
|
|
9
10
|
|
|
10
11
|
type ChannelKey = keyof ChannelRegistry | (string & {});
|
|
11
12
|
type ChannelData<K> = K extends keyof ChannelRegistry ? ChannelRegistry[K] : any;
|
|
@@ -20,7 +21,6 @@ type CommandResult<K> = K extends keyof CommandRegistry
|
|
|
20
21
|
|
|
21
22
|
type SlotKey = keyof SlotRegistry | (string & {});
|
|
22
23
|
|
|
23
|
-
|
|
24
24
|
export class PlcAPI<S extends ObjectType> {
|
|
25
25
|
private store: PlcStore<S>
|
|
26
26
|
private pipeline: PlcPipeline<S>
|
|
@@ -28,13 +28,24 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
28
28
|
|
|
29
29
|
private transformers = new Map<string, transformerType[]>()
|
|
30
30
|
private commands = new Map<string, CommandFn>();
|
|
31
|
-
private installedFeatures = new Set<string>();
|
|
32
31
|
|
|
33
32
|
constructor(store: PlcStore<S>) {
|
|
34
33
|
this.store = store
|
|
35
34
|
this.pipeline = new PlcPipeline(store)
|
|
36
35
|
}
|
|
37
36
|
|
|
37
|
+
redraw(key: string): void {
|
|
38
|
+
const currentData = this.substores.get(key);
|
|
39
|
+
if (currentData === undefined) return;
|
|
40
|
+
|
|
41
|
+
const dataRef = typeof currentData === 'object' && currentData !== null
|
|
42
|
+
? (Array.isArray(currentData) ? [...currentData] : { ...currentData })
|
|
43
|
+
: currentData;
|
|
44
|
+
|
|
45
|
+
this.substores.set(key, dataRef);
|
|
46
|
+
this.store.set(key as any, dataRef);
|
|
47
|
+
}
|
|
48
|
+
|
|
38
49
|
watch<T>(
|
|
39
50
|
storeKey: string,
|
|
40
51
|
selector: (data: any) => T,
|
|
@@ -122,57 +133,67 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
122
133
|
return () => lastValue
|
|
123
134
|
}
|
|
124
135
|
|
|
125
|
-
register(slot: SlotKey, node: () => React.ReactNode): void;
|
|
126
|
-
register<K extends string>(slot: SlotKey, node: (data: any) => React.ReactNode, dependencyKey: K): void;
|
|
127
|
-
register(slot: SlotKey, node: (
|
|
136
|
+
register(slot: SlotKey, node: (props?: any) => React.ReactNode): void;
|
|
137
|
+
register<K extends string>(slot: SlotKey, node: (data: any, props?: any) => React.ReactNode, dependencyKey: K): void;
|
|
138
|
+
register(slot: SlotKey, node: (arg1?: any, arg2?: any) => React.ReactNode, dependencyKey?: string) {
|
|
128
139
|
if (dependencyKey) {
|
|
129
|
-
const ConnectedWrapper = () => {
|
|
130
|
-
const [
|
|
140
|
+
const ConnectedWrapper = (props: any) => {
|
|
141
|
+
const [storeData, setStoreData] = useState(() => this.substores.get(dependencyKey));
|
|
131
142
|
|
|
132
143
|
useEffect(() => {
|
|
133
144
|
const unsubscribe = this.store.subscribe(dependencyKey as any, () => {
|
|
134
|
-
|
|
145
|
+
setStoreData(this.substores.get(dependencyKey));
|
|
135
146
|
});
|
|
136
147
|
return unsubscribe;
|
|
137
148
|
}, []);
|
|
138
149
|
|
|
139
|
-
return node(
|
|
150
|
+
return <>{node(storeData, props)}</>;
|
|
140
151
|
};
|
|
141
152
|
|
|
142
153
|
this.store.batch(() => {
|
|
143
|
-
this.pipeline.register(slot as string, () => <ConnectedWrapper />);
|
|
154
|
+
this.pipeline.register(slot as string, (p: any) => <ConnectedWrapper {...p} />);
|
|
144
155
|
});
|
|
145
156
|
}
|
|
146
157
|
else {
|
|
147
158
|
this.store.batch(() => {
|
|
148
|
-
this.pipeline.register(slot as string, node
|
|
159
|
+
this.pipeline.register(slot as string, node);
|
|
149
160
|
});
|
|
150
161
|
}
|
|
151
162
|
}
|
|
152
163
|
|
|
153
164
|
scope<T = any>(key: string | "root"): {
|
|
154
165
|
get: () => T;
|
|
155
|
-
update: (updater: (draft: T) => void) => void;
|
|
156
|
-
connect:
|
|
157
|
-
|
|
166
|
+
update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => void;
|
|
167
|
+
connect: <P = {}, R = any>(
|
|
168
|
+
selector: (data: T, props: P) => R
|
|
169
|
+
) => (WrappedComponent: React.ComponentType<P & R>) => React.FC<P>;
|
|
170
|
+
|
|
171
|
+
render: (slotName: SlotKey) => React.FC;
|
|
158
172
|
receive: (context?: any) => any;
|
|
159
173
|
root: PlcAPI<S>;
|
|
160
174
|
} {
|
|
161
175
|
return {
|
|
162
176
|
get: (): T => this.getData(key),
|
|
163
177
|
|
|
164
|
-
update: (updater: (draft: T) => void) => {
|
|
165
|
-
this.update(key as any, updater);
|
|
178
|
+
update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => {
|
|
179
|
+
this.update(key as any, updater, slot, triggerKey);
|
|
166
180
|
},
|
|
167
181
|
|
|
168
|
-
connect:
|
|
169
|
-
|
|
182
|
+
connect: <P = {}, R = any>(
|
|
183
|
+
selector: (data: T, props: P) => R
|
|
184
|
+
) => {
|
|
185
|
+
return this.connect(key, selector);
|
|
170
186
|
},
|
|
171
187
|
|
|
172
188
|
render: (slotName: SlotKey) => {
|
|
173
|
-
|
|
174
|
-
return this.pipeline.render(slotName as string,
|
|
175
|
-
}
|
|
189
|
+
const ScopedSlotRenderer = ({ _scopeData }: { _scopeData: T }) => {
|
|
190
|
+
return <>{this.pipeline.render(slotName as string, _scopeData)}</>;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
return this.connect(
|
|
194
|
+
key,
|
|
195
|
+
(data: T) => ({ _scopeData: data })
|
|
196
|
+
)(ScopedSlotRenderer) as any;
|
|
176
197
|
},
|
|
177
198
|
|
|
178
199
|
receive: (context: any = {}) => {
|
|
@@ -184,22 +205,52 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
184
205
|
};
|
|
185
206
|
}
|
|
186
207
|
|
|
187
|
-
connect<
|
|
188
|
-
|
|
189
|
-
|
|
208
|
+
connect<State = any, OwnProps = {}, ResultProps = {}>(
|
|
209
|
+
key: string,
|
|
210
|
+
selector: (state: State, props: OwnProps) => ResultProps
|
|
211
|
+
): (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => React.FC<OwnProps> {
|
|
212
|
+
return (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => {
|
|
213
|
+
|
|
214
|
+
const ConnectedComponent: React.FC<OwnProps> = (props) => {
|
|
215
|
+
const propsRef = useRef(props);
|
|
216
|
+
propsRef.current = props;
|
|
190
217
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
218
|
+
const [slice, setSlice] = useState<ResultProps>(() => {
|
|
219
|
+
const data = this.substores.get(key);
|
|
220
|
+
return selector(data, props);
|
|
194
221
|
});
|
|
195
222
|
|
|
196
|
-
|
|
197
|
-
unsubscribe()
|
|
198
|
-
|
|
199
|
-
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
const unsubscribe = this.store.subscribe(key as any, () => {
|
|
225
|
+
const currentData = this.substores.get(key);
|
|
226
|
+
const newSlice = selector(currentData, propsRef.current);
|
|
227
|
+
setSlice(prev => {
|
|
228
|
+
if (isEqual(prev, newSlice)) return prev;
|
|
229
|
+
return newSlice;
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
return unsubscribe;
|
|
233
|
+
}, []);
|
|
234
|
+
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
const currentData = this.substores.get(key);
|
|
237
|
+
const newSlice = selector(currentData, props);
|
|
238
|
+
|
|
239
|
+
setSlice(prev => {
|
|
240
|
+
if (isEqual(prev, newSlice)) return prev;
|
|
241
|
+
return newSlice;
|
|
242
|
+
});
|
|
243
|
+
}, [props]);
|
|
200
244
|
|
|
201
|
-
|
|
202
|
-
|
|
245
|
+
if (slice === undefined && this.substores.get(key) === undefined) return null;
|
|
246
|
+
|
|
247
|
+
return <WrappedComponent {...props} {...slice} />;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
|
251
|
+
ConnectedComponent.displayName = `Connect(${displayName})`;
|
|
252
|
+
|
|
253
|
+
return ConnectedComponent;
|
|
203
254
|
};
|
|
204
255
|
}
|
|
205
256
|
|
|
@@ -215,8 +266,8 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
215
266
|
})
|
|
216
267
|
}
|
|
217
268
|
|
|
218
|
-
render(slot: SlotKey) {
|
|
219
|
-
return this.pipeline.render(slot as string)
|
|
269
|
+
render(slot: SlotKey, props?: any) {
|
|
270
|
+
return this.pipeline.render(slot as string, props)
|
|
220
271
|
}
|
|
221
272
|
|
|
222
273
|
invalidate(slot?: SlotKey) {
|
|
@@ -330,7 +381,6 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
330
381
|
}
|
|
331
382
|
|
|
332
383
|
if (triggerKey && triggerKey !== key) {
|
|
333
|
-
|
|
334
384
|
const triggerData = this.substores.get(triggerKey);
|
|
335
385
|
|
|
336
386
|
if (triggerData) {
|
package/src/core/plcPipeline.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useContext } from "react";
|
|
2
2
|
import { ScopeContext } from "../contexts/pipeline";
|
|
3
3
|
import type { ObjectType } from "../types/general";
|
|
4
4
|
import type { ScheduledSlot, Slot } from "../types/pipeline";
|
|
@@ -73,9 +73,15 @@ export class PlcPipeline<S extends ObjectType> {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
private regenerateCache(slot: string) {
|
|
76
|
-
const nodes = this.slots.get(slot)?.map((fn, i) =>
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
const nodes = this.slots.get(slot)?.map((fn, i) => {
|
|
77
|
+
const SlotBridge = () => {
|
|
78
|
+
const dynamicProps = useContext(ScopeContext);
|
|
79
|
+
return <React.Fragment>{fn(dynamicProps)}</React.Fragment>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return <SlotBridge key={i} />;
|
|
83
|
+
}) || [];
|
|
84
|
+
|
|
79
85
|
this.cache.set(slot, nodes);
|
|
80
86
|
}
|
|
81
87
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const isEqual = (a: any, b: any) => {
|
|
2
|
+
if (a === b) return true;
|
|
3
|
+
if (typeof a === 'object' && a !== null && typeof b === 'object' && b !== null) {
|
|
4
|
+
const keysA = Object.keys(a);
|
|
5
|
+
const keysB = Object.keys(b);
|
|
6
|
+
if (keysA.length !== keysB.length) return false;
|
|
7
|
+
return keysA.every(k => a[k] === b[k]);
|
|
8
|
+
}
|
|
9
|
+
return false;
|
|
10
|
+
}
|
package/src/types/pipeline.ts
CHANGED
package/types/plug-code.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ import * as React from 'react';
|
|
|
7
7
|
export declare type ObjectType = Record<string, any>;
|
|
8
8
|
|
|
9
9
|
export type Slot = () => React.ReactNode;
|
|
10
|
+
interface SlotRegistry {}
|
|
11
|
+
type SlotKey = keyof SlotRegistry | (string & {});
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Función para ejecutar lógica de negocio (Acciones).
|
|
@@ -55,9 +57,10 @@ export declare class PlcAPI<S extends ObjectType> {
|
|
|
55
57
|
watch<T>(storeKey: string, selector: (data: any) => T, callback: (newValue: T, oldValue: T) => void): () => void
|
|
56
58
|
override<K extends string>(key: K & "root", data: any, slot?: string): void
|
|
57
59
|
// --- Gestión de UI (Slots & Rendering) ---
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
register
|
|
60
|
+
|
|
61
|
+
redraw(key: string): void
|
|
62
|
+
register(slot: string, node: (props?: any) => React.ReactNode): void;
|
|
63
|
+
register<K extends string>(slot: string, node: (data: any, props?: any) => React.ReactNode, dependencyKey: K): void;
|
|
61
64
|
|
|
62
65
|
/** Envuelve un slot existente (Decorador/Wrapper) */
|
|
63
66
|
wrap(slot: string, fn: (next: () => React.ReactNode) => () => React.ReactNode): void;
|
|
@@ -88,15 +91,18 @@ export declare class PlcAPI<S extends ObjectType> {
|
|
|
88
91
|
*/
|
|
89
92
|
scope<T = any>(key: string): {
|
|
90
93
|
get: () => T;
|
|
91
|
-
update: (updater: (draft: T) => void) => void;
|
|
92
|
-
connect:
|
|
93
|
-
|
|
94
|
+
update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => void;
|
|
95
|
+
connect: <P = {}, R = any>(
|
|
96
|
+
selector: (data: T, props: P) => R
|
|
97
|
+
) => (WrappedComponent: React.ComponentType<P & R>) => React.FC<P>;
|
|
98
|
+
|
|
99
|
+
render: (slotName: SlotKey) => React.FC;
|
|
94
100
|
receive: (context?: any) => any;
|
|
95
101
|
root: PlcAPI<S>;
|
|
96
102
|
};
|
|
97
103
|
|
|
98
104
|
/** Conecta un componente a una parte del estado (HOC) */
|
|
99
|
-
connect<
|
|
105
|
+
connect<State = any, OwnProps = {}, ResultProps = {}>(key: string, selector: (state: State, props: OwnProps) => ResultProps): (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => React.FC<OwnProps>;
|
|
100
106
|
|
|
101
107
|
// --- Pipeline de Datos (Transforms) ---
|
|
102
108
|
|