Merge pull request #2440 from matrix-org/bwindels/smarterresizer
Improve room sublist resizing
This commit is contained in:
commit
0229453482
11 changed files with 407 additions and 229 deletions
|
@ -33,13 +33,25 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_RoomSubList {
|
.mx_RoomSubList {
|
||||||
min-height: 31px;
|
min-height: 31px;
|
||||||
flex: 0 100000000 auto;
|
flex: 0 10000 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomSubList.resized-sized {
|
||||||
|
/*
|
||||||
|
flex-basis to 0 so sublists
|
||||||
|
are not shrinking/growing relative
|
||||||
|
to their content (as would be the case with auto),
|
||||||
|
as this intervenes with sizing an item exactly
|
||||||
|
when not available space is available
|
||||||
|
in the flex container
|
||||||
|
*/
|
||||||
|
flex: 1 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomSubList_nonEmpty {
|
.mx_RoomSubList_nonEmpty {
|
||||||
min-height: 70px;
|
min-height: 74px;
|
||||||
|
|
||||||
.mx_AutoHideScrollbar_offset {
|
.mx_AutoHideScrollbar_offset {
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
|
@ -50,17 +62,6 @@ limitations under the License.
|
||||||
flex: none !important;
|
flex: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomSubList.resized-all {
|
|
||||||
flex: 0 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomSubList.resized-sized {
|
|
||||||
/* resizer set max-height on resized-sized,
|
|
||||||
so that limits the height and hence
|
|
||||||
needs a very small flex-shrink */
|
|
||||||
flex: 0 10000 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomSubList_labelContainer {
|
.mx_RoomSubList_labelContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
@ -36,7 +36,7 @@ import GroupStore from '../../../stores/GroupStore';
|
||||||
import RoomSubList from '../../structures/RoomSubList';
|
import RoomSubList from '../../structures/RoomSubList';
|
||||||
import ResizeHandle from '../elements/ResizeHandle';
|
import ResizeHandle from '../elements/ResizeHandle';
|
||||||
|
|
||||||
import {Resizer, RoomDistributor, RoomSizer} from '../../../resizer'
|
import {Resizer, RoomSubListDistributor} from '../../../resizer'
|
||||||
const HIDE_CONFERENCE_CHANS = true;
|
const HIDE_CONFERENCE_CHANS = true;
|
||||||
const STANDARD_TAGS_REGEX = /^(m\.(favourite|lowpriority|server_notice)|im\.vector\.fake\.(invite|recent|direct|archived))$/;
|
const STANDARD_TAGS_REGEX = /^(m\.(favourite|lowpriority|server_notice)|im\.vector\.fake\.(invite|recent|direct|archived))$/;
|
||||||
|
|
||||||
|
@ -153,7 +153,11 @@ module.exports = React.createClass({
|
||||||
if (typeof newSize === "string") {
|
if (typeof newSize === "string") {
|
||||||
newSize = Number.MAX_SAFE_INTEGER;
|
newSize = Number.MAX_SAFE_INTEGER;
|
||||||
}
|
}
|
||||||
this.subListSizes[id] = newSize;
|
if (newSize === null) {
|
||||||
|
delete this.subListSizes[id];
|
||||||
|
} else {
|
||||||
|
this.subListSizes[id] = newSize;
|
||||||
|
}
|
||||||
window.localStorage.setItem("mx_roomlist_sizes", JSON.stringify(this.subListSizes));
|
window.localStorage.setItem("mx_roomlist_sizes", JSON.stringify(this.subListSizes));
|
||||||
// update overflow indicators
|
// update overflow indicators
|
||||||
this._checkSubListsOverflow();
|
this._checkSubListsOverflow();
|
||||||
|
@ -164,7 +168,7 @@ module.exports = React.createClass({
|
||||||
const cfg = {
|
const cfg = {
|
||||||
onResized: this._onSubListResize,
|
onResized: this._onSubListResize,
|
||||||
};
|
};
|
||||||
this.resizer = new Resizer(this.resizeContainer, RoomDistributor, cfg, RoomSizer);
|
this.resizer = new Resizer(this.resizeContainer, RoomSubListDistributor, cfg);
|
||||||
this.resizer.setClassNames({
|
this.resizer.setClassNames({
|
||||||
handle: "mx_ResizeHandle",
|
handle: "mx_ResizeHandle",
|
||||||
vertical: "mx_ResizeHandle_vertical",
|
vertical: "mx_ResizeHandle_vertical",
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
distributors translate a moving cursor into
|
|
||||||
CSS/DOM changes by calling the sizer
|
|
||||||
|
|
||||||
they have two methods:
|
|
||||||
`resize` receives then new item size
|
|
||||||
`resizeFromContainerOffset` receives resize handle location
|
|
||||||
within the container bounding box. For internal use.
|
|
||||||
This method usually ends up calling `resize` once the start offset is subtracted.
|
|
||||||
the offset from the container edge of where
|
|
||||||
the mouse cursor is.
|
|
||||||
*/
|
|
||||||
class FixedDistributor {
|
|
||||||
constructor(sizer, item, id, config) {
|
|
||||||
this.sizer = sizer;
|
|
||||||
this.item = item;
|
|
||||||
this.id = id;
|
|
||||||
this.beforeOffset = sizer.getItemOffset(this.item);
|
|
||||||
this.onResized = config && config.onResized;
|
|
||||||
}
|
|
||||||
|
|
||||||
resize(itemSize) {
|
|
||||||
this.sizer.setItemSize(this.item, itemSize);
|
|
||||||
if (this.onResized) {
|
|
||||||
this.onResized(itemSize, this.id, this.item);
|
|
||||||
}
|
|
||||||
return itemSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
resizeFromContainerOffset(offset) {
|
|
||||||
this.resize(offset - this.beforeOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class CollapseDistributor extends FixedDistributor {
|
|
||||||
constructor(sizer, item, id, config) {
|
|
||||||
super(sizer, item, id, config);
|
|
||||||
this.toggleSize = config && config.toggleSize;
|
|
||||||
this.onCollapsed = config && config.onCollapsed;
|
|
||||||
this.isCollapsed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
resize(newSize) {
|
|
||||||
const isCollapsedSize = newSize < this.toggleSize;
|
|
||||||
if (isCollapsedSize && !this.isCollapsed) {
|
|
||||||
this.isCollapsed = true;
|
|
||||||
if (this.onCollapsed) {
|
|
||||||
this.onCollapsed(true, this.item);
|
|
||||||
}
|
|
||||||
} else if (!isCollapsedSize && this.isCollapsed) {
|
|
||||||
if (this.onCollapsed) {
|
|
||||||
this.onCollapsed(false, this.item);
|
|
||||||
}
|
|
||||||
this.isCollapsed = false;
|
|
||||||
}
|
|
||||||
if (!isCollapsedSize) {
|
|
||||||
super.resize(newSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
FixedDistributor,
|
|
||||||
CollapseDistributor,
|
|
||||||
};
|
|
53
src/resizer/distributors/collapse.js
Normal file
53
src/resizer/distributors/collapse.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import FixedDistributor from "./fixed";
|
||||||
|
import ResizeItem from "../item";
|
||||||
|
|
||||||
|
class CollapseItem extends ResizeItem {
|
||||||
|
notifyCollapsed(collapsed) {
|
||||||
|
const callback = this.resizer.config.onCollapsed;
|
||||||
|
if (callback) {
|
||||||
|
callback(collapsed, this.id, this.domNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CollapseDistributor extends FixedDistributor {
|
||||||
|
static createItem(resizeHandle, resizer, sizer) {
|
||||||
|
return new CollapseItem(resizeHandle, resizer, sizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(item, config) {
|
||||||
|
super(item);
|
||||||
|
this.toggleSize = config && config.toggleSize;
|
||||||
|
this.isCollapsed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(newSize) {
|
||||||
|
const isCollapsedSize = newSize < this.toggleSize;
|
||||||
|
if (isCollapsedSize && !this.isCollapsed) {
|
||||||
|
this.isCollapsed = true;
|
||||||
|
this.item.notifyCollapsed(true);
|
||||||
|
} else if (!isCollapsedSize && this.isCollapsed) {
|
||||||
|
this.item.notifyCollapsed(false);
|
||||||
|
this.isCollapsed = false;
|
||||||
|
}
|
||||||
|
if (!isCollapsedSize) {
|
||||||
|
super.resize(newSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
src/resizer/distributors/fixed.js
Normal file
55
src/resizer/distributors/fixed.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ResizeItem from "../item";
|
||||||
|
import Sizer from "../sizer";
|
||||||
|
|
||||||
|
/**
|
||||||
|
distributors translate a moving cursor into
|
||||||
|
CSS/DOM changes by calling the sizer
|
||||||
|
|
||||||
|
they have two methods:
|
||||||
|
`resize` receives then new item size
|
||||||
|
`resizeFromContainerOffset` receives resize handle location
|
||||||
|
within the container bounding box. For internal use.
|
||||||
|
This method usually ends up calling `resize` once the start offset is subtracted.
|
||||||
|
*/
|
||||||
|
export default class FixedDistributor {
|
||||||
|
static createItem(resizeHandle, resizer, sizer) {
|
||||||
|
return new ResizeItem(resizeHandle, resizer, sizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createSizer(containerElement, vertical, reverse) {
|
||||||
|
return new Sizer(containerElement, vertical, reverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(item) {
|
||||||
|
this.item = item;
|
||||||
|
this.beforeOffset = item.offset();
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(size) {
|
||||||
|
this.item.setSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeFromContainerOffset(offset) {
|
||||||
|
this.resize(offset - this.beforeOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {}
|
||||||
|
|
||||||
|
finish() {}
|
||||||
|
}
|
132
src/resizer/distributors/roomsublist.js
Normal file
132
src/resizer/distributors/roomsublist.js
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Sizer from "../sizer";
|
||||||
|
import ResizeItem from "../item";
|
||||||
|
|
||||||
|
class RoomSizer extends Sizer {
|
||||||
|
setItemSize(item, size) {
|
||||||
|
item.style.maxHeight = `${Math.round(size)}px`;
|
||||||
|
item.classList.add("resized-sized");
|
||||||
|
}
|
||||||
|
|
||||||
|
clearItemSize(item) {
|
||||||
|
item.style.maxHeight = null;
|
||||||
|
item.classList.remove("resized-sized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoomSubListItem extends ResizeItem {
|
||||||
|
isCollapsed() {
|
||||||
|
return this.domNode.classList.contains("mx_RoomSubList_hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
maxSize() {
|
||||||
|
const header = this.domNode.querySelector(".mx_RoomSubList_labelContainer");
|
||||||
|
const scrollItem = this.domNode.querySelector(".mx_RoomSubList_scroll");
|
||||||
|
const headerHeight = this.sizer.getItemSize(header);
|
||||||
|
return headerHeight + (scrollItem ? scrollItem.scrollHeight : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
minSize() {
|
||||||
|
const isNotEmpty = this.domNode.classList.contains("mx_RoomSubList_nonEmpty");
|
||||||
|
return isNotEmpty ? 74 : 31; //size of header + 1? room tile (see room sub list css)
|
||||||
|
}
|
||||||
|
|
||||||
|
isSized() {
|
||||||
|
return this.domNode.classList.contains("resized-sized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class RoomSubListDistributor {
|
||||||
|
static createItem(resizeHandle, resizer, sizer) {
|
||||||
|
return new RoomSubListItem(resizeHandle, resizer, sizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createSizer(containerElement, vertical, reverse) {
|
||||||
|
return new RoomSizer(containerElement, vertical, reverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(item) {
|
||||||
|
this.item = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleSize() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(size) {
|
||||||
|
//console.log("*** starting resize session with size", size);
|
||||||
|
let item = this.item;
|
||||||
|
while (item) {
|
||||||
|
const minSize = item.minSize();
|
||||||
|
if (item.isCollapsed()) {
|
||||||
|
item = item.previous();
|
||||||
|
} else if (size <= minSize) {
|
||||||
|
//console.log(" - resizing", item.id, "to min size", minSize);
|
||||||
|
item.setSize(minSize);
|
||||||
|
const remainder = minSize - size;
|
||||||
|
item = item.previous();
|
||||||
|
if (item) {
|
||||||
|
size = item.size() - remainder - this._handleSize();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const maxSize = item.maxSize();
|
||||||
|
if (size > maxSize) {
|
||||||
|
// console.log(" - resizing", item.id, "to maxSize", maxSize);
|
||||||
|
item.setSize(maxSize);
|
||||||
|
const remainder = size - maxSize;
|
||||||
|
item = item.previous();
|
||||||
|
if (item) {
|
||||||
|
size = item.size() + remainder; // todo: handle size here?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//console.log(" - resizing", item.id, "to size", size);
|
||||||
|
item.setSize(size);
|
||||||
|
item = null;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log("*** ending resize session");
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeFromContainerOffset(containerOffset) {
|
||||||
|
this.resize(containerOffset - this.item.offset());
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
// set all max-height props to the actual height.
|
||||||
|
let item = this.item.first();
|
||||||
|
const sizes = [];
|
||||||
|
while (item) {
|
||||||
|
if (!item.isCollapsed()) {
|
||||||
|
sizes.push(item.size());
|
||||||
|
} else {
|
||||||
|
sizes.push(100);
|
||||||
|
}
|
||||||
|
item = item.next();
|
||||||
|
}
|
||||||
|
item = this.item.first();
|
||||||
|
sizes.forEach((size) => {
|
||||||
|
item.setSize(size);
|
||||||
|
item = item.next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,17 +14,14 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Sizer, FlexSizer} from "./sizer";
|
import FixedDistributor from "./distributors/fixed";
|
||||||
import {FixedDistributor, CollapseDistributor} from "./distributors";
|
import CollapseDistributor from "./distributors/collapse";
|
||||||
import {Resizer} from "./resizer";
|
import RoomSubListDistributor from "./distributors/roomsublist";
|
||||||
import {RoomSizer, RoomDistributor} from "./room";
|
import Resizer from "./resizer";
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Resizer,
|
Resizer,
|
||||||
Sizer,
|
|
||||||
FlexSizer,
|
|
||||||
FixedDistributor,
|
FixedDistributor,
|
||||||
CollapseDistributor,
|
CollapseDistributor,
|
||||||
RoomSizer,
|
RoomSubListDistributor,
|
||||||
RoomDistributor,
|
|
||||||
};
|
};
|
||||||
|
|
107
src/resizer/item.js
Normal file
107
src/resizer/item.js
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019 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(handle, resizer, sizer) {
|
||||||
|
const id = handle.getAttribute("data-id");
|
||||||
|
const reverse = resizer.isReverseResizeHandle(handle);
|
||||||
|
const domNode = reverse ? handle.nextElementSibling : handle.previousElementSibling;
|
||||||
|
|
||||||
|
this.domNode = domNode;
|
||||||
|
this.id = id;
|
||||||
|
this.reverse = reverse;
|
||||||
|
this.resizer = resizer;
|
||||||
|
this.sizer = sizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
_copyWith(handle, resizer, sizer) {
|
||||||
|
const Ctor = this.constructor;
|
||||||
|
return new Ctor(handle, 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 = this._copyWith(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);
|
||||||
|
const callback = this.resizer.config.onResized;
|
||||||
|
if (callback) {
|
||||||
|
callback(size, this.id, this.domNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSize() {
|
||||||
|
this.sizer.clearItemSize(this.domNode);
|
||||||
|
const callback = this.resizer.config.onResized;
|
||||||
|
if (callback) {
|
||||||
|
callback(null, this.id, this.domNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
first() {
|
||||||
|
const firstHandle = Array.from(this.domNode.parentElement.children).find(el => {
|
||||||
|
return this.resizer.isResizeHandle(el);
|
||||||
|
});
|
||||||
|
if (firstHandle) {
|
||||||
|
return this._copyWith(firstHandle, this.resizer, this.sizer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last() {
|
||||||
|
const lastHandle = Array.from(this.domNode.parentElement.children).reverse().find(el => {
|
||||||
|
return this.resizer.isResizeHandle(el);
|
||||||
|
});
|
||||||
|
if (lastHandle) {
|
||||||
|
return this._copyWith(lastHandle, this.resizer, this.sizer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Sizer} from "./sizer";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
classNames:
|
classNames:
|
||||||
// class on resize-handle
|
// class on resize-handle
|
||||||
|
@ -28,12 +26,14 @@ classNames:
|
||||||
resizing: string
|
resizing: string
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class Resizer {
|
|
||||||
constructor(container, distributorCtor, distributorCfg, sizerCtor = Sizer) {
|
export default 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, config) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
this.distributorCtor = distributorCtor;
|
this.distributorCtor = distributorCtor;
|
||||||
this.distributorCfg = distributorCfg;
|
this.config = config;
|
||||||
this.sizerCtor = sizerCtor;
|
|
||||||
this.classNames = {
|
this.classNames = {
|
||||||
handle: "resizer-handle",
|
handle: "resizer-handle",
|
||||||
reverse: "resizer-reverse",
|
reverse: "resizer-reverse",
|
||||||
|
@ -79,7 +79,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,6 +103,7 @@ export class Resizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
const {sizer, distributor} = this._createSizerAndDistributor(resizeHandle);
|
const {sizer, distributor} = this._createSizerAndDistributor(resizeHandle);
|
||||||
|
distributor.start();
|
||||||
|
|
||||||
const onMouseMove = (event) => {
|
const onMouseMove = (event) => {
|
||||||
const offset = sizer.offsetFromEvent(event);
|
const offset = sizer.offsetFromEvent(event);
|
||||||
|
@ -106,48 +111,33 @@ export class Resizer {
|
||||||
};
|
};
|
||||||
|
|
||||||
const body = document.body;
|
const body = document.body;
|
||||||
const onMouseUp = (event) => {
|
const finishResize = () => {
|
||||||
if (this.classNames.resizing) {
|
if (this.classNames.resizing) {
|
||||||
this.container.classList.remove(this.classNames.resizing);
|
this.container.classList.remove(this.classNames.resizing);
|
||||||
}
|
}
|
||||||
body.removeEventListener("mouseup", onMouseUp, false);
|
distributor.finish();
|
||||||
|
body.removeEventListener("mouseup", finishResize, false);
|
||||||
|
document.removeEventListener("mouseleave", finishResize, false);
|
||||||
body.removeEventListener("mousemove", onMouseMove, false);
|
body.removeEventListener("mousemove", onMouseMove, false);
|
||||||
};
|
};
|
||||||
body.addEventListener("mouseup", onMouseUp, false);
|
body.addEventListener("mouseup", finishResize, false);
|
||||||
|
document.addEventListener("mouseleave", finishResize, false);
|
||||||
body.addEventListener("mousemove", onMouseMove, false);
|
body.addEventListener("mousemove", onMouseMove, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_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);
|
||||||
|
const Distributor = this.distributorCtor;
|
||||||
// eslint-disable-next-line new-cap
|
const sizer = Distributor.createSizer(this.container, vertical, reverse);
|
||||||
const sizer = new this.sizerCtor(this.container, vertical, reverse);
|
const item = Distributor.createItem(resizeHandle, this, sizer);
|
||||||
|
const distributor = new Distributor(item, this.config);
|
||||||
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
|
|
||||||
const distributor = new this.distributorCtor(
|
|
||||||
sizer, item, id, this.distributorCfg,
|
|
||||||
items, this.container);
|
|
||||||
return {sizer, distributor};
|
return {sizer, distributor};
|
||||||
}
|
}
|
||||||
|
|
||||||
_getResizableItems() {
|
|
||||||
return Array.from(this.container.children).filter(el => {
|
|
||||||
return !this._isResizeHandle(el) && (
|
|
||||||
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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
} else {
|
|
||||||
super.resize(itemSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resizeFromContainerOffset(offset) {
|
|
||||||
return this.resize(offset - this.sizer.getItemOffset(this.item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
RoomSizer,
|
|
||||||
RoomDistributor,
|
|
||||||
};
|
|
|
@ -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 default 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
|
||||||
|
@ -82,6 +64,14 @@ class Sizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearItemSize(item) {
|
||||||
|
if (this.vertical) {
|
||||||
|
item.style.height = null;
|
||||||
|
} else {
|
||||||
|
item.style.width = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@param {MouseEvent} event the mouse event
|
@param {MouseEvent} event the mouse event
|
||||||
@return {number} the distance between the cursor and the edge of the container,
|
@return {number} the distance between the cursor and the edge of the container,
|
||||||
|
@ -96,12 +86,3 @@ class Sizer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FlexSizer extends Sizer {
|
|
||||||
setItemSize(item, size) {
|
|
||||||
item.style.flexGrow = `0`;
|
|
||||||
item.style.flexBasis = `${Math.round(size)}px`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {Sizer, FlexSizer};
|
|
||||||
|
|
Loading…
Reference in a new issue