@knxcloud/lowcode-vue-simulator-renderer
Version:
826 lines (825 loc) • 24.7 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import { defineComponent, h, renderSlot, Suspense, ref, isVNode, shallowRef, shallowReactive, reactive, markRaw, createApp, computed, onUnmounted } from "vue";
import * as VueRouter from "vue-router";
import { RouterView } from "vue-router";
import LowCodeRenderer__default, { config, setupLowCodeRouteGuard, LOWCODE_ROUTE_META, SchemaParser } from "@knxcloud/lowcode-vue-renderer";
export * from "@knxcloud/lowcode-vue-renderer";
import { default as default2, config as config2 } from "@knxcloud/lowcode-vue-renderer";
import { isElement, isObject, isNil, isArray, AssetLoader, getSubComponent, buildUtils, exportSchema, buildComponents } from "@knxcloud/lowcode-utils";
const Layout = defineComponent({
props: {
simulator: {
type: Object,
required: true
}
},
render() {
const { simulator: simulator2, $slots } = this;
const { layout, getComponent } = simulator2;
if (layout) {
const { Component, props = {}, componentName } = layout;
if (Component) {
return h(Component, { ...props, key: "layout", simulator: simulator2 }, $slots);
}
const ComputedComponent = componentName && getComponent(componentName);
if (ComputedComponent) {
return h(ComputedComponent, { ...props, key: "layout", simulator: simulator2 }, $slots);
}
}
return renderSlot($slots, "default");
}
});
const SimulatorRendererView = defineComponent({
props: {
simulator: {
type: Object,
required: true
}
},
render() {
const { simulator: simulator2 } = this;
return h(Layout, { simulator: simulator2 }, () => {
return h(RouterView, null, {
default: ({ Component }) => {
return Component && h(Suspense, null, () => h(Component));
}
});
});
}
});
const Renderer = defineComponent({
props: {
simulator: {
type: Object,
required: true
},
documentInstance: {
type: Object,
required: true
}
},
setup: () => ({ renderer: ref() }),
render() {
const { documentInstance, simulator: simulator2 } = this;
const { schema, scope, messages, appHelper, key } = documentInstance;
const { designMode, device, locale, components, requestHandlersMap } = simulator2;
return h(LowCodeRenderer__default, {
ref: "renderer",
key,
scope,
schema,
locale,
device,
messages,
appHelper,
components,
designMode,
requestHandlersMap,
disableCompMock: simulator2.disableCompMock,
thisRequiredInJSE: simulator2.thisRequiredInJSE,
getNode: (id) => documentInstance.getNode(id),
onCompGetCtx: (schema2, ref2) => documentInstance.mountInstance(schema2.id, ref2)
});
}
});
const Leaf = defineComponent({
name: "Leaf",
render() {
return renderSlot(this.$slots, "default");
}
});
Object.assign(Leaf, {
displayName: "Leaf",
componentMetadata: {
componentName: "Leaf",
configure: {
props: [
{
name: "children",
setter: "StringSetter"
}
],
supports: false
}
}
});
const Slot = defineComponent({
render() {
return renderSlot(this.$slots, "default", this.$props, () => {
return [h("div", { class: "lc-container" })];
});
}
});
Object.assign(Slot, {
displayName: "Slot",
componentMetadata: {
componentName: "Slot",
configure: {
props: [
{
name: "___title",
title: "插槽标题",
setter: "StringSetter",
defaultValue: "插槽容器"
},
{
name: "___params",
title: "插槽入参",
setter: {
componentName: "ArraySetter",
props: {
itemSetter: {
componentName: "StringSetter",
props: {
placeholder: "参数名称"
}
}
}
}
}
],
component: {
isContainer: true,
disableBehaviors: "*"
},
supports: false
}
}
});
const Page = defineComponent((props, { slots }) => {
return () => h("div", { class: "lc-page", ...props }, slots);
});
Object.assign(Page, {
displayName: "Page",
componentMetadata: {
componentName: "Page",
configure: {
supports: {
style: true,
className: true
},
component: {
isContainer: true,
disableBehaviors: "*"
}
}
}
});
const host = window.LCSimulatorHost;
const cycleRange = document.createRange();
function getClientRects(node) {
if (!node.parentNode)
return [];
if (isElement(node)) {
return [node.getBoundingClientRect()];
}
cycleRange.selectNode(node);
return Array.from(cycleRange.getClientRects());
}
const SYMBOL_VDID = Symbol("_LCDocId");
const SYMBOL_VNID = Symbol("_LCNodeId");
const SYMBOL_VInstance = Symbol("_LCVueInstance");
const SYMBOL_RECORD_FLAG = Symbol("_LCVueCompRecord");
function isVNodeHTMLElement(el) {
return isObject(el) && !isNil(el.__vueParentComponent);
}
function isCompRootHTMLElement(el) {
return isObject(el) && SYMBOL_VDID in el;
}
function isComponentRecord(el) {
return isObject(el) && SYMBOL_RECORD_FLAG in el;
}
function createComponentRecord(did, nid, cid) {
return {
did,
nid,
cid,
[SYMBOL_RECORD_FLAG]: true
};
}
function getCompRootData(el) {
return {
docId: el[SYMBOL_VDID],
nodeId: el[SYMBOL_VNID],
instance: el[SYMBOL_VInstance]
};
}
function setCompRootData(el, data) {
el[SYMBOL_VDID] = data.docId;
el[SYMBOL_VNID] = data.nodeId;
el[SYMBOL_VInstance] = data.instance;
}
function isCommentNode(el) {
return el.nodeType === 8;
}
function isTextNode(el) {
return el.nodeType === 3;
}
function isDomNode(el) {
return isObject(el) && "nodeType" in el && (el.nodeType === Node.ELEMENT_NODE || el.nodeType === Node.TEXT_NODE);
}
function isEmptyNode(el) {
return isCommentNode(el) || isTextNode(el) && el.nodeValue === "";
}
function getClosestNodeInstance(el, specId) {
if (!document.contains(el)) {
return null;
}
return getClosestNodeInstanceByElement(el, specId);
}
function getClosestNodeInstanceByElement(el, specId) {
while (el) {
if (isVNodeHTMLElement(el)) {
const component = el.__vueParentComponent;
return getClosestNodeInstanceByComponent(component, specId);
}
if (isCompRootHTMLElement(el)) {
const { nodeId, docId, instance } = getCompRootData(el);
if (!specId || specId === nodeId) {
return {
docId,
nodeId,
instance: createComponentRecord(docId, nodeId, instance.$.uid)
};
}
}
el = el.parentElement;
}
return null;
}
function getClosestNodeInstanceByComponent(instance, specId) {
while (instance) {
const el = instance.vnode.el;
if (el && isCompRootHTMLElement(el)) {
const { nodeId, docId, instance: instance2 } = getCompRootData(el);
if (!specId || specId === nodeId) {
return {
docId,
nodeId,
instance: createComponentRecord(docId, nodeId, instance2.$.uid)
};
}
}
instance = instance.parent;
}
return null;
}
function findDOMNodes(instance) {
const els = [];
const el = instance.$el;
if (isEmptyNode(el)) {
const internalInstance = instance.$;
appendSiblingElement(els, internalInstance, el, (node) => {
return node.previousSibling;
});
appendDescendantComponent(els, internalInstance);
appendSiblingElement(els, internalInstance, el, (node) => {
return node.nextSibling;
});
} else {
els.push(el);
}
return els;
}
function appendSiblingElement(target, instance, el, next) {
let nextNode = next(el);
while (nextNode) {
if (isEmptyNode(nextNode)) {
nextNode = next(nextNode);
continue;
}
if (isVNodeHTMLElement(nextNode)) {
const childInstance = nextNode.__vueParentComponent;
if (isChildInstance(instance, childInstance)) {
target.unshift(nextNode);
nextNode = next(nextNode);
continue;
}
}
break;
}
}
function appendDescendantComponent(target, instance) {
const subNode = instance.subTree;
const current = subNode.el;
if (isValidElement(current)) {
target.push(current);
return true;
}
if (isArray(subNode.children) && subNode.children.length > 0) {
return appendDescendantChildren(target, subNode.children);
} else if (subNode.component) {
return appendDescendantComponent(target, subNode.component);
}
return false;
}
function appendDescendantChildren(target, children) {
const validElements = children.map(({ el }) => el).filter(isValidElement);
if (validElements.length > 0) {
target.push(...validElements);
return true;
} else {
return children.length > 0 && children.some((item) => {
if (isArray(item.children) && item.children.length > 0) {
return appendDescendantChildren(
target,
item.children.filter(
(child) => isVNode(child)
)
);
} else if (item.component) {
return appendDescendantComponent(target, item.component);
}
return false;
});
}
}
function isValidElement(el) {
if (el && isDomNode(el) && !isEmptyNode(el)) {
const rect = getClientRects(el);
return rect.some((item) => item.width || item.height);
}
return false;
}
function isChildInstance(target, source) {
if (source == null)
return false;
if (target.uid > source.uid)
return false;
if (target.uid === source.uid)
return true;
return isChildInstance(target, source.parent);
}
function warn(...messages) {
return console.warn("[vue-simulator-renderer]:", ...messages);
}
class Cursor {
constructor() {
__publicField(this, "states", /* @__PURE__ */ new Set());
}
setDragging(flag) {
if (flag) {
this.addState("dragging");
} else {
this.removeState("dragging");
}
}
setXResizing(flag) {
if (flag) {
this.addState("x-resizing");
} else {
this.removeState("x-resizing");
}
}
setYResizing(flag) {
if (flag) {
this.addState("y-resizing");
} else {
this.removeState("y-resizing");
}
}
setCopy(flag) {
if (flag) {
this.addState("copy");
} else {
this.removeState("copy");
}
}
isCopy() {
return this.states.has("copy");
}
release() {
for (const state of this.states) {
this.removeState(state);
}
}
addState(state) {
if (!this.states.has(state)) {
this.states.add(state);
document.documentElement.classList.add(`lc-cursor-${state}`);
}
}
removeState(state) {
if (this.states.has(state)) {
this.states.delete(state);
document.documentElement.classList.remove(`lc-cursor-${state}`);
}
}
}
const cursor = new Cursor();
let nativeSelectionEnabled = true;
const preventSelection = (e) => {
if (nativeSelectionEnabled) {
return null;
}
e.preventDefault();
e.stopPropagation();
return false;
};
document.addEventListener("selectstart", preventSelection, true);
document.addEventListener("dragstart", preventSelection, true);
function setNativeSelection(enableFlag) {
nativeSelectionEnabled = enableFlag;
}
function deepMerge(o1, o2) {
if (isObject(o1) && isObject(o2)) {
const result = Object.assign({}, o1);
Object.keys(o2).forEach((key) => {
Reflect.set(result, key, deepMerge(o1[key], o2[key]));
});
return result;
}
return o2 != null ? o2 : o1;
}
function parseFileNameToPath(fileName) {
const path = fileName.endsWith("/index.vue") ? fileName.slice(0, fileName.length - 10) : fileName.replace(/\.(\w*)$/, "");
return "/" + path.replace(/^\//, "");
}
Object.assign(window, { VueRouter });
const loader = new AssetLoader();
const builtinComponents = { Slot, Leaf, Page };
function createDocumentInstance(document2, context) {
const instancesMap = /* @__PURE__ */ new Map();
const vueInstanceMap = /* @__PURE__ */ new Map();
const timestamp = ref(Date.now());
const schema = computed(() => {
var _a;
void timestamp.value;
return (_a = exportSchema(document2)) != null ? _a : {
fileName: "/",
componentName: "Page"
};
});
const checkInstanceMounted = (instance) => {
return "$" in instance ? instance.$.isMounted : !!instance;
};
const setHostInstance = (docId, nodeId, instances) => {
const instanceRecords = !instances ? null : instances.map((inst) => createComponentRecord(docId, nodeId, inst.$.uid));
host.setInstance(docId, nodeId, instanceRecords);
};
const getComponentInstance = (id) => {
return vueInstanceMap.get(id);
};
const mountInstance = (id, instanceOrEl) => {
const docId = document2.id;
if (instanceOrEl == null) {
let instances2 = instancesMap.get(id);
if (instances2) {
instances2 = instances2.filter(checkInstanceMounted);
if (instances2.length > 0) {
instancesMap.set(id, instances2);
setHostInstance(docId, id, instances2);
} else {
instancesMap.delete(id);
setHostInstance(docId, id, null);
}
}
return;
}
let el;
let instance;
if ("$" in instanceOrEl) {
instance = instanceOrEl;
el = instance.$el;
} else if (isVNodeHTMLElement(instanceOrEl)) {
instance = instanceOrEl.__vueParentComponent.proxy;
el = instanceOrEl;
} else {
return;
}
const origId = getCompRootData(el).nodeId;
if (origId && origId !== id) {
unmountInstance(origId, instance);
}
onUnmounted(() => unmountInstance(id, instance), instance.$);
setCompRootData(el, {
nodeId: id,
docId,
instance
});
let instances = instancesMap.get(id);
if (instances) {
const l = instances.length;
instances = instances.filter(checkInstanceMounted);
let updated = instances.length !== l;
if (!instances.includes(instance)) {
instances.push(instance);
updated = true;
}
if (!updated)
return;
} else {
instances = [instance];
}
vueInstanceMap.set(instance.$.uid, instance);
instancesMap.set(id, instances);
setHostInstance(docId, id, instances);
};
const unmountInstance = (id, instance) => {
const instances = instancesMap.get(id);
if (instances) {
const i = instances.indexOf(instance);
if (i > -1) {
const [instance2] = instances.splice(i, 1);
vueInstanceMap.delete(instance2.$.uid);
setHostInstance(document2.id, id, instances);
}
}
};
const getNode = (id) => {
return id ? document2.getNode(id) : null;
};
return reactive({
id: computed(() => document2.id),
path: computed(() => {
var _a;
return parseFileNameToPath((_a = schema.value.fileName) != null ? _a : "");
}),
get key() {
return `${document2.id}:${timestamp.value}`;
},
scope: computed(() => ({})),
schema,
appHelper: computed(() => {
var _a;
const _schema = schema.value;
const {
utils: utilsInContext,
constants: constantsInContext,
...otherHelpers
} = context.appHelper;
return {
utils: {
...utilsInContext,
...buildUtils(host.libraryMap, (_a = Reflect.get(_schema, "utils")) != null ? _a : [])
},
constants: {
...constantsInContext,
...Reflect.get(_schema, "constants")
},
...otherHelpers
};
}),
document: computed(() => document2),
messages: computed(() => deepMerge(context.i18n, Reflect.get(schema.value, "i18n"))),
instancesMap: computed(() => instancesMap),
getNode,
mountInstance,
unmountInstance,
getComponentInstance,
rerender: () => {
const now = Date.now();
if (context.suspense) {
Object.assign(timestamp, {
_value: now,
_rawValue: now
});
} else {
timestamp.value = now;
}
SchemaParser.cleanCachedModules();
}
});
}
function createSimulatorRenderer() {
const layout = shallowRef({});
const device = shallowRef("default");
const locale = shallowRef();
const autoRender = shallowRef(host.autoRender);
const designMode = shallowRef("design");
const libraryMap = shallowRef({});
const components = shallowRef({});
const componentsMap = shallowRef({});
const disableCompMock = shallowRef(true);
const requestHandlersMap = shallowRef({});
const documentInstances = shallowRef([]);
const thisRequiredInJSE = shallowRef(false);
const context = shallowReactive({
i18n: {},
appHelper: {
utils: {},
constants: {}
},
suspense: false
});
const disposeFunctions = [];
const documentInstanceMap = /* @__PURE__ */ new Map();
function _buildComponents() {
components.value = {
...builtinComponents,
...buildComponents(libraryMap.value, componentsMap.value)
};
}
const simulator2 = reactive({
config: markRaw(config),
layout,
device,
locale,
designMode,
libraryMap,
components,
autoRender,
componentsMap,
disableCompMock,
documentInstances,
requestHandlersMap,
thisRequiredInJSE,
isSimulatorRenderer: true
});
simulator2.app = markRaw(createApp(SimulatorRendererView, { simulator: simulator2 }));
simulator2.router = markRaw(
VueRouter.createRouter({
history: VueRouter.createMemoryHistory("/"),
routes: []
})
);
simulator2.getComponent = (componentName) => {
const paths = componentName.split(".");
const subs = [];
while (paths.length > 0) {
const component = components.value[componentName];
if (component) {
return getSubComponent(component, subs);
}
const sub = paths.pop();
if (!sub)
break;
subs.unshift(sub);
componentName = paths.join(".");
}
return null;
};
simulator2.getClosestNodeInstance = (el, specId) => {
var _a;
if (isComponentRecord(el)) {
const { cid, did } = el;
const documentInstance = documentInstanceMap.get(did);
const instance = (_a = documentInstance == null ? void 0 : documentInstance.getComponentInstance(cid)) != null ? _a : null;
return instance && getClosestNodeInstanceByComponent(instance.$, specId);
}
return getClosestNodeInstance(el, specId);
};
simulator2.findDOMNodes = (instance) => {
if (instance) {
const { did, cid } = instance;
const documentInstance = documentInstanceMap.get(did);
const compInst = documentInstance == null ? void 0 : documentInstance.getComponentInstance(cid);
return compInst ? findDOMNodes(compInst) : null;
}
return null;
};
simulator2.getComponent = (componentName) => components.value[componentName];
simulator2.getClientRects = (element) => getClientRects(element);
simulator2.setNativeSelection = (enable) => setNativeSelection(enable);
simulator2.setDraggingState = (state) => cursor.setDragging(state);
simulator2.setCopyState = (state) => cursor.setCopy(state);
simulator2.clearState = () => cursor.release();
simulator2.rerender = () => documentInstances.value.forEach((doc) => doc.rerender());
simulator2.dispose = () => {
simulator2.app.unmount();
disposeFunctions.forEach((fn) => fn());
};
simulator2.getCurrentDocument = () => {
var _a;
const crr = host.project.currentDocument;
const docs = documentInstances.value;
return crr ? (_a = docs.find((doc) => doc.id === crr.id)) != null ? _a : null : null;
};
simulator2.load = (assets) => loader.load(assets);
simulator2.loadAsyncLibrary = async (asyncLibraryMap) => {
await loader.loadAsyncLibrary(asyncLibraryMap);
_buildComponents();
};
let running = false;
simulator2.run = () => {
if (running)
return;
running = true;
const containerId = "app";
let container = document.getElementById(containerId);
if (!container) {
container = document.createElement("div");
document.body.appendChild(container);
container.id = containerId;
}
document.documentElement.classList.add("engine-page");
document.body.classList.add("engine-document");
simulator2.app.use(simulator2.router).mount(container);
host.project.setRendererReady(simulator2);
};
disposeFunctions.push(
host.connect(simulator2, () => {
var _a, _b, _c;
const config22 = host.project.get("config") || {};
layout.value = (_a = config22.layout) != null ? _a : {};
disableCompMock.value = isArray(config22.disableCompMock) ? config22.disableCompMock : Boolean(config22.disableCompMock);
if (libraryMap.value !== host.libraryMap || componentsMap.value !== host.designer.componentsMap) {
libraryMap.value = host.libraryMap || {};
componentsMap.value = host.designer.componentsMap;
_buildComponents();
}
locale.value = host.locale;
device.value = host.device;
designMode.value = host.designMode;
requestHandlersMap.value = (_b = host.requestHandlersMap) != null ? _b : {};
thisRequiredInJSE.value = (_c = host.thisRequiredInJSE) != null ? _c : false;
documentInstances.value.forEach((doc) => doc.rerender());
setupLowCodeRouteGuard(simulator2.router, {
thisRequired: thisRequiredInJSE.value,
scopePath: "renderer.runtimeScope"
});
})
);
disposeFunctions.push(
host.autorun(async () => {
const { router } = simulator2;
documentInstances.value = host.project.documents.map((doc) => {
let documentInstance = documentInstanceMap.get(doc.id);
if (!documentInstance) {
documentInstance = createDocumentInstance(doc, context);
documentInstanceMap.set(doc.id, documentInstance);
} else if (router.hasRoute(documentInstance.id)) {
router.removeRoute(documentInstance.id);
}
router.addRoute({
name: documentInstance.id,
path: documentInstance.path,
meta: {
[LOWCODE_ROUTE_META]: documentInstance.schema
},
component: Renderer,
props: ((doc2, sim) => () => ({
simulator: sim,
documentInstance: doc2
}))(documentInstance, simulator2)
});
return documentInstance;
});
router.getRoutes().forEach((route) => {
const id = route.name;
const hasDoc = documentInstances.value.some((doc) => doc.id === id);
if (!hasDoc) {
router.removeRoute(id);
documentInstanceMap.delete(id);
}
});
const inst = simulator2.getCurrentDocument();
if (inst) {
try {
context.suspense = true;
await router.replace({ name: inst.id, force: true });
} finally {
context.suspense = false;
}
}
})
);
host.componentsConsumer.consume(async (componentsAsset) => {
if (componentsAsset) {
await loader.load(componentsAsset);
_buildComponents();
}
});
host.injectionConsumer.consume((data) => {
var _a;
if (data.appHelper) {
const { utils, constants, ...others } = data.appHelper;
Object.assign(context.appHelper, {
utils: isArray(utils) ? buildUtils(host.libraryMap, utils) : utils != null ? utils : {},
constants: constants != null ? constants : {},
...others
});
}
context.i18n = (_a = data.i18n) != null ? _a : {};
});
return simulator2;
}
const simulator = createSimulatorRenderer();
const index = "";
const win = window;
if (typeof win !== "undefined") {
win.SimulatorRenderer = simulator;
}
win.addEventListener("load", () => {
if (!win.__VUE_HMR_RUNTIME__) {
warn("检测到您正在使用 vue 运行时的生产环境版本");
warn("这将导致画布的部分功能异常,请使用非生产环境版本代替");
warn("https://unpkg.com/vue/dist/vue.runtime.global.js");
}
});
win.addEventListener("beforeunload", () => {
win.LCSimulatorHost = null;
win.SimulatorRenderer = null;
simulator.dispose();
});
export {
default2 as VueRenderer,
simulator as default,
config2 as vueRendererConfig
};
//# sourceMappingURL=vue-simulator-renderer.mjs.map