pull/21833/head
Bruno Windels 2018-12-19 23:51:19 +01:00
parent ad4991cd8b
commit d558ea1dbf
5 changed files with 189 additions and 79 deletions

View File

@ -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;

78
src/resizer/item.js Normal file
View File

@ -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);
}
}
}

View File

@ -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);
});
}
}

View File

@ -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());
}
}

View File

@ -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};