import { reactive, watch } from "vue"; import { Attrib, Entity, EntityClass, GetSetPick, TGroup, TLayer, TShape, } from "../type"; import { inRevise, toRawType } from "./public"; export const setShapeConfig = ( shape: T, config: GetSetPick ) => { for (const key in config) { shape[key as any](config[key]); } }; export const getChangePart = (newIds: T[], oldIds: T[] = []) => { const addPort = newIds.filter((newId) => !oldIds.includes(newId)); const delPort = oldIds.filter((oldId) => !newIds.includes(oldId)); const holdPort = oldIds.filter((oldId) => newIds.includes(oldId)); return { addPort, delPort, holdPort }; }; export const getChangeAllPoart = ( newAttribs: T[], oldAttribs: T[] ) => { const newIds = newAttribs.map(({ id }) => id); const oldIds = oldAttribs.map(({ id }) => id); const ports = getChangePart(newIds, oldIds); // 数组子项引用变化 const changePort = newAttribs .filter( (newAttrib) => !oldAttribs.includes(newAttrib) && oldIds.includes(newAttrib.id) ) .map((attrib) => attrib.id); oldAttribs = newAttribs; return { ...ports, changePort }; }; type AttribsChange = ReturnType & { changePort: string[]; }; export const watchAttribs = ( attribs: Attrib[], callback: (data: AttribsChange) => void, immediate = true ) => { return watch( () => [...attribs], (newAttribs, oldAttribs = []) => { callback(getChangeAllPoart(newAttribs, oldAttribs)); }, { immediate, flush: "pre" } ); }; export const attribsJoinEntity = < T extends Attrib, C extends EntityClass, P extends Attrib >( attribs: T[], Type: C, parent?: Entity

, joinDestory = false, extra?: (self: InstanceType) => void ) => { const findAttrib = (id: Attrib["id"]) => attribs.find((attrib) => attrib.id === id); const cache: { [key in Attrib["id"]]: InstanceType } = reactive({}); const destory = (id: Attrib["id"]) => { if (id in cache) { cache[id].destory(); } const shape = cache[id].getShape(); shape && shape.destroy(); delete cache[id]; }; const add = (id: Attrib["id"]) => { const attrib = findAttrib(id); if (attrib) { cache[id] = new Type({ attrib, }) as InstanceType; extra && extra(cache[id]); cache[id].init && cache[id].init(); cache[id].attachBefore && cache[id].attachBefore(); parent && cache[id].setParent(parent); cache[id].attachAfter && cache[id].attachAfter(); } }; const stopWatchAttribs = watchAttribs( attribs, ({ addPort, delPort, changePort }) => { delPort.forEach(destory); addPort.forEach(add); changePort.forEach((id) => { cache[id].setAttrib(findAttrib(id)); }); } ); return { entitys: cache, destory: () => { stopWatchAttribs(); if (joinDestory) { attribs.forEach((attrib) => destory(attrib.id)); } }, }; }; export const deptComputed = >( getter: () => T ) => { const data = reactive(getter()); const stop = watch(getter, (newData) => { if (inRevise(data, newData)) { if (Array.isArray(newData)) { newData.forEach((item, ndx) => { data[ndx] = item; }); } else { Object.keys(data).forEach((key) => delete data[key]); Object.assign(data, newData); } } }); return { data: data as T, stop, }; }; export const partialComputed = (getter: () => T[]) => { const data = reactive(getter()) as T[]; const stop = watch(getter, (newData, oldData) => { const { addPort, delPort, changePort } = getChangeAllPoart( newData, oldData ); for (const delId of delPort) { const ndx = data.findIndex((i) => i.id === delId); ~ndx && data.splice(ndx, 1); } for (const addId of addPort) { const addItem = newData.find((i) => i.id === addId); addItem && data.push(addItem); } for (const changeId of changePort) { const dataNdx = data.findIndex((i) => i.id === changeId); const newDataNdx = newData.findIndex((i) => i.id === changeId); if (inRevise(data[dataNdx], newData[newDataNdx])) { data[dataNdx] = newData[newDataNdx]; } } }); return { data, stop, }; }; export const depPartialUpdate = (newData: T, oldData: T): T => { if (!inRevise(newData, oldData)) { return oldData; } const nData = newData as any, oData = oldData as any; const type = toRawType(nData); switch (type) { case "Array": for (let i = 0; i < nData.length; i++) { oData[i] = depPartialUpdate(nData[i], oData[i]); } while (oData.length !== nData.length) { oData.pop(); } break; case "Object": const oKeys = Object.keys(oData).sort(); const nKeys = Object.keys(nData).sort(); const { addPort, delPort, holdPort } = getChangePart(nKeys, oKeys); for (let i = 0; i < holdPort.length; i++) { oData[oKeys[i]] = depPartialUpdate( nData[holdPort[i]], oData[holdPort[i]] ); } addPort.forEach((key) => (oData[key] = nData[key])); delPort.forEach((key) => delete oData[key]); break; default: return newData; } return oldData; };