plug-code 1.2.0 → 2.0.1
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 +135 -94
- package/dist/core/helpers/core.d.ts +2 -0
- package/dist/core/helpers/core.js +32 -0
- package/dist/core/hooks/plcHooks.d.ts +15 -0
- package/dist/core/hooks/plcHooks.js +47 -0
- package/dist/core/plcAPI.d.ts +83 -0
- package/dist/core/plcAPI.js +472 -0
- package/dist/core/plcPipeline.d.ts +8 -0
- package/dist/core/plcPipeline.js +35 -0
- package/dist/core/plcScheduler.d.ts +7 -0
- package/dist/core/plcScheduler.js +22 -0
- package/dist/core/plcStore.d.ts +33 -0
- package/dist/core/plcStore.js +159 -0
- package/dist/core/ui/plcCore.d.ts +8 -0
- package/dist/core/ui/plcCore.js +40 -0
- package/dist/core/ui/plcErrorBoundary.d.ts +17 -0
- package/dist/core/ui/plcErrorBoundary.js +17 -0
- package/dist/core/ui/plcInspector.d.ts +5 -0
- package/dist/core/ui/plcInspector.js +27 -0
- package/dist/core/ui/plcLayout.d.ts +28 -0
- package/dist/core/ui/plcLayout.js +125 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/types/core/api.d.ts +7 -0
- package/dist/types/core/api.js +1 -0
- package/dist/types/core/general.d.ts +15 -0
- package/dist/types/core/general.js +1 -0
- package/dist/types/core/registry.d.ts +7 -0
- package/dist/types/core/registry.js +1 -0
- package/dist/types/core/ui.d.ts +7 -0
- package/dist/types/core/ui.js +1 -0
- package/dist/types/registry.d.ts +20 -0
- package/dist/types/registry.js +1 -0
- package/package.json +16 -22
- package/index.d.ts +0 -1
- package/src/contexts/pipeline.tsx +0 -4
- package/src/core/plcAPI.tsx +0 -393
- package/src/core/plcPipeline.tsx +0 -87
- package/src/core/plcStore.tsx +0 -94
- package/src/helpers/core.ts +0 -10
- package/src/plug-code.tsx +0 -64
- package/src/types/api.ts +0 -8
- package/src/types/features.ts +0 -7
- package/src/types/general.ts +0 -2
- package/src/types/pipeline.ts +0 -3
- package/src/types/registry.ts +0 -4
- package/src/types/store.ts +0 -2
- package/tsconfig.json +0 -15
- package/types/plug-code.d.ts +0 -164
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { produce } from "immer";
|
|
2
|
+
import { isEqual } from "./helpers/core";
|
|
3
|
+
export class PlcStore {
|
|
4
|
+
storeData = new Map();
|
|
5
|
+
substoresData = new Map();
|
|
6
|
+
subscriptions = new Map();
|
|
7
|
+
isBatching = false;
|
|
8
|
+
dirtyKeys = new Set();
|
|
9
|
+
activeDependencyTracker = null;
|
|
10
|
+
// ----------------------
|
|
11
|
+
// Root store
|
|
12
|
+
// ----------------------
|
|
13
|
+
createStore(key, initial) {
|
|
14
|
+
if (this.storeData.has(key))
|
|
15
|
+
return;
|
|
16
|
+
this.storeData.set(key, initial);
|
|
17
|
+
this.markDirty(key);
|
|
18
|
+
}
|
|
19
|
+
getStore(key) {
|
|
20
|
+
const value = this.storeData.get(key);
|
|
21
|
+
this.recordDependency(key, value);
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
setStore(key, updater) {
|
|
25
|
+
const prev = this.storeData.get(key);
|
|
26
|
+
const next = typeof updater === 'function'
|
|
27
|
+
? produce(prev, updater)
|
|
28
|
+
: updater;
|
|
29
|
+
if (isEqual(prev, next))
|
|
30
|
+
return;
|
|
31
|
+
this.storeData.set(key, next);
|
|
32
|
+
this.markDirty(key);
|
|
33
|
+
}
|
|
34
|
+
// ----------------------
|
|
35
|
+
// Substores
|
|
36
|
+
// ----------------------
|
|
37
|
+
createSubstore(substore, key, initial) {
|
|
38
|
+
if (!this.substoresData.has(substore))
|
|
39
|
+
this.substoresData.set(substore, new Map());
|
|
40
|
+
const fStore = this.substoresData.get(substore);
|
|
41
|
+
if (fStore.has(key))
|
|
42
|
+
return;
|
|
43
|
+
fStore.set(key, initial);
|
|
44
|
+
this.markDirty(`${substore}:${key}`);
|
|
45
|
+
}
|
|
46
|
+
getSubstore(substore, key) {
|
|
47
|
+
const fStore = this.substoresData.get(substore);
|
|
48
|
+
const value = fStore?.get(key);
|
|
49
|
+
this.recordDependency(`${substore}:${key}`, value);
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
setSubstore(substore, key, updater) {
|
|
53
|
+
if (!this.substoresData.has(substore))
|
|
54
|
+
this.substoresData.set(substore, new Map());
|
|
55
|
+
const fStore = this.substoresData.get(substore);
|
|
56
|
+
const prev = fStore.get(key);
|
|
57
|
+
const next = typeof updater === 'function'
|
|
58
|
+
? produce(prev, updater)
|
|
59
|
+
: updater;
|
|
60
|
+
if (isEqual(prev, next))
|
|
61
|
+
return;
|
|
62
|
+
fStore.set(key, next);
|
|
63
|
+
this.markDirty(`${substore}:${key}`);
|
|
64
|
+
}
|
|
65
|
+
// ----------------------
|
|
66
|
+
// Dependency tracking
|
|
67
|
+
// ----------------------
|
|
68
|
+
recordDependency(key, value) {
|
|
69
|
+
if (this.activeDependencyTracker)
|
|
70
|
+
this.activeDependencyTracker.set(key, value);
|
|
71
|
+
}
|
|
72
|
+
async runWithDependencyTracker(tracker, fn) {
|
|
73
|
+
const prev = this.activeDependencyTracker;
|
|
74
|
+
this.activeDependencyTracker = tracker;
|
|
75
|
+
try {
|
|
76
|
+
return await fn();
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
this.activeDependencyTracker = prev;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// ----------------------
|
|
83
|
+
// Subscriptions & batching
|
|
84
|
+
// ----------------------
|
|
85
|
+
batch(fn) {
|
|
86
|
+
const prev = this.isBatching;
|
|
87
|
+
this.isBatching = true;
|
|
88
|
+
try {
|
|
89
|
+
fn();
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
this.isBatching = prev;
|
|
93
|
+
this.flush();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
subscribe(key, listener) {
|
|
97
|
+
if (!this.subscriptions.has(key))
|
|
98
|
+
this.subscriptions.set(key, new Set());
|
|
99
|
+
this.subscriptions.get(key).add(listener);
|
|
100
|
+
return () => this.subscriptions.get(key).delete(listener);
|
|
101
|
+
}
|
|
102
|
+
markDirty(key) {
|
|
103
|
+
if (this.isBatching)
|
|
104
|
+
this.dirtyKeys.add(key);
|
|
105
|
+
else
|
|
106
|
+
this.notify(key);
|
|
107
|
+
}
|
|
108
|
+
flush() {
|
|
109
|
+
if (this.isBatching)
|
|
110
|
+
return;
|
|
111
|
+
for (const key of this.dirtyKeys)
|
|
112
|
+
this.notify(key);
|
|
113
|
+
this.dirtyKeys.clear();
|
|
114
|
+
}
|
|
115
|
+
notify(key) {
|
|
116
|
+
const listeners = this.subscriptions.get(key);
|
|
117
|
+
listeners?.forEach(fn => {
|
|
118
|
+
try {
|
|
119
|
+
fn();
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
console.error('[PlcStore] listener threw', err);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// ----------------------
|
|
128
|
+
// LRUCache
|
|
129
|
+
// ----------------------
|
|
130
|
+
export class LRUCache {
|
|
131
|
+
maxEntries;
|
|
132
|
+
map = new Map();
|
|
133
|
+
constructor(maxEntries = 100) {
|
|
134
|
+
this.maxEntries = maxEntries;
|
|
135
|
+
}
|
|
136
|
+
get(key) {
|
|
137
|
+
if (!this.map.has(key))
|
|
138
|
+
return undefined;
|
|
139
|
+
const val = this.map.get(key);
|
|
140
|
+
this.map.delete(key);
|
|
141
|
+
this.map.set(key, val);
|
|
142
|
+
return val;
|
|
143
|
+
}
|
|
144
|
+
set(key, value) {
|
|
145
|
+
if (this.map.has(key))
|
|
146
|
+
this.map.delete(key);
|
|
147
|
+
else if (this.map.size >= this.maxEntries) {
|
|
148
|
+
const oldest = this.map.keys().next().value;
|
|
149
|
+
this.map.delete(oldest);
|
|
150
|
+
}
|
|
151
|
+
this.map.set(key, value);
|
|
152
|
+
}
|
|
153
|
+
delete(key) {
|
|
154
|
+
this.map.delete(key);
|
|
155
|
+
}
|
|
156
|
+
clear() {
|
|
157
|
+
this.map.clear();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { JSX } from "react";
|
|
2
|
+
type virtualContainerType = {
|
|
3
|
+
content: JSX.Element[];
|
|
4
|
+
config: any;
|
|
5
|
+
contextData: any;
|
|
6
|
+
};
|
|
7
|
+
export declare function VirtualContainer({ content, config, contextData }: virtualContainerType): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { ScopeContext } from "./plcLayout";
|
|
4
|
+
export function VirtualContainer({ content, config, contextData }) {
|
|
5
|
+
const ref = React.useRef(null);
|
|
6
|
+
const rowRefs = React.useRef([]);
|
|
7
|
+
const pipeline = React.useContext(ScopeContext)?.pipeline;
|
|
8
|
+
React.useLayoutEffect(() => {
|
|
9
|
+
if (!ref.current)
|
|
10
|
+
return;
|
|
11
|
+
config.viewport = ref.current.clientHeight;
|
|
12
|
+
}, []);
|
|
13
|
+
const rowHeight = config.itemHeight ?? config.estimatedHeight;
|
|
14
|
+
const total = content.length;
|
|
15
|
+
const start = Math.max(0, Math.floor(config.scrollTop / rowHeight) - config.overscan);
|
|
16
|
+
const end = Math.min(total, Math.ceil((config.scrollTop + config.viewport) / rowHeight) + config.overscan);
|
|
17
|
+
rowRefs.current = [];
|
|
18
|
+
const visible = content.slice(start, end);
|
|
19
|
+
React.useLayoutEffect(() => {
|
|
20
|
+
if (!config.needsMeasure || rowRefs.current.length === 0)
|
|
21
|
+
return;
|
|
22
|
+
const avg = rowRefs.current.reduce((s, el) => s + el.offsetHeight, 0) /
|
|
23
|
+
rowRefs.current.length;
|
|
24
|
+
config.estimatedHeight = avg;
|
|
25
|
+
config.measured = true;
|
|
26
|
+
config.needsMeasure = false;
|
|
27
|
+
pipeline?.scheduleJob(() => { }, 100);
|
|
28
|
+
}, [visible]);
|
|
29
|
+
return (_jsx("div", { ref: ref, onScroll: e => {
|
|
30
|
+
const top = e.currentTarget.scrollTop;
|
|
31
|
+
config.scrollTop = top;
|
|
32
|
+
pipeline?.scheduleJob(() => { }, 100);
|
|
33
|
+
}, style: { overflow: "auto", height: "100%" }, children: _jsx("div", { style: { height: total * rowHeight }, children: _jsx("div", { style: { transform: `translateY(${start * rowHeight}px)` }, children: contextData ? (_jsx(ScopeContext.Provider, { value: contextData, children: visible.map((node, i) => (_jsx("div", { ref: el => {
|
|
34
|
+
if (el)
|
|
35
|
+
rowRefs.current.push(el);
|
|
36
|
+
}, children: node }, i))) })) : (visible.map((node, i) => (_jsx("div", { ref: el => {
|
|
37
|
+
if (el)
|
|
38
|
+
rowRefs.current.push(el);
|
|
39
|
+
}, children: node }, i)))) }) }) }));
|
|
40
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Component, ErrorInfo } from "react";
|
|
2
|
+
export declare class SlotErrorBoundary extends Component<{
|
|
3
|
+
id: string;
|
|
4
|
+
fallback?: React.ReactNode;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
}, {
|
|
7
|
+
hasError: boolean;
|
|
8
|
+
}> {
|
|
9
|
+
state: {
|
|
10
|
+
hasError: boolean;
|
|
11
|
+
};
|
|
12
|
+
static getDerivedStateFromError(_: Error): {
|
|
13
|
+
hasError: boolean;
|
|
14
|
+
};
|
|
15
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
16
|
+
render(): string | number | bigint | boolean | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Component } from "react";
|
|
3
|
+
export class SlotErrorBoundary extends Component {
|
|
4
|
+
state = { hasError: false };
|
|
5
|
+
static getDerivedStateFromError(_) {
|
|
6
|
+
return { hasError: true };
|
|
7
|
+
}
|
|
8
|
+
componentDidCatch(error, errorInfo) {
|
|
9
|
+
console.error(`[PlcLayout] Error in slot item '${this.props.id}':`, error, errorInfo);
|
|
10
|
+
}
|
|
11
|
+
render() {
|
|
12
|
+
if (this.state.hasError) {
|
|
13
|
+
return this.props.fallback || (_jsxs("div", { style: { padding: 4, color: 'red', fontSize: '0.8em', border: '1px dashed red' }, children: ["Error: ", this.props.id] }));
|
|
14
|
+
}
|
|
15
|
+
return this.props.children;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
const styles = {
|
|
4
|
+
container: { position: 'fixed', bottom: 0, right: 0, width: '300px', height: '400px', background: '#222', color: '#fff', overflow: 'auto', zIndex: 9999, padding: '10px', borderTopLeftRadius: '8px', fontSize: '12px', fontFamily: 'monospace', boxShadow: '-2px -2px 10px rgba(0,0,0,0.5)' },
|
|
5
|
+
header: { borderBottom: '1px solid #444', paddingBottom: '5px', marginBottom: '5px', fontWeight: 'bold', display: 'flex', justifyContent: 'space-between' },
|
|
6
|
+
section: { marginBottom: '10px' },
|
|
7
|
+
label: { color: '#888', marginBottom: '2px' },
|
|
8
|
+
item: { paddingLeft: '10px', color: '#4CAF50' },
|
|
9
|
+
value: { color: '#ce9178' }
|
|
10
|
+
};
|
|
11
|
+
export const PlcInspector = ({ api }) => {
|
|
12
|
+
const [, setTick] = useState(0);
|
|
13
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!isOpen)
|
|
16
|
+
return;
|
|
17
|
+
const interval = setInterval(() => setTick(t => t + 1), 1000);
|
|
18
|
+
return () => clearInterval(interval);
|
|
19
|
+
}, [isOpen]);
|
|
20
|
+
if (!isOpen) {
|
|
21
|
+
return (_jsx("button", { onClick: () => setIsOpen(true), style: { position: 'fixed', bottom: 10, right: 10, zIndex: 9999, background: '#333', color: '#fff', border: 'none', padding: '5px 10px', borderRadius: '4px', cursor: 'pointer' }, children: "\uD83D\uDEE0\uFE0F PLC" }));
|
|
22
|
+
}
|
|
23
|
+
const slotsMap = api.layout.slots;
|
|
24
|
+
const slots = Array.from(slotsMap.entries());
|
|
25
|
+
const storeKeys = Array.from(api.store.storeData.keys());
|
|
26
|
+
return (_jsxs("div", { style: styles.container, children: [_jsxs("div", { style: styles.header, children: [_jsx("span", { children: "PLC Framework Debugger" }), _jsx("button", { onClick: () => setIsOpen(false), style: { background: 'none', border: 'none', color: '#fff', cursor: 'pointer' }, children: "\u2715" })] }), _jsxs("div", { style: styles.section, children: [_jsxs("div", { style: styles.label, children: ["ACTIVE SLOTS (", slots.length, ")"] }), slots.map(([name, items]) => (_jsxs("div", { children: [_jsx("span", { style: { color: '#569CD6' }, children: name }), _jsxs("span", { style: { color: '#888' }, children: [" (", items.length, ")"] }), items.map((it) => (_jsxs("div", { style: styles.item, children: ["\u2514 ", it.id, " ", _jsxs("span", { style: { fontSize: '0.9em', color: '#888' }, children: ["(prio: ", it.priority, ")"] })] }, it.id)))] }, name)))] }), _jsxs("div", { style: styles.section, children: [_jsx("div", { style: styles.label, children: "ROOT STORE KEYS" }), storeKeys.map((k) => (_jsxs("div", { style: styles.item, children: ["\u2022 ", k] }, k)))] })] }));
|
|
27
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SlotWrapper } from "../../types/core/ui";
|
|
3
|
+
export declare const ScopeContext: React.Context<any>;
|
|
4
|
+
export type SlotItem = {
|
|
5
|
+
id: string;
|
|
6
|
+
priority: number;
|
|
7
|
+
keepAlive: boolean;
|
|
8
|
+
fn: (props?: any) => React.ReactNode;
|
|
9
|
+
};
|
|
10
|
+
export declare class PlcLayout {
|
|
11
|
+
private slots;
|
|
12
|
+
private virtualRegistry;
|
|
13
|
+
private wrappers;
|
|
14
|
+
private renderCache;
|
|
15
|
+
private cacheVersion;
|
|
16
|
+
markVirtual(slot: string, config?: {
|
|
17
|
+
itemHeight?: number;
|
|
18
|
+
overscan?: number;
|
|
19
|
+
}): void;
|
|
20
|
+
wrap(slot: string, wrapper: SlotWrapper, priority?: number): void;
|
|
21
|
+
register(slot: string, id: string, fn: (props?: any) => React.ReactNode, priority?: number, keepAlive?: boolean): void;
|
|
22
|
+
unregister(slot: string, id: string): void;
|
|
23
|
+
invalidate(slot?: string): void;
|
|
24
|
+
render(slot: string, props?: any): React.ReactNode;
|
|
25
|
+
private renderBase;
|
|
26
|
+
getSlotItems(slot: string): SlotItem[];
|
|
27
|
+
getPriority(slot: string, id: string): number | undefined;
|
|
28
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, Component } from "react";
|
|
3
|
+
import { VirtualContainer } from "./plcCore";
|
|
4
|
+
export const ScopeContext = createContext(undefined);
|
|
5
|
+
// ---------------------------------------------------------
|
|
6
|
+
// Error Boundary
|
|
7
|
+
// ---------------------------------------------------------
|
|
8
|
+
class SlotErrorBoundary extends Component {
|
|
9
|
+
state = { hasError: false };
|
|
10
|
+
static getDerivedStateFromError(_) {
|
|
11
|
+
return { hasError: true };
|
|
12
|
+
}
|
|
13
|
+
componentDidCatch(error, errorInfo) {
|
|
14
|
+
console.error(`[PlcLayout] Error in slot item '${this.props.id}':`, error, errorInfo);
|
|
15
|
+
}
|
|
16
|
+
render() {
|
|
17
|
+
if (this.state.hasError) {
|
|
18
|
+
return this.props.fallback || (_jsxs("div", { style: { padding: 4, color: 'red', fontSize: '0.8em', border: '1px dashed red' }, children: ["Error: ", this.props.id] }));
|
|
19
|
+
}
|
|
20
|
+
return this.props.children;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class PlcLayout {
|
|
24
|
+
slots = new Map();
|
|
25
|
+
virtualRegistry = new Map();
|
|
26
|
+
wrappers = new Map();
|
|
27
|
+
renderCache = new Map();
|
|
28
|
+
cacheVersion = new Map();
|
|
29
|
+
// ---------------------------------------------------------
|
|
30
|
+
// Virtualization
|
|
31
|
+
// ---------------------------------------------------------
|
|
32
|
+
markVirtual(slot, config) {
|
|
33
|
+
this.virtualRegistry.set(slot, {
|
|
34
|
+
itemHeight: config?.itemHeight ?? 32,
|
|
35
|
+
overscan: config?.overscan ?? 6,
|
|
36
|
+
measured: !!config?.itemHeight,
|
|
37
|
+
estimatedHeight: config?.itemHeight ?? 32,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// ---------------------------------------------------------
|
|
41
|
+
// Middleware (WRAP)
|
|
42
|
+
// ---------------------------------------------------------
|
|
43
|
+
wrap(slot, wrapper, priority = 0) {
|
|
44
|
+
if (!this.wrappers.has(slot)) {
|
|
45
|
+
this.wrappers.set(slot, []);
|
|
46
|
+
}
|
|
47
|
+
this.wrappers.get(slot).push(wrapper);
|
|
48
|
+
this.invalidate(slot);
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------
|
|
51
|
+
// Slot Manager
|
|
52
|
+
// ---------------------------------------------------------
|
|
53
|
+
register(slot, id, fn, priority = 0, keepAlive = false) {
|
|
54
|
+
if (!this.slots.has(slot)) {
|
|
55
|
+
this.slots.set(slot, []);
|
|
56
|
+
this.cacheVersion.set(slot, 0);
|
|
57
|
+
}
|
|
58
|
+
const list = this.slots.get(slot);
|
|
59
|
+
const existingIdx = list.findIndex(x => x.id === id);
|
|
60
|
+
const item = { id, priority, fn, keepAlive };
|
|
61
|
+
if (existingIdx >= 0)
|
|
62
|
+
list[existingIdx] = item;
|
|
63
|
+
else
|
|
64
|
+
list.push(item);
|
|
65
|
+
list.sort((a, b) => b.priority - a.priority);
|
|
66
|
+
this.invalidate(slot);
|
|
67
|
+
}
|
|
68
|
+
unregister(slot, id) {
|
|
69
|
+
const list = this.slots.get(slot);
|
|
70
|
+
if (!list)
|
|
71
|
+
return;
|
|
72
|
+
const idx = list.findIndex(x => x.id === id);
|
|
73
|
+
if (idx >= 0) {
|
|
74
|
+
list.splice(idx, 1);
|
|
75
|
+
this.invalidate(slot);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// ---------------------------------------------------------
|
|
79
|
+
// Render
|
|
80
|
+
// ---------------------------------------------------------
|
|
81
|
+
invalidate(slot) {
|
|
82
|
+
if (slot) {
|
|
83
|
+
this.renderCache.delete(slot);
|
|
84
|
+
this.cacheVersion.set(slot, (this.cacheVersion.get(slot) || 0) + 1);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
this.renderCache.clear();
|
|
88
|
+
for (const k of this.cacheVersion.keys()) {
|
|
89
|
+
this.cacheVersion.set(k, (this.cacheVersion.get(k) || 0) + 1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
render(slot, props) {
|
|
94
|
+
let content = this.renderBase(slot, props);
|
|
95
|
+
const slotWrappers = this.wrappers.get(slot);
|
|
96
|
+
if (slotWrappers && slotWrappers.length > 0) {
|
|
97
|
+
for (const wrapFn of slotWrappers) {
|
|
98
|
+
content = wrapFn(content, props);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return content;
|
|
102
|
+
}
|
|
103
|
+
renderBase(slot, props) {
|
|
104
|
+
const list = this.slots.get(slot) || [];
|
|
105
|
+
const virtualConfig = this.virtualRegistry.get(slot);
|
|
106
|
+
if (virtualConfig && list.length > 0) {
|
|
107
|
+
const protectedItems = list.map(item => ({
|
|
108
|
+
...item,
|
|
109
|
+
fn: (p) => (_jsx(SlotErrorBoundary, { id: item.id, children: item.fn(p) }))
|
|
110
|
+
}));
|
|
111
|
+
const content = protectedItems.map((item, i) => (_jsx(ScopeContext.Provider, { value: props, children: item.fn(props) }, item.id)));
|
|
112
|
+
return (_jsx(VirtualContainer, { content: content, config: virtualConfig, contextData: props }));
|
|
113
|
+
}
|
|
114
|
+
return list.map(item => {
|
|
115
|
+
const isActive = props?.activeId ? props.activeId === item.id : true;
|
|
116
|
+
if (!isActive && !item.keepAlive)
|
|
117
|
+
return null;
|
|
118
|
+
return (_jsx("div", { style: { display: isActive ? undefined : 'none' }, children: _jsx(SlotErrorBoundary, { id: item.id, children: _jsx(ScopeContext.Provider, { value: props, children: item.fn(props) }) }) }, item.id));
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
getSlotItems(slot) { return this.slots.get(slot) || []; }
|
|
122
|
+
getPriority(slot, id) {
|
|
123
|
+
return this.slots.get(slot)?.find(x => x.id === id)?.priority;
|
|
124
|
+
}
|
|
125
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./core/plcAPI";
|
|
2
|
+
export * from "./core/plcStore";
|
|
3
|
+
export * from "./core/plcPipeline";
|
|
4
|
+
export * from "./core/plcScheduler";
|
|
5
|
+
export * from "./core/helpers/core";
|
|
6
|
+
export * from "./types/core/general";
|
|
7
|
+
export * from "./types/core/api";
|
|
8
|
+
export * from "./types/core/registry";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./core/plcAPI";
|
|
2
|
+
export * from "./core/plcStore";
|
|
3
|
+
export * from "./core/plcPipeline";
|
|
4
|
+
export * from "./core/plcScheduler";
|
|
5
|
+
export * from "./core/helpers/core";
|
|
6
|
+
export * from "./types/core/general";
|
|
7
|
+
export * from "./types/core/api";
|
|
8
|
+
export * from "./types/core/registry";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type CommandFn<Payload = any, Result = any> = (payload?: Payload) => Promise<Result> | Result;
|
|
2
|
+
export type CommandWrapper<P = any, R = any> = (next: CommandFn<P, R>) => CommandFn<P, R>;
|
|
3
|
+
export type transformerType<T = any> = {
|
|
4
|
+
id: string;
|
|
5
|
+
priority: number;
|
|
6
|
+
fn: (data: T, context: any) => T;
|
|
7
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PlcAPI } from "../../core/plcAPI";
|
|
2
|
+
import { CommandFn } from "./api";
|
|
3
|
+
export type ObjectType = Record<string, any>;
|
|
4
|
+
export type ModuleManifest = {
|
|
5
|
+
name: string;
|
|
6
|
+
state?: Record<string, any>;
|
|
7
|
+
commands?: Record<string, CommandFn>;
|
|
8
|
+
slots?: Record<string, {
|
|
9
|
+
id: string;
|
|
10
|
+
component: (props?: any) => React.ReactNode;
|
|
11
|
+
priority?: number;
|
|
12
|
+
keepAlive?: boolean;
|
|
13
|
+
}[]>;
|
|
14
|
+
onLoad?: (api: PlcAPI) => void;
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface RootStoreRegistry {
|
|
2
|
+
}
|
|
3
|
+
export interface CommandRegistry {
|
|
4
|
+
}
|
|
5
|
+
export interface SlotRegistry {
|
|
6
|
+
}
|
|
7
|
+
export interface FeatureRegistry {
|
|
8
|
+
}
|
|
9
|
+
export type StoreKey = keyof RootStoreRegistry extends never ? string : keyof RootStoreRegistry;
|
|
10
|
+
export type StoreValue<K extends StoreKey> = K extends keyof RootStoreRegistry ? RootStoreRegistry[K] : any;
|
|
11
|
+
export type CommandKey = keyof CommandRegistry extends never ? string : keyof CommandRegistry;
|
|
12
|
+
export type SlotKey = keyof SlotRegistry extends never ? string : keyof SlotRegistry;
|
|
13
|
+
export type FeatureKey = keyof FeatureRegistry extends never ? string : keyof FeatureRegistry;
|
|
14
|
+
export type CmdPayload<K extends CommandKey> = K extends keyof CommandRegistry ? (CommandRegistry[K] extends {
|
|
15
|
+
payload: infer P;
|
|
16
|
+
} ? P : void) : any;
|
|
17
|
+
export type CmdResult<K extends CommandKey> = K extends keyof CommandRegistry ? (CommandRegistry[K] extends {
|
|
18
|
+
result: infer R;
|
|
19
|
+
} ? R : void) : any;
|
|
20
|
+
export type SlotProps<K extends SlotKey> = K extends keyof SlotRegistry ? SlotRegistry[K] : any;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,37 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plug-code",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "
|
|
6
|
-
"types": "
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
"require": "./src/plug-code.tsx"
|
|
11
|
-
}
|
|
12
|
-
},
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
13
10
|
"scripts": {
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/AlaunS/plug-code.git"
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"watch": "tsc --watch",
|
|
13
|
+
"test": "jest"
|
|
20
14
|
},
|
|
21
15
|
"keywords": [],
|
|
22
16
|
"author": "",
|
|
23
17
|
"license": "ISC",
|
|
24
18
|
"type": "commonjs",
|
|
25
|
-
"
|
|
26
|
-
"
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/jest": "^30.0.0",
|
|
21
|
+
"@types/node": "^25.0.3",
|
|
22
|
+
"@types/react": "^19.2.7",
|
|
23
|
+
"jest": "^30.2.0",
|
|
24
|
+
"ts-jest": "^29.4.6",
|
|
25
|
+
"typescript": "^5.9.3"
|
|
27
26
|
},
|
|
28
|
-
"homepage": "https://github.com/AlaunS/plug-code#readme",
|
|
29
27
|
"dependencies": {
|
|
30
28
|
"immer": "^11.1.3",
|
|
31
29
|
"react": "^19.2.3"
|
|
32
|
-
},
|
|
33
|
-
"devDependencies": {
|
|
34
|
-
"@types/react": "^19.2.7",
|
|
35
|
-
"typescript": "^5.4.2"
|
|
36
30
|
}
|
|
37
31
|
}
|
package/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./types/plug-code";
|