From d558ea1dbf8af83fffc488b75d7d973031f54f06 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 19 Dec 2018 23:51:19 +0100 Subject: [PATCH] WIP --- src/resizer/distributors.js | 19 ++----- src/resizer/item.js | 78 ++++++++++++++++++++++++++ src/resizer/resizer.js | 38 +++++++------ src/resizer/room.js | 109 +++++++++++++++++++++++++++--------- src/resizer/sizer.js | 24 +------- 5 files changed, 189 insertions(+), 79 deletions(-) create mode 100644 src/resizer/item.js diff --git a/src/resizer/distributors.js b/src/resizer/distributors.js index caf677a18f..cb876bf98c 100644 --- a/src/resizer/distributors.js +++ b/src/resizer/distributors.js @@ -27,20 +27,13 @@ the offset from the container edge of where the mouse cursor is. */ class FixedDistributor { - constructor(sizer, item, id, config) { - this.sizer = sizer; + constructor(item) { this.item = item; - this.id = id; - this.beforeOffset = sizer.getItemOffset(this.item); - this.onResized = config && config.onResized; + this.beforeOffset = item.offset(); } - resize(itemSize) { - this.sizer.setItemSize(this.item, itemSize); - if (this.onResized) { - this.onResized(itemSize, this.id, this.item); - } - return itemSize; + resize(size) { + this.item.setSize(size); } resizeFromContainerOffset(offset) { @@ -50,8 +43,8 @@ class FixedDistributor { class CollapseDistributor extends FixedDistributor { - constructor(sizer, item, id, config) { - super(sizer, item, id, config); + constructor(item, sizer, _container, config) { + super(item); this.toggleSize = config && config.toggleSize; this.onCollapsed = config && config.onCollapsed; this.isCollapsed = false; diff --git a/src/resizer/item.js b/src/resizer/item.js new file mode 100644 index 0000000000..ad508150a5 --- /dev/null +++ b/src/resizer/item.js @@ -0,0 +1,78 @@ +/* +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. +*/ + +export default class ResizeItem { + constructor(domNode, id, reverse, resizer, sizer) { + this.domNode = domNode; + this.id = id; + this.reverse = reverse; + this.resizer = resizer; + this.sizer = sizer; + } + + static fromResizeHandle(handle, resizer, sizer) { + const id = handle.getAttribute("data-id"); + const reverse = resizer.isReverseResizeHandle(handle); + const domNode = reverse ? handle.nextElementSibling : handle.previousElementSibling; + return new ResizeItem(domNode, id, reverse, resizer, sizer); + } + + _advance(forwards) { + // opposite direction from fromResizeHandle to get back to handle + let handle = this.reverse ? + this.domNode.previousElementSibling : + this.domNode.nextElementSibling; + const moveNext = forwards !== this.reverse; // xor + // iterate at least once to avoid infinite loop + do { + if (moveNext) { + handle = handle.nextElementSibling; + } else { + handle = handle.previousElementSibling; + } + } while(handle && !this.resizer.isResizeHandle(handle)); + if (handle) { + const nextHandle = ResizeItem.fromResizeHandle(handle, this.resizer, this.sizer); + nextHandle.reverse = this.reverse; + return nextHandle; + } + } + + next() { + return this._advance(true); + } + + previous() { + return this._advance(false); + } + + size() { + return this.sizer.getItemSize(this.domNode); + } + + offset() { + return this.sizer.getItemOffset(this.domNode); + } + + setSize(size) { + this.sizer.setItemSize(this.domNode, size); + console.log("resizing", this.domNode, "to", size, this.size()); + const callback = this.resizer.distributorCtor.onResized; + if (callback) { + callback(size, this.id, this.domNode); + } + } +} diff --git a/src/resizer/resizer.js b/src/resizer/resizer.js index 0e113b3664..8bdbc6cffe 100644 --- a/src/resizer/resizer.js +++ b/src/resizer/resizer.js @@ -15,6 +15,7 @@ limitations under the License. */ import {Sizer} from "./sizer"; +import ResizeItem from "./item"; /* classNames: @@ -28,7 +29,10 @@ classNames: resizing: string */ + export class Resizer { + // TODO move vertical/horizontal to config option/container class + // as it doesn't make sense to mix them within one container/Resizer constructor(container, distributorCtor, distributorCfg, sizerCtor = Sizer) { this.container = container; this.distributorCtor = distributorCtor; @@ -79,7 +83,11 @@ export class Resizer { } } - _isResizeHandle(el) { + isReverseResizeHandle(el) { + return el && el.classList.contains(this.classNames.reverse); + } + + isResizeHandle(el) { return el && el.classList.contains(this.classNames.handle); } @@ -119,35 +127,29 @@ export class Resizer { _createSizerAndDistributor(resizeHandle) { const vertical = resizeHandle.classList.contains(this.classNames.vertical); - const reverse = resizeHandle.classList.contains(this.classNames.reverse); - + const reverse = this.isReverseResizeHandle(resizeHandle); // eslint-disable-next-line new-cap const sizer = new this.sizerCtor(this.container, vertical, reverse); - - const items = this._getResizableItems(); - const prevItem = resizeHandle.previousElementSibling; - // if reverse, resize the item after the handle instead of before, so + 1 - const itemIndex = items.indexOf(prevItem) + (reverse ? 1 : 0); - const item = items[itemIndex]; - const id = resizeHandle.getAttribute("data-id"); + const item = ResizeItem.fromResizeHandle(resizeHandle, this, sizer); // eslint-disable-next-line new-cap const distributor = new this.distributorCtor( - sizer, item, id, this.distributorCfg, - items, this.container); + item, + sizer, + this.container, + this.distributorCfg + ); return {sizer, distributor}; } - _getResizableItems() { - return Array.from(this.container.children).filter(el => { - return !this._isResizeHandle(el) && ( - this._isResizeHandle(el.previousElementSibling) || - this._isResizeHandle(el.nextElementSibling)); + _getResizableItems(reverse) { + return this._getResizeHandles().map((handle) => { + return ResizeItem.fromResizeHandle(handle); }); } _getResizeHandles() { return Array.from(this.container.children).filter(el => { - return this._isResizeHandle(el); + return this.isResizeHandle(el); }); } } diff --git a/src/resizer/room.js b/src/resizer/room.js index def12d49eb..fda09940b1 100644 --- a/src/resizer/room.js +++ b/src/resizer/room.js @@ -15,42 +15,99 @@ limitations under the License. */ import {Sizer} from "./sizer"; -import {FixedDistributor} from "./distributors"; class RoomSizer extends Sizer { setItemSize(item, size) { - const isString = typeof size === "string"; - const cl = item.classList; - if (isString) { - if (size === "resized-all") { - cl.add("resized-all"); - cl.remove("resized-sized"); - item.style.maxHeight = null; - } - } else { - cl.add("resized-sized"); - cl.remove("resized-all"); - item.style.maxHeight = `${Math.round(size)}px`; - } + item.style.maxHeight = `${Math.round(size)}px`; } } -class RoomDistributor extends FixedDistributor { - resize(itemSize) { - const scrollItem = this.item.querySelector(".mx_RoomSubList_scroll"); - if (!scrollItem) { - return; //FIXME: happens when starting the page on a community url, taking the safe way out for now - } - const fixedHeight = this.item.offsetHeight - scrollItem.offsetHeight; - if (itemSize > (fixedHeight + scrollItem.scrollHeight)) { - super.resize("resized-all"); +/* +class RoomSubList extends ResizeItem { + collapsed() { + + } + + scrollSizes() { + return {offsetHeight, scrollHeight}; + } + + id() { + + } +} +*/ + +const MIN_SIZE = 70; +// would be good to have a way in here to know if the item can be resized +// - collapsed items can't be resized (.mx_RoomSubList_hidden) +// - items at MIN_SIZE can't be resized smaller +// - items at maxContentHeight can't be resized larger + +// if you shrink the predecesor, and start dragging down again afterwards, which item has to grow? + +class RoomDistributor { + constructor(item) { + this.item = item; + } + + _handleSize() { + return 1; + } + + // returns the remainder of size it didn't consume for this item + _sizeItem(item, size) { + // if collapsed, do nothing and subtract own height + if (item.domNode.classList.contains("mx_RoomSubList_hidden")) { + return; + } else if (size < MIN_SIZE) { + item.setSize(MIN_SIZE); } else { - super.resize(itemSize); + const scrollItem = item.domNode.querySelector(".mx_RoomSubList_scroll"); + const headerHeight = item.size() - scrollItem.offsetHeight; + const maxContentHeight = headerHeight + scrollItem.scrollHeight; + // avoid flexbox growing larger than the content height + if (size > maxContentHeight) { + item.setSize(maxContentHeight); + } else { + item.setSize(size); + } } } - resizeFromContainerOffset(offset) { - return this.resize(offset - this.sizer.getItemOffset(this.item)); + resize(size) { + if (size < 0) { + console.log("NEGATIVE SIZE RESIZE RESIZE RESIZE!!!", size); + } + let item = this.item; + // move to item that is at position of cursor + // this would happen if the cursor goes beyond the min-height + while (item && size < 0) { + item = item.previous(); + if (item) { + size = item.size() - size - this._handleSize(); + } + } + // change size of item and previous items from here + while(item && size > 0) { + const itemSize = item.size(); + this._sizeItem(item, size); + const delta = item.size() - itemSize; + const remainder = size - delta; + // pass remainder to previous item + if (remainder !== 0) { + item = item.previous(); + if (item) { + size = item.size() - remainder - this._handleSize(); + } + } else { + item = null; + } + } + } + + resizeFromContainerOffset(containerOffset) { + this.resize(containerOffset - this.item.offset()); } } diff --git a/src/resizer/sizer.js b/src/resizer/sizer.js index 303214854b..1cb212e20a 100644 --- a/src/resizer/sizer.js +++ b/src/resizer/sizer.js @@ -18,31 +18,13 @@ limitations under the License. implements DOM/CSS operations for resizing. The sizer determines what CSS mechanism is used for sizing items, like flexbox, ... */ -class Sizer { +export class Sizer { constructor(container, vertical, reverse) { this.container = container; this.reverse = reverse; this.vertical = vertical; } - getItemPercentage(item) { - /* - const flexGrow = window.getComputedStyle(item).flexGrow; - if (flexGrow === "") { - return null; - } - return parseInt(flexGrow) / 1000; - */ - const style = window.getComputedStyle(item); - const sizeStr = this.vertical ? style.height : style.width; - const size = parseInt(sizeStr, 10); - return size / this.getTotalSize(); - } - - setItemPercentage(item, percent) { - item.style.flexGrow = Math.round(percent * 1000); - } - /** @param {Element} item the dom element being resized @return {number} how far the edge of the item is from the edge of the container @@ -97,11 +79,9 @@ class Sizer { } } -class FlexSizer extends Sizer { +export class FlexSizer extends Sizer { setItemSize(item, size) { item.style.flexGrow = `0`; item.style.flexBasis = `${Math.round(size)}px`; } } - -module.exports = {Sizer, FlexSizer};