diff --git a/package.json b/package.json index 02e168c..acd4516 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bd/core", - "version": "1.8.8", + "version": "1.9.0", "type": "module", "description": "百搭UI组件库的核心", "main": "dist/index.js", diff --git a/src/constants.js b/src/constants.js index 78e6d21..fb81dee 100644 --- a/src/constants.js +++ b/src/constants.js @@ -36,7 +36,6 @@ const boolMap = Object.create(null) export { boolMap } export const WC_PART = Symbol('wc_path') -export const NO_CHANGE = Symbol('wc-noChange') export const NOTHING = Symbol('wc-nothing') export const __finalized__ = Symbol('finalized') export const __props__ = Symbol('props') diff --git a/src/html.js b/src/html.js index 3557309..a074251 100644 --- a/src/html.js +++ b/src/html.js @@ -1,4 +1,4 @@ -import { boolMap, WC_PART, NO_CHANGE, NOTHING } from './constants.js' +import { boolMap, WC_PART, NOTHING } from './constants.js' import { animate, MODES } from './anim.js' const boundAttributeSuffix = '$wc$' @@ -45,6 +45,8 @@ const COMMENT_PART = 7 const templateCache = new WeakMap() const walker = document.createTreeWalker(document, 129, null, false) +function noop() {} + function getTemplateHtml(strings, type) { let len = strings.length - 1 let attrNames = [] @@ -170,12 +172,19 @@ class Template { ) let statics = value.split(marker) let m = /([#:@])?(.*)/.exec(realName) + let decorates = [] + + if (m[1] === '@' && m[2].includes('.')) { + decorates = m[2].split('.') + m[2] = decorates.shift() + } parts.push({ type: ATTRIBUTE_PART, index: nodeIndex, name: m[2], strings: statics, + decorates, ctor: m[1] === ':' ? PropertyPart @@ -231,50 +240,6 @@ class Template { return el } } -function resolveDirective(part, value, parent = part, attributeIndex) { - if (value === NO_CHANGE) { - return value - } - let currentDirective = - attributeIndex !== void 0 - ? parent.__directives?.[attributeIndex] - : parent.__directive - - let nextDirectiveConstructor = isPrimitive(value) - ? void 0 - : value['_$litDirective$'] - - if (currentDirective?.constructor !== nextDirectiveConstructor) { - currentDirective._$notifyDirectiveConnectionChanged?.call( - currentDirective, - false - ) - - if (nextDirectiveConstructor === void 0) { - currentDirective = void 0 - } else { - currentDirective = new nextDirectiveConstructor(part) - currentDirective._$initialize(part, parent, attributeIndex) - } - if (attributeIndex !== void 0) { - if (!parent.__directives) { - parent.__directives = [] - } - parent.__directives[attributeIndex] = currentDirective - } else { - parent.__directive = currentDirective - } - } - if (currentDirective !== void 0) { - value = resolveDirective( - part, - currentDirective._$resolve(part, value.values), - currentDirective, - attributeIndex - ) - } - return value -} class TemplateInstance { constructor(template, parent) { @@ -334,6 +299,7 @@ class TemplateInstance { node, templatePart.name, templatePart.strings, + templatePart.decorates, this, options ) @@ -364,7 +330,7 @@ class TemplateInstance { for (let part of this._parts) { if (part !== void 0) { if (part.strings !== void 0) { - part._$setValue(values, part, i) + part._$setValue(values, i) i += part.strings.length - 2 } else { part._$setValue(values[i]) @@ -403,15 +369,14 @@ class ChildPart { get endNode() { return this._$endNode } - _$setValue(value, directiveParent = this) { - value = resolveDirective(this, value, directiveParent) + _$setValue(value) { if (isPrimitive(value)) { if (value === NOTHING || value == null || value === '') { if (this._$committedValue !== NOTHING) { this.#clear() } this._$committedValue = NOTHING - } else if (value !== this._$committedValue && value !== NO_CHANGE) { + } else if (value !== this._$committedValue) { this._commitText(value) } } else if (value['__dom_type__'] !== void 0) { @@ -522,12 +487,13 @@ class ChildPart { } // 常规属性 class AttributePart { - constructor(element, name, strings, parent, options = {}) { + constructor(element, name, strings, decorates, parent, options = {}) { this.type = ATTRIBUTE_PART this._$committedValue = NOTHING this._$disconnectableChildren = void 0 this.element = element this.name = name + this.decorates = decorates this._$parent = parent this.options = options if (strings.length > 2 || strings[0] !== '' || strings[1] !== '') { @@ -544,15 +510,12 @@ class AttributePart { return this._$parent._$isConnected } - _$setValue(value, directiveParent = this, valueIndex, noCommit) { + _$setValue(value, valueIndex) { let strings = this.strings - let change = false + let changed = false if (strings === void 0) { - value = resolveDirective(this, value, directiveParent, 0) - change = - !isPrimitive(value) || - (value !== this._$committedValue && value !== NO_CHANGE) - if (change) { + changed = !isPrimitive(value) || value !== this._$committedValue + if (changed) { this._$committedValue = value } } else { @@ -560,16 +523,9 @@ class AttributePart { value = strings[0] for (let i = 0; i < strings.length - 1; i++) { - let v = resolveDirective( - this, - values[valueIndex + i], - directiveParent, - i - ) - if (v === NO_CHANGE) { - v = this._$committedValue[i] - } - change || (change = !isPrimitive(v) || v !== this._$committedValue[i]) + let v = values[valueIndex + i] + + changed || (changed = !isPrimitive(v) || v !== this._$committedValue[i]) if (v === NOTHING) { value = NOTHING } else if (value !== NOTHING) { @@ -578,7 +534,7 @@ class AttributePart { this._$committedValue[i] = v } } - if (change && !noCommit) { + if (changed) { this.commitValue(value) } } @@ -638,57 +594,87 @@ class AnimPart extends AttributePart { // 事件属性 class EventPart extends AttributePart { + #listener = null + + #prevent = noop + #stop = noop + #checkSelf = noop + constructor(...args) { super(...args) this.type = EVENT_PART } - _$setValue(newListener, directiveParent = this) { - newListener = - resolveDirective(this, newListener, directiveParent, 0) || NOTHING - if (newListener === NO_CHANGE) { - return - } + _$setValue(listener) { let host = this.options.host - let oldListener = this._$committedValue - let shouldRemoveListener = - (newListener === NOTHING && oldListener !== NOTHING) || - newListener.capture !== oldListener.capture || - newListener.once !== oldListener.once || - newListener.passive !== oldListener.passive - let shouldAddListener = - newListener !== NOTHING && - (oldListener === NOTHING || shouldRemoveListener) + let options = {} - if (shouldRemoveListener) { - this.element.removeEventListener(this.name, this, oldListener) + if (this.decorates.length) { + for (let it of this.decorates) { + switch (it) { + case 'stop': + this.#stop = ev => ev.stopPropagation() + break + case 'prevent': + this.#prevent = ev => ev.preventDefault() + break + case 'self': + this.#checkSelf = ev => ev.target === this.element + break + case 'capture': + case 'once': + case 'passive': + options[it] = true + break + } + } } - if (shouldAddListener) { - this.element.addEventListener(this.name, this, newListener) + + let shouldRemove = listener !== this.#listener + + if (this.#listener && host.$events[this.name]) { + for (let it of host.$events[this.name]) { + if (it.el === this.element) { + shouldRemove = + options.capture !== it.capture || + options.once !== it.once || + options.passive !== it.passive + if (shouldRemove) { + this.element.removeEventListener(this.name, it.listener, it.options) + } + break + } + } } - this._$committedValue = newListener - if (host) { + + if (listener && shouldRemove) { + this.element.addEventListener(this.name, this, options) + this.#listener = listener + if (host.$events[this.name]) { host.$events[this.name].push({ el: this.element, - listener: this + listener: this, + options }) } else { host.$events[this.name] = [ { el: this.element, - listener: this + listener: this, + options } ] } } } - handleEvent(event) { - if (typeof this._$committedValue === 'function') { - this._$committedValue.call(this.options.host || this.element, event) - } else { - this._$committedValue.handleEvent(event) + handleEvent(ev) { + this.#stop(ev) + this.#prevent(ev) + if (this.#checkSelf(ev) === false) { + return } + this.#listener.call(this.options.host, ev) } } @@ -703,9 +689,7 @@ class ElementPart { get _$isConnected() { return this._$parent._$isConnected } - _$setValue(value) { - resolveDirective(this, value) - } + _$setValue(value) {} } export function render(value, container, options = {}) {