UNPKG

@msom/dom

Version:

@msom/dom

233 lines (229 loc) 7.75 kB
import { compareObjects, getComponentDefinition, getGlobalData, isComponent, isObject, ownKeysAndPrototypeOwnKeys, parseClass, parseStyle, setGlobalData } from "@msom/common"; import { createReaction, observer, withoutTrack } from "@msom/reaction"; import { __decorate, __metadata } from "tslib"; //#region src/element.ts setGlobalData("@msom/dom", {}); const componentVDOMMap = /* @__PURE__ */new WeakMap(); const TEXT_NODE = "TEXT_NODE"; function isIterator(v) { if (typeof v === "object" && v !== null || typeof v === "function") return Reflect.has(v, Symbol.iterator);else return false; } function createElement(type, config, ...children) { config = config || {}; const _config = { ...config, children: children.map((v) => { const handle = (_v) => { if (isIterator(_v)) return [..._v].map(handle);else if (typeof _v === "object" || _v === void 0 || _v === false || _v === null) return _v;else return createTextElement(String(_v)); }; return handle(v); }) }; return { type, props: _config }; } function createTextElement(text) { return { type: TEXT_NODE, props: { nodeValue: text, children: [] } }; } function createDom(element) { const { children, class: _class, style, $key, $ref, $context, ...props } = element.props; const dom = element.type === TEXT_NODE ? document.createTextNode("") : document.createElement(element.type); if (_class) props.className = `${parseClass(_class)} ${props.className || ""}`.trim(); if (style) Object.assign(props, { style: parseStyle(style) }); Reflect.ownKeys(props).filter((key) => typeof key === "string" && key.startsWith("on")).forEach((key) => { const event = Reflect.get(props, key, props); Reflect.deleteProperty(props, key); dom.addEventListener(key.slice(2).toLowerCase(), function (e) { const _e = new Proxy(e, { get: (target, prop, receiver) => { if (prop === "nativeEvent") return receiver; const value = Reflect.get(target, prop, target); return typeof value === "function" ? value.bind(target) : value; }, set: Reflect.set }); event.bind(this)(_e); }); }); Object.assign(dom, props); return dom; } const eventBindingMap = /* @__PURE__ */new WeakMap(); const componentCache = /* @__PURE__ */new Map(); function _mountComponent(element, container) { withoutTrack(() => { let { children, $ref, ...props } = element.props; const componentDefinition = getComponentDefinition(element.type); if (!componentDefinition) return; const { $events } = componentDefinition; const $eventKeys = ownKeysAndPrototypeOwnKeys($events); const { ..._props } = props; if ($eventKeys.size()) { for (const eventKey of $eventKeys) if (Reflect.has(_props, eventKey)) delete _props[eventKey]; } const component = (() => { let component$1 = componentCache.get(_props.$key); if (!component$1) { component$1 = new element.type(_props); _props.$key != void 0 && componentCache.set(_props.$key, component$1); } else component$1.set(_props); return component$1; })(); children = [children].flat(); if (children && children.length > 0) { const c = children.map((c$1) => { if (c$1.type === TEXT_NODE && typeof c$1.props.nodeValue === "function") return c$1.props.nodeValue;else return c$1; }); component.setJSX(c.length > 1 ? c : c[0]); } if ($ref) { const _$ref = [$ref].flat(); for (const ref of _$ref) ref.set(component); } if ($eventKeys.size()) { const binding = eventBindingMap.get(component) || {}; eventBindingMap.set(component, binding); for (const _key of $eventKeys) { const key = _key; component.un(key, binding[key]); Reflect.deleteProperty(binding, key); const on = props[key]; if (on && typeof on === "function") { component.on(key, on); Object.assign(binding, { [key]: { on } }); } } } component.created(); const domGlobalData = getGlobalData("@msom/dom"); const { rendering } = domGlobalData; component.$owner = rendering; component.onunmounted(createReaction(() => { const prevVDOM = componentVDOMMap.get(component); const mounted = component.isMounted(); if (component.el) container.removeChild(component.el); const vDOM = component.mount() || void 0; componentVDOMMap.set(component, vDOM); const isChanged = patchVDOM(vDOM, prevVDOM); const dom = renderer(vDOM, container); component.rendered(); if (!isChanged) return; component.el = dom; if (dom) { if (!Reflect.get(dom, "$owner")) Object.assign(dom, { $owner: component }); container.appendChild(dom); if (!mounted) component.mounted(); } }, { scheduler: "nextFrame" }).disposer()); }); } function renderer(element, container) { if (!element) return; if (typeof element !== "object") element = createTextElement(typeof element === "function" ? element : String(element)); if (isIterator(element)) { for (const e of element) renderer(e, container); return; } const _element = element; let { children, $ref } = _element.props; if (typeof _element.type === "function") { if (isComponent(_element.type)) _mountComponent(_element, container); } else { const dom = createDom(_element); if ($ref) { const refs = [$ref].flat(); refs.forEach((ref) => ref.set(dom)); } children = [children].flat(); if (children && children.length > 0) [...children].flat().forEach((child) => { const childDom = renderer(child, dom); if (childDom) dom.appendChild(childDom); }); container.appendChild(dom); return dom; } } function mountWith(mount, container) { const element = mount(); element && renderer(element, container); } function mountComponent(component, container) { const element = component.mount(); element && renderer(element, container); } /** * * @param vDOM * @param prevVDOM * @returns 返回时否有变 */ function patchVDOM(vDOM, prevVDOM) { if (!prevVDOM) return true;else if (isObject(vDOM) && isObject(prevVDOM)) return !compareObjects(vDOM, prevVDOM);else return !Object.is(vDOM, prevVDOM); } //#endregion //#region src/addStyle.ts function addStyle(cssType) { new CSSStyleSheet({ baseURL: URL.createObjectURL(new Blob([typeof cssType === "string" ? cssType : parseStyle(cssType)], { type: "text/plain" })) }); } //#endregion //#region src/Ref.ts var Ref = class { set(el) { if (this.data) this.data.push(el);else { this.data = []; this.set(el); } } get(index) { return this.data?.[index]; } }; __decorate([observer(), __metadata("design:type", Array)], Ref.prototype, "data", void 0); function createRef() { return new Ref(); } var MapRef = class { set(el) { if (this.data) this.data.set(el.$key || el, el);else { this.data = /* @__PURE__ */new Map(); this.set(el); } } get(key) { return this.data?.get(key); } }; __decorate([observer(), __metadata("design:type", Map)], MapRef.prototype, "data", void 0); function createMapRef() { return new MapRef(); } var SingleRef = class { current; set(el) { this.current = el; this.data = el; } }; __decorate([observer(), __metadata("design:type", Object)], SingleRef.prototype, "data", void 0); function createSingleRef() { return new SingleRef(); } //#endregion export { MapRef, Ref, SingleRef, TEXT_NODE, addStyle, createElement, createMapRef, createRef, createSingleRef, mountComponent, mountWith }; //# sourceMappingURL=index.js.map