/* Copyright 2024 New Vector Ltd. Copyright 2020 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ .mx_RoomSublist { margin-left: 8px; margin-bottom: 4px; &.mx_RoomSublist_hidden { display: none; } &:not(.mx_RoomSublist_minimized) { .mx_RoomSublist_headerContainer { height: auto; } } .mx_RoomSublist_headerContainer { /* Create a flexbox to make alignment easy */ display: flex; align-items: center; /* *************************** */ /* Sticky Headers Start */ /* Ideally we'd be able to use `position: sticky; top: 0; bottom: 0;` on the */ /* headerContainer, however due to our layout concerns we actually have to */ /* calculate it manually so we can sticky things in the right places. We also */ /* target the headerText instead of the container to reduce jumps when scrolling, */ /* and to help hide the badges/other buttons that could appear on hover. This */ /* all works by ensuring the header text has a fixed height when sticky so the */ /* fixed height of the container can maintain the scroll position. */ /* The combined height must be set in the LeftPanel component for sticky headers */ /* to work correctly. */ padding-bottom: 8px; height: 24px; color: $secondary-content; .mx_RoomSublist_stickableContainer { width: 100%; } .mx_RoomSublist_stickable { flex: 1; max-width: 100%; /* Create a flexbox to make ordering easy */ display: flex; align-items: center; /* We use a generic sticky class for 2 reasons: to reduce style duplication and */ /* to identify when a header is sticky. If we didn't have a consistent sticky class, */ /* we'd have to do the "is sticky" checks again on click, as clicking the header */ /* when sticky scrolls instead of collapses the list. */ &.mx_RoomSublist_headerContainer_sticky { position: fixed; height: 32px; /* to match the header container */ /* width set by JS because of a compat issue between Firefox and Chrome */ width: calc(100% - 15px); } /* We don't have a top style because the top is dependent on the room list header's */ /* height, and is therefore calculated in JS. */ /* The class, mx_RoomSublist_headerContainer_stickyTop, is applied though. */ } /* Sticky Headers End */ /* *************************** */ .mx_RoomSublist_badgeContainer { /* Create another flexbox row because it's super easy to position the badge this way. */ display: flex; align-items: center; justify-content: center; /* Apply the width and margin to the badge so the container doesn't occupy dead space */ .mx_NotificationBadge { /* Do not set a width so the badges get properly sized */ margin-left: 8px; /* same as menu+aux buttons */ } } &:not(.mx_RoomSublist_headerContainer_withAux) { .mx_NotificationBadge { margin-right: 4px; /* just to push it over a bit, aligning it with the other elements */ } } .mx_RoomSublist_auxButton, .mx_RoomSublist_menuButton { margin-left: 8px; /* should be the same as the notification badge */ position: relative; width: 24px; height: 24px; border-radius: 8px; &::before { content: ""; width: 16px; height: 16px; position: absolute; top: 4px; left: 4px; mask-position: center; mask-size: contain; mask-repeat: no-repeat; background: var(--cpd-color-icon-secondary); } } .mx_RoomSublist_auxButton:hover, .mx_RoomSublist_menuButton:hover { background: $panel-actions; } /* Hide the menu button by default */ .mx_RoomSublist_menuButton { visibility: hidden; width: 0; margin: 0; } .mx_RoomSublist_auxButton::before { mask-image: url("$(res)/img/element-icons/roomlist/plus.svg"); } .mx_RoomSublist_menuButton::before { mask-image: url("$(res)/img/element-icons/context-menu.svg"); } .mx_RoomSublist_headerText { flex: 1; max-width: calc(100% - 16px); /* 16px is the badge width */ font: var(--cpd-font-body-sm-semibold); /* Ellipsize any text overflow */ text-overflow: ellipsis; overflow: hidden; white-space: nowrap; .mx_RoomSublist_collapseBtn { display: inline-block; position: relative; width: 14px; height: 14px; margin-right: 6px; &::before { content: ""; width: 18px; height: 18px; position: absolute; mask-position: center; mask-size: contain; mask-repeat: no-repeat; background-color: var(--cpd-color-icon-secondary); mask-image: url("$(res)/img/feather-customised/chevron-down.svg"); } &.mx_RoomSublist_collapseBtn_collapsed::before { transform: rotate(-90deg); } } } } /* In the general case, we reserve space for each sublist header to prevent */ /* scroll jumps when they become sticky. However, that leaves a gap when */ /* scrolled to the top above the first sublist (whose header can only ever */ /* stick to top), so we make sure to exclude the first visible sublist. */ &:not(.mx_RoomSublist_hidden) ~ .mx_RoomSublist .mx_RoomSublist_stickableContainer { height: 24px; } .mx_RoomSublist_resizeBox { position: relative; /* Create another flexbox column for the tiles */ display: flex; flex-direction: column; overflow: hidden; .mx_RoomSublist_tiles { flex: 1 0 0; overflow: hidden; overflow: clip; /* need this to be flex otherwise the overflow hidden from above */ /* sometimes vertically centers the clipped list ... no idea why it would do this */ /* as the box model should be top aligned. Happens in both FF and Chromium */ display: flex; flex-direction: column; align-self: stretch; /* without this Firefox will prefer pushing the resizer & show more/less button into the overflow */ min-height: 0; mask-image: linear-gradient(0deg, transparent, black 4px); } &.mx_RoomSublist_resizeBox_forceExpanded .mx_RoomSublist_tiles { /* in this state the div can collapse its height entirely in Chromium, */ /* so prevent that by allowing overflow */ overflow: visible; /* clear the min-height to make it not collapse entirely in a state with no active resizer */ min-height: unset; } .mx_RoomSublist_resizerHandles_showNButton { flex: 0 0 32px; } .mx_RoomSublist_resizerHandles { flex: 0 0 4px; display: flex; justify-content: center; width: 100%; } /* Class name comes from the ResizableBox component */ /* The hover state needs to use the whole sublist, not just the resizable box, */ /* so that selector is below and one level higher. */ .mx_RoomSublist_resizerHandle { cursor: ns-resize; border-radius: 3px; /* Override styles from library */ max-width: 64px; height: 4px !important; /* Update RESIZE_HANDLE_HEIGHT if this changes */ /* This is positioned directly below the 'show more' button. */ position: relative !important; bottom: 0 !important; /* override from library */ } &:hover, &.mx_RoomSublist_hasMenuOpen { .mx_RoomSublist_resizerHandle { opacity: 0.8; background-color: $primary-content; } } } .mx_RoomSublist_showNButton { cursor: pointer; font-size: $font-13px; line-height: $font-18px; color: $secondary-content; /* Update the render() function for RoomSublist if these change */ /* Update the ListLayout class for minVisibleTiles if these change. */ height: 24px; padding-bottom: 4px; /* We create a flexbox to cheat at alignment */ display: flex; align-items: center; .mx_RoomSublist_showNButtonChevron { position: relative; width: 18px; height: 18px; margin-left: 12px; margin-right: 16px; mask-position: center; mask-size: contain; mask-repeat: no-repeat; background: $tertiary-content; left: -1px; /* adjust for image position */ } .mx_RoomSublist_showMoreButtonChevron, .mx_RoomSublist_showLessButtonChevron { mask-image: url("$(res)/img/feather-customised/chevron-down.svg"); } .mx_RoomSublist_showLessButtonChevron { transform: rotate(180deg); } } &.mx_RoomSublist_hasMenuOpen, &:not(.mx_RoomSublist_minimized) > .mx_RoomSublist_headerContainer:focus-within, &:not(.mx_RoomSublist_minimized) > .mx_RoomSublist_headerContainer:hover { .mx_RoomSublist_menuButton { visibility: visible; width: 24px; margin-left: 8px; } } &.mx_RoomSublist_minimized { .mx_RoomSublist_headerContainer { height: auto; flex-direction: column; position: relative; .mx_RoomSublist_badgeContainer { order: 0; align-self: flex-end; margin-right: 0; } .mx_RoomSublist_stickable { order: 1; max-width: 100%; } .mx_RoomSublist_auxButton { order: 2; visibility: visible; width: 32px !important; /* !important to override hover styles */ height: 32px !important; /* !important to override hover styles */ margin-left: 0 !important; /* !important to override hover styles */ background-color: $panel-actions; margin-top: 8px; &::before { top: 8px; left: 8px; } } } .mx_RoomSublist_resizeBox { align-items: center; } .mx_RoomSublist_showNButton { flex-direction: column; .mx_RoomSublist_showNButtonChevron { margin-right: 12px; /* to center */ } } .mx_RoomSublist_menuButton { height: 16px; } &.mx_RoomSublist_hasMenuOpen, & > .mx_RoomSublist_headerContainer:hover { .mx_RoomSublist_menuButton { visibility: visible; position: absolute; bottom: 48px; /* align to middle of name, 40px for aux button (with padding) and 8px for alignment */ right: 0; width: 16px; height: 16px; border-radius: 0; z-index: 1; /* occlude the list name */ /* This is the same color as the left panel background because it needs */ /* to occlude the sublist title */ background-color: $roomlist-bg-color; &::before { top: 0; left: 0; } } &.mx_RoomSublist_headerContainer:not(.mx_RoomSublist_headerContainer_withAux) { .mx_RoomSublist_menuButton { bottom: 8px; /* align to the middle of name, 40px less than the `bottom` above. */ } } } } } .mx_RoomSublist_contextMenu { padding: 20px 16px; width: 250px; hr { margin-top: 16px; margin-bottom: 16px; margin-right: 16px; /* additional 16px */ border: 1px solid $primary-content; opacity: 0.1; } .mx_RoomSublist_contextMenu_title { font-size: $font-15px; line-height: $font-20px; font-weight: var(--cpd-font-weight-semibold); margin-bottom: 4px; } .mx_StyledRadioButton, .mx_Checkbox { margin-top: 8px; } } .mx_RoomSublist_skeletonUI { position: relative; margin-left: 4px; height: 240px; &::before { background: $roomsublist-skeleton-ui-bg; width: 100%; height: 100%; content: ""; position: absolute; mask-repeat: repeat-y; mask-size: auto 48px; mask-image: url("$(res)/img/element-icons/roomlist/skeleton-ui.svg"); } } .mx_RoomSublist_minimized .mx_RoomSublist_skeletonUI { width: 32px; /* cut off the horizontal lines in the svg */ margin-left: 10px; /* align with sublist + buttons */ }