matrix-react-sdk
Version:
SDK for matrix.org using React
915 lines (780 loc) • 124 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.HEADER_HEIGHT = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var React = _interopRequireWildcard(require("react"));
var _classnames = _interopRequireDefault(require("classnames"));
var _RovingTabIndex = require("../../../accessibility/RovingTabIndex");
var _languageHandler = require("../../../languageHandler");
var _AccessibleButton = _interopRequireDefault(require("../../views/elements/AccessibleButton"));
var _RoomTile = _interopRequireDefault(require("./RoomTile"));
var _ContextMenu = require("../../structures/ContextMenu");
var _RoomListStore = _interopRequireWildcard(require("../../../stores/room-list/RoomListStore"));
var _models = require("../../../stores/room-list/algorithms/models");
var _models2 = require("../../../stores/room-list/models");
var _dispatcher = _interopRequireDefault(require("../../../dispatcher/dispatcher"));
var _NotificationBadge = _interopRequireDefault(require("./NotificationBadge"));
var _AccessibleTooltipButton = _interopRequireDefault(require("../elements/AccessibleTooltipButton"));
var _Keyboard = require("../../../Keyboard");
var _reResizable = require("re-resizable");
var _polyfill = require("../../../@types/polyfill");
var _RoomNotificationStateStore = require("../../../stores/notifications/RoomNotificationStateStore");
var _RoomListLayoutStore = _interopRequireDefault(require("../../../stores/room-list/RoomListLayoutStore"));
var _arrays = require("../../../utils/arrays");
var _objects = require("../../../utils/objects");
var _IconizedContextMenu = _interopRequireDefault(require("../context_menus/IconizedContextMenu"));
var _KeyBindingsManager = require("../../../KeyBindingsManager");
var _replaceableComponent = require("../../../utils/replaceableComponent");
var _dec, _class, _temp;
const SHOW_N_BUTTON_HEIGHT = 28; // As defined by CSS
const RESIZE_HANDLE_HEIGHT = 4; // As defined by CSS
const HEADER_HEIGHT = 32; // As defined by CSS
exports.HEADER_HEIGHT = HEADER_HEIGHT;
const MAX_PADDING_HEIGHT = SHOW_N_BUTTON_HEIGHT + RESIZE_HANDLE_HEIGHT; // HACK: We really shouldn't have to do this.
(0, _polyfill.polyfillTouchEvent)();
let RoomSublist = (_dec = (0, _replaceableComponent.replaceableComponent)("views.rooms.RoomSublist"), _dec(_class = (_temp = class RoomSublist extends React.Component
/*:: <IProps, IState>*/
{
constructor(props
/*: IProps*/
) {
super(props);
(0, _defineProperty2.default)(this, "headerButton", /*#__PURE__*/(0, React.createRef)());
(0, _defineProperty2.default)(this, "sublistRef", /*#__PURE__*/(0, React.createRef)());
(0, _defineProperty2.default)(this, "dispatcherRef", void 0);
(0, _defineProperty2.default)(this, "layout", void 0);
(0, _defineProperty2.default)(this, "heightAtStart", void 0);
(0, _defineProperty2.default)(this, "isBeingFiltered", void 0);
(0, _defineProperty2.default)(this, "notificationState", void 0);
(0, _defineProperty2.default)(this, "onListsUpdated", () => {
const stateUpdates
/*: IState & any*/
= {}; // &any is to avoid a cast on the initializer
if (this.props.extraTiles) {
const nameCondition = _RoomListStore.default.instance.getFirstNameFilterCondition();
if (nameCondition) {
stateUpdates.filteredExtraTiles = this.props.extraTiles.filter(t => nameCondition.matches(t.props.displayName || ""));
} else if (this.state.filteredExtraTiles) {
stateUpdates.filteredExtraTiles = null;
}
}
const currentRooms = this.state.rooms;
const newRooms = (0, _arrays.arrayFastClone)(_RoomListStore.default.instance.orderedLists[this.props.tagId] || []);
if ((0, _arrays.arrayHasOrderChange)(currentRooms, newRooms)) {
stateUpdates.rooms = newRooms;
}
const isStillBeingFiltered = !!_RoomListStore.default.instance.getFirstNameFilterCondition();
if (isStillBeingFiltered !== this.isBeingFiltered) {
this.isBeingFiltered = isStillBeingFiltered;
if (isStillBeingFiltered) {
stateUpdates.isExpanded = true;
} else {
stateUpdates.isExpanded = !this.layout.isCollapsed;
}
}
if (Object.keys(stateUpdates).length > 0) {
this.setState(stateUpdates);
}
});
(0, _defineProperty2.default)(this, "onAction", (payload
/*: ActionPayload*/
) => {
if (payload.action === "view_room" && payload.show_room_tile && this.state.rooms) {
// XXX: we have to do this a tick later because we have incorrect intermediate props during a room change
// where we lose the room we are changing from temporarily and then it comes back in an update right after.
setImmediate(() => {
const roomIndex = this.state.rooms.findIndex(r => r.roomId === payload.room_id);
if (!this.state.isExpanded && roomIndex > -1) {
this.toggleCollapsed();
} // extend the visible section to include the room if it is entirely invisible
if (roomIndex >= this.numVisibleTiles) {
this.layout.visibleTiles = this.layout.tilesWithPadding(roomIndex + 1, MAX_PADDING_HEIGHT);
this.forceUpdate(); // because the layout doesn't trigger a re-render
}
});
}
});
(0, _defineProperty2.default)(this, "onAddRoom", e => {
e.stopPropagation();
if (this.props.onAddRoom) this.props.onAddRoom();
});
(0, _defineProperty2.default)(this, "onResize", (e
/*: MouseEvent | TouchEvent*/
, travelDirection
/*: Direction*/
, refToElement
/*: HTMLDivElement*/
, delta
/*: ResizeDelta*/
) => {
const newHeight = this.heightAtStart + delta.height;
this.applyHeightChange(newHeight);
this.setState({
height: newHeight
});
});
(0, _defineProperty2.default)(this, "onResizeStart", () => {
this.heightAtStart = this.state.height;
this.setState({
isResizing: true
});
});
(0, _defineProperty2.default)(this, "onResizeStop", (e
/*: MouseEvent | TouchEvent*/
, travelDirection
/*: Direction*/
, refToElement
/*: HTMLDivElement*/
, delta
/*: ResizeDelta*/
) => {
const newHeight = this.heightAtStart + delta.height;
this.applyHeightChange(newHeight);
this.setState({
isResizing: false,
height: newHeight
});
});
(0, _defineProperty2.default)(this, "onShowAllClick", () => {
// read number of visible tiles before we mutate it
const numVisibleTiles = this.numVisibleTiles;
const newHeight = this.layout.tilesToPixelsWithPadding(this.numTiles, this.padding);
this.applyHeightChange(newHeight);
this.setState({
height: newHeight
}, () => {
// focus the top-most new room
this.focusRoomTile(numVisibleTiles);
});
});
(0, _defineProperty2.default)(this, "onShowLessClick", () => {
const newHeight = this.layout.tilesToPixelsWithPadding(this.layout.defaultVisibleTiles, this.padding);
this.applyHeightChange(newHeight);
this.setState({
height: newHeight
});
});
(0, _defineProperty2.default)(this, "focusRoomTile", (index
/*: number*/
) => {
if (!this.sublistRef.current) return;
const elements = this.sublistRef.current.querySelectorAll(".mx_RoomTile");
const element = elements && elements[index];
if (element) {
element.focus();
}
});
(0, _defineProperty2.default)(this, "onOpenMenuClick", (ev
/*: React.MouseEvent*/
) => {
ev.preventDefault();
ev.stopPropagation();
const target = ev.target;
this.setState({
contextMenuPosition: target.getBoundingClientRect()
});
});
(0, _defineProperty2.default)(this, "onContextMenu", (ev
/*: React.MouseEvent*/
) => {
ev.preventDefault();
ev.stopPropagation();
this.setState({
contextMenuPosition: {
left: ev.clientX,
top: ev.clientY,
height: 0
}
});
});
(0, _defineProperty2.default)(this, "onAddRoomContextMenu", (ev
/*: React.MouseEvent*/
) => {
ev.preventDefault();
ev.stopPropagation();
const target = ev.target;
this.setState({
addRoomContextMenuPosition: target.getBoundingClientRect()
});
});
(0, _defineProperty2.default)(this, "onCloseMenu", () => {
this.setState({
contextMenuPosition: null
});
});
(0, _defineProperty2.default)(this, "onCloseAddRoomMenu", () => {
this.setState({
addRoomContextMenuPosition: null
});
});
(0, _defineProperty2.default)(this, "onUnreadFirstChanged", async () => {
const isUnreadFirst = _RoomListStore.default.instance.getListOrder(this.props.tagId) === _models.ListAlgorithm.Importance;
const newAlgorithm = isUnreadFirst ? _models.ListAlgorithm.Natural : _models.ListAlgorithm.Importance;
await _RoomListStore.default.instance.setListOrder(this.props.tagId, newAlgorithm);
this.forceUpdate(); // because if the sublist doesn't have any changes then we will miss the list order change
});
(0, _defineProperty2.default)(this, "onTagSortChanged", async (sort
/*: SortAlgorithm*/
) => {
await _RoomListStore.default.instance.setTagSorting(this.props.tagId, sort);
});
(0, _defineProperty2.default)(this, "onMessagePreviewChanged", () => {
this.layout.showPreviews = !this.layout.showPreviews;
this.forceUpdate(); // because the layout doesn't trigger a re-render
});
(0, _defineProperty2.default)(this, "onBadgeClick", (ev
/*: React.MouseEvent*/
) => {
ev.preventDefault();
ev.stopPropagation();
let room;
if (this.props.tagId === _models2.DefaultTagID.Invite) {
// switch to first room as that'll be the top of the list for the user
room = this.state.rooms && this.state.rooms[0];
} else {
// find the first room with a count of the same colour as the badge count
room = _RoomListStore.default.instance.unfilteredLists[this.props.tagId].find((r
/*: Room*/
) => {
const notifState = this.notificationState.getForRoom(r);
return notifState.count > 0 && notifState.color === this.notificationState.color;
});
}
if (room) {
_dispatcher.default.dispatch({
action: 'view_room',
room_id: room.roomId,
show_room_tile: true // to make sure the room gets scrolled into view
});
}
});
(0, _defineProperty2.default)(this, "onHeaderClick", () => {
const possibleSticky = this.headerButton.current.parentElement;
const sublist = possibleSticky.parentElement.parentElement;
const list = sublist.parentElement.parentElement; // the scrollTop is capped at the height of the header in LeftPanel, the top header is always sticky
const isAtTop = list.scrollTop <= HEADER_HEIGHT;
const isAtBottom = list.scrollTop >= list.scrollHeight - list.offsetHeight;
const isStickyTop = possibleSticky.classList.contains('mx_RoomSublist_headerContainer_stickyTop');
const isStickyBottom = possibleSticky.classList.contains('mx_RoomSublist_headerContainer_stickyBottom');
if (isStickyBottom && !isAtBottom || isStickyTop && !isAtTop) {
// is sticky - jump to list
sublist.scrollIntoView({
behavior: 'smooth'
});
} else {
// on screen - toggle collapse
const isExpanded = this.state.isExpanded;
this.toggleCollapsed(); // if the bottom list is collapsed then scroll it in so it doesn't expand off screen
if (!isExpanded && isStickyBottom) {
setImmediate(() => {
sublist.scrollIntoView({
behavior: 'smooth'
});
});
}
}
});
(0, _defineProperty2.default)(this, "toggleCollapsed", () => {
this.layout.isCollapsed = this.state.isExpanded;
this.setState({
isExpanded: !this.layout.isCollapsed
});
setImmediate(() => this.props.onResize()); // needs to happen when the DOM is updated
});
(0, _defineProperty2.default)(this, "onHeaderKeyDown", (ev
/*: React.KeyboardEvent*/
) => {
const action = (0, _KeyBindingsManager.getKeyBindingsManager)().getRoomListAction(ev);
switch (action) {
case _KeyBindingsManager.RoomListAction.CollapseSection:
ev.stopPropagation();
if (this.state.isExpanded) {
// Collapse the room sublist if it isn't already
this.toggleCollapsed();
}
break;
case _KeyBindingsManager.RoomListAction.ExpandSection:
{
ev.stopPropagation();
if (!this.state.isExpanded) {
// Expand the room sublist if it isn't already
this.toggleCollapsed();
} else if (this.sublistRef.current) {
// otherwise focus the first room
const element = this.sublistRef.current.querySelector(".mx_RoomTile");
if (element) {
element.focus();
}
}
break;
}
}
});
(0, _defineProperty2.default)(this, "onKeyDown", (ev
/*: React.KeyboardEvent*/
) => {
switch (ev.key) {
// On ARROW_LEFT go to the sublist header
case _Keyboard.Key.ARROW_LEFT:
ev.stopPropagation();
this.headerButton.current.focus();
break;
// Consume ARROW_RIGHT so it doesn't cause focus to get sent to composer
case _Keyboard.Key.ARROW_RIGHT:
ev.stopPropagation();
}
});
this.layout = _RoomListLayoutStore.default.instance.getLayoutFor(this.props.tagId);
this.heightAtStart = 0;
this.isBeingFiltered = !!_RoomListStore.default.instance.getFirstNameFilterCondition();
this.notificationState = _RoomNotificationStateStore.RoomNotificationStateStore.instance.getListState(this.props.tagId);
this.state = {
contextMenuPosition: null,
addRoomContextMenuPosition: null,
isResizing: false,
isExpanded: this.isBeingFiltered ? this.isBeingFiltered : !this.layout.isCollapsed,
height: 0,
// to be fixed in a moment, we need `rooms` to calculate this.
rooms: (0, _arrays.arrayFastClone)(_RoomListStore.default.instance.orderedLists[this.props.tagId] || [])
}; // Why Object.assign() and not this.state.height? Because TypeScript says no.
this.state = Object.assign(this.state, {
height: this.calculateInitialHeight()
});
}
calculateInitialHeight() {
const requestedVisibleTiles = Math.max(Math.floor(this.layout.visibleTiles), this.layout.minVisibleTiles);
const tileCount = Math.min(this.numTiles, requestedVisibleTiles);
return this.layout.tilesToPixelsWithPadding(tileCount, this.padding);
}
get padding() {
let padding = RESIZE_HANDLE_HEIGHT; // this is used for calculating the max height of the whole container,
// and takes into account whether there should be room reserved for the show more/less button
// when fully expanded. We can't rely purely on the layout's defaultVisible tile count
// because there are conditions in which we need to know that the 'show more' button
// is present while well under the default tile limit.
const needsShowMore = this.numTiles > this.numVisibleTiles; // ...but also check this or we'll miss if the section is expanded and we need a
// 'show less'
const needsShowLess = this.numTiles > this.layout.defaultVisibleTiles;
if (needsShowMore || needsShowLess) {
padding += SHOW_N_BUTTON_HEIGHT;
}
return padding;
}
get extraTiles()
/*: ReactComponentElement<typeof ExtraTile>[] | null*/
{
if (this.state.filteredExtraTiles) {
return this.state.filteredExtraTiles;
}
if (this.props.extraTiles) {
return this.props.extraTiles;
}
return null;
}
get numTiles()
/*: number*/
{
return RoomSublist.calcNumTiles(this.state.rooms, this.extraTiles);
}
static calcNumTiles(rooms
/*: Room[]*/
, extraTiles
/*: any[]*/
) {
return (rooms || []).length + (extraTiles || []).length;
}
get numVisibleTiles()
/*: number*/
{
const nVisible = Math.ceil(this.layout.visibleTiles);
return Math.min(nVisible, this.numTiles);
}
componentDidUpdate(prevProps
/*: Readonly<IProps>*/
, prevState
/*: Readonly<IState>*/
) {
const prevExtraTiles = prevState.filteredExtraTiles || prevProps.extraTiles; // as the rooms can come in one by one we need to reevaluate
// the amount of available rooms to cap the amount of requested visible rooms by the layout
if (RoomSublist.calcNumTiles(prevState.rooms, prevExtraTiles) !== this.numTiles) {
this.setState({
height: this.calculateInitialHeight()
});
}
}
shouldComponentUpdate(nextProps
/*: Readonly<IProps>*/
, nextState
/*: Readonly<IState>*/
)
/*: boolean*/
{
if ((0, _objects.objectHasDiff)(this.props, nextProps)) {
// Something we don't care to optimize has updated, so update.
return true;
} // Do the same check used on props for state, without the rooms we're going to no-op
const prevStateNoRooms = (0, _objects.objectExcluding)(this.state, ['rooms']);
const nextStateNoRooms = (0, _objects.objectExcluding)(nextState, ['rooms']);
if ((0, _objects.objectHasDiff)(prevStateNoRooms, nextStateNoRooms)) {
return true;
} // If we're supposed to handle extra tiles, take the performance hit and re-render all the
// time so we don't have to consider them as part of the visible room optimization.
const prevExtraTiles = this.props.extraTiles || [];
const nextExtraTiles = nextState.filteredExtraTiles || nextProps.extraTiles || [];
if (prevExtraTiles.length > 0 || nextExtraTiles.length > 0) {
return true;
} // If we're about to update the height of the list, we don't really care about which rooms
// are visible or not for no-op purposes, so ensure that the height calculation runs through.
if (RoomSublist.calcNumTiles(nextState.rooms, nextExtraTiles) !== this.numTiles) {
return true;
} // Before we go analyzing the rooms, we can see if we're collapsed. If we're collapsed, we don't need
// to render anything. We do this after the height check though to ensure that the height gets appropriately
// calculated for when/if we become uncollapsed.
if (!nextState.isExpanded) {
return false;
} // Quickly double check we're not about to break something due to the number of rooms changing.
if (this.state.rooms.length !== nextState.rooms.length) {
return true;
} // Finally, determine if the room update (as presumably that's all that's left) is within
// our visible range. If it is, then do a render. If the update is outside our visible range
// then we can skip the update.
//
// We also optimize for order changing here: if the update did happen in our visible range
// but doesn't result in the list re-sorting itself then there's no reason for us to update
// on our own.
const prevSlicedRooms = this.state.rooms.slice(0, this.numVisibleTiles);
const nextSlicedRooms = nextState.rooms.slice(0, this.numVisibleTiles);
if ((0, _arrays.arrayHasOrderChange)(prevSlicedRooms, nextSlicedRooms)) {
return true;
} // Finally, nothing happened so no-op the update
return false;
}
componentDidMount() {
this.dispatcherRef = _dispatcher.default.register(this.onAction);
_RoomListStore.default.instance.on(_RoomListStore.LISTS_UPDATE_EVENT, this.onListsUpdated);
}
componentWillUnmount() {
_dispatcher.default.unregister(this.dispatcherRef);
_RoomListStore.default.instance.off(_RoomListStore.LISTS_UPDATE_EVENT, this.onListsUpdated);
}
applyHeightChange(newHeight
/*: number*/
) {
const heightInTiles = Math.ceil(this.layout.pixelsToTiles(newHeight - this.padding));
this.layout.visibleTiles = Math.min(this.numTiles, heightInTiles);
}
renderVisibleTiles()
/*: React.ReactElement[]*/
{
if (!this.state.isExpanded) {
// don't waste time on rendering
return [];
}
const tiles
/*: React.ReactElement[]*/
= [];
if (this.state.rooms) {
const visibleRooms = this.state.rooms.slice(0, this.numVisibleTiles);
for (const room of visibleRooms) {
tiles.push( /*#__PURE__*/React.createElement(_RoomTile.default, {
room: room,
key: `room-${room.roomId}`,
resizeNotifier: this.props.resizeNotifier,
showMessagePreview: this.layout.showPreviews,
isMinimized: this.props.isMinimized,
tag: this.props.tagId
}));
}
}
if (this.extraTiles) {
// HACK: We break typing here, but this 'extra tiles' property shouldn't exist.
tiles.push(...this.extraTiles);
} // We only have to do this because of the extra tiles. We do it conditionally
// to avoid spending cycles on slicing. It's generally fine to do this though
// as users are unlikely to have more than a handful of tiles when the extra
// tiles are used.
if (tiles.length > this.numVisibleTiles) {
return tiles.slice(0, this.numVisibleTiles);
}
return tiles;
}
renderMenu()
/*: React.ReactElement*/
{
let contextMenu = null;
if (this.state.contextMenuPosition) {
const isAlphabetical = _RoomListStore.default.instance.getTagSorting(this.props.tagId) === _models.SortAlgorithm.Alphabetic;
const isUnreadFirst = _RoomListStore.default.instance.getListOrder(this.props.tagId) === _models.ListAlgorithm.Importance; // Invites don't get some nonsense options, so only add them if we have to.
let otherSections = null;
if (this.props.tagId !== _models2.DefaultTagID.Invite) {
otherSections = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("hr", null), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
className: "mx_RoomSublist_contextMenu_title"
}, (0, _languageHandler._t)("Appearance")), /*#__PURE__*/React.createElement(_ContextMenu.StyledMenuItemCheckbox, {
onClose: this.onCloseMenu,
onChange: this.onUnreadFirstChanged,
checked: isUnreadFirst
}, (0, _languageHandler._t)("Show rooms with unread messages first")), /*#__PURE__*/React.createElement(_ContextMenu.StyledMenuItemCheckbox, {
onClose: this.onCloseMenu,
onChange: this.onMessagePreviewChanged,
checked: this.layout.showPreviews
}, (0, _languageHandler._t)("Show previews of messages"))));
}
contextMenu = /*#__PURE__*/React.createElement(_ContextMenu.ContextMenu, {
chevronFace: _ContextMenu.ChevronFace.None,
left: this.state.contextMenuPosition.left,
top: this.state.contextMenuPosition.top + this.state.contextMenuPosition.height,
onFinished: this.onCloseMenu
}, /*#__PURE__*/React.createElement("div", {
className: "mx_RoomSublist_contextMenu"
}, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
className: "mx_RoomSublist_contextMenu_title"
}, (0, _languageHandler._t)("Sort by")), /*#__PURE__*/React.createElement(_ContextMenu.StyledMenuItemRadio, {
onClose: this.onCloseMenu,
onChange: () => this.onTagSortChanged(_models.SortAlgorithm.Recent),
checked: !isAlphabetical,
name: `mx_${this.props.tagId}_sortBy`
}, (0, _languageHandler._t)("Activity")), /*#__PURE__*/React.createElement(_ContextMenu.StyledMenuItemRadio, {
onClose: this.onCloseMenu,
onChange: () => this.onTagSortChanged(_models.SortAlgorithm.Alphabetic),
checked: isAlphabetical,
name: `mx_${this.props.tagId}_sortBy`
}, (0, _languageHandler._t)("A-Z"))), otherSections));
} else if (this.state.addRoomContextMenuPosition) {
contextMenu = /*#__PURE__*/React.createElement(_IconizedContextMenu.default, {
chevronFace: _ContextMenu.ChevronFace.None,
left: this.state.addRoomContextMenuPosition.left - 7 // center align with the handle
,
top: this.state.addRoomContextMenuPosition.top + this.state.addRoomContextMenuPosition.height,
onFinished: this.onCloseAddRoomMenu,
compact: true
}, this.props.addRoomContextMenu(this.onCloseAddRoomMenu));
}
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_ContextMenu.ContextMenuTooltipButton, {
className: "mx_RoomSublist_menuButton",
onClick: this.onOpenMenuClick,
title: (0, _languageHandler._t)("List options"),
isExpanded: !!this.state.contextMenuPosition
}), contextMenu);
}
renderHeader()
/*: React.ReactElement*/
{
return /*#__PURE__*/React.createElement(_RovingTabIndex.RovingTabIndexWrapper, {
inputRef: this.headerButton
}, ({
onFocus,
isActive,
ref
}) => {
const tabIndex = isActive ? 0 : -1;
let ariaLabel = (0, _languageHandler._t)("Jump to first unread room.");
if (this.props.tagId === _models2.DefaultTagID.Invite) {
ariaLabel = (0, _languageHandler._t)("Jump to first invite.");
}
const badge = /*#__PURE__*/React.createElement(_NotificationBadge.default, {
forceCount: true,
notification: this.notificationState,
onClick: this.onBadgeClick,
tabIndex: tabIndex,
"aria-label": ariaLabel
});
let addRoomButton = null;
if (!!this.props.onAddRoom) {
addRoomButton = /*#__PURE__*/React.createElement(_AccessibleTooltipButton.default, {
tabIndex: tabIndex,
onClick: this.onAddRoom,
className: "mx_RoomSublist_auxButton",
tooltipClassName: "mx_RoomSublist_addRoomTooltip",
"aria-label": this.props.addRoomLabel || (0, _languageHandler._t)("Add room"),
title: this.props.addRoomLabel
});
} else if (this.props.addRoomContextMenu) {
addRoomButton = /*#__PURE__*/React.createElement(_ContextMenu.ContextMenuTooltipButton, {
tabIndex: tabIndex,
onClick: this.onAddRoomContextMenu,
className: "mx_RoomSublist_auxButton",
tooltipClassName: "mx_RoomSublist_addRoomTooltip",
"aria-label": this.props.addRoomLabel || (0, _languageHandler._t)("Add room"),
title: this.props.addRoomLabel,
isExpanded: !!this.state.addRoomContextMenuPosition
});
}
const collapseClasses = (0, _classnames.default)({
'mx_RoomSublist_collapseBtn': true,
'mx_RoomSublist_collapseBtn_collapsed': !this.state.isExpanded
});
const classes = (0, _classnames.default)({
'mx_RoomSublist_headerContainer': true,
'mx_RoomSublist_headerContainer_withAux': !!addRoomButton
});
const badgeContainer = /*#__PURE__*/React.createElement("div", {
className: "mx_RoomSublist_badgeContainer"
}, badge);
let Button
/*: React.ComponentType<React.ComponentProps<typeof AccessibleButton>>*/
= _AccessibleButton.default;
if (this.props.isMinimized) {
Button = _AccessibleTooltipButton.default;
} // Note: the addRoomButton conditionally gets moved around
// the DOM depending on whether or not the list is minimized.
// If we're minimized, we want it below the header so it
// doesn't become sticky.
// The same applies to the notification badge.
return /*#__PURE__*/React.createElement("div", {
className: classes,
onKeyDown: this.onHeaderKeyDown,
onFocus: onFocus,
"aria-label": this.props.label
}, /*#__PURE__*/React.createElement("div", {
className: "mx_RoomSublist_stickable"
}, /*#__PURE__*/React.createElement(Button, {
onFocus: onFocus,
inputRef: ref,
tabIndex: tabIndex,
className: "mx_RoomSublist_headerText",
role: "treeitem",
"aria-expanded": this.state.isExpanded,
"aria-level": 1,
onClick: this.onHeaderClick,
onContextMenu: this.onContextMenu,
title: this.props.isMinimized ? this.props.label : undefined
}, /*#__PURE__*/React.createElement("span", {
className: collapseClasses
}), /*#__PURE__*/React.createElement("span", null, this.props.label)), this.renderMenu(), this.props.isMinimized ? null : badgeContainer, this.props.isMinimized ? null : addRoomButton), this.props.isMinimized ? badgeContainer : null, this.props.isMinimized ? addRoomButton : null);
});
}
onScrollPrevent(e
/*: React.UIEvent<HTMLDivElement>*/
) {
// the RoomTile calls scrollIntoView and the browser may scroll a div we do not wish to be scrollable
// this fixes https://github.com/vector-im/element-web/issues/14413
e.target.scrollTop = 0;
}
render()
/*: React.ReactElement*/
{
const visibleTiles = this.renderVisibleTiles();
const classes = (0, _classnames.default)({
'mx_RoomSublist': true,
'mx_RoomSublist_hasMenuOpen': !!this.state.contextMenuPosition,
'mx_RoomSublist_minimized': this.props.isMinimized,
'mx_RoomSublist_hidden': !this.state.rooms.length && !this.props.extraTiles?.length && this.props.alwaysVisible !== true
});
let content = null;
if (visibleTiles.length > 0) {
const layout = this.layout; // to shorten calls
const minTiles = Math.min(layout.minVisibleTiles, this.numTiles);
const showMoreAtMinHeight = minTiles < this.numTiles;
const minHeightPadding = RESIZE_HANDLE_HEIGHT + (showMoreAtMinHeight ? SHOW_N_BUTTON_HEIGHT : 0);
const minTilesPx = layout.tilesToPixelsWithPadding(minTiles, minHeightPadding);
const maxTilesPx = layout.tilesToPixelsWithPadding(this.numTiles, this.padding);
const showMoreBtnClasses = (0, _classnames.default)({
'mx_RoomSublist_showNButton': true
}); // If we're hiding rooms, show a 'show more' button to the user. This button
// floats above the resize handle, if we have one present. If the user has all
// tiles visible, it becomes 'show less'.
let showNButton = null;
if (maxTilesPx > this.state.height) {
// the height of all the tiles is greater than the section height: we need a 'show more' button
const nonPaddedHeight = this.state.height - RESIZE_HANDLE_HEIGHT - SHOW_N_BUTTON_HEIGHT;
const amountFullyShown = Math.floor(nonPaddedHeight / this.layout.tileHeight);
const numMissing = this.numTiles - amountFullyShown;
const label = (0, _languageHandler._t)("Show %(count)s more", {
count: numMissing
});
let showMoreText = /*#__PURE__*/React.createElement("span", {
className: "mx_RoomSublist_showNButtonText"
}, label);
if (this.props.isMinimized) showMoreText = null;
showNButton = /*#__PURE__*/React.createElement(_RovingTabIndex.RovingAccessibleButton, {
role: "treeitem",
onClick: this.onShowAllClick,
className: showMoreBtnClasses,
"aria-label": label
}, /*#__PURE__*/React.createElement("span", {
className: "mx_RoomSublist_showMoreButtonChevron mx_RoomSublist_showNButtonChevron"
}), showMoreText);
} else if (this.numTiles > this.layout.defaultVisibleTiles) {
// we have all tiles visible - add a button to show less
const label = (0, _languageHandler._t)("Show less");
let showLessText = /*#__PURE__*/React.createElement("span", {
className: "mx_RoomSublist_showNButtonText"
}, label);
if (this.props.isMinimized) showLessText = null;
showNButton = /*#__PURE__*/React.createElement(_RovingTabIndex.RovingAccessibleButton, {
role: "treeitem",
onClick: this.onShowLessClick,
className: showMoreBtnClasses,
"aria-label": label
}, /*#__PURE__*/React.createElement("span", {
className: "mx_RoomSublist_showLessButtonChevron mx_RoomSublist_showNButtonChevron"
}), showLessText);
} // Figure out if we need a handle
const handles
/*: Enable*/
= {
bottom: true,
// the only one we need, but the others must be explicitly false
bottomLeft: false,
bottomRight: false,
left: false,
right: false,
top: false,
topLeft: false,
topRight: false
};
if (layout.visibleTiles >= this.numTiles && this.numTiles <= layout.minVisibleTiles) {
// we're at a minimum, don't have a bottom handle
handles.bottom = false;
} // We have to account for padding so we can accommodate a 'show more' button and
// the resize handle, which are pinned to the bottom of the container. This is the
// easiest way to have a resize handle below the button as otherwise we're writing
// our own resize handling and that doesn't sound fun.
//
// The layout class has some helpers for dealing with padding, as we don't want to
// apply it in all cases. If we apply it in all cases, the resizing feels like it
// goes backwards and can become wildly incorrect (visibleTiles says 18 when there's
// only mathematically 7 possible).
const handleWrapperClasses = (0, _classnames.default)({
'mx_RoomSublist_resizerHandles': true,
'mx_RoomSublist_resizerHandles_showNButton': !!showNButton
});
content = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_reResizable.Resizable, {
size: {
height: this.state.height
},
minHeight: minTilesPx,
maxHeight: maxTilesPx,
onResizeStart: this.onResizeStart,
onResizeStop: this.onResizeStop,
onResize: this.onResize,
handleWrapperClass: handleWrapperClasses,
handleClasses: {
bottom: "mx_RoomSublist_resizerHandle"
},
className: "mx_RoomSublist_resizeBox",
enable: handles
}, /*#__PURE__*/React.createElement("div", {
className: "mx_RoomSublist_tiles",
onScroll: this.onScrollPrevent
}, visibleTiles), showNButton));
} else if (this.props.showSkeleton && this.state.isExpanded) {
content = /*#__PURE__*/React.createElement("div", {
className: "mx_RoomSublist_skeletonUI"
});
}
return /*#__PURE__*/React.createElement("div", {
ref: this.sublistRef,
className: classes,
role: "group",
"aria-label": this.props.label,
onKeyDown: this.onKeyDown
}, this.renderHeader(), content);
}
}, _temp)) || _class);
exports.default = RoomSublist;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3ZpZXdzL3Jvb21zL1Jvb21TdWJsaXN0LnRzeCJdLCJuYW1lcyI6WyJTSE9XX05fQlVUVE9OX0hFSUdIVCIsIlJFU0laRV9IQU5ETEVfSEVJR0hUIiwiSEVBREVSX0hFSUdIVCIsIk1BWF9QQURESU5HX0hFSUdIVCIsIlJvb21TdWJsaXN0IiwiUmVhY3QiLCJDb21wb25lbnQiLCJjb25zdHJ1Y3RvciIsInByb3BzIiwic3RhdGVVcGRhdGVzIiwiZXh0cmFUaWxlcyIsIm5hbWVDb25kaXRpb24iLCJSb29tTGlzdFN0b3JlIiwiaW5zdGFuY2UiLCJnZXRGaXJzdE5hbWVGaWx0ZXJDb25kaXRpb24iLCJmaWx0ZXJlZEV4dHJhVGlsZXMiLCJmaWx0ZXIiLCJ0IiwibWF0Y2hlcyIsImRpc3BsYXlOYW1lIiwic3RhdGUiLCJjdXJyZW50Um9vbXMiLCJyb29tcyIsIm5ld1Jvb21zIiwib3JkZXJlZExpc3RzIiwidGFnSWQiLCJpc1N0aWxsQmVpbmdGaWx0ZXJlZCIsImlzQmVpbmdGaWx0ZXJlZCIsImlzRXhwYW5kZWQiLCJsYXlvdXQiLCJpc0NvbGxhcHNlZCIsIk9iamVjdCIsImtleXMiLCJsZW5ndGgiLCJzZXRTdGF0ZSIsInBheWxvYWQiLCJhY3Rpb24iLCJzaG93X3Jvb21fdGlsZSIsInNldEltbWVkaWF0ZSIsInJvb21JbmRleCIsImZpbmRJbmRleCIsInIiLCJyb29tSWQiLCJyb29tX2lkIiwidG9nZ2xlQ29sbGFwc2VkIiwibnVtVmlzaWJsZVRpbGVzIiwidmlzaWJsZVRpbGVzIiwidGlsZXNXaXRoUGFkZGluZyIsImZvcmNlVXBkYXRlIiwiZSIsInN0b3BQcm9wYWdhdGlvbiIsIm9uQWRkUm9vbSIsInRyYXZlbERpcmVjdGlvbiIsInJlZlRvRWxlbWVudCIsImRlbHRhIiwibmV3SGVpZ2h0IiwiaGVpZ2h0QXRTdGFydCIsImhlaWdodCIsImFwcGx5SGVpZ2h0Q2hhbmdlIiwiaXNSZXNpemluZyIsInRpbGVzVG9QaXhlbHNXaXRoUGFkZGluZyIsIm51bVRpbGVzIiwicGFkZGluZyIsImZvY3VzUm9vbVRpbGUiLCJkZWZhdWx0VmlzaWJsZVRpbGVzIiwiaW5kZXgiLCJzdWJsaXN0UmVmIiwiY3VycmVudCIsImVsZW1lbnRzIiwicXVlcnlTZWxlY3RvckFsbCIsImVsZW1lbnQiLCJmb2N1cyIsImV2IiwicHJldmVudERlZmF1bHQiLCJ0YXJnZXQiLCJjb250ZXh0TWVudVBvc2l0aW9uIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwibGVmdCIsImNsaWVudFgiLCJ0b3AiLCJjbGllbnRZIiwiYWRkUm9vbUNvbnRleHRNZW51UG9zaXRpb24iLCJpc1VucmVhZEZpcnN0IiwiZ2V0TGlzdE9yZGVyIiwiTGlzdEFsZ29yaXRobSIsIkltcG9ydGFuY2UiLCJuZXdBbGdvcml0aG0iLCJOYXR1cmFsIiwic2V0TGlzdE9yZGVyIiwic29ydCIsInNldFRhZ1NvcnRpbmciLCJzaG93UHJldmlld3MiLCJyb29tIiwiRGVmYXVsdFRhZ0lEIiwiSW52aXRlIiwidW5maWx0ZXJlZExpc3RzIiwiZmluZCIsIm5vdGlmU3RhdGUiLCJub3RpZmljYXRpb25TdGF0ZSIsImdldEZvclJvb20iLCJjb3VudCIsImNvbG9yIiwiZGlzIiwiZGlzcGF0Y2giLCJwb3NzaWJsZVN0aWNreSIsImhlYWRlckJ1dHRvbiIsInBhcmVudEVsZW1lbnQiLCJzdWJsaXN0IiwibGlzdCIsImlzQXRUb3AiLCJzY3JvbGxUb3AiLCJpc0F0Qm90dG9tIiwic2Nyb2xsSGVpZ2h0Iiwib2Zmc2V0SGVpZ2h0IiwiaXNTdGlja3lUb3AiLCJjbGFzc0xpc3QiLCJjb250YWlucyIsImlzU3RpY2t5Qm90dG9tIiwic2Nyb2xsSW50b1ZpZXciLCJiZWhhdmlvciIsIm9uUmVzaXplIiwiZ2V0Um9vbUxpc3RBY3Rpb24iLCJSb29tTGlzdEFjdGlvbiIsIkNvbGxhcHNlU2VjdGlvbiIsIkV4cGFuZFNlY3Rpb24iLCJxdWVyeVNlbGVjdG9yIiwia2V5IiwiS2V5IiwiQVJST1dfTEVGVCIsIkFSUk9XX1JJR0hUIiwiUm9vbUxpc3RMYXlvdXRTdG9yZSIsImdldExheW91dEZvciIsIlJvb21Ob3RpZmljYXRpb25TdGF0ZVN0b3JlIiwiZ2V0TGlzdFN0YXRlIiwiYXNzaWduIiwiY2FsY3VsYXRlSW5pdGlhbEhlaWdodCIsInJlcXVlc3RlZFZpc2libGVUaWxlcyIsIk1hdGgiLCJtYXgiLCJmbG9vciIsIm1pblZpc2libGVUaWxlcyIsInRpbGVDb3VudCIsIm1pbiIsIm5lZWRzU2hvd01vcmUiLCJuZWVkc1Nob3dMZXNzIiwiY2FsY051bVRpbGVzIiwiblZpc2libGUiLCJjZWlsIiwiY29tcG9uZW50RGlkVXBkYXRlIiwicHJldlByb3BzIiwicHJldlN0YXRlIiwicHJldkV4dHJhVGlsZXMiLCJzaG91bGRDb21wb25lbnRVcGRhdGUiLCJuZXh0UHJvcHMiLCJuZXh0U3RhdGUiLCJwcmV2U3RhdGVOb1Jvb21zIiwibmV4dFN0YXRlTm9Sb29tcyIsIm5leHRFeHRyYVRpbGVzIiwicHJldlNsaWNlZFJvb21zIiwic2xpY2UiLCJuZXh0U2xpY2VkUm9vbXMiLCJjb21wb25lbnREaWRNb3VudCIsImRpc3BhdGNoZXJSZWYiLCJkZWZhdWx0RGlzcGF0Y2hlciIsInJlZ2lzdGVyIiwib25BY3Rpb24iLCJvbiIsIkxJU1RTX1VQREFURV9FVkVOVCIsIm9uTGlzdHNVcGRhdGVkIiwiY29tcG9uZW50V2lsbFVubW91bnQiLCJ1bnJlZ2lzdGVyIiwib2ZmIiwiaGVpZ2h0SW5UaWxlcyIsInBpeGVsc1RvVGlsZXMiLCJyZW5kZXJWaXNpYmxlVGlsZXMiLCJ0aWxlcyIsInZpc2libGVSb29tcyIsInB1c2giLCJyZXNpemVOb3RpZmllciIsImlzTWluaW1pemVkIiwicmVuZGVyTWVudSIsImNvbnRleHRNZW51IiwiaXNBbHBoYWJldGljYWwiLCJnZXRUYWdTb3J0aW5nIiwiU29ydEFsZ29yaXRobSIsIkFscGhhYmV0aWMiLCJvdGhlclNlY3Rpb25zIiwib25DbG9zZU1lbnUiLCJvblVucmVhZEZpcnN0Q2hhbmdlZCIsIm9uTWVzc2FnZVByZXZpZXdDaGFuZ2VkIiwiQ2hldnJvbkZhY2UiLCJOb25lIiwib25UYWdTb3J0Q2hhbmdlZCIsIlJlY2VudCIsIm9uQ2xvc2VBZGRSb29tTWVudSIsImFkZFJvb21Db250ZXh0TWVudSIsIm9uT3Blbk1lbnVDbGljayIsInJlbmRlckhlYWRlciIsIm9uRm9jdXMiLCJpc0FjdGl2ZSIsInJlZiIsInRhYkluZGV4IiwiYXJpYUxhYmVsIiwiYmFkZ2UiLCJvbkJhZGdlQ2xpY2siLCJhZGRSb29tQnV0dG9uIiwiYWRkUm9vbUxhYmVsIiwib25BZGRSb29tQ29udGV4dE1lbnUiLCJjb2xsYXBzZUNsYXNzZXMiLCJjbGFzc2VzIiwiYmFkZ2VDb250YWluZXIiLCJCdXR0b24iLCJBY2Nlc3NpYmxlQnV0dG9uIiwiQWNjZXNzaWJsZVRvb2x0aXBCdXR0b24iLCJvbkhlYWRlcktleURvd24iLCJsYWJlbCIsIm9uSGVhZGVyQ2xpY2siLCJvbkNvbnRleHRNZW51IiwidW5kZWZpbmVkIiwib25TY3JvbGxQcmV2ZW50IiwicmVuZGVyIiwiYWx3YXlzVmlzaWJsZSIsImNvbnRlbnQiLCJtaW5UaWxlcyIsInNob3dNb3JlQXRNaW5IZWlnaHQiLCJtaW5IZWlnaHRQYWRkaW5nIiwibWluVGlsZXNQeCIsIm1heFRpbGVzUHgiLCJzaG93TW9yZUJ0bkNsYXNzZXMiLCJzaG93TkJ1dHRvbiIsIm5vblBhZGRlZEhlaWdodCIsImFtb3VudEZ1bGx5U2hvd24iLCJ0aWxlSGVpZ2h0IiwibnVtTWlzc2luZyIsInNob3dNb3JlVGV4dCIsIm9uU2hvd0FsbENsaWNrIiwic2hvd0xlc3NUZXh0Iiwib25TaG93TGVzc0NsaWNrIiwiaGFuZGxlcyIsImJvdHRvbSIsImJvdHRvbUxlZnQiLCJib3R0b21SaWdodCIsInJpZ2h0IiwidG9wTGVmdCIsInRvcFJpZ2h0IiwiaGFuZGxlV3JhcHBlckNsYXNzZXMiLCJvblJlc2l6ZVN0YXJ0Iiwib25SZXNpemVTdG9wIiwic2hvd1NrZWxldG9uIiwib25LZXlE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