Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/nuedom/src/compiler/html5.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const JS = '_ $e document window location localStorage sessionStorage ale

export const EVENTS = 'click submit change input focus blur keydown keyup keypress mouseover mouseout mousedown mouseup mousemove mouseenter mouseleave wheel scroll resize load unload beforeunload error abort touchstart touchend touchmove touchcancel drag dragstart dragend dragenter dragleave dragover drop animationstart animationend animationiteration transitionend contextmenu dblclick pointerdown pointermove cut copy paste'.split(' ')

export const BOOLEAN = 'disabled checked selected hidden readonly required autofocus autoplay async controls defer loop multiple muted nowrap open reversed scoped seamless sorted translate visibility pointer-events draggable contenteditable'.split(' ')
export const BOOLEAN = 'disabled checked selected hidden readonly required autofocus autoplay async controls defer loop multiple muted nowrap open reversed scoped seamless sorted translate visibility pointer-events contenteditable'.split(' ')


export const HTML5_TAGS = 'a abbr address area article aside audio b base bdi bdo blockquote body br button canvas caption cite code col colgroup data datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hr html i iframe img input ins kbd label legend li link main map mark meta meter nav noscript object ol optgroup option output p param picture pre progress q rp rt ruby s samp script section select small source span strong style sub summary sup table tbody td template textarea tfoot th thead time title tr track u ul var video wbr slot portal'.split(' ')
Expand Down
37 changes: 33 additions & 4 deletions packages/nuedom/src/dom/diff.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

// Nue • (c) 2025 Tero Piirainen & contributors, MIT Licensed

/*
Expand Down Expand Up @@ -27,13 +26,17 @@ export function domdiff(prev, next) {
const parent = prev.parentNode
if (prev == next) return prev
if (!prev && next) return parent?.appendChild(next)
if (!next && prev) return parent?.removeChild(prev)
if (!next && prev){
cleanupObserver(prev)
return parent?.removeChild(prev)
}

if (prev.nodeType == 3 && next.nodeType == 3) {
prev.textContent = next.textContent
return prev
}
if (prev.nodeType != next.nodeType || prev.tagName != next.tagName) {
cleanupObserver(prev)
return parent.replaceChild(next, prev)
}

Expand Down Expand Up @@ -61,7 +64,11 @@ function diffChildren(prev, kids) {
const prevKid = prevKids[i]
const kid = kids[i]
if (!prevKid) prev.appendChild(kid)
else if (!kid) prev.removeChild(prevKids[i])
else if (!kid)
{
cleanupObserver(prevKids[i])
prev.removeChild(prevKids[i])
}
else domdiff(prevKid, kid, prev)
}
}
Expand All @@ -70,10 +77,32 @@ function diffChildrenByKey(prev, kids) {
const prevKids = Array.from(prev.childNodes)
const keyMap = {}
for (let kid of prevKids) keyMap[kid.getAttribute('key')] = kid


for (let kid of prevKids) {
const key = kid.getAttribute('key')
if (!kids.find(k => k.getAttribute('key') === key)) {
cleanupObserver(kid)
}
}

while (prev.firstChild) prev.removeChild(prev.firstChild)
for (let kid of kids) {
const key = kid.getAttribute('key')
const prevKid = keyMap[key]
prev.appendChild(prevKid ? domdiff(prevKid, kid, prev) : kid)
}
}
}


function cleanupObserver(node) {
if (node._nueObserver) {
node._nueObserver.disconnect()
delete node._nueObserver
}


if (node.childNodes) {
Array.from(node.childNodes).forEach(child => cleanupObserver(child))
}
}
50 changes: 45 additions & 5 deletions packages/nuedom/src/dom/node.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@

// Nue • (c) 2025 Tero Piirainen & contributors, MIT Licensed

import { domdiff } from './diff.js'

const is_browser = typeof window == 'object'


export function createNode(ast, data={}, opts={}, parent) {
const { script } = ast
let root
Expand All @@ -25,7 +23,7 @@ export function createNode(ast, data={}, opts={}, parent) {
}

// Object.assign(self, getAttrData(ast, self))
const self = { ...data, ...opts.globals, ...getAttrData(ast, data), update, parent }
const self = { ...data, ...opts.globals, ...getAttrData(ast, data), update, cleanup,parent }


if (script) {
Expand Down Expand Up @@ -80,7 +78,31 @@ export function createNode(ast, data={}, opts={}, parent) {

setAttributes(tag, ast, self)
if (parent && ast.is_child) setAttributes(tag, parent.ast, parent.self)



if (is_browser) {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes') {
const attrName = mutation.attributeName
const attr = ast.attr?.find(a => a.name === attrName)

if (attr && !attr.is_data) {
const newValue = tag.getAttribute(attrName)
if (attr.bool) {
self[attrName] = tag.hasAttribute(attrName)
} else {
self[attrName] = newValue
}
}
}
})
})

observer.observe(tag, { attributes: true })

tag._nueObserver = observer
}
const am = tag.classList.length
if (am > (opts.max_class_names || 3)) {
console.error(`More than ${am} class names in class="${tag.classList }"`)
Expand Down Expand Up @@ -202,14 +224,32 @@ export function createNode(ast, data={}, opts={}, parent) {
return frag
}

function cleanup() {
if (root) {
cleanupObserver(root)
}
fire('oncleanup')
}

function findComponent(ast, self) {
let { mount, tag } = ast
if (mount) tag = mount.fn ? exec(mount.fn, self) : mount.val
return opts.deps?.find(c => c != ast && tag == (c.is || c.tag))
}

return {
mount, update, render, ast, self, fire, get root() { return root },
mount, update, render, ast, self, fire,cleanup, get root() { return root },
}
}

function cleanupObserver(node) {
if (node._nueObserver) {
node._nueObserver.disconnect()
delete node._nueObserver
}

if (node.childNodes) {
Array.from(node.childNodes).forEach(child => cleanupObserver(child))
}
}

Expand Down