UNPKG

matrix-react-sdk

Version:
915 lines (780 loc) 124 kB
"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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