ui: adjusting slot placement and classes
This commit is contained in:
parent
7a85f318b6
commit
8603439716
4 changed files with 98 additions and 61 deletions
|
|
@ -110,7 +110,7 @@ abstract class VParent<T extends Node = Node> extends VNode<T> {
|
||||||
/* Inserting */
|
/* Inserting */
|
||||||
this.mountChildren();
|
this.mountChildren();
|
||||||
if (this.notMontedChildren.size !== 0) {
|
if (this.notMontedChildren.size !== 0) {
|
||||||
console.error('VParent: Something went wrong with children insertion');
|
console.error('VParent: Something went wrong with children insertion', this.notMontedChildren);
|
||||||
}
|
}
|
||||||
/* Removing in-between */
|
/* Removing in-between */
|
||||||
const { node } = this;
|
const { node } = this;
|
||||||
|
|
@ -155,62 +155,6 @@ export class VDocument extends VParent<Document> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VSlot extends VElement {
|
|
||||||
assignedNodes: VChild[] = [];
|
|
||||||
|
|
||||||
addAssigned(child: VChild) {
|
|
||||||
if (this.assignedNodes.indexOf(child) === -1) {
|
|
||||||
this.assignedNodes.push(child);
|
|
||||||
this.notMontedChildren.add(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeAssigned(child: VChild) {
|
|
||||||
this.assignedNodes = this.assignedNodes.filter((c) => c !== child);
|
|
||||||
this.notMontedChildren.delete(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
private mountAssigned() {
|
|
||||||
let nextMounted: VChild | null = null;
|
|
||||||
for (let i = this.assignedNodes.length - 1; i >= 0; i--) {
|
|
||||||
const child = this.assignedNodes[i];
|
|
||||||
if (this.notMontedChildren.has(child)) {
|
|
||||||
this.node.insertBefore(
|
|
||||||
child.node,
|
|
||||||
nextMounted ? nextMounted.node : null,
|
|
||||||
);
|
|
||||||
this.notMontedChildren.delete(child);
|
|
||||||
}
|
|
||||||
if (!this.notMontedChildren.has(child)) {
|
|
||||||
nextMounted = child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
applyChanges() {
|
|
||||||
if (this.assignedNodes.length > 0) {
|
|
||||||
this.assignedNodes.forEach((c) => c.applyChanges());
|
|
||||||
this.mountAssigned();
|
|
||||||
const { node } = this;
|
|
||||||
const realChildren = node.childNodes;
|
|
||||||
if (realChildren.length > 0) {
|
|
||||||
for (let j = 0; j < this.assignedNodes.length; j++) {
|
|
||||||
while (realChildren[j] !== this.assignedNodes[j].node) {
|
|
||||||
if (isNode(realChildren[j])) {
|
|
||||||
node.removeChild(realChildren[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (realChildren.length > this.assignedNodes.length) {
|
|
||||||
node.removeChild(node.lastChild as Node);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
super.applyChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class VShadowRoot extends VParent<ShadowRoot> {
|
export class VShadowRoot extends VParent<ShadowRoot> {
|
||||||
constructor(protected readonly createNode: () => ShadowRoot) {
|
constructor(protected readonly createNode: () => ShadowRoot) {
|
||||||
super();
|
super();
|
||||||
|
|
@ -353,6 +297,62 @@ export class VElement extends VParent<Element> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class VSlot extends VElement {
|
||||||
|
assignedNodes: VChild[] = [];
|
||||||
|
|
||||||
|
addAssigned(child: VChild) {
|
||||||
|
if (this.assignedNodes.indexOf(child) === -1) {
|
||||||
|
this.assignedNodes.push(child);
|
||||||
|
this.notMontedChildren.add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAssigned(child: VChild) {
|
||||||
|
this.assignedNodes = this.assignedNodes.filter((c) => c !== child);
|
||||||
|
this.notMontedChildren.delete(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
private mountAssigned() {
|
||||||
|
let nextMounted: VChild | null = null;
|
||||||
|
for (let i = this.assignedNodes.length - 1; i >= 0; i--) {
|
||||||
|
const child = this.assignedNodes[i];
|
||||||
|
if (this.notMontedChildren.has(child)) {
|
||||||
|
this.node.insertBefore(
|
||||||
|
child.node,
|
||||||
|
nextMounted ? nextMounted.node : null,
|
||||||
|
);
|
||||||
|
this.notMontedChildren.delete(child);
|
||||||
|
}
|
||||||
|
if (!this.notMontedChildren.has(child)) {
|
||||||
|
nextMounted = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyChanges() {
|
||||||
|
if (this.assignedNodes.length > 0) {
|
||||||
|
this.assignedNodes.forEach((c) => c.applyChanges());
|
||||||
|
this.mountAssigned();
|
||||||
|
const { node } = this;
|
||||||
|
const realChildren = node.childNodes;
|
||||||
|
if (realChildren.length > 0) {
|
||||||
|
for (let j = 0; j < this.assignedNodes.length; j++) {
|
||||||
|
while (realChildren[j] !== this.assignedNodes[j].node) {
|
||||||
|
if (isNode(realChildren[j])) {
|
||||||
|
node.removeChild(realChildren[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (realChildren.length > this.assignedNodes.length) {
|
||||||
|
node.removeChild(node.lastChild as Node);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.applyChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class VHTMLElement extends VElement {
|
export class VHTMLElement extends VElement {
|
||||||
constructor(node: HTMLElement) {
|
constructor(node: HTMLElement) {
|
||||||
super('HTML', false);
|
super('HTML', false);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@openreplay/tracker",
|
"name": "@openreplay/tracker",
|
||||||
"description": "The OpenReplay tracker main package",
|
"description": "The OpenReplay tracker main package",
|
||||||
"version": "17.0.0-beta.0",
|
"version": "17.1.0",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"logging",
|
"logging",
|
||||||
"replay"
|
"replay"
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ type TagTypeMap = {
|
||||||
style: HTMLStyleElement | SVGStyleElement
|
style: HTMLStyleElement | SVGStyleElement
|
||||||
link: HTMLLinkElement
|
link: HTMLLinkElement
|
||||||
canvas: HTMLCanvasElement
|
canvas: HTMLCanvasElement
|
||||||
|
slot: HTMLSlotElement
|
||||||
}
|
}
|
||||||
export function hasTag<T extends keyof TagTypeMap>(
|
export function hasTag<T extends keyof TagTypeMap>(
|
||||||
el: Node,
|
el: Node,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ import {
|
||||||
UnbindNodes,
|
UnbindNodes,
|
||||||
SetNodeAttribute,
|
SetNodeAttribute,
|
||||||
AdoptedSSInsertRuleURLBased,
|
AdoptedSSInsertRuleURLBased,
|
||||||
AdoptedSSAddOwner
|
AdoptedSSAddOwner,
|
||||||
|
SetNodeSlot,
|
||||||
} from '../messages.gen.js'
|
} from '../messages.gen.js'
|
||||||
import App from '../index.js'
|
import App from '../index.js'
|
||||||
import {
|
import {
|
||||||
|
|
@ -199,6 +200,7 @@ export default abstract class Observer {
|
||||||
private readonly indexes: Array<number> = []
|
private readonly indexes: Array<number> = []
|
||||||
private readonly attributesMap: Map<number, Set<string>> = new Map()
|
private readonly attributesMap: Map<number, Set<string>> = new Map()
|
||||||
private readonly textSet: Set<number> = new Set()
|
private readonly textSet: Set<number> = new Set()
|
||||||
|
private readonly slotMap: Map<number, number | undefined> = new Map()
|
||||||
private readonly disableSprites: boolean = false
|
private readonly disableSprites: boolean = false
|
||||||
/**
|
/**
|
||||||
* this option means that, instead of using link element with href to load css,
|
* this option means that, instead of using link element with href to load css,
|
||||||
|
|
@ -428,6 +430,18 @@ export default abstract class Observer {
|
||||||
|
|
||||||
private bindNode(node: Node): void {
|
private bindNode(node: Node): void {
|
||||||
const [id, isNew] = this.app.nodes.registerNode(node)
|
const [id, isNew] = this.app.nodes.registerNode(node)
|
||||||
|
if (isElementNode(node) && hasTag(node, 'slot')) {
|
||||||
|
this.app.nodes.attachNodeListener(node, 'slotchange', () => {
|
||||||
|
const sl = node as HTMLSlotElement
|
||||||
|
sl.assignedNodes({ flatten: true }).forEach((n) => {
|
||||||
|
const nid = this.app.nodes.getID(n)
|
||||||
|
if (nid !== undefined) {
|
||||||
|
this.recents.set(nid, RecentsType.Removed)
|
||||||
|
this.commitNode(nid)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
this.recents.set(id, RecentsType.New)
|
this.recents.set(id, RecentsType.New)
|
||||||
} else if (this.recents.get(id) !== RecentsType.New) {
|
} else if (this.recents.get(id) !== RecentsType.New) {
|
||||||
|
|
@ -463,6 +477,9 @@ export default abstract class Observer {
|
||||||
|
|
||||||
private unbindTree(node: Node) {
|
private unbindTree(node: Node) {
|
||||||
const id = this.app.nodes.unregisterNode(node)
|
const id = this.app.nodes.unregisterNode(node)
|
||||||
|
if (id !== undefined) {
|
||||||
|
this.slotMap.delete(id)
|
||||||
|
}
|
||||||
if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
|
if (id !== undefined && this.recents.get(id) === RecentsType.Removed) {
|
||||||
// Sending RemoveNode only for parent to maintain
|
// Sending RemoveNode only for parent to maintain
|
||||||
this.app.send(RemoveNode(id))
|
this.app.send(RemoveNode(id))
|
||||||
|
|
@ -501,8 +518,8 @@ export default abstract class Observer {
|
||||||
if (isRootNode(node)) {
|
if (isRootNode(node)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// @ts-ignore SALESFORCE
|
const slot = (node as any).assignedSlot as HTMLSlotElement | null
|
||||||
const parent = node.assignedSlot ? node.assignedSlot : node.parentNode
|
const parent = node.parentNode
|
||||||
let parentID: number | undefined
|
let parentID: number | undefined
|
||||||
|
|
||||||
// Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
|
// Disable parent check for the upper context HTMLHtmlElement, because it is root there... (before)
|
||||||
|
|
@ -576,10 +593,29 @@ export default abstract class Observer {
|
||||||
this.app.send(CreateTextNode(id, parentID as number, index))
|
this.app.send(CreateTextNode(id, parentID as number, index))
|
||||||
this.throttledSetNodeData(id, parent as Element, node.data)
|
this.throttledSetNodeData(id, parent as Element, node.data)
|
||||||
}
|
}
|
||||||
|
if (slot) {
|
||||||
|
const slotID = this.app.nodes.getID(slot)
|
||||||
|
console.log('Openreplay: slotID', slotID, 'for node', id, node, 'slot', slot)
|
||||||
|
if (slotID !== undefined) {
|
||||||
|
this.slotMap.set(id, slotID)
|
||||||
|
this.app.send(SetNodeSlot(id, slotID))
|
||||||
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (recentsType === RecentsType.Removed && parentID !== undefined) {
|
if (recentsType === RecentsType.Removed && parentID !== undefined) {
|
||||||
this.app.send(MoveNode(id, parentID, index))
|
this.app.send(MoveNode(id, parentID, index))
|
||||||
|
console.log('RM Openreplay', id, node, 'slot', slot)
|
||||||
|
if (slot) {
|
||||||
|
const slotID = this.app.nodes.getID(slot)
|
||||||
|
if (slotID !== undefined && this.slotMap.get(id) !== slotID) {
|
||||||
|
this.slotMap.set(id, slotID)
|
||||||
|
this.app.send(SetNodeSlot(id, slotID))
|
||||||
|
}
|
||||||
|
} else if (this.slotMap.has(id)) {
|
||||||
|
this.slotMap.delete(id)
|
||||||
|
this.app.send(SetNodeSlot(id, 0))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const attr = this.attributesMap.get(id)
|
const attr = this.attributesMap.get(id)
|
||||||
if (attr !== undefined) {
|
if (attr !== undefined) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue