fix (frontend/player): instant insertion of style elements & correct children deletion
This commit is contained in:
parent
683b0b3ceb
commit
dabe7f0e44
2 changed files with 52 additions and 26 deletions
|
|
@ -14,6 +14,16 @@ type HTMLElementWithValue = HTMLInputElement | HTMLTextAreaElement | HTMLSelectE
|
|||
const IGNORED_ATTRS = [ "autocomplete", "name" ];
|
||||
const ATTR_NAME_REGEXP = /([^\t\n\f \/>"'=]+)/; // regexp costs ~
|
||||
|
||||
|
||||
// TODO: filter out non-relevant prefixes
|
||||
// function replaceCSSPrefixes(css: string) {
|
||||
// return css
|
||||
// .replace(/\-ms\-/g, "")
|
||||
// .replace(/\-webkit\-/g, "")
|
||||
// .replace(/\-moz\-/g, "")
|
||||
// .replace(/\-webkit\-/g, "")
|
||||
// }
|
||||
|
||||
export default class DOMManager extends ListWalker<Message> {
|
||||
private vTexts: Map<number, VText> = new Map() // map vs object here?
|
||||
private vElements: Map<number, VElement> = new Map()
|
||||
|
|
@ -144,6 +154,9 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
this.insertNode(msg)
|
||||
this.removeBodyScroll(msg.id, vn)
|
||||
this.removeAutocomplete(element)
|
||||
if (['STYLE', 'style', 'link'].includes(msg.tag)) {
|
||||
vn.enforceInsertion()
|
||||
}
|
||||
return
|
||||
case "move_node":
|
||||
this.insertNode(msg);
|
||||
|
|
@ -287,7 +300,7 @@ export default class DOMManager extends ListWalker<Message> {
|
|||
|
||||
// @ts-ignore
|
||||
this.vElements.get(0).applyChanges()
|
||||
this.vRoots.forEach(rt => rt.applyChanges())
|
||||
this.vRoots.forEach(rt => rt.applyChanges()) // MBTODO (optimisation): affected set
|
||||
|
||||
// Thinkabout (read): css preload
|
||||
// What if we go back before it is ready? We'll have two handlres?
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ abstract class VParent {
|
|||
this.children = this.children.filter(ch => ch !== child)
|
||||
child.parentNode = null
|
||||
}
|
||||
|
||||
applyChanges() {
|
||||
const node = this.node
|
||||
if (!node) {
|
||||
|
|
@ -25,17 +26,20 @@ abstract class VParent {
|
|||
return
|
||||
}
|
||||
const realChildren = node.childNodes
|
||||
for (let i = 0; i < this.children.length; i++) {
|
||||
const ch = this.children[i]
|
||||
ch.applyChanges()
|
||||
if (ch.node.parentNode !== node) {
|
||||
const nextSibling = realChildren[i]
|
||||
node.insertBefore(ch.node, nextSibling || null)
|
||||
}
|
||||
if (realChildren[i] !== ch.node) {
|
||||
node.removeChild(realChildren[i])
|
||||
let i: number
|
||||
// apply correct children order
|
||||
for (i = 0; i < this.children.length; i++) {
|
||||
const child = this.children[i]
|
||||
child.applyChanges()
|
||||
//while (realChildren[i] shouldn't be there) remove it //optimal way
|
||||
if (realChildren[i] !== child.node) {
|
||||
node.insertBefore(child.node, realChildren[i] || null)
|
||||
}
|
||||
}
|
||||
// remove rest
|
||||
while(realChildren[i]) {
|
||||
node.removeChild(realChildren[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +56,9 @@ export class VDocument extends VParent {
|
|||
// iframe not mounted yet
|
||||
return
|
||||
}
|
||||
const htmlNode = this.children[0].node
|
||||
const child = this.children[0]
|
||||
child.applyChanges()
|
||||
const htmlNode = child.node
|
||||
if (htmlNode.parentNode !== this.node) {
|
||||
this.node.replaceChild(htmlNode, this.node.documentElement)
|
||||
}
|
||||
|
|
@ -66,7 +72,6 @@ export class VFragment extends VParent {
|
|||
export class VElement extends VParent {
|
||||
parentNode: VParent | null = null
|
||||
private newAttributes: Map<string, string | false> = new Map()
|
||||
//private props: Record<string, string | number | boolean>
|
||||
constructor(public readonly node: Element) { super() }
|
||||
setAttribute(name: string, value: string) {
|
||||
this.newAttributes.set(name, value)
|
||||
|
|
@ -75,6 +80,14 @@ export class VElement extends VParent {
|
|||
this.newAttributes.set(name, false)
|
||||
}
|
||||
|
||||
enforceInsertion() {
|
||||
let vNode: VElement = this
|
||||
while (vNode.parentNode instanceof VElement) {
|
||||
vNode = vNode.parentNode
|
||||
}
|
||||
(vNode.parentNode || vNode).applyChanges()
|
||||
}
|
||||
|
||||
applyChanges() {
|
||||
this.newAttributes.forEach((value, key) => {
|
||||
if (value === false) {
|
||||
|
|
@ -96,31 +109,31 @@ export class VElement extends VParent {
|
|||
type StyleSheetCallback = (s: CSSStyleSheet) => void
|
||||
export type StyleElement = HTMLStyleElement | SVGStyleElement
|
||||
export class VStyleElement extends VElement {
|
||||
private loaded = false
|
||||
// private loaded = false
|
||||
private stylesheetCallbacks: StyleSheetCallback[] = []
|
||||
constructor(public readonly node: StyleElement) {
|
||||
super(node) // Is it compiled correctly or with 2 node assignments?
|
||||
node.onload = () => {
|
||||
const sheet = node.sheet
|
||||
if (sheet) {
|
||||
this.stylesheetCallbacks.forEach(cb => cb(sheet))
|
||||
} else {
|
||||
console.warn("Style onload: sheet is null")
|
||||
}
|
||||
this.loaded = true
|
||||
}
|
||||
// node.onload = () => {
|
||||
// const sheet = node.sheet
|
||||
// if (sheet) {
|
||||
// this.stylesheetCallbacks.forEach(cb => cb(sheet))
|
||||
// } else {
|
||||
// console.warn("Style onload: sheet is null")
|
||||
// }
|
||||
// this.loaded = true
|
||||
// }
|
||||
}
|
||||
|
||||
onStyleSheet(cb: StyleSheetCallback) {
|
||||
if (this.loaded) {
|
||||
// if (this.loaded) {
|
||||
if (!this.node.sheet) {
|
||||
console.warn("Style tag is loaded, but sheet is null")
|
||||
return
|
||||
}
|
||||
cb(this.node.sheet)
|
||||
} else {
|
||||
this.stylesheetCallbacks.push(cb)
|
||||
}
|
||||
// } else {
|
||||
// this.stylesheetCallbacks.push(cb)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue