From 329ded92c114459b525573cd18d7f8133100a5a4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 20 Oct 2020 11:03:03 +0100 Subject: [PATCH 1/2] Convert resizer to Typescript Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/LoggedInView.tsx | 14 +-- .../distributors/{collapse.js => collapse.ts} | 25 ++++-- .../distributors/{fixed.js => fixed.ts} | 20 +++-- src/resizer/{index.js => index.ts} | 6 +- src/resizer/{item.js => item.ts} | 64 ++++++------- src/resizer/{resizer.js => resizer.ts} | 89 +++++++++++-------- src/resizer/{sizer.js => sizer.ts} | 34 +++---- 7 files changed, 141 insertions(+), 111 deletions(-) rename src/resizer/distributors/{collapse.js => collapse.ts} (67%) rename src/resizer/distributors/{fixed.js => fixed.ts} (69%) rename src/resizer/{index.js => index.ts} (76%) rename src/resizer/{item.js => item.ts} (57%) rename src/resizer/{resizer.js => resizer.ts} (62%) rename src/resizer/{sizer.js => sizer.ts} (77%) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 15ea20618e..03277a84f9 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -52,6 +52,7 @@ import RoomListStore from "../../stores/room-list/RoomListStore"; import NonUrgentToastContainer from "./NonUrgentToastContainer"; import { ToggleRightPanelPayload } from "../../dispatcher/payloads/ToggleRightPanelPayload"; import { IThreepidInvite } from "../../stores/ThreepidInviteStore"; +import { ICollapseConfig } from "../../resizer/distributors/collapse"; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. @@ -205,13 +206,8 @@ class LoggedInView extends React.Component { }; _createResizer() { - const classNames = { - handle: "mx_ResizeHandle", - vertical: "mx_ResizeHandle_vertical", - reverse: "mx_ResizeHandle_reverse", - }; let size; - const collapseConfig = { + const collapseConfig: ICollapseConfig = { toggleSize: 260 - 50, onCollapsed: (collapsed) => { if (collapsed) { @@ -234,7 +230,11 @@ class LoggedInView extends React.Component { }, }; const resizer = new Resizer(this._resizeContainer.current, CollapseDistributor, collapseConfig); - resizer.setClassNames(classNames); + resizer.setClassNames({ + handle: "mx_ResizeHandle", + vertical: "mx_ResizeHandle_vertical", + reverse: "mx_ResizeHandle_reverse", + }); return resizer; } diff --git a/src/resizer/distributors/collapse.js b/src/resizer/distributors/collapse.ts similarity index 67% rename from src/resizer/distributors/collapse.js rename to src/resizer/distributors/collapse.ts index 784532a0eb..19d72e968b 100644 --- a/src/resizer/distributors/collapse.js +++ b/src/resizer/distributors/collapse.ts @@ -16,9 +16,16 @@ limitations under the License. import FixedDistributor from "./fixed"; import ResizeItem from "../item"; +import Resizer, {IConfig} from "../resizer"; +import Sizer from "../sizer"; -class CollapseItem extends ResizeItem { - notifyCollapsed(collapsed) { +export interface ICollapseConfig extends IConfig { + toggleSize: number; + onCollapsed?(collapsed: boolean, id: string, element: HTMLElement): void; +} + +class CollapseItem extends ResizeItem { + notifyCollapsed(collapsed: boolean) { const callback = this.resizer.config.onCollapsed; if (callback) { callback(collapsed, this.id, this.domNode); @@ -26,18 +33,20 @@ class CollapseItem extends ResizeItem { } } -export default class CollapseDistributor extends FixedDistributor { - static createItem(resizeHandle, resizer, sizer) { +export default class CollapseDistributor extends FixedDistributor { + static createItem(resizeHandle: HTMLDivElement, resizer: Resizer, sizer: Sizer) { return new CollapseItem(resizeHandle, resizer, sizer); } - constructor(item, config) { + private readonly toggleSize: number; + private isCollapsed = false; + + constructor(item: CollapseItem) { super(item); - this.toggleSize = config && config.toggleSize; - this.isCollapsed = false; + this.toggleSize = item.resizer?.config?.toggleSize; } - resize(newSize) { + public resize(newSize: number) { const isCollapsedSize = newSize < this.toggleSize; if (isCollapsedSize && !this.isCollapsed) { this.isCollapsed = true; diff --git a/src/resizer/distributors/fixed.js b/src/resizer/distributors/fixed.ts similarity index 69% rename from src/resizer/distributors/fixed.js rename to src/resizer/distributors/fixed.ts index e93c6fbcee..d901c4fcb6 100644 --- a/src/resizer/distributors/fixed.js +++ b/src/resizer/distributors/fixed.ts @@ -16,6 +16,7 @@ limitations under the License. import ResizeItem from "../item"; import Sizer from "../sizer"; +import Resizer, {IConfig} from "../resizer"; /** distributors translate a moving cursor into @@ -27,29 +28,30 @@ they have two methods: 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) { +export default class FixedDistributor = ResizeItem> { + static createItem(resizeHandle: HTMLDivElement, resizer: Resizer, sizer: Sizer): ResizeItem { return new ResizeItem(resizeHandle, resizer, sizer); } - static createSizer(containerElement, vertical, reverse) { + static createSizer(containerElement: HTMLElement, vertical: boolean, reverse: boolean): Sizer { return new Sizer(containerElement, vertical, reverse); } - constructor(item) { - this.item = item; + private readonly beforeOffset: number; + + constructor(public readonly item: I) { this.beforeOffset = item.offset(); } - resize(size) { + public resize(size: number) { this.item.setSize(size); } - resizeFromContainerOffset(offset) { + public resizeFromContainerOffset(offset: number) { this.resize(offset - this.beforeOffset); } - start() {} + public start() {} - finish() {} + public finish() {} } diff --git a/src/resizer/index.js b/src/resizer/index.ts similarity index 76% rename from src/resizer/index.js rename to src/resizer/index.ts index 1fd8f4da46..da015715d0 100644 --- a/src/resizer/index.js +++ b/src/resizer/index.ts @@ -15,6 +15,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -export FixedDistributor from "./distributors/fixed"; -export CollapseDistributor from "./distributors/collapse"; -export Resizer from "./resizer"; +export {default as FixedDistributor} from "./distributors/fixed"; +export {default as CollapseDistributor} from "./distributors/collapse"; +export {default as Resizer} from "./resizer"; diff --git a/src/resizer/item.js b/src/resizer/item.ts similarity index 57% rename from src/resizer/item.js rename to src/resizer/item.ts index 2e06ad217c..ec60b593e5 100644 --- a/src/resizer/item.js +++ b/src/resizer/item.ts @@ -14,25 +14,30 @@ 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; +import Resizer, {IConfig} from "./resizer"; +import Sizer from "./sizer"; - this.domNode = domNode; - this.id = id; - this.reverse = reverse; - this.resizer = resizer; - this.sizer = sizer; +export default class ResizeItem { + public readonly domNode: HTMLElement; + protected readonly id: string; + protected reverse: boolean; + + constructor( + handle: HTMLElement, + public readonly resizer: Resizer, + public readonly sizer: Sizer, + ) { + this.reverse = resizer.isReverseResizeHandle(handle); + this.domNode = (this.reverse ? handle.nextElementSibling : handle.previousElementSibling); + this.id = handle.getAttribute("data-id"); } - _copyWith(handle, resizer, sizer) { - const Ctor = this.constructor; + private copyWith(handle: HTMLElement, resizer: Resizer, sizer: Sizer) { + const Ctor = this.constructor as typeof ResizeItem; return new Ctor(handle, resizer, sizer); } - _advance(forwards) { + private advance(forwards: boolean) { // opposite direction from fromResizeHandle to get back to handle let handle = this.reverse ? this.domNode.previousElementSibling : @@ -45,32 +50,32 @@ export default class ResizeItem { } else { handle = handle.previousElementSibling; } - } while (handle && !this.resizer.isResizeHandle(handle)); + } while (handle && !this.resizer.isResizeHandle(handle)); if (handle) { - const nextHandle = this._copyWith(handle, this.resizer, this.sizer); + const nextHandle = this.copyWith(handle, this.resizer, this.sizer); nextHandle.reverse = this.reverse; return nextHandle; } } - next() { - return this._advance(true); + public next() { + return this.advance(true); } - previous() { - return this._advance(false); + public previous() { + return this.advance(false); } - size() { + public size() { return this.sizer.getItemSize(this.domNode); } - offset() { + public offset() { return this.sizer.getItemOffset(this.domNode); } - setSize(size) { + public setSize(size: number) { this.sizer.setItemSize(this.domNode, size); const callback = this.resizer.config.onResized; if (callback) { @@ -78,7 +83,7 @@ export default class ResizeItem { } } - clearSize() { + public clearSize() { this.sizer.clearItemSize(this.domNode); const callback = this.resizer.config.onResized; if (callback) { @@ -86,22 +91,21 @@ export default class ResizeItem { } } - - first() { + public first() { const firstHandle = Array.from(this.domNode.parentElement.children).find(el => { - return this.resizer.isResizeHandle(el); + return this.resizer.isResizeHandle(el); }); if (firstHandle) { - return this._copyWith(firstHandle, this.resizer, this.sizer); + return this.copyWith(firstHandle, this.resizer, this.sizer); } } - last() { + public last() { const lastHandle = Array.from(this.domNode.parentElement.children).reverse().find(el => { - return this.resizer.isResizeHandle(el); + return this.resizer.isResizeHandle(el); }); if (lastHandle) { - return this._copyWith(lastHandle, this.resizer, this.sizer); + return this.copyWith(lastHandle, this.resizer, this.sizer); } } } diff --git a/src/resizer/resizer.js b/src/resizer/resizer.ts similarity index 62% rename from src/resizer/resizer.js rename to src/resizer/resizer.ts index 1e75bf3bdf..f296b733e0 100644 --- a/src/resizer/resizer.js +++ b/src/resizer/resizer.ts @@ -1,6 +1,6 @@ /* Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,86 +15,101 @@ See the License for the specific language governing permissions and limitations under the License. */ -/* -classNames: +import FixedDistributor from "./distributors/fixed"; +import ResizeItem from "./item"; +import Sizer from "./sizer"; + +interface IClassNames { // class on resize-handle - handle: string + handle?: string; // class on resize-handle - reverse: string + reverse?: string; // class on resize-handle - vertical: string + vertical?: string; // class on container - resizing: string -*/ + resizing?: string; +} +export interface IConfig { + onResizeStart?(): void; + onResizeStop?(): void; + onResized?(size: number, id: string, element: HTMLElement): void; +} + +export default class Resizer { + private classNames: IClassNames; -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) { + constructor( + public container: HTMLElement, + private readonly distributorCtor: { + new(item: ResizeItem): FixedDistributor; + createItem(resizeHandle: HTMLDivElement, resizer: Resizer, sizer: Sizer): ResizeItem; + createSizer(containerElement: HTMLElement, vertical: boolean, reverse: boolean): Sizer; + }, + public readonly config?: C, + ) { if (!container) { throw new Error("Resizer requires a non-null `container` arg"); } - this.container = container; - this.distributorCtor = distributorCtor; - this.config = config; + this.classNames = { handle: "resizer-handle", reverse: "resizer-reverse", vertical: "resizer-vertical", resizing: "resizer-resizing", }; - this._onMouseDown = this._onMouseDown.bind(this); } - setClassNames(classNames) { + public setClassNames(classNames: IClassNames) { this.classNames = classNames; } - attach() { - this.container.addEventListener("mousedown", this._onMouseDown, false); + public attach() { + this.container.addEventListener("mousedown", this.onMouseDown, false); } - detach() { - this.container.removeEventListener("mousedown", this._onMouseDown, false); + public detach() { + this.container.removeEventListener("mousedown", this.onMouseDown, false); } /** Gives the distributor for a specific resize handle, as if you would have started to drag that handle. Can be used to manipulate the size of an item programmatically. @param {number} handleIndex the index of the resize handle in the container - @return {Distributor} a new distributor for the given handle + @return {FixedDistributor} a new distributor for the given handle */ - forHandleAt(handleIndex) { - const handles = this._getResizeHandles(); + public forHandleAt(handleIndex: number): FixedDistributor { + const handles = this.getResizeHandles(); const handle = handles[handleIndex]; if (handle) { - const {distributor} = this._createSizerAndDistributor(handle); + const {distributor} = this.createSizerAndDistributor(handle); return distributor; } } - forHandleWithId(id) { - const handles = this._getResizeHandles(); + public forHandleWithId(id: string): FixedDistributor { + const handles = this.getResizeHandles(); const handle = handles.find((h) => h.getAttribute("data-id") === id); if (handle) { - const {distributor} = this._createSizerAndDistributor(handle); + const {distributor} = this.createSizerAndDistributor(handle); return distributor; } } - isReverseResizeHandle(el) { + public isReverseResizeHandle(el: HTMLElement) { return el && el.classList.contains(this.classNames.reverse); } - isResizeHandle(el) { + public isResizeHandle(el: HTMLElement) { return el && el.classList.contains(this.classNames.handle); } - _onMouseDown(event) { + private onMouseDown = (event: MouseEvent) => { // use closest in case the resize handle contains // child dom nodes that can be the target - const resizeHandle = event.target && event.target.closest(`.${this.classNames.handle}`); + const resizeHandle = event.target && (event.target).closest(`.${this.classNames.handle}`); if (!resizeHandle || resizeHandle.parentElement !== this.container) { return; } @@ -109,7 +124,7 @@ export default class Resizer { this.config.onResizeStart(); } - const {sizer, distributor} = this._createSizerAndDistributor(resizeHandle); + const {sizer, distributor} = this.createSizerAndDistributor(resizeHandle); distributor.start(); const onMouseMove = (event) => { @@ -133,21 +148,21 @@ export default class Resizer { body.addEventListener("mouseup", finishResize, false); document.addEventListener("mouseleave", finishResize, false); body.addEventListener("mousemove", onMouseMove, false); - } + }; - _createSizerAndDistributor(resizeHandle) { + private createSizerAndDistributor(resizeHandle: HTMLDivElement) { const vertical = resizeHandle.classList.contains(this.classNames.vertical); const reverse = this.isReverseResizeHandle(resizeHandle); const Distributor = this.distributorCtor; const sizer = Distributor.createSizer(this.container, vertical, reverse); const item = Distributor.createItem(resizeHandle, this, sizer); - const distributor = new Distributor(item, this.config); + const distributor = new Distributor(item); return {sizer, distributor}; } - _getResizeHandles() { + private getResizeHandles(): HTMLDivElement[] { return Array.from(this.container.children).filter(el => { - return this.isResizeHandle(el); - }); + return this.isResizeHandle(el); + }) as HTMLDivElement[]; } } diff --git a/src/resizer/sizer.js b/src/resizer/sizer.ts similarity index 77% rename from src/resizer/sizer.js rename to src/resizer/sizer.ts index 4ce9232457..54e21c1483 100644 --- a/src/resizer/sizer.js +++ b/src/resizer/sizer.ts @@ -19,18 +19,18 @@ implements DOM/CSS operations for resizing. The sizer determines what CSS mechanism is used for sizing items, like flexbox, ... */ export default class Sizer { - constructor(container, vertical, reverse) { - this.container = container; - this.reverse = reverse; - this.vertical = vertical; - } + constructor( + protected readonly container: HTMLElement, + protected readonly vertical: boolean, + protected readonly reverse: boolean, + ) {} /** @param {Element} item the dom element being resized @return {number} how far the edge of the item is from the edge of the container */ - getItemOffset(item) { - const offset = (this.vertical ? item.offsetTop : item.offsetLeft) - this._getOffset(); + public getItemOffset(item: HTMLElement): number { + const offset = (this.vertical ? item.offsetTop : item.offsetLeft) - this.getOffset(); if (this.reverse) { return this.getTotalSize() - (offset + this.getItemSize(item)); } else { @@ -42,33 +42,33 @@ export default class Sizer { @param {Element} item the dom element being resized @return {number} the width/height of an item in the container */ - getItemSize(item) { + public getItemSize(item: HTMLElement): number { return this.vertical ? item.offsetHeight : item.offsetWidth; } /** @return {number} the width/height of the container */ - getTotalSize() { + public getTotalSize(): number { return this.vertical ? this.container.offsetHeight : this.container.offsetWidth; } /** @return {number} container offset to offsetParent */ - _getOffset() { + private getOffset(): number { return this.vertical ? this.container.offsetTop : this.container.offsetLeft; } /** @return {number} container offset to document */ - _getPageOffset() { + private getPageOffset(): number { let element = this.container; let offset = 0; while (element) { const pos = this.vertical ? element.offsetTop : element.offsetLeft; offset = offset + pos; - element = element.offsetParent; + element = element.offsetParent; } return offset; } - setItemSize(item, size) { + public setItemSize(item: HTMLElement, size: number) { if (this.vertical) { item.style.height = `${Math.round(size)}px`; } else { @@ -76,7 +76,7 @@ export default class Sizer { } } - clearItemSize(item) { + public clearItemSize(item: HTMLElement) { if (this.vertical) { item.style.height = null; } else { @@ -89,12 +89,12 @@ export default class Sizer { @return {number} the distance between the cursor and the edge of the container, along the applicable axis (vertical or horizontal) */ - offsetFromEvent(event) { + public offsetFromEvent(event: MouseEvent) { const pos = this.vertical ? event.pageY : event.pageX; if (this.reverse) { - return (this._getPageOffset() + this.getTotalSize()) - pos; + return (this.getPageOffset() + this.getTotalSize()) - pos; } else { - return pos - this._getPageOffset(); + return pos - this.getPageOffset(); } } } From 01498653d841896a59d65d03655d880831054beb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 20 Oct 2020 23:42:12 +0100 Subject: [PATCH 2/2] update copyrights --- src/resizer/distributors/collapse.ts | 2 +- src/resizer/distributors/fixed.ts | 2 +- src/resizer/index.ts | 1 - src/resizer/item.ts | 2 +- src/resizer/resizer.ts | 3 +-- src/resizer/sizer.ts | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/resizer/distributors/collapse.ts b/src/resizer/distributors/collapse.ts index 19d72e968b..ddf3bd687e 100644 --- a/src/resizer/distributors/collapse.ts +++ b/src/resizer/distributors/collapse.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019 - 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/resizer/distributors/fixed.ts b/src/resizer/distributors/fixed.ts index d901c4fcb6..59ed845467 100644 --- a/src/resizer/distributors/fixed.ts +++ b/src/resizer/distributors/fixed.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019 - 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/resizer/index.ts b/src/resizer/index.ts index da015715d0..e0e72ff159 100644 --- a/src/resizer/index.ts +++ b/src/resizer/index.ts @@ -1,5 +1,4 @@ /* -Copyright 2018 New Vector Ltd Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/resizer/item.ts b/src/resizer/item.ts index ec60b593e5..1b42c19a4a 100644 --- a/src/resizer/item.ts +++ b/src/resizer/item.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019 - 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/resizer/resizer.ts b/src/resizer/resizer.ts index f296b733e0..b8a6777da0 100644 --- a/src/resizer/resizer.ts +++ b/src/resizer/resizer.ts @@ -1,6 +1,5 @@ /* -Copyright 2018 New Vector Ltd -Copyright 2019, 2020 The Matrix.org Foundation C.I.C. +Copyright 2018 - 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/resizer/sizer.ts b/src/resizer/sizer.ts index 54e21c1483..4630ffd6bf 100644 --- a/src/resizer/sizer.ts +++ b/src/resizer/sizer.ts @@ -1,5 +1,5 @@ /* -Copyright 2018 New Vector Ltd +Copyright 2018 - 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.