mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Utilize static event proxies to prevent resize-related events from leaking
Signed-off-by: Alex Culliere <alozhkin@mirantis.com>
This commit is contained in:
parent
40a54a886c
commit
84dcf323cd
@ -51,31 +51,6 @@ import { CommandContainer } from "./command-palette/command-container";
|
|||||||
import { KubeObjectStore } from "../kube-object.store";
|
import { KubeObjectStore } from "../kube-object.store";
|
||||||
import { clusterContext } from "./context";
|
import { clusterContext } from "./context";
|
||||||
|
|
||||||
/**
|
|
||||||
* Hacking into document event listeners to detect leaks
|
|
||||||
* TODO: remove this
|
|
||||||
*/
|
|
||||||
(function watchEvent() {
|
|
||||||
const addOne = document.addEventListener;
|
|
||||||
const removeOne = document.removeEventListener;
|
|
||||||
|
|
||||||
(document as any)["listeners"] = new Map<any, any>();
|
|
||||||
|
|
||||||
document.addEventListener = (type: string, listener: any) => {
|
|
||||||
const registry: Map<any, any> = (document as any)["listeners"];
|
|
||||||
|
|
||||||
registry.set(listener, type);
|
|
||||||
addOne(type, listener);
|
|
||||||
};
|
|
||||||
|
|
||||||
document.removeEventListener = (type: string, listener: any) => {
|
|
||||||
const registry: Map<any, any> = (document as any)["listeners"];
|
|
||||||
|
|
||||||
registry.delete(listener);
|
|
||||||
removeOne(type, listener);
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class App extends React.Component {
|
export class App extends React.Component {
|
||||||
static async init() {
|
static async init() {
|
||||||
|
|||||||
@ -24,7 +24,16 @@ export interface TableCellProps extends React.DOMAttributes<HTMLDivElement> {
|
|||||||
_sorting?: Partial<TableSortParams>; // <Table> sorting state, don't use this prop outside (!)
|
_sorting?: Partial<TableSortParams>; // <Table> sorting state, don't use this prop outside (!)
|
||||||
_sort?(sortBy: TableSortBy): void; // <Table> sort function, don't use this prop outside (!)
|
_sort?(sortBy: TableSortBy): void; // <Table> sort function, don't use this prop outside (!)
|
||||||
_nowrap?: boolean; // indicator, might come from parent <TableHead>, don't use this prop outside (!)
|
_nowrap?: boolean; // indicator, might come from parent <TableHead>, don't use this prop outside (!)
|
||||||
_onResize?(width: number): void; // <Table> resize callbalck, don't use this prop outside (!)
|
|
||||||
|
/**
|
||||||
|
* Triggers every time this cell is resized by user
|
||||||
|
* @param width desired width of this cell
|
||||||
|
*/
|
||||||
|
_onResize?(width: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers once the user has finished resizing this cell
|
||||||
|
*/
|
||||||
_onResizeComplete?(): void; // triggers a callback when user stops resizing
|
_onResizeComplete?(): void; // triggers a callback when user stops resizing
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +45,77 @@ export class TableCell extends React.Component<TableCellProps> {
|
|||||||
private resizeHandlerState?: ResizeHandlerState;
|
private resizeHandlerState?: ResizeHandlerState;
|
||||||
private cellContainer: React.RefObject<HTMLDivElement> = React.createRef();
|
private cellContainer: React.RefObject<HTMLDivElement> = React.createRef();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores an instance of `<TableCell>` that is currently responsible for handling resize-related events
|
||||||
|
*/
|
||||||
|
private static currentEventHandlingInstance?: TableCell;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends static event proxies to document `mousemove` and `mouseup` events for resizing,
|
||||||
|
* sets a particulate instance of `<TableCell>` to handle these events
|
||||||
|
* @param handlingInstance instance of `<TableCell>` responsible for handling events
|
||||||
|
*/
|
||||||
|
private static addMouseEventListeners(handlingInstance: TableCell) {
|
||||||
|
TableCell.currentEventHandlingInstance = handlingInstance;
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", TableCell.mouseMoveEventProxy);
|
||||||
|
document.addEventListener("mouseup", TableCell.mouseUpEventProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detaches static proxies from document `mousemove` and `mouseup`,
|
||||||
|
* cleans up the reference to current event handling instance
|
||||||
|
*/
|
||||||
|
private static removeMouseEventListeners() {
|
||||||
|
document.removeEventListener("mousemove", TableCell.mouseMoveEventProxy);
|
||||||
|
document.removeEventListener("mouseup", TableCell.mouseUpEventProxy);
|
||||||
|
|
||||||
|
delete TableCell.currentEventHandlingInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy for document-level `mousemove` events.
|
||||||
|
*
|
||||||
|
* Forwards the event to a member function
|
||||||
|
* of a current event handling instance of `<TableCell>`.
|
||||||
|
*
|
||||||
|
* Static to prevent it from leaking events.
|
||||||
|
*/
|
||||||
|
private static mouseMoveEventProxy(event: MouseEvent) {
|
||||||
|
TableCell.currentEventHandlingInstance.mouseMoveHandler(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy for document-level `mouseup` events.
|
||||||
|
*
|
||||||
|
* Forwards the event to a member function
|
||||||
|
* of a current event handling instance of `<TableCell>`.
|
||||||
|
*
|
||||||
|
* Static to prevent it from leaking events.
|
||||||
|
*/
|
||||||
|
private static mouseUpEventProxy() {
|
||||||
|
TableCell.currentEventHandlingInstance.mouseUpHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
private mouseMoveHandler = throttle((event: MouseEvent) => {
|
||||||
|
if (!this.resizeHandlerState) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const diffPosX = event.pageX - this.resizeHandlerState.mousePosX;
|
||||||
|
const currentWidth = this.cellContainer.current.offsetWidth;
|
||||||
|
|
||||||
|
this.props?._onResize(currentWidth + diffPosX);
|
||||||
|
this.resizeHandlerState.mousePosX = event.pageX;
|
||||||
|
}, ERGONOMIC_RESIZE_THROTTLE_RATE);
|
||||||
|
|
||||||
|
private mouseUpHandler() {
|
||||||
|
TableCell.removeMouseEventListeners();
|
||||||
|
|
||||||
|
this.props?._onResizeComplete();
|
||||||
|
delete this.resizeHandlerState;
|
||||||
|
}
|
||||||
|
|
||||||
@autobind()
|
@autobind()
|
||||||
onClick(evt: React.MouseEvent<HTMLDivElement>) {
|
onClick(evt: React.MouseEvent<HTMLDivElement>) {
|
||||||
if (this.props.onClick) {
|
if (this.props.onClick) {
|
||||||
@ -48,7 +128,7 @@ export class TableCell extends React.Component<TableCellProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.removeMouseEventListeners();
|
TableCell.removeMouseEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSortable() {
|
get isSortable() {
|
||||||
@ -81,37 +161,24 @@ export class TableCell extends React.Component<TableCellProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addMouseEventListeners() {
|
|
||||||
document.addEventListener("mousemove", this.mouseMoveHandler.bind(this));
|
|
||||||
document.addEventListener("mouseup", this.mouseUpHandler.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMouseEventListeners() {
|
|
||||||
document.removeEventListener("mousemove", this.mouseMoveHandler.bind(this));
|
|
||||||
document.removeEventListener("mouseup", this.mouseUpHandler.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
mouseMoveHandler = throttle((event: MouseEvent) => {
|
|
||||||
if (!this.resizeHandlerState) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const diffPosX = event.pageX - this.resizeHandlerState.mousePosX;
|
|
||||||
const currentWidth = this.cellContainer.current.offsetWidth;
|
|
||||||
|
|
||||||
this.props?._onResize(currentWidth + diffPosX);
|
|
||||||
this.resizeHandlerState.mousePosX = event.pageX;
|
|
||||||
}, ERGONOMIC_RESIZE_THROTTLE_RATE);
|
|
||||||
|
|
||||||
mouseUpHandler() {
|
|
||||||
this.removeMouseEventListeners();
|
|
||||||
console.log((document as any)["listeners"]);
|
|
||||||
this.props?._onResizeComplete();
|
|
||||||
this.resizeHandlerState = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className, checkbox, isChecked, isResizable: resizable, size, sortBy, _sort, _sorting, _nowrap, children, title, renderBoolean: displayBoolean, showWithColumn, _onResize, ...cellProps } = this.props;
|
const {
|
||||||
|
className,
|
||||||
|
checkbox,
|
||||||
|
isChecked,
|
||||||
|
isResizable: resizable,
|
||||||
|
size,
|
||||||
|
sortBy,
|
||||||
|
_sort,
|
||||||
|
_sorting,
|
||||||
|
_nowrap,
|
||||||
|
children,
|
||||||
|
title,
|
||||||
|
renderBoolean: displayBoolean,
|
||||||
|
showWithColumn,
|
||||||
|
_onResize,
|
||||||
|
_onResizeComplete,
|
||||||
|
...cellProps } = this.props;
|
||||||
const classNames = cssNames("TableCell", className, {
|
const classNames = cssNames("TableCell", className, {
|
||||||
checkbox,
|
checkbox,
|
||||||
nowrap: _nowrap,
|
nowrap: _nowrap,
|
||||||
@ -126,7 +193,7 @@ export class TableCell extends React.Component<TableCellProps> {
|
|||||||
this.resizeHandlerState = {
|
this.resizeHandlerState = {
|
||||||
mousePosX: event.pageX
|
mousePosX: event.pageX
|
||||||
};
|
};
|
||||||
this.addMouseEventListeners();
|
TableCell.addMouseEventListeners(this);
|
||||||
},
|
},
|
||||||
// prevent click events from triggering
|
// prevent click events from triggering
|
||||||
onClick: prevDefault(() => {})
|
onClick: prevDefault(() => {})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user