@djvlc/runtime-host-react 1.1.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/dist/index.cjs +485 -0
- package/dist/index.d.cts +221 -0
- package/dist/index.d.ts +221 -0
- package/dist/index.js +447 -0
- package/package.json +54 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
DJVProvider: () => DJVProvider,
|
|
24
|
+
DJVRenderer: () => DJVRenderer,
|
|
25
|
+
RuntimeContext: () => RuntimeContext,
|
|
26
|
+
RuntimeProvider: () => RuntimeProvider,
|
|
27
|
+
useAction: () => useAction,
|
|
28
|
+
useDJVRuntime: () => useDJVRuntime,
|
|
29
|
+
useData: () => useData,
|
|
30
|
+
useHostApi: () => useHostApi,
|
|
31
|
+
useQuery: () => useQuery,
|
|
32
|
+
useReactRuntime: () => useReactRuntime,
|
|
33
|
+
useRuntimeContext: () => useRuntimeContext,
|
|
34
|
+
useRuntimeEvent: () => useRuntimeEvent,
|
|
35
|
+
useRuntimeState: () => useRuntimeState,
|
|
36
|
+
useRuntimeStateWritable: () => useRuntimeStateWritable
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(index_exports);
|
|
39
|
+
|
|
40
|
+
// src/react-runtime.ts
|
|
41
|
+
var import_react = require("react");
|
|
42
|
+
var import_runtime_core = require("@djvlc/runtime-core");
|
|
43
|
+
function useReactRuntime(options) {
|
|
44
|
+
const [runtime, setRuntime] = (0, import_react.useState)(null);
|
|
45
|
+
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
46
|
+
const [phase, setPhase] = (0, import_react.useState)("idle");
|
|
47
|
+
const [page, setPage] = (0, import_react.useState)(null);
|
|
48
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
49
|
+
const [hostApi, setHostApi] = (0, import_react.useState)(null);
|
|
50
|
+
const runtimeRef = (0, import_react.useRef)(null);
|
|
51
|
+
const init = (0, import_react.useCallback)(async () => {
|
|
52
|
+
const container = options.containerRef?.current;
|
|
53
|
+
if (!container) {
|
|
54
|
+
throw new Error("Container element not found");
|
|
55
|
+
}
|
|
56
|
+
const runtimeInstance = (0, import_runtime_core.createRuntime)({
|
|
57
|
+
...options,
|
|
58
|
+
container,
|
|
59
|
+
onError: (err) => {
|
|
60
|
+
setError(err);
|
|
61
|
+
options.onError?.(err);
|
|
62
|
+
},
|
|
63
|
+
onEvent: (event) => {
|
|
64
|
+
options.onEvent?.(event);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
runtimeRef.current = runtimeInstance;
|
|
68
|
+
setRuntime(runtimeInstance);
|
|
69
|
+
runtimeInstance.onStateChange((state) => {
|
|
70
|
+
setPhase(state.phase);
|
|
71
|
+
setLoading(state.phase !== "ready" && state.phase !== "error");
|
|
72
|
+
if (state.page) {
|
|
73
|
+
setPage(state.page);
|
|
74
|
+
}
|
|
75
|
+
if (state.error) {
|
|
76
|
+
setError(state.error);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
await runtimeInstance.init();
|
|
80
|
+
setHostApi(runtimeInstance.getHostApi());
|
|
81
|
+
}, [options]);
|
|
82
|
+
const load = (0, import_react.useCallback)(async () => {
|
|
83
|
+
if (!runtimeRef.current) {
|
|
84
|
+
throw new Error("Runtime not initialized");
|
|
85
|
+
}
|
|
86
|
+
const result = await runtimeRef.current.load();
|
|
87
|
+
setPage(result);
|
|
88
|
+
setHostApi(runtimeRef.current.getHostApi());
|
|
89
|
+
return result;
|
|
90
|
+
}, []);
|
|
91
|
+
const render = (0, import_react.useCallback)(async () => {
|
|
92
|
+
if (!runtimeRef.current) {
|
|
93
|
+
throw new Error("Runtime not initialized");
|
|
94
|
+
}
|
|
95
|
+
await runtimeRef.current.render();
|
|
96
|
+
setLoading(false);
|
|
97
|
+
}, []);
|
|
98
|
+
const destroy = (0, import_react.useCallback)(() => {
|
|
99
|
+
runtimeRef.current?.destroy();
|
|
100
|
+
runtimeRef.current = null;
|
|
101
|
+
setRuntime(null);
|
|
102
|
+
setHostApi(null);
|
|
103
|
+
setPage(null);
|
|
104
|
+
setError(null);
|
|
105
|
+
setPhase("idle");
|
|
106
|
+
setLoading(true);
|
|
107
|
+
}, []);
|
|
108
|
+
const setVariable = (0, import_react.useCallback)((key, value) => {
|
|
109
|
+
runtimeRef.current?.setVariable(key, value);
|
|
110
|
+
}, []);
|
|
111
|
+
const refreshData = (0, import_react.useCallback)(async (queryId) => {
|
|
112
|
+
await runtimeRef.current?.refreshData(queryId);
|
|
113
|
+
}, []);
|
|
114
|
+
return {
|
|
115
|
+
runtime,
|
|
116
|
+
loading,
|
|
117
|
+
phase,
|
|
118
|
+
page,
|
|
119
|
+
error,
|
|
120
|
+
hostApi,
|
|
121
|
+
init,
|
|
122
|
+
load,
|
|
123
|
+
render,
|
|
124
|
+
destroy,
|
|
125
|
+
setVariable,
|
|
126
|
+
refreshData
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/context.tsx
|
|
131
|
+
var import_react2 = require("react");
|
|
132
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
133
|
+
var defaultState = {
|
|
134
|
+
phase: "idle",
|
|
135
|
+
page: null,
|
|
136
|
+
variables: {},
|
|
137
|
+
queries: {},
|
|
138
|
+
components: /* @__PURE__ */ new Map(),
|
|
139
|
+
error: null,
|
|
140
|
+
destroyed: false
|
|
141
|
+
};
|
|
142
|
+
var RuntimeContext = (0, import_react2.createContext)({
|
|
143
|
+
runtime: null,
|
|
144
|
+
state: defaultState,
|
|
145
|
+
hostApi: null
|
|
146
|
+
});
|
|
147
|
+
function RuntimeProvider({
|
|
148
|
+
runtime,
|
|
149
|
+
state,
|
|
150
|
+
hostApi,
|
|
151
|
+
children
|
|
152
|
+
}) {
|
|
153
|
+
const value = {
|
|
154
|
+
runtime,
|
|
155
|
+
state,
|
|
156
|
+
hostApi
|
|
157
|
+
};
|
|
158
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RuntimeContext.Provider, { value, children });
|
|
159
|
+
}
|
|
160
|
+
function useRuntimeContext() {
|
|
161
|
+
const context = (0, import_react2.useContext)(RuntimeContext);
|
|
162
|
+
return context;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/components/DJVRenderer.tsx
|
|
166
|
+
var import_react3 = require("react");
|
|
167
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
168
|
+
function DJVRenderer({
|
|
169
|
+
pageUid,
|
|
170
|
+
apiBaseUrl,
|
|
171
|
+
cdnBaseUrl,
|
|
172
|
+
channel = "prod",
|
|
173
|
+
userId,
|
|
174
|
+
deviceId,
|
|
175
|
+
authToken,
|
|
176
|
+
previewToken,
|
|
177
|
+
debug = false,
|
|
178
|
+
enableSRI = true,
|
|
179
|
+
onLoad,
|
|
180
|
+
onError,
|
|
181
|
+
onReady,
|
|
182
|
+
loadingComponent,
|
|
183
|
+
errorComponent,
|
|
184
|
+
children,
|
|
185
|
+
className,
|
|
186
|
+
style
|
|
187
|
+
}) {
|
|
188
|
+
const containerRef = (0, import_react3.useRef)(null);
|
|
189
|
+
const [state, setState] = (0, import_react3.useState)({
|
|
190
|
+
phase: "idle",
|
|
191
|
+
page: null,
|
|
192
|
+
variables: {},
|
|
193
|
+
queries: {},
|
|
194
|
+
components: /* @__PURE__ */ new Map(),
|
|
195
|
+
error: null,
|
|
196
|
+
destroyed: false
|
|
197
|
+
});
|
|
198
|
+
const options = {
|
|
199
|
+
pageUid,
|
|
200
|
+
apiBaseUrl,
|
|
201
|
+
cdnBaseUrl,
|
|
202
|
+
channel,
|
|
203
|
+
userId,
|
|
204
|
+
deviceId,
|
|
205
|
+
authToken,
|
|
206
|
+
previewToken,
|
|
207
|
+
debug,
|
|
208
|
+
enableSRI,
|
|
209
|
+
containerRef,
|
|
210
|
+
onError: (error2) => {
|
|
211
|
+
onError?.(error2);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
const {
|
|
215
|
+
runtime,
|
|
216
|
+
loading,
|
|
217
|
+
phase: _phase,
|
|
218
|
+
page: _page,
|
|
219
|
+
error,
|
|
220
|
+
hostApi,
|
|
221
|
+
init,
|
|
222
|
+
load,
|
|
223
|
+
render,
|
|
224
|
+
destroy
|
|
225
|
+
} = useReactRuntime(options);
|
|
226
|
+
void _phase;
|
|
227
|
+
void _page;
|
|
228
|
+
const initAndLoad = (0, import_react3.useCallback)(async () => {
|
|
229
|
+
if (!containerRef.current) return;
|
|
230
|
+
try {
|
|
231
|
+
await init();
|
|
232
|
+
const pageData = await load();
|
|
233
|
+
onLoad?.(pageData);
|
|
234
|
+
await render();
|
|
235
|
+
onReady?.();
|
|
236
|
+
if (runtime) {
|
|
237
|
+
runtime.onStateChange((newState) => {
|
|
238
|
+
setState(newState);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
} catch (err) {
|
|
242
|
+
}
|
|
243
|
+
}, [init, load, render, runtime, onLoad, onReady]);
|
|
244
|
+
(0, import_react3.useEffect)(() => {
|
|
245
|
+
initAndLoad();
|
|
246
|
+
return () => {
|
|
247
|
+
destroy();
|
|
248
|
+
};
|
|
249
|
+
}, [pageUid]);
|
|
250
|
+
const renderLoading = () => {
|
|
251
|
+
if (!loading) return null;
|
|
252
|
+
return loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "djvlc-loading", children: [
|
|
253
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "djvlc-loading-spinner" }),
|
|
254
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u52A0\u8F7D\u4E2D..." })
|
|
255
|
+
] });
|
|
256
|
+
};
|
|
257
|
+
const renderError = () => {
|
|
258
|
+
if (!error) return null;
|
|
259
|
+
if (typeof errorComponent === "function") {
|
|
260
|
+
return errorComponent(error);
|
|
261
|
+
}
|
|
262
|
+
return errorComponent || /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "djvlc-error", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
263
|
+
"\u52A0\u8F7D\u5931\u8D25\uFF1A",
|
|
264
|
+
error.message
|
|
265
|
+
] }) });
|
|
266
|
+
};
|
|
267
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RuntimeProvider, { runtime, state, hostApi, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
268
|
+
"div",
|
|
269
|
+
{
|
|
270
|
+
ref: containerRef,
|
|
271
|
+
className: `djvlc-renderer ${className || ""}`,
|
|
272
|
+
style,
|
|
273
|
+
children: [
|
|
274
|
+
renderLoading(),
|
|
275
|
+
renderError(),
|
|
276
|
+
children
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
) });
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/components/DJVProvider.tsx
|
|
283
|
+
var import_react4 = require("react");
|
|
284
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
285
|
+
function DJVProvider({
|
|
286
|
+
runtime,
|
|
287
|
+
hostApi,
|
|
288
|
+
children
|
|
289
|
+
}) {
|
|
290
|
+
const [state, setState] = (0, import_react4.useState)(
|
|
291
|
+
runtime?.getState() || {
|
|
292
|
+
phase: "idle",
|
|
293
|
+
page: null,
|
|
294
|
+
variables: {},
|
|
295
|
+
queries: {},
|
|
296
|
+
components: /* @__PURE__ */ new Map(),
|
|
297
|
+
error: null,
|
|
298
|
+
destroyed: false
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
(0, import_react4.useEffect)(() => {
|
|
302
|
+
if (!runtime) return;
|
|
303
|
+
const unsubscribe = runtime.onStateChange((newState) => {
|
|
304
|
+
setState(newState);
|
|
305
|
+
});
|
|
306
|
+
return () => {
|
|
307
|
+
unsubscribe();
|
|
308
|
+
};
|
|
309
|
+
}, [runtime]);
|
|
310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RuntimeProvider, { runtime, state, hostApi, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "djvlc-provider", children }) });
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/hooks/useDJVRuntime.ts
|
|
314
|
+
var import_react5 = require("react");
|
|
315
|
+
function useDJVRuntime() {
|
|
316
|
+
const context = useRuntimeContext();
|
|
317
|
+
const loading = (0, import_react5.useMemo)(() => {
|
|
318
|
+
const phase = context.state.phase;
|
|
319
|
+
return phase !== "ready" && phase !== "error";
|
|
320
|
+
}, [context.state.phase]);
|
|
321
|
+
return {
|
|
322
|
+
runtime: context.runtime,
|
|
323
|
+
state: context.state,
|
|
324
|
+
loading,
|
|
325
|
+
phase: context.state.phase,
|
|
326
|
+
error: context.state.error,
|
|
327
|
+
page: context.state.page
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function useHostApi() {
|
|
331
|
+
const context = useRuntimeContext();
|
|
332
|
+
if (!context.hostApi) {
|
|
333
|
+
throw new Error("HostAPI not available. Make sure runtime is initialized.");
|
|
334
|
+
}
|
|
335
|
+
return context.hostApi;
|
|
336
|
+
}
|
|
337
|
+
function useRuntimeState(key) {
|
|
338
|
+
const context = useRuntimeContext();
|
|
339
|
+
return context.state.variables[key];
|
|
340
|
+
}
|
|
341
|
+
function useRuntimeStateWritable(key, defaultValue) {
|
|
342
|
+
const context = useRuntimeContext();
|
|
343
|
+
const value = context.state.variables[key] ?? defaultValue;
|
|
344
|
+
const setValue = (0, import_react5.useCallback)(
|
|
345
|
+
(newValue) => {
|
|
346
|
+
context.runtime?.setVariable(key, newValue);
|
|
347
|
+
},
|
|
348
|
+
[context.runtime, key]
|
|
349
|
+
);
|
|
350
|
+
return [value, setValue];
|
|
351
|
+
}
|
|
352
|
+
function useQuery(queryId) {
|
|
353
|
+
const context = useRuntimeContext();
|
|
354
|
+
const [loading, setLoading] = (0, import_react5.useState)(false);
|
|
355
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
356
|
+
const data = context.state.queries[queryId];
|
|
357
|
+
const refetch = (0, import_react5.useCallback)(async () => {
|
|
358
|
+
setLoading(true);
|
|
359
|
+
setError(null);
|
|
360
|
+
try {
|
|
361
|
+
await context.runtime?.refreshData(queryId);
|
|
362
|
+
} catch (e) {
|
|
363
|
+
setError(e);
|
|
364
|
+
} finally {
|
|
365
|
+
setLoading(false);
|
|
366
|
+
}
|
|
367
|
+
}, [context.runtime, queryId]);
|
|
368
|
+
return {
|
|
369
|
+
data,
|
|
370
|
+
loading,
|
|
371
|
+
error,
|
|
372
|
+
refetch
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
function useAction(actionType) {
|
|
376
|
+
const context = useRuntimeContext();
|
|
377
|
+
const [loading, setLoading] = (0, import_react5.useState)(false);
|
|
378
|
+
const [result, setResult] = (0, import_react5.useState)();
|
|
379
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
380
|
+
const execute = (0, import_react5.useCallback)(
|
|
381
|
+
async (params) => {
|
|
382
|
+
const hostApi = context.hostApi;
|
|
383
|
+
if (!hostApi) {
|
|
384
|
+
throw new Error("HostAPI not available");
|
|
385
|
+
}
|
|
386
|
+
setLoading(true);
|
|
387
|
+
setError(null);
|
|
388
|
+
try {
|
|
389
|
+
const response = await hostApi.executeAction(
|
|
390
|
+
actionType,
|
|
391
|
+
params
|
|
392
|
+
);
|
|
393
|
+
if (response.success) {
|
|
394
|
+
setResult(response.data);
|
|
395
|
+
return response.data;
|
|
396
|
+
} else {
|
|
397
|
+
throw new Error(response.message || "Action failed");
|
|
398
|
+
}
|
|
399
|
+
} catch (e) {
|
|
400
|
+
setError(e);
|
|
401
|
+
throw e;
|
|
402
|
+
} finally {
|
|
403
|
+
setLoading(false);
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
[context.hostApi, actionType]
|
|
407
|
+
);
|
|
408
|
+
return {
|
|
409
|
+
execute,
|
|
410
|
+
loading,
|
|
411
|
+
result,
|
|
412
|
+
error
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function useData(queryId, params, options) {
|
|
416
|
+
const context = useRuntimeContext();
|
|
417
|
+
const [data, setData] = (0, import_react5.useState)();
|
|
418
|
+
const [loading, setLoading] = (0, import_react5.useState)(false);
|
|
419
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
420
|
+
const refetch = (0, import_react5.useCallback)(
|
|
421
|
+
async (newParams) => {
|
|
422
|
+
const hostApi = context.hostApi;
|
|
423
|
+
if (!hostApi) {
|
|
424
|
+
throw new Error("HostAPI not available");
|
|
425
|
+
}
|
|
426
|
+
setLoading(true);
|
|
427
|
+
setError(null);
|
|
428
|
+
try {
|
|
429
|
+
const response = await hostApi.requestData(
|
|
430
|
+
queryId,
|
|
431
|
+
newParams || params
|
|
432
|
+
);
|
|
433
|
+
if (response.success) {
|
|
434
|
+
setData(response.data);
|
|
435
|
+
} else {
|
|
436
|
+
throw new Error(response.message || "Query failed");
|
|
437
|
+
}
|
|
438
|
+
} catch (e) {
|
|
439
|
+
setError(e);
|
|
440
|
+
} finally {
|
|
441
|
+
setLoading(false);
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
[context.hostApi, queryId, params]
|
|
445
|
+
);
|
|
446
|
+
(0, import_react5.useEffect)(() => {
|
|
447
|
+
if (options?.immediate !== false && context.hostApi) {
|
|
448
|
+
refetch();
|
|
449
|
+
}
|
|
450
|
+
}, [context.hostApi]);
|
|
451
|
+
return {
|
|
452
|
+
data,
|
|
453
|
+
loading,
|
|
454
|
+
error,
|
|
455
|
+
refetch
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function useRuntimeEvent(eventType, handler) {
|
|
459
|
+
const context = useRuntimeContext();
|
|
460
|
+
(0, import_react5.useEffect)(() => {
|
|
461
|
+
const unsubscribe = context.runtime?.on(eventType, (event) => {
|
|
462
|
+
handler(event.data);
|
|
463
|
+
});
|
|
464
|
+
return () => {
|
|
465
|
+
unsubscribe?.();
|
|
466
|
+
};
|
|
467
|
+
}, [context.runtime, eventType, handler]);
|
|
468
|
+
}
|
|
469
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
470
|
+
0 && (module.exports = {
|
|
471
|
+
DJVProvider,
|
|
472
|
+
DJVRenderer,
|
|
473
|
+
RuntimeContext,
|
|
474
|
+
RuntimeProvider,
|
|
475
|
+
useAction,
|
|
476
|
+
useDJVRuntime,
|
|
477
|
+
useData,
|
|
478
|
+
useHostApi,
|
|
479
|
+
useQuery,
|
|
480
|
+
useReactRuntime,
|
|
481
|
+
useRuntimeContext,
|
|
482
|
+
useRuntimeEvent,
|
|
483
|
+
useRuntimeState,
|
|
484
|
+
useRuntimeStateWritable
|
|
485
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { RuntimeOptions, DjvlcRuntime, RuntimePhase, PageResolveResult, RuntimeError, HostAPI, RuntimeState } from '@djvlc/runtime-core';
|
|
2
|
+
export { HostAPI, PageResolveResult, RuntimeError, RuntimeOptions, RuntimePhase, RuntimeState } from '@djvlc/runtime-core';
|
|
3
|
+
import * as react from 'react';
|
|
4
|
+
import react__default, { ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React 运行时封装
|
|
8
|
+
* 将核心运行时封装为 React 友好的 API
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* React 运行时选项
|
|
13
|
+
*/
|
|
14
|
+
interface ReactRuntimeOptions extends Omit<RuntimeOptions, 'container'> {
|
|
15
|
+
/** 容器元素引用 */
|
|
16
|
+
containerRef?: React.RefObject<HTMLElement>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* React 运行时返回值
|
|
20
|
+
*/
|
|
21
|
+
interface ReactRuntimeReturn {
|
|
22
|
+
/** 运行时实例 */
|
|
23
|
+
runtime: DjvlcRuntime | null;
|
|
24
|
+
/** 加载状态 */
|
|
25
|
+
loading: boolean;
|
|
26
|
+
/** 当前阶段 */
|
|
27
|
+
phase: RuntimePhase;
|
|
28
|
+
/** 页面数据 */
|
|
29
|
+
page: PageResolveResult | null;
|
|
30
|
+
/** 错误信息 */
|
|
31
|
+
error: RuntimeError | null;
|
|
32
|
+
/** Host API */
|
|
33
|
+
hostApi: HostAPI | null;
|
|
34
|
+
/** 初始化 */
|
|
35
|
+
init: () => Promise<void>;
|
|
36
|
+
/** 加载页面 */
|
|
37
|
+
load: () => Promise<PageResolveResult>;
|
|
38
|
+
/** 渲染页面 */
|
|
39
|
+
render: () => Promise<void>;
|
|
40
|
+
/** 销毁 */
|
|
41
|
+
destroy: () => void;
|
|
42
|
+
/** 设置变量 */
|
|
43
|
+
setVariable: (key: string, value: unknown) => void;
|
|
44
|
+
/** 刷新数据 */
|
|
45
|
+
refreshData: (queryId: string) => Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 使用 React 运行时 Hook
|
|
49
|
+
*/
|
|
50
|
+
declare function useReactRuntime(options: ReactRuntimeOptions): ReactRuntimeReturn;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 运行时上下文值
|
|
54
|
+
*/
|
|
55
|
+
interface RuntimeContextValue {
|
|
56
|
+
runtime: DjvlcRuntime | null;
|
|
57
|
+
state: RuntimeState;
|
|
58
|
+
hostApi: HostAPI | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 运行时上下文
|
|
62
|
+
*/
|
|
63
|
+
declare const RuntimeContext: react.Context<RuntimeContextValue>;
|
|
64
|
+
/**
|
|
65
|
+
* Runtime Provider Props
|
|
66
|
+
*/
|
|
67
|
+
interface RuntimeProviderProps {
|
|
68
|
+
/** 运行时实例 */
|
|
69
|
+
runtime: DjvlcRuntime | null;
|
|
70
|
+
/** 运行时状态 */
|
|
71
|
+
state: RuntimeState;
|
|
72
|
+
/** Host API */
|
|
73
|
+
hostApi: HostAPI | null;
|
|
74
|
+
/** 子元素 */
|
|
75
|
+
children: ReactNode;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Runtime Provider 组件
|
|
79
|
+
*/
|
|
80
|
+
declare function RuntimeProvider({ runtime, state, hostApi, children, }: RuntimeProviderProps): JSX.Element;
|
|
81
|
+
/**
|
|
82
|
+
* 使用运行时上下文
|
|
83
|
+
*/
|
|
84
|
+
declare function useRuntimeContext(): RuntimeContextValue;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* DJV 渲染器组件
|
|
88
|
+
* React 组件,用于渲染低代码页面
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* DJV 渲染器 Props
|
|
93
|
+
*/
|
|
94
|
+
interface DJVRendererProps {
|
|
95
|
+
/** 页面 UID */
|
|
96
|
+
pageUid: string;
|
|
97
|
+
/** API 基础 URL */
|
|
98
|
+
apiBaseUrl: string;
|
|
99
|
+
/** CDN 基础 URL */
|
|
100
|
+
cdnBaseUrl: string;
|
|
101
|
+
/** 渠道 */
|
|
102
|
+
channel?: 'preview' | 'prod' | 'gray';
|
|
103
|
+
/** 用户 ID */
|
|
104
|
+
userId?: string;
|
|
105
|
+
/** 设备 ID */
|
|
106
|
+
deviceId?: string;
|
|
107
|
+
/** 认证 Token */
|
|
108
|
+
authToken?: string;
|
|
109
|
+
/** 预览 Token */
|
|
110
|
+
previewToken?: string;
|
|
111
|
+
/** 调试模式 */
|
|
112
|
+
debug?: boolean;
|
|
113
|
+
/** 是否启用 SRI */
|
|
114
|
+
enableSRI?: boolean;
|
|
115
|
+
/** 加载完成回调 */
|
|
116
|
+
onLoad?: (page: PageResolveResult) => void;
|
|
117
|
+
/** 错误回调 */
|
|
118
|
+
onError?: (error: RuntimeError) => void;
|
|
119
|
+
/** 就绪回调 */
|
|
120
|
+
onReady?: () => void;
|
|
121
|
+
/** 加载中显示 */
|
|
122
|
+
loadingComponent?: ReactNode;
|
|
123
|
+
/** 错误显示 */
|
|
124
|
+
errorComponent?: ReactNode | ((error: RuntimeError) => ReactNode);
|
|
125
|
+
/** 子元素 */
|
|
126
|
+
children?: ReactNode;
|
|
127
|
+
/** 容器类名 */
|
|
128
|
+
className?: string;
|
|
129
|
+
/** 容器样式 */
|
|
130
|
+
style?: react__default.CSSProperties;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* DJV 渲染器组件
|
|
134
|
+
*/
|
|
135
|
+
declare function DJVRenderer({ pageUid, apiBaseUrl, cdnBaseUrl, channel, userId, deviceId, authToken, previewToken, debug, enableSRI, onLoad, onError, onReady, loadingComponent, errorComponent, children, className, style, }: DJVRendererProps): JSX.Element;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* DJV Provider 组件
|
|
139
|
+
* 提供运行时上下文给子组件
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* DJV Provider Props
|
|
144
|
+
*/
|
|
145
|
+
interface DJVProviderProps {
|
|
146
|
+
/** 运行时实例 */
|
|
147
|
+
runtime: DjvlcRuntime | null;
|
|
148
|
+
/** Host API */
|
|
149
|
+
hostApi: HostAPI | null;
|
|
150
|
+
/** 子元素 */
|
|
151
|
+
children: ReactNode;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* DJV Provider 组件
|
|
155
|
+
* 用于手动控制运行时时使用
|
|
156
|
+
*/
|
|
157
|
+
declare function DJVProvider({ runtime, hostApi, children, }: DJVProviderProps): JSX.Element;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* React Hooks
|
|
161
|
+
* 运行时相关的 React Hooks
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 使用 DJV 运行时
|
|
166
|
+
*/
|
|
167
|
+
declare function useDJVRuntime(): {
|
|
168
|
+
runtime: RuntimeContextValue['runtime'];
|
|
169
|
+
state: RuntimeContextValue['state'];
|
|
170
|
+
loading: boolean;
|
|
171
|
+
phase: RuntimePhase;
|
|
172
|
+
error: RuntimeError | null;
|
|
173
|
+
page: PageResolveResult | null;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* 使用 Host API
|
|
177
|
+
*/
|
|
178
|
+
declare function useHostApi(): HostAPI;
|
|
179
|
+
/**
|
|
180
|
+
* 使用运行时状态
|
|
181
|
+
*/
|
|
182
|
+
declare function useRuntimeState<T = unknown>(key: string): T | undefined;
|
|
183
|
+
/**
|
|
184
|
+
* 使用可写状态
|
|
185
|
+
*/
|
|
186
|
+
declare function useRuntimeStateWritable<T = unknown>(key: string, defaultValue?: T): [T, (value: T) => void];
|
|
187
|
+
/**
|
|
188
|
+
* 使用查询结果
|
|
189
|
+
*/
|
|
190
|
+
declare function useQuery<T = unknown>(queryId: string): {
|
|
191
|
+
data: T | undefined;
|
|
192
|
+
loading: boolean;
|
|
193
|
+
error: Error | null;
|
|
194
|
+
refetch: () => Promise<void>;
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* 使用动作
|
|
198
|
+
*/
|
|
199
|
+
declare function useAction<T = unknown, P = Record<string, unknown>>(actionType: string): {
|
|
200
|
+
execute: (params?: P) => Promise<T | undefined>;
|
|
201
|
+
loading: boolean;
|
|
202
|
+
result: T | undefined;
|
|
203
|
+
error: Error | null;
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* 使用数据请求
|
|
207
|
+
*/
|
|
208
|
+
declare function useData<T = unknown, P = Record<string, unknown>>(queryId: string, params?: P, options?: {
|
|
209
|
+
immediate?: boolean;
|
|
210
|
+
}): {
|
|
211
|
+
data: T | undefined;
|
|
212
|
+
loading: boolean;
|
|
213
|
+
error: Error | null;
|
|
214
|
+
refetch: (newParams?: P) => Promise<void>;
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* 使用事件监听
|
|
218
|
+
*/
|
|
219
|
+
declare function useRuntimeEvent<T = unknown>(eventType: string, handler: (data: T) => void): void;
|
|
220
|
+
|
|
221
|
+
export { DJVProvider, type DJVProviderProps, DJVRenderer, type DJVRendererProps, type ReactRuntimeOptions, type ReactRuntimeReturn, RuntimeContext, type RuntimeContextValue, RuntimeProvider, type RuntimeProviderProps, useAction, useDJVRuntime, useData, useHostApi, useQuery, useReactRuntime, useRuntimeContext, useRuntimeEvent, useRuntimeState, useRuntimeStateWritable };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { RuntimeOptions, DjvlcRuntime, RuntimePhase, PageResolveResult, RuntimeError, HostAPI, RuntimeState } from '@djvlc/runtime-core';
|
|
2
|
+
export { HostAPI, PageResolveResult, RuntimeError, RuntimeOptions, RuntimePhase, RuntimeState } from '@djvlc/runtime-core';
|
|
3
|
+
import * as react from 'react';
|
|
4
|
+
import react__default, { ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* React 运行时封装
|
|
8
|
+
* 将核心运行时封装为 React 友好的 API
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* React 运行时选项
|
|
13
|
+
*/
|
|
14
|
+
interface ReactRuntimeOptions extends Omit<RuntimeOptions, 'container'> {
|
|
15
|
+
/** 容器元素引用 */
|
|
16
|
+
containerRef?: React.RefObject<HTMLElement>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* React 运行时返回值
|
|
20
|
+
*/
|
|
21
|
+
interface ReactRuntimeReturn {
|
|
22
|
+
/** 运行时实例 */
|
|
23
|
+
runtime: DjvlcRuntime | null;
|
|
24
|
+
/** 加载状态 */
|
|
25
|
+
loading: boolean;
|
|
26
|
+
/** 当前阶段 */
|
|
27
|
+
phase: RuntimePhase;
|
|
28
|
+
/** 页面数据 */
|
|
29
|
+
page: PageResolveResult | null;
|
|
30
|
+
/** 错误信息 */
|
|
31
|
+
error: RuntimeError | null;
|
|
32
|
+
/** Host API */
|
|
33
|
+
hostApi: HostAPI | null;
|
|
34
|
+
/** 初始化 */
|
|
35
|
+
init: () => Promise<void>;
|
|
36
|
+
/** 加载页面 */
|
|
37
|
+
load: () => Promise<PageResolveResult>;
|
|
38
|
+
/** 渲染页面 */
|
|
39
|
+
render: () => Promise<void>;
|
|
40
|
+
/** 销毁 */
|
|
41
|
+
destroy: () => void;
|
|
42
|
+
/** 设置变量 */
|
|
43
|
+
setVariable: (key: string, value: unknown) => void;
|
|
44
|
+
/** 刷新数据 */
|
|
45
|
+
refreshData: (queryId: string) => Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 使用 React 运行时 Hook
|
|
49
|
+
*/
|
|
50
|
+
declare function useReactRuntime(options: ReactRuntimeOptions): ReactRuntimeReturn;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 运行时上下文值
|
|
54
|
+
*/
|
|
55
|
+
interface RuntimeContextValue {
|
|
56
|
+
runtime: DjvlcRuntime | null;
|
|
57
|
+
state: RuntimeState;
|
|
58
|
+
hostApi: HostAPI | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 运行时上下文
|
|
62
|
+
*/
|
|
63
|
+
declare const RuntimeContext: react.Context<RuntimeContextValue>;
|
|
64
|
+
/**
|
|
65
|
+
* Runtime Provider Props
|
|
66
|
+
*/
|
|
67
|
+
interface RuntimeProviderProps {
|
|
68
|
+
/** 运行时实例 */
|
|
69
|
+
runtime: DjvlcRuntime | null;
|
|
70
|
+
/** 运行时状态 */
|
|
71
|
+
state: RuntimeState;
|
|
72
|
+
/** Host API */
|
|
73
|
+
hostApi: HostAPI | null;
|
|
74
|
+
/** 子元素 */
|
|
75
|
+
children: ReactNode;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Runtime Provider 组件
|
|
79
|
+
*/
|
|
80
|
+
declare function RuntimeProvider({ runtime, state, hostApi, children, }: RuntimeProviderProps): JSX.Element;
|
|
81
|
+
/**
|
|
82
|
+
* 使用运行时上下文
|
|
83
|
+
*/
|
|
84
|
+
declare function useRuntimeContext(): RuntimeContextValue;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* DJV 渲染器组件
|
|
88
|
+
* React 组件,用于渲染低代码页面
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* DJV 渲染器 Props
|
|
93
|
+
*/
|
|
94
|
+
interface DJVRendererProps {
|
|
95
|
+
/** 页面 UID */
|
|
96
|
+
pageUid: string;
|
|
97
|
+
/** API 基础 URL */
|
|
98
|
+
apiBaseUrl: string;
|
|
99
|
+
/** CDN 基础 URL */
|
|
100
|
+
cdnBaseUrl: string;
|
|
101
|
+
/** 渠道 */
|
|
102
|
+
channel?: 'preview' | 'prod' | 'gray';
|
|
103
|
+
/** 用户 ID */
|
|
104
|
+
userId?: string;
|
|
105
|
+
/** 设备 ID */
|
|
106
|
+
deviceId?: string;
|
|
107
|
+
/** 认证 Token */
|
|
108
|
+
authToken?: string;
|
|
109
|
+
/** 预览 Token */
|
|
110
|
+
previewToken?: string;
|
|
111
|
+
/** 调试模式 */
|
|
112
|
+
debug?: boolean;
|
|
113
|
+
/** 是否启用 SRI */
|
|
114
|
+
enableSRI?: boolean;
|
|
115
|
+
/** 加载完成回调 */
|
|
116
|
+
onLoad?: (page: PageResolveResult) => void;
|
|
117
|
+
/** 错误回调 */
|
|
118
|
+
onError?: (error: RuntimeError) => void;
|
|
119
|
+
/** 就绪回调 */
|
|
120
|
+
onReady?: () => void;
|
|
121
|
+
/** 加载中显示 */
|
|
122
|
+
loadingComponent?: ReactNode;
|
|
123
|
+
/** 错误显示 */
|
|
124
|
+
errorComponent?: ReactNode | ((error: RuntimeError) => ReactNode);
|
|
125
|
+
/** 子元素 */
|
|
126
|
+
children?: ReactNode;
|
|
127
|
+
/** 容器类名 */
|
|
128
|
+
className?: string;
|
|
129
|
+
/** 容器样式 */
|
|
130
|
+
style?: react__default.CSSProperties;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* DJV 渲染器组件
|
|
134
|
+
*/
|
|
135
|
+
declare function DJVRenderer({ pageUid, apiBaseUrl, cdnBaseUrl, channel, userId, deviceId, authToken, previewToken, debug, enableSRI, onLoad, onError, onReady, loadingComponent, errorComponent, children, className, style, }: DJVRendererProps): JSX.Element;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* DJV Provider 组件
|
|
139
|
+
* 提供运行时上下文给子组件
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* DJV Provider Props
|
|
144
|
+
*/
|
|
145
|
+
interface DJVProviderProps {
|
|
146
|
+
/** 运行时实例 */
|
|
147
|
+
runtime: DjvlcRuntime | null;
|
|
148
|
+
/** Host API */
|
|
149
|
+
hostApi: HostAPI | null;
|
|
150
|
+
/** 子元素 */
|
|
151
|
+
children: ReactNode;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* DJV Provider 组件
|
|
155
|
+
* 用于手动控制运行时时使用
|
|
156
|
+
*/
|
|
157
|
+
declare function DJVProvider({ runtime, hostApi, children, }: DJVProviderProps): JSX.Element;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* React Hooks
|
|
161
|
+
* 运行时相关的 React Hooks
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 使用 DJV 运行时
|
|
166
|
+
*/
|
|
167
|
+
declare function useDJVRuntime(): {
|
|
168
|
+
runtime: RuntimeContextValue['runtime'];
|
|
169
|
+
state: RuntimeContextValue['state'];
|
|
170
|
+
loading: boolean;
|
|
171
|
+
phase: RuntimePhase;
|
|
172
|
+
error: RuntimeError | null;
|
|
173
|
+
page: PageResolveResult | null;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* 使用 Host API
|
|
177
|
+
*/
|
|
178
|
+
declare function useHostApi(): HostAPI;
|
|
179
|
+
/**
|
|
180
|
+
* 使用运行时状态
|
|
181
|
+
*/
|
|
182
|
+
declare function useRuntimeState<T = unknown>(key: string): T | undefined;
|
|
183
|
+
/**
|
|
184
|
+
* 使用可写状态
|
|
185
|
+
*/
|
|
186
|
+
declare function useRuntimeStateWritable<T = unknown>(key: string, defaultValue?: T): [T, (value: T) => void];
|
|
187
|
+
/**
|
|
188
|
+
* 使用查询结果
|
|
189
|
+
*/
|
|
190
|
+
declare function useQuery<T = unknown>(queryId: string): {
|
|
191
|
+
data: T | undefined;
|
|
192
|
+
loading: boolean;
|
|
193
|
+
error: Error | null;
|
|
194
|
+
refetch: () => Promise<void>;
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* 使用动作
|
|
198
|
+
*/
|
|
199
|
+
declare function useAction<T = unknown, P = Record<string, unknown>>(actionType: string): {
|
|
200
|
+
execute: (params?: P) => Promise<T | undefined>;
|
|
201
|
+
loading: boolean;
|
|
202
|
+
result: T | undefined;
|
|
203
|
+
error: Error | null;
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* 使用数据请求
|
|
207
|
+
*/
|
|
208
|
+
declare function useData<T = unknown, P = Record<string, unknown>>(queryId: string, params?: P, options?: {
|
|
209
|
+
immediate?: boolean;
|
|
210
|
+
}): {
|
|
211
|
+
data: T | undefined;
|
|
212
|
+
loading: boolean;
|
|
213
|
+
error: Error | null;
|
|
214
|
+
refetch: (newParams?: P) => Promise<void>;
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* 使用事件监听
|
|
218
|
+
*/
|
|
219
|
+
declare function useRuntimeEvent<T = unknown>(eventType: string, handler: (data: T) => void): void;
|
|
220
|
+
|
|
221
|
+
export { DJVProvider, type DJVProviderProps, DJVRenderer, type DJVRendererProps, type ReactRuntimeOptions, type ReactRuntimeReturn, RuntimeContext, type RuntimeContextValue, RuntimeProvider, type RuntimeProviderProps, useAction, useDJVRuntime, useData, useHostApi, useQuery, useReactRuntime, useRuntimeContext, useRuntimeEvent, useRuntimeState, useRuntimeStateWritable };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
// src/react-runtime.ts
|
|
2
|
+
import { useState, useCallback, useRef } from "react";
|
|
3
|
+
import {
|
|
4
|
+
createRuntime
|
|
5
|
+
} from "@djvlc/runtime-core";
|
|
6
|
+
function useReactRuntime(options) {
|
|
7
|
+
const [runtime, setRuntime] = useState(null);
|
|
8
|
+
const [loading, setLoading] = useState(true);
|
|
9
|
+
const [phase, setPhase] = useState("idle");
|
|
10
|
+
const [page, setPage] = useState(null);
|
|
11
|
+
const [error, setError] = useState(null);
|
|
12
|
+
const [hostApi, setHostApi] = useState(null);
|
|
13
|
+
const runtimeRef = useRef(null);
|
|
14
|
+
const init = useCallback(async () => {
|
|
15
|
+
const container = options.containerRef?.current;
|
|
16
|
+
if (!container) {
|
|
17
|
+
throw new Error("Container element not found");
|
|
18
|
+
}
|
|
19
|
+
const runtimeInstance = createRuntime({
|
|
20
|
+
...options,
|
|
21
|
+
container,
|
|
22
|
+
onError: (err) => {
|
|
23
|
+
setError(err);
|
|
24
|
+
options.onError?.(err);
|
|
25
|
+
},
|
|
26
|
+
onEvent: (event) => {
|
|
27
|
+
options.onEvent?.(event);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
runtimeRef.current = runtimeInstance;
|
|
31
|
+
setRuntime(runtimeInstance);
|
|
32
|
+
runtimeInstance.onStateChange((state) => {
|
|
33
|
+
setPhase(state.phase);
|
|
34
|
+
setLoading(state.phase !== "ready" && state.phase !== "error");
|
|
35
|
+
if (state.page) {
|
|
36
|
+
setPage(state.page);
|
|
37
|
+
}
|
|
38
|
+
if (state.error) {
|
|
39
|
+
setError(state.error);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
await runtimeInstance.init();
|
|
43
|
+
setHostApi(runtimeInstance.getHostApi());
|
|
44
|
+
}, [options]);
|
|
45
|
+
const load = useCallback(async () => {
|
|
46
|
+
if (!runtimeRef.current) {
|
|
47
|
+
throw new Error("Runtime not initialized");
|
|
48
|
+
}
|
|
49
|
+
const result = await runtimeRef.current.load();
|
|
50
|
+
setPage(result);
|
|
51
|
+
setHostApi(runtimeRef.current.getHostApi());
|
|
52
|
+
return result;
|
|
53
|
+
}, []);
|
|
54
|
+
const render = useCallback(async () => {
|
|
55
|
+
if (!runtimeRef.current) {
|
|
56
|
+
throw new Error("Runtime not initialized");
|
|
57
|
+
}
|
|
58
|
+
await runtimeRef.current.render();
|
|
59
|
+
setLoading(false);
|
|
60
|
+
}, []);
|
|
61
|
+
const destroy = useCallback(() => {
|
|
62
|
+
runtimeRef.current?.destroy();
|
|
63
|
+
runtimeRef.current = null;
|
|
64
|
+
setRuntime(null);
|
|
65
|
+
setHostApi(null);
|
|
66
|
+
setPage(null);
|
|
67
|
+
setError(null);
|
|
68
|
+
setPhase("idle");
|
|
69
|
+
setLoading(true);
|
|
70
|
+
}, []);
|
|
71
|
+
const setVariable = useCallback((key, value) => {
|
|
72
|
+
runtimeRef.current?.setVariable(key, value);
|
|
73
|
+
}, []);
|
|
74
|
+
const refreshData = useCallback(async (queryId) => {
|
|
75
|
+
await runtimeRef.current?.refreshData(queryId);
|
|
76
|
+
}, []);
|
|
77
|
+
return {
|
|
78
|
+
runtime,
|
|
79
|
+
loading,
|
|
80
|
+
phase,
|
|
81
|
+
page,
|
|
82
|
+
error,
|
|
83
|
+
hostApi,
|
|
84
|
+
init,
|
|
85
|
+
load,
|
|
86
|
+
render,
|
|
87
|
+
destroy,
|
|
88
|
+
setVariable,
|
|
89
|
+
refreshData
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/context.tsx
|
|
94
|
+
import { createContext, useContext } from "react";
|
|
95
|
+
import { jsx } from "react/jsx-runtime";
|
|
96
|
+
var defaultState = {
|
|
97
|
+
phase: "idle",
|
|
98
|
+
page: null,
|
|
99
|
+
variables: {},
|
|
100
|
+
queries: {},
|
|
101
|
+
components: /* @__PURE__ */ new Map(),
|
|
102
|
+
error: null,
|
|
103
|
+
destroyed: false
|
|
104
|
+
};
|
|
105
|
+
var RuntimeContext = createContext({
|
|
106
|
+
runtime: null,
|
|
107
|
+
state: defaultState,
|
|
108
|
+
hostApi: null
|
|
109
|
+
});
|
|
110
|
+
function RuntimeProvider({
|
|
111
|
+
runtime,
|
|
112
|
+
state,
|
|
113
|
+
hostApi,
|
|
114
|
+
children
|
|
115
|
+
}) {
|
|
116
|
+
const value = {
|
|
117
|
+
runtime,
|
|
118
|
+
state,
|
|
119
|
+
hostApi
|
|
120
|
+
};
|
|
121
|
+
return /* @__PURE__ */ jsx(RuntimeContext.Provider, { value, children });
|
|
122
|
+
}
|
|
123
|
+
function useRuntimeContext() {
|
|
124
|
+
const context = useContext(RuntimeContext);
|
|
125
|
+
return context;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/components/DJVRenderer.tsx
|
|
129
|
+
import { useRef as useRef2, useEffect, useState as useState2, useCallback as useCallback2 } from "react";
|
|
130
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
131
|
+
function DJVRenderer({
|
|
132
|
+
pageUid,
|
|
133
|
+
apiBaseUrl,
|
|
134
|
+
cdnBaseUrl,
|
|
135
|
+
channel = "prod",
|
|
136
|
+
userId,
|
|
137
|
+
deviceId,
|
|
138
|
+
authToken,
|
|
139
|
+
previewToken,
|
|
140
|
+
debug = false,
|
|
141
|
+
enableSRI = true,
|
|
142
|
+
onLoad,
|
|
143
|
+
onError,
|
|
144
|
+
onReady,
|
|
145
|
+
loadingComponent,
|
|
146
|
+
errorComponent,
|
|
147
|
+
children,
|
|
148
|
+
className,
|
|
149
|
+
style
|
|
150
|
+
}) {
|
|
151
|
+
const containerRef = useRef2(null);
|
|
152
|
+
const [state, setState] = useState2({
|
|
153
|
+
phase: "idle",
|
|
154
|
+
page: null,
|
|
155
|
+
variables: {},
|
|
156
|
+
queries: {},
|
|
157
|
+
components: /* @__PURE__ */ new Map(),
|
|
158
|
+
error: null,
|
|
159
|
+
destroyed: false
|
|
160
|
+
});
|
|
161
|
+
const options = {
|
|
162
|
+
pageUid,
|
|
163
|
+
apiBaseUrl,
|
|
164
|
+
cdnBaseUrl,
|
|
165
|
+
channel,
|
|
166
|
+
userId,
|
|
167
|
+
deviceId,
|
|
168
|
+
authToken,
|
|
169
|
+
previewToken,
|
|
170
|
+
debug,
|
|
171
|
+
enableSRI,
|
|
172
|
+
containerRef,
|
|
173
|
+
onError: (error2) => {
|
|
174
|
+
onError?.(error2);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const {
|
|
178
|
+
runtime,
|
|
179
|
+
loading,
|
|
180
|
+
phase: _phase,
|
|
181
|
+
page: _page,
|
|
182
|
+
error,
|
|
183
|
+
hostApi,
|
|
184
|
+
init,
|
|
185
|
+
load,
|
|
186
|
+
render,
|
|
187
|
+
destroy
|
|
188
|
+
} = useReactRuntime(options);
|
|
189
|
+
void _phase;
|
|
190
|
+
void _page;
|
|
191
|
+
const initAndLoad = useCallback2(async () => {
|
|
192
|
+
if (!containerRef.current) return;
|
|
193
|
+
try {
|
|
194
|
+
await init();
|
|
195
|
+
const pageData = await load();
|
|
196
|
+
onLoad?.(pageData);
|
|
197
|
+
await render();
|
|
198
|
+
onReady?.();
|
|
199
|
+
if (runtime) {
|
|
200
|
+
runtime.onStateChange((newState) => {
|
|
201
|
+
setState(newState);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
} catch (err) {
|
|
205
|
+
}
|
|
206
|
+
}, [init, load, render, runtime, onLoad, onReady]);
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
initAndLoad();
|
|
209
|
+
return () => {
|
|
210
|
+
destroy();
|
|
211
|
+
};
|
|
212
|
+
}, [pageUid]);
|
|
213
|
+
const renderLoading = () => {
|
|
214
|
+
if (!loading) return null;
|
|
215
|
+
return loadingComponent || /* @__PURE__ */ jsxs("div", { className: "djvlc-loading", children: [
|
|
216
|
+
/* @__PURE__ */ jsx2("div", { className: "djvlc-loading-spinner" }),
|
|
217
|
+
/* @__PURE__ */ jsx2("span", { children: "\u52A0\u8F7D\u4E2D..." })
|
|
218
|
+
] });
|
|
219
|
+
};
|
|
220
|
+
const renderError = () => {
|
|
221
|
+
if (!error) return null;
|
|
222
|
+
if (typeof errorComponent === "function") {
|
|
223
|
+
return errorComponent(error);
|
|
224
|
+
}
|
|
225
|
+
return errorComponent || /* @__PURE__ */ jsx2("div", { className: "djvlc-error", children: /* @__PURE__ */ jsxs("span", { children: [
|
|
226
|
+
"\u52A0\u8F7D\u5931\u8D25\uFF1A",
|
|
227
|
+
error.message
|
|
228
|
+
] }) });
|
|
229
|
+
};
|
|
230
|
+
return /* @__PURE__ */ jsx2(RuntimeProvider, { runtime, state, hostApi, children: /* @__PURE__ */ jsxs(
|
|
231
|
+
"div",
|
|
232
|
+
{
|
|
233
|
+
ref: containerRef,
|
|
234
|
+
className: `djvlc-renderer ${className || ""}`,
|
|
235
|
+
style,
|
|
236
|
+
children: [
|
|
237
|
+
renderLoading(),
|
|
238
|
+
renderError(),
|
|
239
|
+
children
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
) });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/components/DJVProvider.tsx
|
|
246
|
+
import { useState as useState3, useEffect as useEffect2 } from "react";
|
|
247
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
248
|
+
function DJVProvider({
|
|
249
|
+
runtime,
|
|
250
|
+
hostApi,
|
|
251
|
+
children
|
|
252
|
+
}) {
|
|
253
|
+
const [state, setState] = useState3(
|
|
254
|
+
runtime?.getState() || {
|
|
255
|
+
phase: "idle",
|
|
256
|
+
page: null,
|
|
257
|
+
variables: {},
|
|
258
|
+
queries: {},
|
|
259
|
+
components: /* @__PURE__ */ new Map(),
|
|
260
|
+
error: null,
|
|
261
|
+
destroyed: false
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
useEffect2(() => {
|
|
265
|
+
if (!runtime) return;
|
|
266
|
+
const unsubscribe = runtime.onStateChange((newState) => {
|
|
267
|
+
setState(newState);
|
|
268
|
+
});
|
|
269
|
+
return () => {
|
|
270
|
+
unsubscribe();
|
|
271
|
+
};
|
|
272
|
+
}, [runtime]);
|
|
273
|
+
return /* @__PURE__ */ jsx3(RuntimeProvider, { runtime, state, hostApi, children: /* @__PURE__ */ jsx3("div", { className: "djvlc-provider", children }) });
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// src/hooks/useDJVRuntime.ts
|
|
277
|
+
import { useCallback as useCallback3, useState as useState4, useEffect as useEffect3, useMemo } from "react";
|
|
278
|
+
function useDJVRuntime() {
|
|
279
|
+
const context = useRuntimeContext();
|
|
280
|
+
const loading = useMemo(() => {
|
|
281
|
+
const phase = context.state.phase;
|
|
282
|
+
return phase !== "ready" && phase !== "error";
|
|
283
|
+
}, [context.state.phase]);
|
|
284
|
+
return {
|
|
285
|
+
runtime: context.runtime,
|
|
286
|
+
state: context.state,
|
|
287
|
+
loading,
|
|
288
|
+
phase: context.state.phase,
|
|
289
|
+
error: context.state.error,
|
|
290
|
+
page: context.state.page
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function useHostApi() {
|
|
294
|
+
const context = useRuntimeContext();
|
|
295
|
+
if (!context.hostApi) {
|
|
296
|
+
throw new Error("HostAPI not available. Make sure runtime is initialized.");
|
|
297
|
+
}
|
|
298
|
+
return context.hostApi;
|
|
299
|
+
}
|
|
300
|
+
function useRuntimeState(key) {
|
|
301
|
+
const context = useRuntimeContext();
|
|
302
|
+
return context.state.variables[key];
|
|
303
|
+
}
|
|
304
|
+
function useRuntimeStateWritable(key, defaultValue) {
|
|
305
|
+
const context = useRuntimeContext();
|
|
306
|
+
const value = context.state.variables[key] ?? defaultValue;
|
|
307
|
+
const setValue = useCallback3(
|
|
308
|
+
(newValue) => {
|
|
309
|
+
context.runtime?.setVariable(key, newValue);
|
|
310
|
+
},
|
|
311
|
+
[context.runtime, key]
|
|
312
|
+
);
|
|
313
|
+
return [value, setValue];
|
|
314
|
+
}
|
|
315
|
+
function useQuery(queryId) {
|
|
316
|
+
const context = useRuntimeContext();
|
|
317
|
+
const [loading, setLoading] = useState4(false);
|
|
318
|
+
const [error, setError] = useState4(null);
|
|
319
|
+
const data = context.state.queries[queryId];
|
|
320
|
+
const refetch = useCallback3(async () => {
|
|
321
|
+
setLoading(true);
|
|
322
|
+
setError(null);
|
|
323
|
+
try {
|
|
324
|
+
await context.runtime?.refreshData(queryId);
|
|
325
|
+
} catch (e) {
|
|
326
|
+
setError(e);
|
|
327
|
+
} finally {
|
|
328
|
+
setLoading(false);
|
|
329
|
+
}
|
|
330
|
+
}, [context.runtime, queryId]);
|
|
331
|
+
return {
|
|
332
|
+
data,
|
|
333
|
+
loading,
|
|
334
|
+
error,
|
|
335
|
+
refetch
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function useAction(actionType) {
|
|
339
|
+
const context = useRuntimeContext();
|
|
340
|
+
const [loading, setLoading] = useState4(false);
|
|
341
|
+
const [result, setResult] = useState4();
|
|
342
|
+
const [error, setError] = useState4(null);
|
|
343
|
+
const execute = useCallback3(
|
|
344
|
+
async (params) => {
|
|
345
|
+
const hostApi = context.hostApi;
|
|
346
|
+
if (!hostApi) {
|
|
347
|
+
throw new Error("HostAPI not available");
|
|
348
|
+
}
|
|
349
|
+
setLoading(true);
|
|
350
|
+
setError(null);
|
|
351
|
+
try {
|
|
352
|
+
const response = await hostApi.executeAction(
|
|
353
|
+
actionType,
|
|
354
|
+
params
|
|
355
|
+
);
|
|
356
|
+
if (response.success) {
|
|
357
|
+
setResult(response.data);
|
|
358
|
+
return response.data;
|
|
359
|
+
} else {
|
|
360
|
+
throw new Error(response.message || "Action failed");
|
|
361
|
+
}
|
|
362
|
+
} catch (e) {
|
|
363
|
+
setError(e);
|
|
364
|
+
throw e;
|
|
365
|
+
} finally {
|
|
366
|
+
setLoading(false);
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
[context.hostApi, actionType]
|
|
370
|
+
);
|
|
371
|
+
return {
|
|
372
|
+
execute,
|
|
373
|
+
loading,
|
|
374
|
+
result,
|
|
375
|
+
error
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function useData(queryId, params, options) {
|
|
379
|
+
const context = useRuntimeContext();
|
|
380
|
+
const [data, setData] = useState4();
|
|
381
|
+
const [loading, setLoading] = useState4(false);
|
|
382
|
+
const [error, setError] = useState4(null);
|
|
383
|
+
const refetch = useCallback3(
|
|
384
|
+
async (newParams) => {
|
|
385
|
+
const hostApi = context.hostApi;
|
|
386
|
+
if (!hostApi) {
|
|
387
|
+
throw new Error("HostAPI not available");
|
|
388
|
+
}
|
|
389
|
+
setLoading(true);
|
|
390
|
+
setError(null);
|
|
391
|
+
try {
|
|
392
|
+
const response = await hostApi.requestData(
|
|
393
|
+
queryId,
|
|
394
|
+
newParams || params
|
|
395
|
+
);
|
|
396
|
+
if (response.success) {
|
|
397
|
+
setData(response.data);
|
|
398
|
+
} else {
|
|
399
|
+
throw new Error(response.message || "Query failed");
|
|
400
|
+
}
|
|
401
|
+
} catch (e) {
|
|
402
|
+
setError(e);
|
|
403
|
+
} finally {
|
|
404
|
+
setLoading(false);
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
[context.hostApi, queryId, params]
|
|
408
|
+
);
|
|
409
|
+
useEffect3(() => {
|
|
410
|
+
if (options?.immediate !== false && context.hostApi) {
|
|
411
|
+
refetch();
|
|
412
|
+
}
|
|
413
|
+
}, [context.hostApi]);
|
|
414
|
+
return {
|
|
415
|
+
data,
|
|
416
|
+
loading,
|
|
417
|
+
error,
|
|
418
|
+
refetch
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function useRuntimeEvent(eventType, handler) {
|
|
422
|
+
const context = useRuntimeContext();
|
|
423
|
+
useEffect3(() => {
|
|
424
|
+
const unsubscribe = context.runtime?.on(eventType, (event) => {
|
|
425
|
+
handler(event.data);
|
|
426
|
+
});
|
|
427
|
+
return () => {
|
|
428
|
+
unsubscribe?.();
|
|
429
|
+
};
|
|
430
|
+
}, [context.runtime, eventType, handler]);
|
|
431
|
+
}
|
|
432
|
+
export {
|
|
433
|
+
DJVProvider,
|
|
434
|
+
DJVRenderer,
|
|
435
|
+
RuntimeContext,
|
|
436
|
+
RuntimeProvider,
|
|
437
|
+
useAction,
|
|
438
|
+
useDJVRuntime,
|
|
439
|
+
useData,
|
|
440
|
+
useHostApi,
|
|
441
|
+
useQuery,
|
|
442
|
+
useReactRuntime,
|
|
443
|
+
useRuntimeContext,
|
|
444
|
+
useRuntimeEvent,
|
|
445
|
+
useRuntimeState,
|
|
446
|
+
useRuntimeStateWritable
|
|
447
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@djvlc/runtime-host-react",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "DJV 低代码平台 React 宿主适配器",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --clean --external react",
|
|
21
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch --external react",
|
|
22
|
+
"test": "vitest run --passWithNoTests",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"clean": "rimraf dist"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@djvlc/runtime-core": "1.0.0",
|
|
30
|
+
"@djvlc/contracts-types": "^1.4.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.10.0",
|
|
34
|
+
"@types/react": "^18.2.0",
|
|
35
|
+
"eslint": "^8.55.0",
|
|
36
|
+
"rimraf": "^5.0.5",
|
|
37
|
+
"tsup": "^8.0.0",
|
|
38
|
+
"typescript": "^5.3.0",
|
|
39
|
+
"vitest": "^1.0.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": "^18.0.0"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"lowcode",
|
|
46
|
+
"runtime",
|
|
47
|
+
"react",
|
|
48
|
+
"djv"
|
|
49
|
+
],
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"license": "MIT"
|
|
54
|
+
}
|