UNPKG

@dooboostore/dom-render

Version:
629 lines (628 loc) 23.2 kB
var __decorate = function(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = function(k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var _a, _b; import { ElementUtils } from "@dooboostore/core-web"; import { RawSet } from "../rawsets/RawSet.js"; import { DomRenderNoProxy } from "../decorators/DomRenderNoProxy.js"; import { ConvertUtils } from "@dooboostore/core"; import { ReflectUtils } from "@dooboostore/core"; import { Subject } from "@dooboostore/core"; import { MessageOperator } from "@dooboostore/core"; const ATTRIBUTE_METADATA_KEY = Symbol("attribute"); const QUERY_METADATA_KEY = Symbol("query"); const EVENT_METADATA_KEY = Symbol("event"); function attribute(nameOrOptions) { return (target, propertyKey, descriptor) => { const attributes = ReflectUtils.getMetadata(ATTRIBUTE_METADATA_KEY, target.constructor) || []; let name; let converter; if (typeof nameOrOptions === "string") { name = nameOrOptions; } else if (nameOrOptions) { name = nameOrOptions.name || String(propertyKey); converter = nameOrOptions.converter; } else { name = String(propertyKey); } const attributeMetadata = { propertyKey, name, converter, isMethod: !!descriptor }; attributes.push(attributeMetadata); ReflectUtils.defineMetadata(ATTRIBUTE_METADATA_KEY, attributes, target.constructor); }; } function query(selectorOrOptions) { return (target, propertyKey, descriptor) => { const queries = ReflectUtils.getMetadata(QUERY_METADATA_KEY, target.constructor) || []; let selector; let convert; let onDestroy; let refreshTime; let refreshRawSetRendered; if (typeof selectorOrOptions === "string") { selector = selectorOrOptions; } else { selector = selectorOrOptions.selector; convert = selectorOrOptions.convert; onDestroy = selectorOrOptions.onDestroy; refreshTime = selectorOrOptions.refreshIntervalTime; refreshRawSetRendered = selectorOrOptions.refreshRawSetRendered; } const queryMetadata = { selector, propertyKey, isMethod: !!descriptor, convert, onDestroy, refreshTime, refreshRawSetRendered }; queries.push(queryMetadata); ReflectUtils.defineMetadata(QUERY_METADATA_KEY, queries, target.constructor); }; } function event(options) { return (target, propertyKey, descriptor) => { const events = ReflectUtils.getMetadata(EVENT_METADATA_KEY, target.constructor) || []; const eventMetadata = { query: options.query, eventName: options.name, methodName: propertyKey, onDestroy: options.onDestroy, refreshTime: options.refreshTime, refreshRawSetRendered: options.refreshRawSetRendered }; events.push(eventMetadata); ReflectUtils.defineMetadata(EVENT_METADATA_KEY, events, target.constructor); }; } class ComponentBase { constructor(_config) { this._config = _config; this._children = []; this._boundEventListeners = []; this._queryBindings = []; this._eventBindings = []; this._refreshTimers = /* @__PURE__ */ new Map(); this.childrenSetSubject = new Subject(); this.onRawSetRenderedOtherDataSubject = new Subject(); this.onChildRawSetRenderedOtherDataSubject = new Subject(); } get componentConfig() { return this._config; } get render() { return this._render; } get rawSet() { return this._rawSet; } get domRenderConfig() { return this.rawSet?.dataSet.config; } _queryElements(selector, rawSet) { const e = this.getElement(); if (e) { return ElementUtils.querySelectorAll(e, selector); } else { let startNode = rawSet.point?.start; let endNode = rawSet.point?.end; if (rawSet.point?.start && startNode && !startNode.isConnected && startNode instanceof HTMLElement && startNode.hasAttribute("id")) { rawSet.point.start = startNode = rawSet.dataSet.config.window.document.querySelector("#" + startNode.id); } if (rawSet.point?.end && endNode && !endNode.isConnected && endNode instanceof HTMLElement && endNode.hasAttribute("id")) { rawSet.point.end = endNode = rawSet.dataSet.config.window.document.querySelector("#" + endNode.id); } if (startNode && endNode) { return ElementUtils.querySelectorAll({ start: startNode, end: endNode }, selector); } } return []; } _cleanupEventListeners() { for (const { element, eventName, listener } of this._boundEventListeners) { element.removeEventListener(eventName, listener); } this._boundEventListeners = []; } _cleanupQueryBindings() { for (const { element, metadata } of this._queryBindings) { if (metadata.onDestroy) { metadata.onDestroy(this, element); } this._clearRefreshTimer(metadata); } this._queryBindings = []; } _cleanupEventBindings() { for (const { element, metadata, listener } of this._eventBindings) { element.removeEventListener(metadata.eventName, listener); if (metadata.onDestroy) { metadata.onDestroy(this, element); } this._clearRefreshTimer(metadata); } this._eventBindings = []; } _applyQueryBinding(queryInfo, elements, newElementsOnly = elements) { const { propertyKey, isMethod, convert } = queryInfo; elements.forEach((element) => { this._queryBindings.push({ element, metadata: queryInfo }); }); const processedElements = convert ? elements.map(convert) : elements; const processedNewElements = convert ? newElementsOnly.map(convert) : newElementsOnly; if (isMethod) { const method = this[propertyKey]; if (typeof method === "function") { const paramTypes = ReflectUtils.getParameterTypes(this, propertyKey); if (paramTypes && paramTypes.length > 0 && paramTypes[0] === Array) { method.call(this, processedNewElements, { all: processedElements }); } else { if (processedNewElements.length > 0) { method.call(this, processedNewElements[0], { all: processedElements, newAll: processedNewElements }); } } } } else { const propertyType = ReflectUtils.getType(this, propertyKey); if (propertyType === Array) { this[propertyKey] = processedElements; } else { this[propertyKey] = processedElements[0] || null; } } } _setupQueryDecorators(rawSet) { if (!rawSet) return; const queries = ReflectUtils.getMetadata(QUERY_METADATA_KEY, this.constructor); if (queries && Array.isArray(queries)) { for (const queryInfo of queries) { const elements = this._queryElements(queryInfo.selector, rawSet); this._applyQueryBinding(queryInfo, elements); this._setupRefreshTimer(queryInfo); } } } _updateQueryBinding(queryInfo, rawSet, forceUpdate = false) { const newElements = this._queryElements(queryInfo.selector, rawSet); const currentBindings = this._queryBindings.filter((binding) => binding.metadata === queryInfo); const currentElements = currentBindings.map((binding) => binding.element); const elementsChanged = forceUpdate || !this._arraysEqual(currentElements, newElements); if (elementsChanged) { let addedElements = []; if (forceUpdate) { currentBindings.forEach(({ element, metadata }) => { if (metadata.onDestroy) { metadata.onDestroy(this, element); } }); addedElements = newElements; } else { const removedElements = currentElements.filter((element) => !newElements.includes(element)); addedElements = newElements.filter((element) => !currentElements.includes(element)); removedElements.forEach((element) => { if (queryInfo.onDestroy) { queryInfo.onDestroy(this, element); } }); } this._queryBindings = this._queryBindings.filter((binding) => binding.metadata !== queryInfo); this._applyQueryBinding(queryInfo, newElements, addedElements); } } _updateQueryDecorators(rawSet) { if (!rawSet) return; const queries = ReflectUtils.getMetadata(QUERY_METADATA_KEY, this.constructor); if (queries && Array.isArray(queries)) { for (const queryInfo of queries) { this._updateQueryBinding(queryInfo, rawSet); } } } _arraysEqual(arr1, arr2) { if (arr1.length !== arr2.length) return false; return arr1.every((element, index) => element === arr2[index]); } _setupRefreshTimer(metadata) { if (!metadata.refreshTime || !this._rawSet) return; const timer = setInterval(() => { if (!this._rawSet) return; if ("selector" in metadata) { this._updateQueryBinding(metadata, this._rawSet); } else { this._updateEventBinding(metadata, this._rawSet); } }, metadata.refreshTime); this._refreshTimers.set(metadata, timer); } _clearRefreshTimer(metadata) { const timer = this._refreshTimers.get(metadata); if (timer) { clearInterval(timer); this._refreshTimers.delete(metadata); } } _clearAllRefreshTimers() { for (const timer of Array.from(this._refreshTimers.values())) { clearInterval(timer); } this._refreshTimers.clear(); } async onRawSetRendered(rawSet, otherData) { this.onRawSetRenderedOtherDataSubject.next(otherData); const queries = ReflectUtils.getMetadata(QUERY_METADATA_KEY, this.constructor); if (queries) { queries.filter((it) => it.refreshRawSetRendered).forEach((queryInfo) => { this._updateQueryBinding(queryInfo, rawSet, true); }); } const events = ReflectUtils.getMetadata(EVENT_METADATA_KEY, this.constructor); if (events) { events.filter((it) => it.refreshRawSetRendered).forEach((eventInfo) => { this._updateEventBinding(eventInfo, rawSet, true); }); } } async onChildRawSetRendered() { this.onChildRawSetRenderedOtherDataSubject.next(); } /** * Manually refresh all query and event decorators * Useful when DOM elements are dynamically added/removed and you want to update bindings */ refreshDecorators() { if (this._rawSet) { this._updateQueryDecorators(this._rawSet); this._updateEventDecorators(this._rawSet); } } /** * Manually refresh specific query decorators by property key * @param propertyKeys - Array of property keys to refresh, or single property key */ refreshQueryDecorators(propertyKeys) { if (!this._rawSet) return; const keys = Array.isArray(propertyKeys) ? propertyKeys : [propertyKeys]; const queries = ReflectUtils.getMetadata(QUERY_METADATA_KEY, this.constructor); if (queries && Array.isArray(queries)) { for (const queryInfo of queries) { if (keys.includes(queryInfo.propertyKey)) { this._updateQueryBinding(queryInfo, this._rawSet, true); } } } } /** * Manually refresh specific event decorators by method name * @param methodNames - Array of method names to refresh, or single method name */ refreshEventDecorators(methodNames) { if (!this._rawSet) return; const names = Array.isArray(methodNames) ? methodNames : [methodNames]; const events = ReflectUtils.getMetadata(EVENT_METADATA_KEY, this.constructor); if (events && Array.isArray(events)) { for (const eventInfo of events) { if (names.includes(eventInfo.methodName)) { this._updateEventBinding(eventInfo, this._rawSet, true); } } } } _applyEventBinding(eventInfo, elements) { elements.forEach((element) => { const listener = this[eventInfo.methodName].bind(this); element.addEventListener(eventInfo.eventName, listener); this._eventBindings.push({ element, metadata: eventInfo, listener }); this._boundEventListeners.push({ element, eventName: eventInfo.eventName, listener }); }); } _setupEventDecorators(rawSet) { if (!rawSet) return; const events = ReflectUtils.getMetadata(EVENT_METADATA_KEY, this.constructor); if (events && Array.isArray(events)) { for (const eventInfo of events) { const elements = this._queryElements(eventInfo.query, rawSet); this._applyEventBinding(eventInfo, elements); this._setupRefreshTimer(eventInfo); } } } _updateEventBinding(eventInfo, rawSet, forceUpdate = false) { const newElements = this._queryElements(eventInfo.query, rawSet); const currentBindings = this._eventBindings.filter((binding) => binding.metadata === eventInfo); const currentElements = currentBindings.map((binding) => binding.element); const elementsChanged = forceUpdate || !this._arraysEqual(currentElements, newElements); if (elementsChanged) { if (forceUpdate) { currentBindings.forEach(({ element, metadata, listener }) => { element.removeEventListener(metadata.eventName, listener); if (metadata.onDestroy) { metadata.onDestroy(this, element); } }); } else { const removedElements = currentElements.filter((element) => !newElements.includes(element)); const removedBindings = currentBindings.filter((binding) => removedElements.includes(binding.element)); removedBindings.forEach(({ element, metadata, listener }) => { element.removeEventListener(metadata.eventName, listener); if (metadata.onDestroy) { metadata.onDestroy(this, element); } }); } this._eventBindings = this._eventBindings.filter((binding) => binding.metadata !== eventInfo); this._boundEventListeners = this._boundEventListeners.filter((binding) => !currentBindings.some((cb) => cb.element === binding.element && cb.metadata.eventName === binding.eventName)); this._applyEventBinding(eventInfo, newElements); } } _updateEventDecorators(rawSet) { if (!rawSet) return; const events = ReflectUtils.getMetadata(EVENT_METADATA_KEY, this.constructor); if (events && Array.isArray(events)) { for (const eventInfo of events) { this._updateEventBinding(eventInfo, rawSet); } } } getAttributeNames(attribute2 = this._attribute) { const target = attribute2; if (!target) return []; const keys = Object.keys(target); const names = /* @__PURE__ */ new Set(); for (const k of keys) { names.add(k); } return Array.from(names); } getAttribute(name, attribute2 = this._attribute) { const target = attribute2; if (!target) { return null; } const nameString = String(name); let p = nameString; if (p in target) { return target[p]; } p = ConvertUtils.snakeToCamelCase(nameString); if (p in target) { return target[p]; } p = ConvertUtils.camelToSnakeCase(nameString); if (p in target) { return target[p]; } p = nameString.toUpperCase(); if (p in target) { return target[p]; } p = nameString.toLowerCase(); if (p in target) { return target[p]; } return null; } hasAttribute(name) { return this.getAttribute(name) !== null; } equalsAttributeName(n, name) { return n === name || ConvertUtils.snakeToCamelCase(n) === name || ConvertUtils.camelToSnakeCase(n) === name || n.toUpperCase() === name.toUpperCase() || n.toLowerCase() === name.toLowerCase(); } get uuid() { return this._rawSet?.uuid; } get childrenSet() { return this._children; } get children() { return this._children.map((it) => it.instance); } getElement() { return (this.render ?? this.rawSet?.dataSet?.render)?.getElement?.(); } getFirstChild(type) { return this._children.find((it) => it.instance instanceof type)?.instance; } getParentThis() { return this.render?.parentThis; } getChildren(typeOrTypes) { const targets = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes]; return this._children.filter((childSet) => targets.some((targetConstructor) => childSet.instance instanceof targetConstructor))?.map((it) => it.instance); } setRawSet(rawSet) { this._rawSet = rawSet; if (this._rawSet) { this.setAttribute(this._rawSet.dataSet?.render?.attribute); } } setAttribute(attribute2) { this._attribute = attribute2; } // TODO: rawSet isConnected로 체크해서 들어가있는것만 처리되도록 또는 이미들어가있는것도 뺴야될것같은데.. 적용하긴했음..지켜봐야될듯. onCreatedThisChild(child, data) { const n = [...this._children]; n.push({ instance: child, data }); this._children = n.filter((it) => it.data.render?.rawSet?.isConnected); this.childrenSetSubject.next(this._children); if (this._rawSet) { this._updateQueryDecorators(this._rawSet); this._updateEventDecorators(this._rawSet); } } onCreatedThisChildDebounce(childrenSet) { } onRawSetRenderedDebounce(childrenSet) { } onChildRawSetRenderedDebounce() { } onCreateRenderData(data) { this._render = data.render; if (data.render?.rawSet) { this.setRawSet(data.render.rawSet); } if (this._config?.onlyParentType) { const isOk = Array.isArray(this._config?.onlyParentType) ? this._config?.onlyParentType : [this._config?.onlyParentType].some((it) => data.render?.parentThis instanceof it); if (!isOk) { throw new Error("only my parentThis " + this._config?.onlyParentType); } } } onChangeAttrRender(name, value, other) { if (other?.rawSet) { this.setRawSet(other.rawSet); } const attributes = ReflectUtils.getMetadata(ATTRIBUTE_METADATA_KEY, this.constructor); if (attributes && Array.isArray(attributes)) { for (const attrInfo of attributes) { if (this.equalsAttributeName(name, attrInfo.name)) { const finalValue = attrInfo.converter ? attrInfo.converter(value) : value; if (attrInfo.isMethod) { const method = this[attrInfo.propertyKey]; if (typeof method === "function") { method.call(this, finalValue); } } else { this[attrInfo.propertyKey] = finalValue; } } } } } async onInitRender(param, rawSet) { const window = rawSet.dataSet?.config?.window; this.createChildrenDebounceSubscription = this.childrenSetSubject.pipe(MessageOperator.debounceTime(this.componentConfig?.createChildrenDebounce ?? 1, { setTimeout: window ? window.setTimeout.bind(window) : void 0, clearTimeout: window ? window.clearTimeout.bind(window) : void 0 })).subscribe((it) => { this.onCreatedThisChildDebounce(it); }); this.onRawSetRenderedOtherDataSubjectSubscription = this.onRawSetRenderedOtherDataSubject.pipe(MessageOperator.bufferTime(this.componentConfig?.onRawSetRenderedOtherDataDebounce ?? 1, { skipEmpty: true, setInterval: window ? window.setInterval.bind(window) : void 0, clearInterval: window ? window.clearInterval.bind(window) : void 0, setTimeout: window ? window.setTimeout.bind(window) : void 0, clearTimeout: window ? window.clearTimeout.bind(window) : void 0 })).subscribe((it) => { this.onRawSetRenderedDebounce(it); }); this.onChildRawSetRenderedOtherDataSubjectSubscription = this.onChildRawSetRenderedOtherDataSubject.pipe(MessageOperator.debounceTime(this.componentConfig?.onChildRawSetRenderedOtherDataDebounce ?? 10, { setTimeout: window ? window.setTimeout.bind(window) : void 0, clearTimeout: window ? window.clearTimeout.bind(window) : void 0 })).subscribe((it) => { this.onChildRawSetRenderedDebounce(); }); if (rawSet) { this.setRawSet(rawSet); const e = this.getElement(); if (e) { e.component = this; } this._setupQueryDecorators(rawSet); this._setupEventDecorators(rawSet); } } onDrThisBind() { } onDrThisUnBind() { this.onDestroy(); } onDestroyRender(data) { this.onDestroy(); } onDestroy() { this._cleanupEventListeners(); this._cleanupQueryBindings(); this._cleanupEventBindings(); this._clearAllRefreshTimers(); this.createChildrenDebounceSubscription?.unsubscribe(); this.onRawSetRenderedOtherDataSubjectSubscription?.unsubscribe(); this.onChildRawSetRenderedOtherDataSubjectSubscription?.unsubscribe(); } } __decorate([ DomRenderNoProxy, __metadata("design:type", typeof (_a = typeof RawSet !== "undefined" && RawSet) === "function" ? _a : Object) ], ComponentBase.prototype, "_rawSet", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Array) ], ComponentBase.prototype, "_children", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Object) ], ComponentBase.prototype, "_render", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Object) ], ComponentBase.prototype, "_attribute", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Array) ], ComponentBase.prototype, "_boundEventListeners", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Array) ], ComponentBase.prototype, "_queryBindings", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Array) ], ComponentBase.prototype, "_eventBindings", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", typeof (_b = typeof Map !== "undefined" && Map) === "function" ? _b : Object) ], ComponentBase.prototype, "_refreshTimers", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Object) ], ComponentBase.prototype, "childrenSetSubject", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Object) ], ComponentBase.prototype, "createChildrenDebounceSubscription", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Object) ], ComponentBase.prototype, "onRawSetRenderedOtherDataSubject", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Object) ], ComponentBase.prototype, "onChildRawSetRenderedOtherDataSubject", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Object) ], ComponentBase.prototype, "onRawSetRenderedOtherDataSubjectSubscription", void 0); __decorate([ DomRenderNoProxy, __metadata("design:type", Object) ], ComponentBase.prototype, "onChildRawSetRenderedOtherDataSubjectSubscription", void 0); export { ATTRIBUTE_METADATA_KEY, ComponentBase, EVENT_METADATA_KEY, QUERY_METADATA_KEY, attribute, event, query }; //# sourceMappingURL=ComponentBase.js.map