|
|
@ -16,40 +16,7 @@ export abstract class BaseComponentNode extends SortableTreeNode<Component> { |
|
|
* BaseComponentNode, or null if there are no children. |
|
|
* BaseComponentNode, or null if there are no children. |
|
|
*/ |
|
|
*/ |
|
|
getChildren(): BaseComponentNode[] | null { |
|
|
getChildren(): BaseComponentNode[] | null { |
|
|
return this.getDisplayedChildren(); |
|
|
return this.model.components().map((comp: Component) => new (this.constructor as any)(comp)); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Get the list of displayed children, i.e., components that have a valid HTML element. |
|
|
|
|
|
* Cached values are used to avoid recalculating the display status unnecessarily. |
|
|
|
|
|
* @returns {BaseComponentNode[] | null} - The list of displayed children wrapped in |
|
|
|
|
|
* BaseComponentNode, or null if there are no displayed children. |
|
|
|
|
|
*/ |
|
|
|
|
|
private getDisplayedChildren(): BaseComponentNode[] | null { |
|
|
|
|
|
const children = this.model.components(); |
|
|
|
|
|
const displayedChildren = children.filter((child) => this.isChildDisplayed(child)); |
|
|
|
|
|
|
|
|
|
|
|
return displayedChildren.map((comp: Component) => new (this.constructor as any)(comp)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Check if a child is displayed, using cached value if available. |
|
|
|
|
|
* @param {Component} child - The child component to check. |
|
|
|
|
|
* @returns {boolean} - Whether the child is displayed. |
|
|
|
|
|
*/ |
|
|
|
|
|
private isChildDisplayed(child: Component): boolean { |
|
|
|
|
|
// Check if display status is cached
|
|
|
|
|
|
if (this.displayCache.has(child)) { |
|
|
|
|
|
return this.displayCache.get(child)!; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const element = child.getEl(); |
|
|
|
|
|
const displayed = isDisplayed(element); |
|
|
|
|
|
|
|
|
|
|
|
// Cache the display status
|
|
|
|
|
|
this.displayCache.set(child, displayed); |
|
|
|
|
|
|
|
|
|
|
|
return displayed; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -65,13 +32,13 @@ export abstract class BaseComponentNode extends SortableTreeNode<Component> { |
|
|
/** |
|
|
/** |
|
|
* Add a child component to this node at the specified index. |
|
|
* Add a child component to this node at the specified index. |
|
|
* @param {BaseComponentNode} node - The child node to add. |
|
|
* @param {BaseComponentNode} node - The child node to add. |
|
|
* @param {number} displayIndex - The visual index at which to insert the child. |
|
|
* @param {number} index - The index at which to insert the child. |
|
|
* @param {{ action: string }} options - Options for the operation, with the default action being 'add-component'. |
|
|
* @param {{ action: string }} options - Options for the operation, with the default action being 'add-component'. |
|
|
* @returns {BaseComponentNode} - The newly added child node wrapped in BaseComponentNode. |
|
|
* @returns {BaseComponentNode} - The newly added child node wrapped in BaseComponentNode. |
|
|
*/ |
|
|
*/ |
|
|
addChildAt( |
|
|
addChildAt( |
|
|
node: BaseComponentNode, |
|
|
node: BaseComponentNode, |
|
|
displayIndex: number, |
|
|
index: number, |
|
|
options: { action: string } = { action: 'add-component' }, |
|
|
options: { action: string } = { action: 'add-component' }, |
|
|
): BaseComponentNode { |
|
|
): BaseComponentNode { |
|
|
const insertingTextableIntoText = this.model?.isInstanceOf?.('text') && node?.model?.get?.('textable'); |
|
|
const insertingTextableIntoText = this.model?.isInstanceOf?.('text') && node?.model?.get?.('textable'); |
|
|
@ -82,7 +49,7 @@ export abstract class BaseComponentNode extends SortableTreeNode<Component> { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const newModel = this.model.components().add(node.model, { |
|
|
const newModel = this.model.components().add(node.model, { |
|
|
at: this.getRealIndex(displayIndex), |
|
|
at: index, |
|
|
action: options.action, |
|
|
action: options.action, |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
@ -91,11 +58,11 @@ export abstract class BaseComponentNode extends SortableTreeNode<Component> { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Remove a child component at the specified index. |
|
|
* Remove a child component at the specified index. |
|
|
* @param {number} displayIndex - The visual index of the child to remove. |
|
|
* @param {number} index - The visual index of the child to remove. |
|
|
* @param {{ temporary: boolean }} options - Whether to temporarily remove the child. |
|
|
* @param {{ temporary: boolean }} options - Whether to temporarily remove the child. |
|
|
*/ |
|
|
*/ |
|
|
removeChildAt(displayIndex: number, options: { temporary: boolean } = { temporary: false }): void { |
|
|
removeChildAt(index: number, options: { temporary: boolean } = { temporary: false }): void { |
|
|
const child = this.model.components().at(this.getRealIndex(displayIndex)); |
|
|
const child = this.model.components().at(index); |
|
|
if (child) { |
|
|
if (child) { |
|
|
this.model.components().remove(child, options as any); |
|
|
this.model.components().remove(child, options as any); |
|
|
} |
|
|
} |
|
|
@ -107,49 +74,27 @@ export abstract class BaseComponentNode extends SortableTreeNode<Component> { |
|
|
* @returns {number} - The index of the child node, or -1 if not found. |
|
|
* @returns {number} - The index of the child node, or -1 if not found. |
|
|
*/ |
|
|
*/ |
|
|
indexOfChild(node: BaseComponentNode): number { |
|
|
indexOfChild(node: BaseComponentNode): number { |
|
|
return this.getDisplayIndex(node); |
|
|
return this.getIndex(node); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Get the index of the given node within the displayed children. |
|
|
* Get the index of the given node. |
|
|
* @param {BaseComponentNode} node - The node to find. |
|
|
* @param {BaseComponentNode} node - The node to find. |
|
|
* @returns {number} - The display index of the node, or -1 if not found. |
|
|
* @returns {number} - The index of the node, or -1 if not found. |
|
|
*/ |
|
|
*/ |
|
|
private getDisplayIndex(node: BaseComponentNode): number { |
|
|
private getIndex(node: BaseComponentNode): number { |
|
|
const displayedChildren = this.getDisplayedChildren(); |
|
|
const Children = this.getChildren(); |
|
|
return displayedChildren ? displayedChildren.findIndex((displayedNode) => displayedNode.model === node.model) : -1; |
|
|
return Children ? Children.findIndex((Node) => Node.model === node.model) : -1; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Convert a display index to the actual index within the component's children array. |
|
|
|
|
|
* @param {number} index - The display index to convert. |
|
|
|
|
|
* @returns {number} - The corresponding real index, or -1 if not found. |
|
|
|
|
|
*/ |
|
|
|
|
|
getRealIndex(index: number): number { |
|
|
|
|
|
if (index === -1) return -1; |
|
|
|
|
|
|
|
|
|
|
|
let displayedCount = 0; |
|
|
|
|
|
const children = this.model.components(); |
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < children.length; i++) { |
|
|
|
|
|
const child = children.at(i); |
|
|
|
|
|
const displayed = this.isChildDisplayed(child); |
|
|
|
|
|
|
|
|
|
|
|
if (displayed) displayedCount++; |
|
|
|
|
|
if (displayedCount === index + 1) return i; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Check if a source node can be moved to a specified index within this component. |
|
|
* Check if a source node can be moved to a specified index within this component. |
|
|
* @param {BaseComponentNode} source - The source node to move. |
|
|
* @param {BaseComponentNode} source - The source node to move. |
|
|
* @param {number} index - The display index to move the source to. |
|
|
* @param {number} index - The index to move the source to. |
|
|
* @returns {boolean} - True if the move is allowed, false otherwise. |
|
|
* @returns {boolean} - True if the move is allowed, false otherwise. |
|
|
*/ |
|
|
*/ |
|
|
canMove(source: BaseComponentNode, index: number): boolean { |
|
|
canMove(source: BaseComponentNode, index: number): boolean { |
|
|
return this.model.em.Components.canMove(this.model, source.model, this.getRealIndex(index)).result; |
|
|
return this.model.em.Components.canMove(this.model, source.model, index).result; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
equals(node?: BaseComponentNode): node is BaseComponentNode { |
|
|
equals(node?: BaseComponentNode): node is BaseComponentNode { |
|
|
@ -229,19 +174,3 @@ export abstract class BaseComponentNode extends SortableTreeNode<Component> { |
|
|
return this.model.get?.('textable'); |
|
|
return this.model.get?.('textable'); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Function to check if an element is displayed in the DOM. |
|
|
|
|
|
* @param {HTMLElement | undefined} element - The element to check. |
|
|
|
|
|
* @returns {boolean} - Whether the element is displayed. |
|
|
|
|
|
*/ |
|
|
|
|
|
function isDisplayed(element: HTMLElement | undefined): boolean { |
|
|
|
|
|
if (!element) return false; |
|
|
|
|
|
return ( |
|
|
|
|
|
typeof element === 'object' && |
|
|
|
|
|
element.nodeType === Node.ELEMENT_NODE && |
|
|
|
|
|
window.getComputedStyle(element).display !== 'none' && |
|
|
|
|
|
element.offsetWidth > 0 && |
|
|
|
|
|
element.offsetHeight > 0 |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|