UNPKG

@knxcloud/lowcode-vue-simulator-renderer

Version:
826 lines (825 loc) 24.7 kB
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