First cut of app drawer tiled resizing
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
1a45d18b94
commit
7be5ff0fe6
3 changed files with 138 additions and 21 deletions
|
@ -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;
|
||||
|
|
|
@ -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 }
|
||||
<div className="mx_AppsContainer" ref={this._collectResizer}>
|
||||
{ apps.map((app, i) => {
|
||||
if (i < 1) return app;
|
||||
return <React.Fragment key={app.key}>
|
||||
<ResizeHandle reverse={i > apps.length / 2} />
|
||||
{ app }
|
||||
</React.Fragment>;
|
||||
}) }
|
||||
</div>
|
||||
</PersistentVResizer>
|
||||
{ spinner }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,10 @@ interface IRoomWidgets {
|
|||
|
||||
export const MAX_PINNED = 3;
|
||||
|
||||
// TODO change order to be order that they were pinned
|
||||
// TODO HARD cap at 3, truncating if needed
|
||||
// TODO call finish more proactively to lock things in
|
||||
|
||||
// TODO consolidate WidgetEchoStore into this
|
||||
// TODO consolidate ActiveWidgetStore into this
|
||||
export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
||||
|
|
Loading…
Reference in a new issue