diff --git a/res/css/_components.scss b/res/css/_components.scss index 128e7235ad..a230842c77 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -1,5 +1,6 @@ // autogenerated by rethemendex.sh @import "./_common.scss"; +@import "./structures/_AutoHideScrollbar.scss"; @import "./structures/_CompatibilityPage.scss"; @import "./structures/_ContextualMenu.scss"; @import "./structures/_CreateRoom.scss"; diff --git a/res/css/structures/_AutoHideScrollbar.scss b/res/css/structures/_AutoHideScrollbar.scss new file mode 100644 index 0000000000..0b8558a61e --- /dev/null +++ b/res/css/structures/_AutoHideScrollbar.scss @@ -0,0 +1,56 @@ +/* +Copyright 2018 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* +1. for browsers that support native overlay auto-hiding scrollbars +*/ +.mx_AutoHideScrollbar { + overflow-y: auto; + -ms-overflow-style: -ms-autohiding-scrollbar; +} +/* +2. webkit also supports overflow:overlay where the scrollbars don't take any space +in the layout but they don't autohide, so do that only on hover +*/ +body.mx_scrollbar_overlay_noautohide .mx_AutoHideScrollbar { + overflow-y: hidden; +} + +body.mx_scrollbar_overlay_noautohide .mx_AutoHideScrollbar:hover { + overflow-y: overlay; +} +/* +3. as a last fallback, compensate for the scrollbar taking up space in the layout +by playing with the paddings. the default below will add a right padding +of the scrollbar width and clear that on hover. +this won't work well on classes that also need to set their padding, +so this needs to be overriden and adjust the padding with calc like so: +``` +body.mx_scrollbar_nooverlay .componentClass.mx_AutoHideScrollbar_overflow:hover { + padding-right: calc(15px - var(--scrollbar-width)) !important; +} +``` +*/ +body.mx_scrollbar_nooverlay .mx_AutoHideScrollbar { + box-sizing: border-box; + overflow-y: hidden; + padding-right: var(--scrollbar-width); +} + +body.mx_scrollbar_nooverlay .mx_AutoHideScrollbar:hover { + overflow-y: auto; + padding-right: 0; +} diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss index 3321433d47..e062a912a5 100644 --- a/res/css/structures/_RoomSubList.scss +++ b/res/css/structures/_RoomSubList.scss @@ -16,14 +16,12 @@ limitations under the License. .mx_RoomSubList { min-height: 31px; - flex: 0 0 auto; + flex: 0 1 auto; display: flex; flex-direction: column; } .mx_RoomSubList_nonEmpty { - min-height: 80px; - flex: 1; margin-bottom: 8px; } @@ -131,9 +129,18 @@ limitations under the License. transform: rotateZ(-90deg); } -.mx_RoomSubList .gm-scrollbar-container { +.mx_RoomSubList_scroll { /* let rooms list grab all available space */ - flex: 1; + flex: 0 1 auto; + padding: 0 15px !important; +} +/* +for browsers that don't support overlay scrollbars, +subtract scrollbar width from right padding on hover when overflowing +so the content doesn't jump when showing the scrollbars +*/ +body.mx_scrollbar_nooverlay .mx_RoomSubList_scroll.mx_AutoHideScrollbar_overflow:hover { + padding-right: calc(15px - var(--scrollbar-width)) !important; } .collapsed { diff --git a/res/css/structures/_TopLeftMenuButton.scss b/res/css/structures/_TopLeftMenuButton.scss index a80e06c620..43a1e27ee4 100644 --- a/res/css/structures/_TopLeftMenuButton.scss +++ b/res/css/structures/_TopLeftMenuButton.scss @@ -15,7 +15,7 @@ limitations under the License. */ .mx_TopLeftMenuButton { - height: 52px; + flex: 0 0 52px; border-bottom: 1px solid $panel-divider-color; color: $topleftmenu-color; background-color: $primary-bg-color; diff --git a/res/css/views/rooms/_RoomList.scss b/res/css/views/rooms/_RoomList.scss index e2557d4b3a..77185452cf 100644 --- a/res/css/views/rooms/_RoomList.scss +++ b/res/css/views/rooms/_RoomList.scss @@ -17,7 +17,7 @@ limitations under the License. .mx_RoomList { /* take up remaining space below TopLeftMenu */ - flex: 1; + flex: 1 1 auto; /* use flexbox to layout sublists */ display: flex; flex-direction: column; diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 78dbc27da3..ca23d79137 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -20,7 +20,7 @@ limitations under the License. align-items: center; cursor: pointer; height: 40px; - margin: 0 12px; + margin: 0; padding: 2px 12px; position: relative; background-color: $secondary-accent-color; diff --git a/src/components/structures/AutoHideScrollbar.js b/src/components/structures/AutoHideScrollbar.js new file mode 100644 index 0000000000..507af2c5f0 --- /dev/null +++ b/src/components/structures/AutoHideScrollbar.js @@ -0,0 +1,118 @@ +/* +Copyright 2018 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; + +// derived from code from github.com/noeldelgado/gemini-scrollbar +// Copyright (c) Noel Delgado (pixelia.me) +function getScrollbarWidth(alternativeOverflow) { + const div = document.createElement('div'); + div.style.position = 'absolute'; + div.style.top = '-9999px'; + div.style.width = '100px'; + div.style.height = '100px'; + div.style.overflow = "scroll"; + if (alternativeOverflow) { + div.style.overflow = alternativeOverflow; + } + div.style.msOverflowStyle = '-ms-autohiding-scrollbar'; + document.body.appendChild(div); + const scrollbarWidth = (div.offsetWidth - div.clientWidth); + document.body.removeChild(div); + return scrollbarWidth; +} + +function install() { + const scrollbarWidth = getScrollbarWidth(); + if (scrollbarWidth !== 0) { + const hasForcedOverlayScrollbar = getScrollbarWidth('overlay') === 0; + // overflow: overlay on webkit doesn't auto hide the scrollbar + if (hasForcedOverlayScrollbar) { + document.body.classList.add("mx_scrollbar_overlay_noautohide"); + } else { + document.body.classList.add("mx_scrollbar_nooverlay"); + const style = document.createElement('style'); + style.type = 'text/css'; + style.innerText = + `body.mx_scrollbar_nooverlay { --scrollbar-width: ${scrollbarWidth}px; }`; + document.head.appendChild(style); + } + } +} + +const installBodyClassesIfNeeded = (function() { + let installed = false; + return function() { + if (!installed) { + install(); + installed = true; + } + }; +})(); + +export default class AutoHideScrollbar extends React.Component { + constructor(props) { + super(props); + this.onOverflow = this.onOverflow.bind(this); + this.onUnderflow = this.onUnderflow.bind(this); + this._collectContainerRef = this._collectContainerRef.bind(this); + } + + onOverflow() { + this.containerRef.classList.add("mx_AutoHideScrollbar_overflow"); + this.containerRef.classList.remove("mx_AutoHideScrollbar_underflow"); + } + + onUnderflow() { + this.containerRef.classList.remove("mx_AutoHideScrollbar_overflow"); + this.containerRef.classList.add("mx_AutoHideScrollbar_underflow"); + } + + _collectContainerRef(ref) { + if (ref && !this.containerRef) { + this.containerRef = ref; + const needsOverflowListener = + document.body.classList.contains("mx_scrollbar_nooverlay"); + + if (needsOverflowListener) { + this.containerRef.addEventListener("overflow", this.onOverflow); + this.containerRef.addEventListener("underflow", this.onUnderflow); + } + if (ref.scrollHeight > ref.clientHeight) { + this.onOverflow(); + } else { + this.onUnderflow(); + } + } + } + + componentWillUnmount() { + if (this.containerRef) { + this.containerRef.removeEventListener("overflow", this.onOverflow); + this.containerRef.removeEventListener("underflow", this.onUnderflow); + } + } + + render() { + installBodyClassesIfNeeded(); + return (
+ { this.props.children } +
); + } +} diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js index cd4c81bd2c..247b12dc88 100644 --- a/src/components/structures/LeftPanel.js +++ b/src/components/structures/LeftPanel.js @@ -179,10 +179,9 @@ const LeftPanel = React.createClass({ const RoomList = sdk.getComponent('rooms.RoomList'); const TagPanel = sdk.getComponent('structures.TagPanel'); const TopLeftMenuButton = sdk.getComponent('structures.TopLeftMenuButton'); - const BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu'); const CallPreview = sdk.getComponent('voip.CallPreview'); - let topBox = ; + const topBox = ; /* const SearchBox = sdk.getComponent('structures.SearchBox'); const topBox = ; diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 490a84bb7f..3ea819e088 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -23,6 +23,7 @@ import dis from '../../dispatcher'; import Unread from '../../Unread'; import * as RoomNotifs from '../../RoomNotifs'; import * as FormattingUtils from '../../utils/FormattingUtils'; +import AutoHideScrollbar from './AutoHideScrollbar'; import { KeyCode } from '../../Keyboard'; import { Group } from 'matrix-js-sdk'; import PropTypes from 'prop-types'; @@ -348,19 +349,17 @@ const RoomSubList = React.createClass({ {this._getHeaderJsx()} ; } else { - const heightEstimation = (len * 40) + 31 + (8 + 8); + const heightEstimation = (len * 44) + 31 + (8 + 8); const style = { - flexGrow: `${heightEstimation}`, maxHeight: `${heightEstimation}px`, }; - const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper"); const tiles = this.makeRoomTiles(); tiles.push(...this.props.extraTiles); - return
+ return
{this._getHeaderJsx()} - + { tiles } - +
; } } else { diff --git a/src/components/structures/TopLeftMenuButton.js b/src/components/structures/TopLeftMenuButton.js index 71ca382e92..00e669acdd 100644 --- a/src/components/structures/TopLeftMenuButton.js +++ b/src/components/structures/TopLeftMenuButton.js @@ -26,7 +26,6 @@ import Avatar from '../../Avatar'; const AVATAR_SIZE = 28; export default class TopLeftMenuButton extends React.Component { - static propTypes = { collapsed: PropTypes.bool.isRequired, }; diff --git a/src/components/views/context_menus/TopLeftMenu.js b/src/components/views/context_menus/TopLeftMenu.js index ec3120cfea..76ac10399c 100644 --- a/src/components/views/context_menus/TopLeftMenu.js +++ b/src/components/views/context_menus/TopLeftMenu.js @@ -19,7 +19,6 @@ import dis from '../../../dispatcher'; import { _t } from '../../../languageHandler'; export class TopLeftMenu extends React.Component { - constructor() { super(); this.openSettings = this.openSettings.bind(this); diff --git a/src/resizer/distributors.js b/src/resizer/distributors.js index a81925a763..29e97e0bce 100644 --- a/src/resizer/distributors.js +++ b/src/resizer/distributors.js @@ -74,7 +74,6 @@ class CollapseDistributor extends FixedDistributor { } class PercentageDistributor { - constructor(sizer, item, _config, items, container) { this.container = container; this.totalSize = sizer.getTotalSize(); diff --git a/src/resizer/room.js b/src/resizer/room.js index a6ad0b5dc2..3d68d16f9b 100644 --- a/src/resizer/room.js +++ b/src/resizer/room.js @@ -34,7 +34,6 @@ class RoomSizer extends Sizer { item.style.maxHeight = `${Math.round(size)}px`; } } - } class RoomDistributor extends FixedDistributor {