Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to expand and collapse all children of segment group #7911

Merged
merged 17 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released

### Added
- Added route `/import?url=<url_to_datasource>` to automatically import and view remote datasets. [#7844](https://github.com/scalableminds/webknossos/pull/7844)
- Added option to expand or collapse all subgroups of a segment group in the segments tab. [#7911](https://github.com/scalableminds/webknossos/pull/7911)

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
EyeInvisibleOutlined,
EyeOutlined,
CloseOutlined,
ShrinkOutlined,
ExpandAltOutlined,
} from "@ant-design/icons";
import type RcTree from "rc-tree";
import { getJobs, startComputeMeshFileJob } from "admin/admin_rest_api";
Expand Down Expand Up @@ -323,6 +325,7 @@ type State = {
[groupId: number]: { areSomeSegmentsVisible: boolean; areSomeSegmentsInvisible: boolean };
};
activeStatisticsModalGroupId: number | null;
expandedGroupKeys: Key[];
};

const formatMagWithLabel = (mag: Vector3, index: number) => {
Expand Down Expand Up @@ -402,6 +405,7 @@ class SegmentsView extends React.Component<Props, State> {
groupToDelete: null,
groupsSegmentsVisibilityStateMap: {},
activeStatisticsModalGroupId: null,
expandedGroupKeys: [],
};
tree: React.RefObject<RcTree>;

Expand All @@ -425,6 +429,10 @@ class SegmentsView extends React.Component<Props, State> {
}

Store.dispatch(ensureSegmentIndexIsLoadedAction(this.props.visibleSegmentationLayer?.name));

const allGroups = this.getSubGroupsAsTreeNodes(MISSING_GROUP_ID);
allGroups.push(this.getKeyForGroupId(MISSING_GROUP_ID));
this.setState({ expandedGroupKeys: allGroups });
}

componentDidUpdate(prevProps: Props) {
Expand Down Expand Up @@ -461,6 +469,38 @@ class SegmentsView extends React.Component<Props, State> {
}
}

onExpandTree = (expandedKeys: Key[]) => {
this.setState({ expandedGroupKeys: expandedKeys });
};

getKeyForGroupId = (groupId: number) => `group-${groupId}`;

getSubGroupsAsTreeNodes = (groupId: number) => {
if (groupId !== MISSING_GROUP_ID) {
return getGroupByIdWithSubgroups(this.props.segmentGroups, groupId)
.filter((group) => group !== groupId)
.map((group) => this.getKeyForGroupId(group));
}
const allSegmentGroups = this.props.segmentGroups.flatMap((group) =>
getGroupByIdWithSubgroups(this.props.segmentGroups, group.groupId),
);
return allSegmentGroups.map((group) => this.getKeyForGroupId(group));
};

expandGroups = (groupsToExpand: Key[]) => {
this.setState({
expandedGroupKeys: this.state.expandedGroupKeys?.concat(groupsToExpand),
});
};

collapseGroups = (groupsToCollapse: Key[]) => {
this.setState({
expandedGroupKeys: this.state.expandedGroupKeys?.filter(
(key) => groupsToCollapse.indexOf(key.toString()) === -1,
),
});
};

onSelectTreeItem = (
keys: Key[],
event: {
Expand Down Expand Up @@ -1405,7 +1445,7 @@ class SegmentsView extends React.Component<Props, State> {
(segmentId) => `segment-${segmentId}`,
);
if (this.props.selectedIds.group != null) {
return mappedIdsToKeys.concat(`group-${this.props.selectedIds.group}`);
return mappedIdsToKeys.concat(this.getKeyForGroupId(this.props.selectedIds.group));
}
return mappedIdsToKeys;
};
Expand Down Expand Up @@ -1686,6 +1726,8 @@ class SegmentsView extends React.Component<Props, State> {
icon: <DeleteOutlined />,
label: "Delete group",
},
this.getExpandSubgroupsItem(id),
this.getCollapseSubgroupsItem(id),
this.getMoveSegmentsHereMenuItem(id),
{
key: "groupAndMeshActionDivider",
Expand Down Expand Up @@ -1792,7 +1834,6 @@ class SegmentsView extends React.Component<Props, State> {
allowDrop={this.allowDrop}
onDrop={this.onDrop}
onSelect={this.onSelectTreeItem}
defaultExpandAll
className="segments-tree"
blockNode
// Passing an explicit height here, makes the tree virtualized
Expand All @@ -1817,6 +1858,8 @@ class SegmentsView extends React.Component<Props, State> {
overflow: "auto", // use hidden when not using virtualization
}}
ref={this.tree}
onExpand={this.onExpandTree}
expandedKeys={this.state.expandedGroupKeys}
/>
</div>
)}
Expand All @@ -1842,6 +1885,55 @@ class SegmentsView extends React.Component<Props, State> {
);
}

getExpandSubgroupsItem(groupId: number) {
const children = this.getSubGroupsAsTreeNodes(groupId);
const areAllChildrenExpanded =
dieknolle3333 marked this conversation as resolved.
Show resolved Hide resolved
children.filter((childNode) => this.state.expandedGroupKeys?.includes(childNode)).length ===
children.length;
dieknolle3333 marked this conversation as resolved.
Show resolved Hide resolved
const isGroupItselfExpanded = this.state.expandedGroupKeys?.includes(
this.getKeyForGroupId(groupId),
);
if (areAllChildrenExpanded && isGroupItselfExpanded) {
return null;
}
const groupsToExpand = children;
// It doesn't make sense to expand subgroups if the group itself is collapsed, so also expand the group.
if (!isGroupItselfExpanded) groupsToExpand.push(this.getKeyForGroupId(groupId));
return {
key: "expandAll",
disabled: !this.props.allowUpdate,
dieknolle3333 marked this conversation as resolved.
Show resolved Hide resolved
onClick: () => {
this.expandGroups(groupsToExpand);
this.closeSegmentOrGroupDropdown();
},
icon: <ExpandAltOutlined />,
label: "Expand all subgroups",
};
}

getCollapseSubgroupsItem(groupId: number) {
const children = this.getSubGroupsAsTreeNodes(groupId);
const areAllChildrenCollapsed =
children.filter((childNode) => this.state.expandedGroupKeys?.includes(childNode)).length ===
0;
const isGroupItselfCollapsed = !this.state.expandedGroupKeys?.includes(
this.getKeyForGroupId(groupId),
);
if (areAllChildrenCollapsed || isGroupItselfCollapsed) {
return null;
}
return {
key: "collapseAll",
disabled: !this.props.allowUpdate,
onClick: () => {
this.collapseGroups(children);
this.closeSegmentOrGroupDropdown();
},
icon: <ShrinkOutlined />,
label: "Collapse all subgroups",
};
}

createGroup(parentGroupId: number): void {
if (!this.props.visibleSegmentationLayer) {
return;
Expand Down