mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-28 17:48:45 +01:00
Unmagnify the final leaf in a layout (#307)
This handles an edge case where a user deletes all unmagnified nodes, leaving a final node that is still magnified. Because we ignore magnify/unmagnify operations when there's only one leaf remaining, this would result in the last node being stuck magnified until a new node is added. Also fixes a bug where the layout would not always update when a new block was added.
This commit is contained in:
parent
f5d2d4c5a4
commit
16e1b7f65c
frontend/layout/lib
@ -1,7 +1,7 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { atomWithThrottle, boundNumber, lazy } from "@/util/util";
|
||||
import { atomWithThrottle, boundNumber } from "@/util/util";
|
||||
import { Atom, atom, Getter, PrimitiveAtom, Setter } from "jotai";
|
||||
import { splitAtom } from "jotai/utils";
|
||||
import { createRef, CSSProperties } from "react";
|
||||
@ -439,6 +439,7 @@ export class LayoutModel {
|
||||
}
|
||||
} else {
|
||||
this.updateTree();
|
||||
this.setTreeStateAtom(force);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -455,17 +456,11 @@ export class LayoutModel {
|
||||
this.setter(this.treeStateAtom, this.treeState);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a hack to ensure that when the updateTree first successfully runs, we set the upstream atom state to persist the initial leaf order.
|
||||
* @see updateTree should be the only caller of this method.
|
||||
*/
|
||||
setTreeStateAtomOnce = lazy(() => this.setTreeStateAtom(true));
|
||||
|
||||
/**
|
||||
* Recursively walks the tree to find leaf nodes, update the resize handles, and compute additional properties for each node.
|
||||
* @param balanceTree Whether the tree should also be balanced as it is walked. This should be done if the tree state has just been updated. Defaults to true.
|
||||
*/
|
||||
updateTree(balanceTree: boolean = true) {
|
||||
updateTree(balanceTree = true) {
|
||||
if (this.displayContainerRef.current) {
|
||||
const newLeafs: LayoutNode[] = [];
|
||||
const newAdditionalProps = {};
|
||||
@ -480,16 +475,16 @@ export class LayoutModel {
|
||||
if (balanceTree) this.treeState.rootNode = balanceNode(this.treeState.rootNode, callback);
|
||||
else walkNodes(this.treeState.rootNode, callback);
|
||||
|
||||
this.setter(this.additionalProps, newAdditionalProps);
|
||||
this.treeState.leafOrder = getLeafOrder(newLeafs, newAdditionalProps);
|
||||
this.validateFocusedNode(this.treeState.leafOrder);
|
||||
this.validateMagnifiedNode(this.treeState.leafOrder, newAdditionalProps);
|
||||
this.cleanupNodeModels(this.treeState.leafOrder);
|
||||
this.setter(
|
||||
this.leafs,
|
||||
newLeafs.sort((a, b) => a.id.localeCompare(b.id))
|
||||
);
|
||||
this.treeState.leafOrder = getLeafOrder(newLeafs, newAdditionalProps);
|
||||
this.setter(this.leafOrder, this.treeState.leafOrder);
|
||||
this.validateFocusedNode(this.treeState.leafOrder);
|
||||
this.cleanupNodeModels();
|
||||
this.setTreeStateAtomOnce();
|
||||
this.setter(this.additionalProps, newAdditionalProps);
|
||||
}
|
||||
}
|
||||
|
||||
@ -637,6 +632,22 @@ export class LayoutModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When a layout is modified and only one leaf is remaining, we need to make sure it is no longer magnified.
|
||||
* @param leafOrder The new leaf order array to use when validating the number of leafs remaining.
|
||||
* @param addlProps The new additional properties object for all leafs in the layout.
|
||||
*/
|
||||
private validateMagnifiedNode(leafOrder: LeafOrderEntry[], addlProps: Record<string, LayoutNodeAdditionalProps>) {
|
||||
if (leafOrder.length == 1) {
|
||||
const lastLeafId = leafOrder[0].nodeid;
|
||||
this.treeState.magnifiedNodeId = undefined;
|
||||
this.magnifiedNodeId = undefined;
|
||||
|
||||
// Unset the transform for the sole leaf.
|
||||
if (addlProps.hasOwnProperty(lastLeafId)) addlProps[lastLeafId].transform = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for the placeholderTransform atom, which computes the new transform value when the pending action changes.
|
||||
* @param pendingAction The new pending action value.
|
||||
@ -778,8 +789,11 @@ export class LayoutModel {
|
||||
return nodeModel;
|
||||
}
|
||||
|
||||
private cleanupNodeModels() {
|
||||
const leafOrder = this.getter(this.leafOrder);
|
||||
/**
|
||||
* Remove orphaned node models when their corresponding leaf is deleted.
|
||||
* @param leafOrder The new leaf order array to use when locating orphaned nodes.
|
||||
*/
|
||||
private cleanupNodeModels(leafOrder: LeafOrderEntry[]) {
|
||||
const orphanedNodeModels = [...this.nodeModels.keys()].filter(
|
||||
(id) => !leafOrder.find((leafEntry) => leafEntry.nodeid == id)
|
||||
);
|
||||
|
@ -53,6 +53,10 @@ export function useTileLayout(tabAtom: Atom<Tab>, tileContent: TileLayoutContent
|
||||
useAtomValue(tabAtom);
|
||||
const layoutModel = useLayoutModel(tabAtom);
|
||||
useResizeObserver(layoutModel?.displayContainerRef, layoutModel?.onContainerResize);
|
||||
|
||||
// Once the TileLayout is mounted, re-run the state update to get all the nodes to flow in the layout.
|
||||
useLayoutEffect(() => fireAndForget(() => layoutModel.onTreeStateAtomUpdated(true)), []);
|
||||
|
||||
useEffect(() => layoutModel.registerTileLayout(tileContent), [tileContent]);
|
||||
return layoutModel;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user