Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design and resizer improvements #113

Merged
merged 3 commits into from
Feb 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import greyDashboardSvgstr from '../style/icons/dashboard_icon_filled_grey.svg';
import blueDashboardSvgstr from '../style/icons/dashboard_icon_filled_blue.svg';
import whiteDashboardOutlineSvgstr from '../style/icons/dashboard_icon_outline_white.svg';
import greyDashboardOutlineSvgstr from '../style/icons/dashboard_icon_outline_grey.svg';
import tealDashboardSvgstr from '../style/icons/dashboard_icon_filled_teal.svg';
import redoIcon from '../style/icons/redo.svg';
import fullscreenIcon from '../style/icons/fullscreen.svg';
import statusIcon from '../style/icons/dummy.svg';
Expand All @@ -17,6 +18,11 @@ import resizer2Svgstr from '../style/icons/drag indicator lines.svg';
* Dashboard icons
*/
export namespace DashboardIcons {
export const tealDashboard = new LabIcon({
name: 'pr-icons:teal-dashboard',
svgstr: tealDashboardSvgstr
});

export const whiteDashboard = new LabIcon({
name: 'pr-icons:white-dashboard',
svgstr: whiteDashboardSvgstr
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const extension: JupyterFrontEndPlugin<IDashboardTracker> = {
contentType: 'file',
extensions: ['.dashboard', '.dash'],
fileFormat: 'text',
icon: DashboardIcons.blueDashboard,
icon: DashboardIcons.tealDashboard,
iconLabel: 'Dashboard',
mimeTypes: ['application/json']
};
Expand Down Expand Up @@ -550,7 +550,7 @@ function addCommands(

commands.addCommand(CommandIDs.createNew, {
label: 'Dashboard',
icon: DashboardIcons.blueDashboard,
icon: DashboardIcons.tealDashboard,
execute: async args => {
// A new file is created and opened separately to override the default
// opening behavior when there's a notebook and open the dashboard in a
Expand Down
8 changes: 4 additions & 4 deletions src/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ export class DashboardLayout extends Layout {
*/
updateLayoutFromWidgetstore(): void {
this._signalChanges = false;
const records = this._widgetstore.getWidgets();
const records = this._widgetstore.get(Widgetstore.WIDGET_SCHEMA);
each(records, record => {
this._updateLayoutFromRecord(record);
});
Expand Down Expand Up @@ -869,10 +869,10 @@ export class DashboardLayout extends Layout {
*/
setTileSize(s: number): void {
this._tileSize = s;
const backgroundPosition = `0 0, 0 ${s}px, ${s}px -${s}px, -${s}px 0px`;
// const backgroundPosition = `0 0, 0 ${s}px, ${s}px -${s}px, -${s}px 0px`;

this.canvas.style.backgroundPosition = backgroundPosition;
this.canvas.style.backgroundSize = `${2 * s}px ${2 * s}px`;
// this.canvas.style.backgroundPosition = backgroundPosition;
this.canvas.style.backgroundSize = `${s}px ${s}px`;
this.parent.update();

this.startBatch();
Expand Down
140 changes: 114 additions & 26 deletions src/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { CodeCell, MarkdownCell } from '@jupyterlab/cells';

import { Widget } from '@lumino/widgets';

import { MessageLoop } from '@lumino/messaging';

import { UUID, MimeData } from '@lumino/coreutils';

import { ArrayExt } from '@lumino/algorithm';
Expand All @@ -23,8 +25,6 @@ import {

import { Signal, ISignal } from '@lumino/signaling';

import { DashboardIcons } from './icons';

import { Widgetstore, WidgetPosition } from './widgetstore';

import { DashboardLayout } from './layout';
Expand Down Expand Up @@ -132,8 +132,20 @@ export class DashboardWidget extends Widget {
this.node.style.opacity = '0';

container.classList.add(DASHBOARD_WIDGET_CHILD_CLASS);
container.appendChild(clone.node);
this.node.appendChild(container);

// Fake an attach in order to render LaTeX properly.
// Note: This is not how you should use Lumino widgets.
if (this.parent) {
if (this.parent!.isAttached) {
MessageLoop.sendMessage(clone, Widget.Msg.BeforeAttach);
container.appendChild(clone.node);
this.node.appendChild(container);
if (this.parent!.isAttached) {
MessageLoop.sendMessage(clone, Widget.Msg.AfterAttach);
}
}
}

this._content = clone;

const done = (): void => {
Expand All @@ -152,8 +164,14 @@ export class DashboardWidget extends Widget {
});
}

const resizer = DashboardWidget.createResizer();
this.node.appendChild(resizer);
const resizerTopLeft = DashboardWidget.createResizer('top-left');
const resizerTopRight = DashboardWidget.createResizer('top-right');
const resizerBottomLeft = DashboardWidget.createResizer('bottom-left');
const resizerBottomRight = DashboardWidget.createResizer('bottom-right');
this.node.appendChild(resizerTopLeft);
this.node.appendChild(resizerTopRight);
this.node.appendChild(resizerBottomLeft);
this.node.appendChild(resizerBottomRight);

this.addClass(DASHBOARD_WIDGET_CLASS);
this.addClass(EDITABLE_WIDGET_CLASS);
Expand Down Expand Up @@ -294,9 +312,19 @@ export class DashboardWidget extends Widget {

// this.node.style.opacity = '0.6';

const elem = target as HTMLElement;
// Set mode to resize if the mousedown happened on a resizer.
if ((target as HTMLElement).classList.contains('pr-Resizer')) {
if (elem.classList.contains('pr-Resizer')) {
this._mouseMode = 'resize';
if (elem.classList.contains('pr-ResizerTopRight')) {
this._selectedResizer = 'top-right';
} else if (elem.classList.contains('pr-ResizerTopLeft')) {
this._selectedResizer = 'top-left';
} else if (elem.classList.contains('pr-ResizerBottomLeft')) {
this._selectedResizer = 'bottom-left';
} else {
this._selectedResizer = 'bottom-right';
}
} else {
this._mouseMode = 'drag';
}
Expand All @@ -305,12 +333,16 @@ export class DashboardWidget extends Widget {

const rect = this.node.getBoundingClientRect();

const { width, height, top, left } = this.pos;

this._clickData = {
pressX: event.clientX,
pressY: event.clientY,
cell,
pressWidth: parseInt(this.node.style.width, 10),
pressHeight: parseInt(this.node.style.height, 10),
origWidth: width,
origHeight: height,
origLeft: left,
origTop: top,
target: this.node.cloneNode(true) as HTMLElement,
widgetX: rect.left,
widgetY: rect.top
Expand Down Expand Up @@ -352,16 +384,44 @@ export class DashboardWidget extends Widget {
* Handle `mousemove` events when the widget mouseMode is `resize`.
*/
private _resizeMouseMove(event: MouseEvent): void {
const { pressX, pressY, pressWidth, pressHeight } = this._clickData;
const {
pressX,
pressY,
origWidth,
origHeight,
origLeft,
origTop
} = this._clickData;

const deltaX = event.clientX - pressX;
const deltaY = event.clientY - pressY;

const width = Math.max(pressWidth + deltaX, DashboardWidget.MIN_WIDTH);
const height = Math.max(pressHeight + deltaY, DashboardWidget.MIN_HEIGHT);
let { width, height, top, left } = this.pos;

switch (this._selectedResizer) {
case 'bottom-right':
width = Math.max(origWidth + deltaX, DashboardWidget.MIN_WIDTH);
height = Math.max(origHeight + deltaY, DashboardWidget.MIN_HEIGHT);
break;
case 'bottom-left':
width = Math.max(origWidth - deltaX, DashboardWidget.MIN_WIDTH);
height = Math.max(origHeight + deltaY, DashboardWidget.MIN_HEIGHT);
left = origLeft + deltaX;
break;
case 'top-right':
width = Math.max(origWidth + deltaX, DashboardWidget.MIN_WIDTH);
height = Math.max(origHeight - deltaY, DashboardWidget.MIN_HEIGHT);
top = origTop + deltaY;
break;
case 'top-left':
width = Math.max(origWidth - deltaX, DashboardWidget.MIN_WIDTH);
height = Math.max(origHeight - deltaY, DashboardWidget.MIN_HEIGHT);
top = origTop + deltaY;
left = origLeft + deltaX;
break;
}

this.node.style.width = `${width}px`;
this.node.style.height = `${height}px`;
this.pos = { width, height, top, left };

if (this.mode === 'grid-edit') {
(this.parent.layout as DashboardLayout).drawDropZone(this.pos, '#2b98f0');
Expand All @@ -375,10 +435,14 @@ export class DashboardWidget extends Widget {
* Fit widget width/height to the width/height of the underlying content.
*/
fitContent(): void {
const element = this._content.node;
const element = this._content.node.firstChild as HTMLElement;
// Pixels are added to prevent weird wrapping issues. Kind of a hack.
this.node.style.width = `${element.clientWidth + 3}px`;
this.node.style.height = `${element.clientHeight + 2}px`;
this.pos = {
width: element.clientWidth + 3,
height: element.clientHeight + 2,
left: undefined,
top: undefined
};
}

/**
Expand Down Expand Up @@ -642,15 +706,18 @@ export class DashboardWidget extends Widget {
private _clickData: {
pressX: number;
pressY: number;
pressWidth: number;
pressHeight: number;
origWidth: number;
origHeight: number;
origLeft: number;
origTop: number;
target: HTMLElement;
cell: CodeCell | MarkdownCell;
widgetX: number;
widgetY: number;
} | null = null;
private _locked = false;
private _content: Widget;
private _selectedResizer: DashboardWidget.ResizerCorner;
}

/**
Expand Down Expand Up @@ -710,18 +777,39 @@ export namespace DashboardWidget {
return `DashboardWidget-${UUID.uuid4()}`;
}

/**
* A type for describing the corner for a widget resizer.
*/
export type ResizerCorner =
| 'top-left'
| 'bottom-left'
| 'top-right'
| 'bottom-right';

/**
* Create a resizer element for a dashboard widget.
*/
export function createResizer(): HTMLElement {
export function createResizer(corner: ResizerCorner): HTMLElement {
const resizer = document.createElement('div');
resizer.classList.add('pr-Resizer');
DashboardIcons.resizer2.element({
container: resizer,
width: '15px',
height: '15px',
pointerEvents: 'none'
});

switch (corner) {
case 'top-left':
resizer.classList.add('pr-ResizerTopLeft');
break;
case 'top-right':
resizer.classList.add('pr-ResizerTopRight');
break;
case 'bottom-left':
resizer.classList.add('pr-ResizerBottomLeft');
break;
case 'bottom-right':
resizer.classList.add('pr-ResizerBottomRight');
break;
default:
resizer.classList.add('pr-ResizerBottomRight');
break;
}

return resizer;
}
Expand Down
Loading