feat: add triggerDomUpdates utility and refactor DOM update logic in main component
This commit is contained in:
		
							parent
							
								
									df92e93e7c
								
							
						
					
					
						commit
						4aed034100
					
				
							
								
								
									
										52
									
								
								src/main.ts
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								src/main.ts
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -59,7 +59,7 @@ export default (options: ComponentOptions) => {
 | 
			
		|||
		constructor() {
 | 
			
		||||
			super()
 | 
			
		||||
 | 
			
		||||
			// initialize dom tree and append to shadow root
 | 
			
		||||
			// initialize dom tree and append to shadow root, as well as initialize state
 | 
			
		||||
			this._initialize()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +85,14 @@ export default (options: ComponentOptions) => {
 | 
			
		|||
							}
 | 
			
		||||
						}
 | 
			
		||||
						// trigger dom updates
 | 
			
		||||
						this._triggerDomUpdates(keyPath)
 | 
			
		||||
						utils.triggerDomUpdates(keyPath, {
 | 
			
		||||
							stateToElementsMap: this._stateToElementsMap,
 | 
			
		||||
							textBindings: this._textBindings,
 | 
			
		||||
							attributeBindings: this._attributeBindings,
 | 
			
		||||
							updateTextNode: this._updateTextNode.bind(this),
 | 
			
		||||
							getNestedState: this._getNestedState.bind(this),
 | 
			
		||||
							scheduleUpdate: this._scheduleUpdate.bind(this),
 | 
			
		||||
						})
 | 
			
		||||
						if (this._statesListeners[keyPath])
 | 
			
		||||
							this._statesListeners[keyPath](value)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -160,45 +167,6 @@ export default (options: ComponentOptions) => {
 | 
			
		|||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private _triggerDomUpdates(keyPath: string) {
 | 
			
		||||
			if (this._stateToElementsMap[keyPath]) {
 | 
			
		||||
				const updateQueue = new Set<HTMLElement>()
 | 
			
		||||
 | 
			
		||||
				for (const element of this._stateToElementsMap[keyPath]) {
 | 
			
		||||
					updateQueue.add(element)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				this._scheduleUpdate(updateQueue)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Update text bindings that depend on this state
 | 
			
		||||
			if (this._textBindings) {
 | 
			
		||||
				// this._textBindings.forEach((binding) => {
 | 
			
		||||
				for (const binding of this._textBindings)
 | 
			
		||||
					if (
 | 
			
		||||
						binding.expr === keyPath ||
 | 
			
		||||
						binding.expr.startsWith(`${keyPath}.`)
 | 
			
		||||
					)
 | 
			
		||||
						this._updateTextNode(
 | 
			
		||||
							binding.node,
 | 
			
		||||
							binding.expr,
 | 
			
		||||
							binding.originalContent,
 | 
			
		||||
						)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Update attribute bindings that depend on this state
 | 
			
		||||
			if (this._attributeBindings) {
 | 
			
		||||
				for (const binding of this._attributeBindings)
 | 
			
		||||
					if (
 | 
			
		||||
						binding.expr === keyPath ||
 | 
			
		||||
						binding.expr.startsWith(`${keyPath}.`)
 | 
			
		||||
					) {
 | 
			
		||||
						const value = this._getNestedState(binding.expr)
 | 
			
		||||
						if (value !== undefined)
 | 
			
		||||
							binding.element.setAttribute(binding.attrName, String(value))
 | 
			
		||||
					}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private _scheduleUpdate(elements: Set<HTMLElement>) {
 | 
			
		||||
			requestAnimationFrame(() => {
 | 
			
		||||
| 
						 | 
				
			
			@ -1025,4 +993,4 @@ export default (options: ComponentOptions) => {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	customElements.define(tag, CustomElementImpl)
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,9 @@
 | 
			
		|||
import { parseTemplate } from './parseTemplate'
 | 
			
		||||
import { processTemplateMacros } from './processTemplateMarco'
 | 
			
		||||
import parseTemplate from './parseTemplate'
 | 
			
		||||
import processTemplateMacros from './processTemplateMarco'
 | 
			
		||||
import triggerDomUpdates from './triggerDomUpdates'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
	parseTemplate,
 | 
			
		||||
	processTemplateMacros,
 | 
			
		||||
	triggerDomUpdates,
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
export function parseTemplate(template: string): Element {
 | 
			
		||||
export default function parseTemplate(template: string): Element {
 | 
			
		||||
	const parser = new DOMParser()
 | 
			
		||||
	const doc = parser.parseFromString(template, 'text/html')
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
export function processTemplateMacros(
 | 
			
		||||
export default function processTemplateMacros(
 | 
			
		||||
	element: Element,
 | 
			
		||||
	context: CustomElement,
 | 
			
		||||
	options: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										54
									
								
								src/utils/triggerDomUpdates.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/utils/triggerDomUpdates.ts
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,54 @@
 | 
			
		|||
export default function triggerDomUpdates(keyPath: string, ops: {
 | 
			
		||||
	stateToElementsMap: Record<string, Set<HTMLElement>>,
 | 
			
		||||
	scheduleUpdate: (elements: Set<HTMLElement>) => void,
 | 
			
		||||
	textBindings: Array<{
 | 
			
		||||
		node: Text
 | 
			
		||||
		expr: string
 | 
			
		||||
		originalContent: string
 | 
			
		||||
	}> | undefined,
 | 
			
		||||
	attributeBindings: Array<{
 | 
			
		||||
		element: Element
 | 
			
		||||
		attrName: string
 | 
			
		||||
		expr: string
 | 
			
		||||
		template: string
 | 
			
		||||
	}> | undefined,
 | 
			
		||||
	updateTextNode: (node: Text, expr: string, template: string) => void,
 | 
			
		||||
	getNestedState: (path: string) => unknown,
 | 
			
		||||
}) {
 | 
			
		||||
	if (ops.stateToElementsMap[keyPath]) {
 | 
			
		||||
		const updateQueue = new Set<HTMLElement>()
 | 
			
		||||
 | 
			
		||||
		for (const element of ops.stateToElementsMap[keyPath])
 | 
			
		||||
			updateQueue.add(element)
 | 
			
		||||
 | 
			
		||||
		ops.scheduleUpdate(updateQueue)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update text bindings that depend on this state
 | 
			
		||||
	if (ops.textBindings) {
 | 
			
		||||
		// this._textBindings.forEach((binding) => {
 | 
			
		||||
		for (const binding of ops.textBindings)
 | 
			
		||||
			if (
 | 
			
		||||
				binding.expr === keyPath ||
 | 
			
		||||
				binding.expr.startsWith(`${keyPath}.`)
 | 
			
		||||
			)
 | 
			
		||||
				ops.updateTextNode(
 | 
			
		||||
					binding.node,
 | 
			
		||||
					binding.expr,
 | 
			
		||||
					binding.originalContent,
 | 
			
		||||
				)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update attribute bindings that depend on this state
 | 
			
		||||
	if (ops.attributeBindings) {
 | 
			
		||||
		for (const binding of ops.attributeBindings)
 | 
			
		||||
			if (
 | 
			
		||||
				binding.expr === keyPath ||
 | 
			
		||||
				binding.expr.startsWith(`${keyPath}.`)
 | 
			
		||||
			) {
 | 
			
		||||
				const value = ops.getNestedState(binding.expr)
 | 
			
		||||
				if (value !== undefined)
 | 
			
		||||
					binding.element.setAttribute(binding.attrName, String(value))
 | 
			
		||||
			}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user