WIP
This commit is contained in:
parent
ad4991cd8b
commit
d558ea1dbf
5 changed files with 189 additions and 79 deletions
|
@ -27,20 +27,13 @@ the offset from the container edge of where
|
||||||
the mouse cursor is.
|
the mouse cursor is.
|
||||||
*/
|
*/
|
||||||
class FixedDistributor {
|
class FixedDistributor {
|
||||||
constructor(sizer, item, id, config) {
|
constructor(item) {
|
||||||
this.sizer = sizer;
|
|
||||||
this.item = item;
|
this.item = item;
|
||||||
this.id = id;
|
this.beforeOffset = item.offset();
|
||||||
this.beforeOffset = sizer.getItemOffset(this.item);
|
|
||||||
this.onResized = config && config.onResized;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resize(itemSize) {
|
resize(size) {
|
||||||
this.sizer.setItemSize(this.item, itemSize);
|
this.item.setSize(size);
|
||||||
if (this.onResized) {
|
|
||||||
this.onResized(itemSize, this.id, this.item);
|
|
||||||
}
|
|
||||||
return itemSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeFromContainerOffset(offset) {
|
resizeFromContainerOffset(offset) {
|
||||||
|
@ -50,8 +43,8 @@ class FixedDistributor {
|
||||||
|
|
||||||
|
|
||||||
class CollapseDistributor extends FixedDistributor {
|
class CollapseDistributor extends FixedDistributor {
|
||||||
constructor(sizer, item, id, config) {
|
constructor(item, sizer, _container, config) {
|
||||||
super(sizer, item, id, config);
|
super(item);
|
||||||
this.toggleSize = config && config.toggleSize;
|
this.toggleSize = config && config.toggleSize;
|
||||||
this.onCollapsed = config && config.onCollapsed;
|
this.onCollapsed = config && config.onCollapsed;
|
||||||
this.isCollapsed = false;
|
this.isCollapsed = false;
|
||||||
|
|
78
src/resizer/item.js
Normal file
78
src/resizer/item.js
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Sizer} from "./sizer";
|
import {Sizer} from "./sizer";
|
||||||
|
import ResizeItem from "./item";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
classNames:
|
classNames:
|
||||||
|
@ -28,7 +29,10 @@ classNames:
|
||||||
resizing: string
|
resizing: string
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
export class Resizer {
|
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) {
|
constructor(container, distributorCtor, distributorCfg, sizerCtor = Sizer) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.distributorCtor = distributorCtor;
|
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);
|
return el && el.classList.contains(this.classNames.handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,35 +127,29 @@ export class Resizer {
|
||||||
|
|
||||||
_createSizerAndDistributor(resizeHandle) {
|
_createSizerAndDistributor(resizeHandle) {
|
||||||
const vertical = resizeHandle.classList.contains(this.classNames.vertical);
|
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
|
// eslint-disable-next-line new-cap
|
||||||
const sizer = new this.sizerCtor(this.container, vertical, reverse);
|
const sizer = new this.sizerCtor(this.container, vertical, reverse);
|
||||||
|
const item = ResizeItem.fromResizeHandle(resizeHandle, this, sizer);
|
||||||
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");
|
|
||||||
// eslint-disable-next-line new-cap
|
// eslint-disable-next-line new-cap
|
||||||
const distributor = new this.distributorCtor(
|
const distributor = new this.distributorCtor(
|
||||||
sizer, item, id, this.distributorCfg,
|
item,
|
||||||
items, this.container);
|
sizer,
|
||||||
|
this.container,
|
||||||
|
this.distributorCfg
|
||||||
|
);
|
||||||
return {sizer, distributor};
|
return {sizer, distributor};
|
||||||
}
|
}
|
||||||
|
|
||||||
_getResizableItems() {
|
_getResizableItems(reverse) {
|
||||||
return Array.from(this.container.children).filter(el => {
|
return this._getResizeHandles().map((handle) => {
|
||||||
return !this._isResizeHandle(el) && (
|
return ResizeItem.fromResizeHandle(handle);
|
||||||
this._isResizeHandle(el.previousElementSibling) ||
|
|
||||||
this._isResizeHandle(el.nextElementSibling));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_getResizeHandles() {
|
_getResizeHandles() {
|
||||||
return Array.from(this.container.children).filter(el => {
|
return Array.from(this.container.children).filter(el => {
|
||||||
return this._isResizeHandle(el);
|
return this.isResizeHandle(el);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,42 +15,99 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Sizer} from "./sizer";
|
import {Sizer} from "./sizer";
|
||||||
import {FixedDistributor} from "./distributors";
|
|
||||||
|
|
||||||
class RoomSizer extends Sizer {
|
class RoomSizer extends Sizer {
|
||||||
setItemSize(item, size) {
|
setItemSize(item, size) {
|
||||||
const isString = typeof size === "string";
|
item.style.maxHeight = `${Math.round(size)}px`;
|
||||||
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`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RoomDistributor extends FixedDistributor {
|
/*
|
||||||
resize(itemSize) {
|
class RoomSubList extends ResizeItem {
|
||||||
const scrollItem = this.item.querySelector(".mx_RoomSubList_scroll");
|
collapsed() {
|
||||||
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;
|
scrollSizes() {
|
||||||
if (itemSize > (fixedHeight + scrollItem.scrollHeight)) {
|
return {offsetHeight, scrollHeight};
|
||||||
super.resize("resized-all");
|
}
|
||||||
|
|
||||||
|
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 {
|
} 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) {
|
resize(size) {
|
||||||
return this.resize(offset - this.sizer.getItemOffset(this.item));
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,31 +18,13 @@ limitations under the License.
|
||||||
implements DOM/CSS operations for resizing.
|
implements DOM/CSS operations for resizing.
|
||||||
The sizer determines what CSS mechanism is used for sizing items, like flexbox, ...
|
The sizer determines what CSS mechanism is used for sizing items, like flexbox, ...
|
||||||
*/
|
*/
|
||||||
class Sizer {
|
export class Sizer {
|
||||||
constructor(container, vertical, reverse) {
|
constructor(container, vertical, reverse) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.reverse = reverse;
|
this.reverse = reverse;
|
||||||
this.vertical = vertical;
|
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
|
@param {Element} item the dom element being resized
|
||||||
@return {number} how far the edge of the item is from the edge of the container
|
@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) {
|
setItemSize(item, size) {
|
||||||
item.style.flexGrow = `0`;
|
item.style.flexGrow = `0`;
|
||||||
item.style.flexBasis = `${Math.round(size)}px`;
|
item.style.flexBasis = `${Math.round(size)}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {Sizer, FlexSizer};
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue