Add functionality to UIStore (#32963)
- Make it possible to query if the window is currently being resized - Emit WidthIncreased/WidthDecreased events
This commit is contained in:
@@ -10,6 +10,8 @@ import EventEmitter from "events";
|
|||||||
|
|
||||||
export enum UI_EVENTS {
|
export enum UI_EVENTS {
|
||||||
Resize = "resize",
|
Resize = "resize",
|
||||||
|
WidthIncreased = "width-increased",
|
||||||
|
WidthDecreased = "width-decreased",
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class UIStore extends EventEmitter {
|
export default class UIStore extends EventEmitter {
|
||||||
@@ -19,9 +21,14 @@ export default class UIStore extends EventEmitter {
|
|||||||
|
|
||||||
private uiElementDimensions = new Map<string, DOMRectReadOnly>();
|
private uiElementDimensions = new Map<string, DOMRectReadOnly>();
|
||||||
private trackedUiElements = new Map<Element, string>();
|
private trackedUiElements = new Map<Element, string>();
|
||||||
|
private timeoutId: number = 0;
|
||||||
|
|
||||||
public windowWidth: number;
|
public windowWidth: number;
|
||||||
public windowHeight: number;
|
public windowHeight: number;
|
||||||
|
/**
|
||||||
|
* Whether the window is currently being resized.
|
||||||
|
*/
|
||||||
|
public isWindowBeingResized: boolean = false;
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super();
|
super();
|
||||||
@@ -38,6 +45,7 @@ export default class UIStore extends EventEmitter {
|
|||||||
public static get instance(): UIStore {
|
public static get instance(): UIStore {
|
||||||
if (!UIStore._instance) {
|
if (!UIStore._instance) {
|
||||||
UIStore._instance = new UIStore();
|
UIStore._instance = new UIStore();
|
||||||
|
window.mxUIStore = UIStore._instance;
|
||||||
}
|
}
|
||||||
return UIStore._instance;
|
return UIStore._instance;
|
||||||
}
|
}
|
||||||
@@ -81,7 +89,12 @@ export default class UIStore extends EventEmitter {
|
|||||||
const windowEntry = entries.find((entry) => entry.target === document.body);
|
const windowEntry = entries.find((entry) => entry.target === document.body);
|
||||||
|
|
||||||
if (windowEntry) {
|
if (windowEntry) {
|
||||||
this.windowWidth = windowEntry.contentRect.width;
|
this.setWindowAsBeingResized();
|
||||||
|
|
||||||
|
const currentWidth = windowEntry.contentRect.width;
|
||||||
|
this.emitWidthChangeEvents(currentWidth);
|
||||||
|
|
||||||
|
this.windowWidth = currentWidth;
|
||||||
this.windowHeight = windowEntry.contentRect.height;
|
this.windowHeight = windowEntry.contentRect.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +108,29 @@ export default class UIStore extends EventEmitter {
|
|||||||
|
|
||||||
this.emit(UI_EVENTS.Resize, entries);
|
this.emit(UI_EVENTS.Resize, entries);
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
window.mxUIStore = UIStore.instance;
|
/**
|
||||||
|
* Emit any necessary {@link UI_EVENTS.WidthIncreased} or {@link UI_EVENTS.WidthDecreased} events.
|
||||||
|
* @param currentWidth The current width of {@link window}
|
||||||
|
*/
|
||||||
|
private emitWidthChangeEvents = (currentWidth: number): void => {
|
||||||
|
if (currentWidth > this.windowWidth) this.emit(UI_EVENTS.WidthIncreased, currentWidth);
|
||||||
|
if (currentWidth < this.windowWidth) this.emit(UI_EVENTS.WidthDecreased, currentWidth);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update {@link UIStore#isWindowBeingResized}.
|
||||||
|
*/
|
||||||
|
private setWindowAsBeingResized = (): void => {
|
||||||
|
// Window is being resized, so set to true.
|
||||||
|
this.isWindowBeingResized = true;
|
||||||
|
// Reset any previous timeout.
|
||||||
|
window.clearTimeout(this.timeoutId);
|
||||||
|
// Set to false after a second.
|
||||||
|
// If the window continues to be resized, this method will be called
|
||||||
|
// again and this setTimeout will be cancelled.
|
||||||
|
this.timeoutId = window.setTimeout(() => {
|
||||||
|
this.isWindowBeingResized = false;
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import UIStore, { UI_EVENTS } from "../../../src/stores/UIStore";
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
describe("UIStore", () => {
|
||||||
|
class MockResizeObserver {
|
||||||
|
public static instance: ResizeObserver;
|
||||||
|
public static callback: ResizeObserverCallback;
|
||||||
|
|
||||||
|
public constructor(callback: ResizeObserverCallback) {
|
||||||
|
MockResizeObserver.callback = callback;
|
||||||
|
MockResizeObserver.instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static changeWidth = (width: number): void => {
|
||||||
|
const entry = {
|
||||||
|
target: document.body,
|
||||||
|
contentRect: {
|
||||||
|
width,
|
||||||
|
height: 1000,
|
||||||
|
},
|
||||||
|
} as unknown as ResizeObserverEntry;
|
||||||
|
MockResizeObserver.callback([entry], MockResizeObserver.instance);
|
||||||
|
};
|
||||||
|
|
||||||
|
public observe = jest.fn();
|
||||||
|
public unobserve = jest.fn();
|
||||||
|
public disconnect = jest.fn();
|
||||||
|
}
|
||||||
|
globalThis.ResizeObserver = MockResizeObserver;
|
||||||
|
|
||||||
|
it("should emit events on width increase/decrease", () => {
|
||||||
|
// eslint-disable-next-line no-restricted-properties
|
||||||
|
window.innerWidth = 500;
|
||||||
|
const store = UIStore.instance;
|
||||||
|
|
||||||
|
const onDecrease = jest.fn();
|
||||||
|
store.on(UI_EVENTS.WidthDecreased, onDecrease);
|
||||||
|
MockResizeObserver.changeWidth(200);
|
||||||
|
expect(onDecrease).toHaveBeenCalledWith(200);
|
||||||
|
|
||||||
|
const onIncrease = jest.fn();
|
||||||
|
store.on(UI_EVENTS.WidthIncreased, onIncrease);
|
||||||
|
MockResizeObserver.changeWidth(700);
|
||||||
|
expect(onIncrease).toHaveBeenCalledWith(700);
|
||||||
|
|
||||||
|
UIStore.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set isWindowBeingResized on resize", () => {
|
||||||
|
// eslint-disable-next-line no-restricted-properties
|
||||||
|
window.innerWidth = 500;
|
||||||
|
const store = UIStore.instance;
|
||||||
|
|
||||||
|
// No resize yet, so expect isWindowBeingResized to be false
|
||||||
|
expect(store.isWindowBeingResized).toBe(false);
|
||||||
|
|
||||||
|
// When resizing the window, expect isWindowBeingResized to be true
|
||||||
|
MockResizeObserver.changeWidth(200);
|
||||||
|
expect(store.isWindowBeingResized).toBe(true);
|
||||||
|
|
||||||
|
// After a second, isWindowBeingResized should again become false
|
||||||
|
jest.runAllTimers();
|
||||||
|
expect(store.isWindowBeingResized).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user