UNPKG

3.16 kBPlain TextView Raw
1/*
2 * Copyright (c) Jupyter Development Team.
3 * Distributed under the terms of the Modified BSD License.
4 */
5
6import { Message } from '@lumino/messaging';
7import { Throttler } from '@lumino/polling';
8import { Widget } from '@lumino/widgets';
9
10const RESIZE_HANDLE_CLASS = 'jp-CellResizeHandle';
11
12const CELL_RESIZED_CLASS = 'jp-mod-resizedCell';
13
14/**
15 * A handle that allows to change input/output proportions in side-by-side mode.
16 */
17export class ResizeHandle extends Widget {
18 constructor(protected targetNode: HTMLElement) {
19 super();
20 this.addClass(RESIZE_HANDLE_CLASS);
21 this._resizer = new Throttler(event => this._resize(event), 50);
22 }
23
24 /**
25 * Dispose the resizer handle.
26 */
27 dispose(): void {
28 this._resizer.dispose();
29 super.dispose();
30 }
31
32 /**
33 * Handle the DOM events for the widget.
34 *
35 * @param event - The DOM event sent to the widget.
36 *
37 */
38 handleEvent(event: Event): void {
39 switch (event.type) {
40 case 'dblclick':
41 this.targetNode.parentNode?.childNodes.forEach(node => {
42 (node as HTMLElement).classList.remove(CELL_RESIZED_CLASS);
43 });
44 document.documentElement.style.setProperty(
45 '--jp-side-by-side-output-size',
46 `1fr`
47 );
48 this._isActive = false;
49 break;
50 case 'mousedown':
51 this._isDragging = true;
52 if (!this._isActive) {
53 this.targetNode.parentNode?.childNodes.forEach(node => {
54 (node as HTMLElement).classList.add(CELL_RESIZED_CLASS);
55 });
56
57 this._isActive = true;
58 }
59 window.addEventListener('mousemove', this);
60 window.addEventListener('mouseup', this);
61 break;
62 case 'mousemove': {
63 if (this._isActive && this._isDragging) {
64 void this._resizer.invoke(event as MouseEvent);
65 }
66 break;
67 }
68 case 'mouseup':
69 this._isDragging = false;
70 window.removeEventListener('mousemove', this);
71 window.removeEventListener('mouseup', this);
72 break;
73 default:
74 break;
75 }
76 }
77
78 /**
79 * Handle `after-attach` messages.
80 */
81 protected onAfterAttach(msg: Message) {
82 this.node.addEventListener('dblclick', this);
83 this.node.addEventListener('mousedown', this);
84 super.onAfterAttach(msg);
85 }
86
87 /**
88 * Handle `before-detach` messages.
89 */
90 protected onBeforeDetach(msg: Message) {
91 this.node.removeEventListener('dblclick', this);
92 this.node.removeEventListener('mousedown', this);
93 super.onBeforeDetach(msg);
94 }
95
96 private _resize(event: MouseEvent): void {
97 // Gate the output size ratio between {0.05, 50} as sensible defaults.
98 const { width, x } = this.targetNode.getBoundingClientRect();
99 const position = event.clientX - x;
100 const ratio = width / position - 1;
101 if (0 < ratio) {
102 const normalized = Math.max(Math.min(Math.abs(ratio), 50), 0.05);
103 document.documentElement.style.setProperty(
104 '--jp-side-by-side-output-size',
105 `${normalized}fr`
106 );
107 }
108 }
109
110 private _isActive = false;
111 private _isDragging = false;
112 private _resizer: Throttler<void, void, [MouseEvent]>;
113}