diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss index 8cb407989d..c96b6fde82 100644 --- a/res/css/views/rooms/_AppsDrawer.scss +++ b/res/css/views/rooms/_AppsDrawer.scss @@ -50,8 +50,8 @@ $MiniAppTileHeight: 200px; } } -.mx_AppsDrawer_hidden { - display: none; +.mx_AppsContainer_resizer { + margin-bottom: 8px; } .mx_AppsContainer { @@ -60,7 +60,9 @@ $MiniAppTileHeight: 200px; align-items: stretch; justify-content: center; height: 100%; - margin-bottom: 8px; + width: 100%; + flex: 1; + min-height: 0; .mx_AppTile:first-of-type { border-left-width: 8px; @@ -70,25 +72,52 @@ $MiniAppTileHeight: 200px; border-right-width: 8px; border-radius: 0 10px 10px 0; } + + .mx_ResizeHandle_horizontal { + position: relative; + + > div { + width: 0; + } + + &:hover::before { + position: absolute; + left: 3px; + top: 50%; + transform: translate(0, -50%); + + height: 64px; // to match width of the ones on roomlist + width: 4px; + border-radius: 4px; + + content: ' '; + + background-color: $primary-fg-color; + opacity: 0.8; + } + } } -.mx_AppsDrawer_minimised .mx_AppsContainer { - // override the re-resizable inline styles - height: inherit !important; - min-height: inherit !important; -} +.mx_AppsDrawer_has2 .mx_AppTile { + width: 50%; -.mx_AddWidget_button { - order: 2; - cursor: pointer; - padding: 0; - margin: -3px auto 5px 0; - color: $accent-color; - font-size: $font-12px; + &:nth-child(3) { + flex-grow: 1; + width: 0; + } +} +.mx_AppsDrawer_has3 .mx_AppTile { + width: 33%; + + &:nth-child(3) { + flex-grow: 1; + width: 0; + } } .mx_AppTile { width: 50%; + min-width: 200px; border: 8px solid $widget-menu-bar-bg-color; border-left-width: 5px; border-right-width: 5px; @@ -102,7 +131,7 @@ $MiniAppTileHeight: 200px; } .mx_AppTileFullWidth { - width: 100%; + width: 100% !important; // to override the inline style set by the resizer margin: 0; padding: 0; border: 5px solid $widget-menu-bar-bg-color; diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index 7d2f9d87ef..9c5265c63c 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, {useState} from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import {Resizable} from "re-resizable"; @@ -31,6 +31,9 @@ import SettingsStore from "../../../settings/SettingsStore"; import {useLocalStorageState} from "../../../hooks/useLocalStorageState"; import ResizeNotifier from "../../../utils/ResizeNotifier"; import WidgetStore from "../../../stores/WidgetStore"; +import ResizeHandle from "../elements/ResizeHandle"; +import Resizer from "../../../resizer/resizer"; +import PercentageDistributor from "../../../resizer/distributors/percentage"; export default class AppsDrawer extends React.Component { static propTypes = { @@ -50,6 +53,9 @@ export default class AppsDrawer extends React.Component { this.state = { apps: this._getApps(), }; + + this._resizeContainer = null; + this.resizer = this._createResizer(); } componentDidMount() { @@ -62,6 +68,9 @@ export default class AppsDrawer extends React.Component { ScalarMessaging.stopListening(); WidgetStore.instance.off(this.props.room.roomId, this._updateApps); if (this.dispatcherRef) dis.unregister(this.dispatcherRef); + if (this._resizeContainer) { + this.resizer.detach(); + } } // TODO: [REACT-WARNING] Replace with appropriate lifecycle event @@ -71,6 +80,70 @@ export default class AppsDrawer extends React.Component { this._updateApps(); } + _createResizer() { + const classNames = { + handle: "mx_ResizeHandle", + vertical: "mx_ResizeHandle_vertical", + reverse: "mx_ResizeHandle_reverse", + }; + const collapseConfig = { + onResizeStart: () => { + this._resizeContainer.classList.add("mx_AppsDrawer_resizing"); + }, + onResizeStop: () => { + this._resizeContainer.classList.remove("mx_AppsDrawer_resizing"); + // persist to localStorage + localStorage.setItem(this._getStorageKey(), JSON.stringify([ + this._getIdString(), + ...this.state.apps.slice(1).map((_, i) => this.resizer.forHandleAt(i).size), + ])); + }, + }; + // pass a truthy container for now, we won't call attach until we update it + const resizer = new Resizer({}, PercentageDistributor, collapseConfig); + resizer.setClassNames(classNames); + return resizer; + } + + _collectResizer = (ref) => { + console.log("@@ _collectResizer"); + if (this._resizeContainer) { + this.resizer.detach(); + } + + if (ref) { + this.resizer.container = ref; + this.resizer.attach(); + } + this._resizeContainer = ref; + this._loadResizerPreferences(); + }; + + _getStorageKey = () => `mx_apps_drawer-${this.props.room.roomId}`; + + _getIdString = () => this.state.apps.map(app => app.id).join("~"); + + _loadResizerPreferences = () => { // TODO call this when changing pinned apps + console.log("@@ _loadResizerPreferences"); + try { + const [idString, ...sizes] = JSON.parse(localStorage.getItem(this._getStorageKey())); + // format: [idString: string, ...percentages: string]; + if (this._getIdString() !== idString) return; + sizes.forEach((size, i) => { + const distributor = this.resizer.forHandleAt(i); + distributor.size = size; + distributor.finish(); + }); + } catch (e) { + console.error(e); + this.state.apps.slice(1).forEach((_, i) => { + const distributor = this.resizer.forHandleAt(i); + distributor.item.clearSize(); + distributor.finish(); + }); + } + }; + onAction = (action) => { const hideWidgetKey = this.props.room.roomId + '_hide_widget_drawer'; switch (action.action) { @@ -94,7 +167,7 @@ export default class AppsDrawer extends React.Component { _updateApps = () => { this.setState({ apps: this._getApps(), - }); + }, this._loadResizerPreferences); }; _launchManageIntegrations() { @@ -147,6 +220,9 @@ export default class AppsDrawer extends React.Component { mx_AppsDrawer: true, mx_AppsDrawer_fullWidth: apps.length < 2, mx_AppsDrawer_resizing: this.state.resizing, + mx_AppsDrawer_has1: apps.length === 1, + mx_AppsDrawer_has2: apps.length === 2, + mx_AppsDrawer_has3: apps.length === 3, }); return ( @@ -156,13 +232,21 @@ export default class AppsDrawer extends React.Component { minHeight={100} maxHeight={this.props.maxHeight ? this.props.maxHeight - 50 : undefined} handleClass="mx_AppsContainer_resizerHandle" - className="mx_AppsContainer" + className="mx_AppsContainer_resizer" resizeNotifier={this.props.resizeNotifier} setResizing={this.setResizing} > - { apps } - { spinner } +