First cut of space panel drag-and-drop ordering

This commit is contained in:
Michael Telatynski 2021-06-03 08:32:36 +01:00
parent 3f12b7280d
commit e334ce8192
6 changed files with 263 additions and 80 deletions

View file

@ -15,8 +15,9 @@ limitations under the License.
*/
import React, { useEffect, useState } from "react";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
import classNames from "classnames";
import {Room} from "matrix-js-sdk/src/models/room";
import { Room } from "matrix-js-sdk/src/models/room";
import {_t} from "../../../languageHandler";
import RoomAvatar from "../avatars/RoomAvatar";
@ -204,58 +205,89 @@ const SpacePanel = () => {
};
const activeSpaces = activeSpace ? [activeSpace] : [];
const expandCollapseButtonTitle = isPanelCollapsed ? _t("Expand space panel") : _t("Collapse space panel");
// TODO drag and drop for re-arranging order
return <RovingTabIndexProvider handleHomeEnd={true} onKeyDown={onKeyDown}>
{({onKeyDownHandler}) => (
<ul
className={classNames("mx_SpacePanel", { collapsed: isPanelCollapsed })}
onKeyDown={onKeyDownHandler}
>
<AutoHideScrollbar className="mx_SpacePanel_spaceTreeWrapper">
<div className="mx_SpaceTreeLevel">
<SpaceButton
className="mx_SpaceButton_home"
onClick={() => SpaceStore.instance.setActiveSpace(null)}
selected={!activeSpace}
tooltip={_t("All rooms")}
notificationState={RoomNotificationStateStore.instance.globalState}
isNarrow={isPanelCollapsed}
return (
<DragDropContext onDragEnd={result => {
if (!result.destination) return; // dropped outside the list
SpaceStore.instance.moveRootSpace(result.source.index, result.destination.index);
}}>
<RovingTabIndexProvider handleHomeEnd={true} onKeyDown={onKeyDown}>
{({onKeyDownHandler}) => (
<ul
className={classNames("mx_SpacePanel", { collapsed: isPanelCollapsed })}
onKeyDown={onKeyDownHandler}
>
<Droppable droppableId="top-level-spaces">
{(provided, snapshot) => (
<AutoHideScrollbar
{...provided.droppableProps}
wrappedRef={provided.innerRef}
className="mx_SpacePanel_spaceTreeWrapper"
style={snapshot.isDraggingOver ? {
pointerEvents: "none",
} : undefined}
>
<div className="mx_SpaceTreeLevel">
<SpaceButton
className="mx_SpaceButton_home"
onClick={() => SpaceStore.instance.setActiveSpace(null)}
selected={!activeSpace}
tooltip={_t("All rooms")}
notificationState={RoomNotificationStateStore.instance.globalState}
isNarrow={isPanelCollapsed}
/>
{ invites.map(s => (
<SpaceItem
key={s.roomId}
space={s}
activeSpaces={activeSpaces}
isPanelCollapsed={isPanelCollapsed}
onExpand={() => setPanelCollapsed(false)}
/>
)) }
{ spaces.map((s, i) => (
<Draggable key={s.roomId} draggableId={s.roomId} index={i}>
{(provided, snapshot) => (
<SpaceItem
{...provided.draggableProps}
{...provided.dragHandleProps}
key={s.roomId}
innerRef={provided.innerRef}
className={snapshot.isDragging
? "mx_SpaceItem_dragging"
: undefined}
space={s}
activeSpaces={activeSpaces}
isPanelCollapsed={isPanelCollapsed}
onExpand={() => setPanelCollapsed(false)}
/>
)}
</Draggable>
)) }
{ provided.placeholder }
</div>
<SpaceButton
className={newClasses}
tooltip={menuDisplayed ? _t("Cancel") : _t("Create a space")}
onClick={menuDisplayed ? closeMenu : () => {
if (!isPanelCollapsed) setPanelCollapsed(true);
openMenu();
}}
isNarrow={isPanelCollapsed}
/>
</AutoHideScrollbar>
)}
</Droppable>
<AccessibleTooltipButton
className={classNames("mx_SpacePanel_toggleCollapse", {expanded: !isPanelCollapsed})}
onClick={() => setPanelCollapsed(!isPanelCollapsed)}
title={isPanelCollapsed ? _t("Expand space panel") : _t("Collapse space panel")}
/>
{ invites.map(s => <SpaceItem
key={s.roomId}
space={s}
activeSpaces={activeSpaces}
isPanelCollapsed={isPanelCollapsed}
onExpand={() => setPanelCollapsed(false)}
/>) }
{ spaces.map(s => <SpaceItem
key={s.roomId}
space={s}
activeSpaces={activeSpaces}
isPanelCollapsed={isPanelCollapsed}
onExpand={() => setPanelCollapsed(false)}
/>) }
</div>
<SpaceButton
className={newClasses}
tooltip={menuDisplayed ? _t("Cancel") : _t("Create a space")}
onClick={menuDisplayed ? closeMenu : () => {
if (!isPanelCollapsed) setPanelCollapsed(true);
openMenu();
}}
isNarrow={isPanelCollapsed}
/>
</AutoHideScrollbar>
<AccessibleTooltipButton
className={classNames("mx_SpacePanel_toggleCollapse", {expanded: !isPanelCollapsed})}
onClick={() => setPanelCollapsed(!isPanelCollapsed)}
title={expandCollapseButtonTitle}
/>
{ contextMenu }
</ul>
)}
</RovingTabIndexProvider>
{ contextMenu }
</ul>
)}
</RovingTabIndexProvider>
</DragDropContext>
);
};
export default SpacePanel;

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import React, {InputHTMLAttributes, LegacyRef} from "react";
import classNames from "classnames";
import {Room} from "matrix-js-sdk/src/models/room";
@ -49,13 +49,14 @@ import {EventType} from "matrix-js-sdk/src/@types/event";
import {StaticNotificationState} from "../../../stores/notifications/StaticNotificationState";
import {NotificationColor} from "../../../stores/notifications/NotificationColor";
interface IItemProps {
interface IItemProps extends InputHTMLAttributes<HTMLLIElement> {
space?: Room;
activeSpaces: Room[];
isNested?: boolean;
isPanelCollapsed?: boolean;
onExpand?: Function;
parents?: Set<string>;
innerRef?: LegacyRef<HTMLLIElement>;
}
interface IItemState {
@ -300,18 +301,18 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
}
render() {
const {space, activeSpaces, isNested} = this.props;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { space, activeSpaces, isNested, isPanelCollapsed, onExpand, parents, innerRef,
...otherProps } = this.props;
const forceCollapsed = this.props.isPanelCollapsed;
const isNarrow = this.props.isPanelCollapsed;
const collapsed = this.state.collapsed || forceCollapsed;
const collapsed = this.state.collapsed || isPanelCollapsed;
const childSpaces = SpaceStore.instance.getChildSpaces(space.roomId)
.filter(s => !this.props.parents?.has(s.roomId));
.filter(s => !parents?.has(s.roomId));
const isActive = activeSpaces.includes(space);
const itemClasses = classNames({
const itemClasses = classNames(this.props.className, {
"mx_SpaceItem": true,
"mx_SpaceItem_narrow": isNarrow,
"mx_SpaceItem_narrow": isPanelCollapsed,
"collapsed": collapsed,
"hasSubSpaces": childSpaces && childSpaces.length,
});
@ -320,7 +321,7 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
const classes = classNames("mx_SpaceButton", {
mx_SpaceButton_active: isActive,
mx_SpaceButton_hasMenuOpen: !!this.state.contextMenuPosition,
mx_SpaceButton_narrow: isNarrow,
mx_SpaceButton_narrow: isPanelCollapsed,
mx_SpaceButton_invite: isInvite,
});
const notificationState = isInvite
@ -333,7 +334,7 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
spaces={childSpaces}
activeSpaces={activeSpaces}
isNested={true}
parents={new Set(this.props.parents).add(this.props.space.roomId)}
parents={new Set(parents).add(space.roomId)}
/>;
}
@ -353,7 +354,7 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
/> : null;
let button;
if (isNarrow) {
if (isPanelCollapsed) {
button = (
<RovingAccessibleTooltipButton
className={classes}
@ -391,7 +392,7 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
}
return (
<li className={itemClasses}>
<li {...otherProps} className={itemClasses} ref={innerRef}>
{ button }
{ childItems }
</li>