plug-code 1.1.15 → 1.1.17
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 +14 -17
- package/README.md +3 -4
- package/package.json +1 -1
- package/src/core/plcAPI.tsx +73 -36
- package/src/core/plcPipeline.tsx +10 -4
- package/src/types/pipeline.ts +1 -1
- package/types/plug-code.d.ts +11 -6
package/LICENSE
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
Plug&Code License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026 AlaunS
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
5
|
+
All rights reserved.
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
Permission is hereby granted to use this software for personal and commercial projects.
|
|
8
|
+
You may modify the software for your own internal use.
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
You may NOT:
|
|
11
|
+
- Redistribute this software or any modified version of it.
|
|
12
|
+
- Sell, sublicense, publish, or share this software.
|
|
13
|
+
- Use this software as the basis for another framework, library, or competing product.
|
|
14
|
+
|
|
15
|
+
This software is provided "as is", without warranty of any kind, express or implied,
|
|
16
|
+
including but not limited to the warranties of merchantability, fitness for a
|
|
17
|
+
particular purpose and noninfringement. In no event shall the author be liable
|
|
18
|
+
for any claim, damages or other liability arising from the use of this software.
|
package/README.md
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
It empowers developers to build complex applications by **plugging in independent feature modules** without tightly coupling the codebase.
|
|
5
5
|
|
|
6
6
|
> **License**
|
|
7
|
-
>
|
|
8
|
-
>
|
|
7
|
+
> This project is licensed under the Plug&Code License.
|
|
8
|
+
> See the LICENSE file for details.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -67,8 +67,7 @@ import { PaginationFeature } from './features/PaginationFeature';
|
|
|
67
67
|
import { SalesFeature } from './features/SalesFeature';
|
|
68
68
|
|
|
69
69
|
export const { useSystemPlc, SystemPlcRoot } = createPlugAndCode((api) => {
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
// It creates root automatically | root = {}
|
|
72
71
|
PaginationFeature(api);
|
|
73
72
|
SalesFeature(api);
|
|
74
73
|
});
|
package/package.json
CHANGED
package/src/core/plcAPI.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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";
|
|
@@ -28,7 +28,6 @@ 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
|
|
@@ -122,57 +121,67 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
122
121
|
return () => lastValue
|
|
123
122
|
}
|
|
124
123
|
|
|
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: (data?: any) => React.ReactNode, dependencyKey?: string) {
|
|
124
|
+
register(slot: SlotKey, node: (props?: any) => React.ReactNode): void;
|
|
125
|
+
register<K extends string>(slot: SlotKey, node: (data: any, props?: any) => React.ReactNode, dependencyKey: K): void;
|
|
126
|
+
register(slot: SlotKey, node: (data?: any, props?: any) => React.ReactNode, dependencyKey?: string) {
|
|
128
127
|
if (dependencyKey) {
|
|
129
|
-
const ConnectedWrapper = () => {
|
|
130
|
-
const [
|
|
128
|
+
const ConnectedWrapper = (props: any) => {
|
|
129
|
+
const [storeData, setStoreData] = useState(() => this.substores.get(dependencyKey));
|
|
131
130
|
|
|
132
131
|
useEffect(() => {
|
|
133
132
|
const unsubscribe = this.store.subscribe(dependencyKey as any, () => {
|
|
134
|
-
|
|
133
|
+
setStoreData(this.substores.get(dependencyKey));
|
|
135
134
|
});
|
|
136
135
|
return unsubscribe;
|
|
137
136
|
}, []);
|
|
138
137
|
|
|
139
|
-
return node(
|
|
138
|
+
return <>{node(storeData, props)}</>;
|
|
140
139
|
};
|
|
141
140
|
|
|
142
141
|
this.store.batch(() => {
|
|
143
|
-
this.pipeline.register(slot as string,
|
|
142
|
+
this.pipeline.register(slot as string, ConnectedWrapper);
|
|
144
143
|
});
|
|
145
144
|
}
|
|
146
145
|
else {
|
|
147
146
|
this.store.batch(() => {
|
|
148
|
-
this.pipeline.register(slot as string, node
|
|
147
|
+
this.pipeline.register(slot as string, node);
|
|
149
148
|
});
|
|
150
149
|
}
|
|
151
150
|
}
|
|
152
151
|
|
|
153
152
|
scope<T = any>(key: string | "root"): {
|
|
154
153
|
get: () => T;
|
|
155
|
-
update: (updater: (draft: T) => void) => void;
|
|
156
|
-
connect:
|
|
157
|
-
|
|
154
|
+
update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => void;
|
|
155
|
+
connect: <P = {}, R = any>(
|
|
156
|
+
selector: (data: T, props: P) => R
|
|
157
|
+
) => (WrappedComponent: React.ComponentType<P & R>) => React.FC<P>;
|
|
158
|
+
|
|
159
|
+
render: (slotName: SlotKey) => React.FC;
|
|
158
160
|
receive: (context?: any) => any;
|
|
159
161
|
root: PlcAPI<S>;
|
|
160
162
|
} {
|
|
161
163
|
return {
|
|
162
164
|
get: (): T => this.getData(key),
|
|
163
165
|
|
|
164
|
-
update: (updater: (draft: T) => void) => {
|
|
165
|
-
this.update(key as any, updater);
|
|
166
|
+
update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => {
|
|
167
|
+
this.update(key as any, updater, slot, triggerKey);
|
|
166
168
|
},
|
|
167
169
|
|
|
168
|
-
connect:
|
|
169
|
-
|
|
170
|
+
connect: <P = {}, R = any>(
|
|
171
|
+
selector: (data: T, props: P) => R
|
|
172
|
+
) => {
|
|
173
|
+
return this.connect(key, selector);
|
|
170
174
|
},
|
|
171
175
|
|
|
172
176
|
render: (slotName: SlotKey) => {
|
|
173
|
-
|
|
174
|
-
return this.pipeline.render(slotName as string,
|
|
175
|
-
}
|
|
177
|
+
const ScopedSlotRenderer = ({ _scopeData }: { _scopeData: T }) => {
|
|
178
|
+
return <>{this.pipeline.render(slotName as string, _scopeData)}</>;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
return this.connect(
|
|
182
|
+
key,
|
|
183
|
+
(data: T) => ({ _scopeData: data })
|
|
184
|
+
)(ScopedSlotRenderer) as any;
|
|
176
185
|
},
|
|
177
186
|
|
|
178
187
|
receive: (context: any = {}) => {
|
|
@@ -184,25 +193,53 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
184
193
|
};
|
|
185
194
|
}
|
|
186
195
|
|
|
187
|
-
connect<
|
|
188
|
-
|
|
189
|
-
|
|
196
|
+
connect<State = any, OwnProps = {}, ResultProps = {}>(
|
|
197
|
+
key: string,
|
|
198
|
+
selector: (state: State, props: OwnProps) => ResultProps
|
|
199
|
+
): (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => React.FC<OwnProps> {
|
|
200
|
+
return (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => {
|
|
201
|
+
|
|
202
|
+
const ConnectedComponent = (props: OwnProps) => {
|
|
203
|
+
const propsRef = useRef(props);
|
|
204
|
+
propsRef.current = props;
|
|
190
205
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
206
|
+
const [slice, setSlice] = useState<ResultProps>(() => {
|
|
207
|
+
const data = this.substores.get(key);
|
|
208
|
+
return selector(data, props);
|
|
194
209
|
});
|
|
195
210
|
|
|
196
|
-
|
|
197
|
-
unsubscribe()
|
|
198
|
-
|
|
199
|
-
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
const unsubscribe = this.store.subscribe(key as any, () => {
|
|
213
|
+
const currentData = this.substores.get(key);
|
|
214
|
+
const newSlice = selector(currentData, propsRef.current);
|
|
215
|
+
|
|
216
|
+
setSlice(prev => {
|
|
217
|
+
if (prev === newSlice) return prev;
|
|
218
|
+
|
|
219
|
+
if (typeof prev === 'object' && prev !== null && typeof newSlice === 'object' && newSlice !== null) {
|
|
220
|
+
const keysA = Object.keys(prev) as Array<keyof ResultProps>;
|
|
221
|
+
const keysB = Object.keys(newSlice) as Array<keyof ResultProps>;
|
|
222
|
+
if (keysA.length === keysB.length && keysA.every(k => prev[k] === newSlice[k])) {
|
|
223
|
+
return prev;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return newSlice;
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
return unsubscribe;
|
|
231
|
+
}, []);
|
|
232
|
+
|
|
233
|
+
if (slice === undefined && this.substores.get(key) === undefined) return null;
|
|
234
|
+
return <WrappedComponent {...props} {...slice} />;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
|
238
|
+
ConnectedComponent.displayName = `Connect(${displayName})`;
|
|
200
239
|
|
|
201
|
-
|
|
202
|
-
return <>{renderer(data)}</>;
|
|
240
|
+
return ConnectedComponent;
|
|
203
241
|
};
|
|
204
242
|
}
|
|
205
|
-
|
|
206
243
|
wrap(slot: SlotKey, fn: (next: () => React.ReactNode) => () => React.ReactNode) {
|
|
207
244
|
this.store.batch(() => {
|
|
208
245
|
this.pipeline.wrap(slot as string, fn)
|
|
@@ -215,8 +252,8 @@ export class PlcAPI<S extends ObjectType> {
|
|
|
215
252
|
})
|
|
216
253
|
}
|
|
217
254
|
|
|
218
|
-
render(slot: SlotKey) {
|
|
219
|
-
return this.pipeline.render(slot as string)
|
|
255
|
+
render(slot: SlotKey, props?: any) {
|
|
256
|
+
return this.pipeline.render(slot as string, props)
|
|
220
257
|
}
|
|
221
258
|
|
|
222
259
|
invalidate(slot?: SlotKey) {
|
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
|
}
|
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).
|
|
@@ -56,8 +58,8 @@ export declare class PlcAPI<S extends ObjectType> {
|
|
|
56
58
|
override<K extends string>(key: K & "root", data: any, slot?: string): void
|
|
57
59
|
// --- Gestión de UI (Slots & Rendering) ---
|
|
58
60
|
|
|
59
|
-
register(slot: string, node: () => React.ReactNode): void;
|
|
60
|
-
register<K extends string>(slot: string, node: (data: any) => React.ReactNode, dependencyKey: K): void;
|
|
61
|
+
register(slot: string, node: (props?: any) => React.ReactNode): void;
|
|
62
|
+
register<K extends string>(slot: string, node: (data: any, props?: any) => React.ReactNode, dependencyKey: K): void;
|
|
61
63
|
|
|
62
64
|
/** Envuelve un slot existente (Decorador/Wrapper) */
|
|
63
65
|
wrap(slot: string, fn: (next: () => React.ReactNode) => () => React.ReactNode): void;
|
|
@@ -88,15 +90,18 @@ export declare class PlcAPI<S extends ObjectType> {
|
|
|
88
90
|
*/
|
|
89
91
|
scope<T = any>(key: string): {
|
|
90
92
|
get: () => T;
|
|
91
|
-
update: (updater: (draft: T) => void) => void;
|
|
92
|
-
connect:
|
|
93
|
-
|
|
93
|
+
update: (updater: (draft: T) => void, slot?: string, triggerKey?: string) => void;
|
|
94
|
+
connect: <P = {}, R = any>(
|
|
95
|
+
selector: (data: T, props: P) => R
|
|
96
|
+
) => (WrappedComponent: React.ComponentType<P & R>) => React.FC<P>;
|
|
97
|
+
|
|
98
|
+
render: (slotName: SlotKey) => React.FC;
|
|
94
99
|
receive: (context?: any) => any;
|
|
95
100
|
root: PlcAPI<S>;
|
|
96
101
|
};
|
|
97
102
|
|
|
98
103
|
/** Conecta un componente a una parte del estado (HOC) */
|
|
99
|
-
connect<
|
|
104
|
+
connect<State = any, OwnProps = {}, ResultProps = {}>(key: string, selector: (state: State, props: OwnProps) => ResultProps): (WrappedComponent: React.ComponentType<OwnProps & ResultProps>) => React.FC<OwnProps>;
|
|
100
105
|
|
|
101
106
|
// --- Pipeline de Datos (Transforms) ---
|
|
102
107
|
|