@msom/dom
Version:
@msom/dom
233 lines (229 loc) • 7.75 kB
JavaScript
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