Skip to content

Commit

Permalink
add resize handles; design improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
cameron-toy committed Feb 4, 2021
1 parent 26931dd commit 67b1213
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 62 deletions.
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
109 changes: 86 additions & 23 deletions src/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,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 @@ -152,8 +150,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 +298,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 +319,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 +370,37 @@ 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 @@ -377,8 +416,12 @@ export class DashboardWidget extends Widget {
fitContent(): void {
const element = this._content.node;
// 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 +685,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 +756,35 @@ 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
87 changes: 52 additions & 35 deletions style/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,79 +13,95 @@
}

.pr-EditableWidget {
border: var(--jp-border-width) solid var(--jp-inverse-layout-color4);
outline: var(--jp-border-width) solid var(--jp-inverse-layout-color4);
outline-offset: calc( -1 * var(--jp-border-width));
background: var(--jp-layout-color0);
overflow: visible;
}

.pr-EditableWidget > .pr-DashboardWidgetChild {
pointer-events: none;
}

.pr-EditableWidget:focus {
border: var(--jp-border-width) solid var(--jp-brand-color1);
}

.pr-EditableWidget:focus:after {
content: '';
position: absolute;
width: 100%;
height: 100%;
z-index: 2;
background: rgba(0,0,0,.10);
outline: var(--jp-border-width) solid var(--jp-brand-color1);
outline-offset: calc( -1 * var(--jp-border-width));
}

.pr-EditableWidget:hover {
border: var(--jp-border-width) solid var(--jp-brand-color1);
outline: var(--jp-border-width) solid var(--jp-brand-color1);
outline-offset: calc( -1 * var(--jp-border-width));
cursor: move;
}

.pr-EditableWidget:hover:after {
content: '';
position: absolute;
width: 100%;
height: 100%;
z-index: 2;
background: rgba(0,0,0,.10);
}

.pr-DashboardArea {
overflow: auto;
}

.pr-Resizer {
display: none;
width: 15px;
height: 15px;
right: 0;
bottom: 0;
width: 8px;
height: 8px;
outline: 2px solid var(--jp-brand-color1);
z-index: 3;
cursor: nwse-resize;
background-color: var(--jp-layout-color0);
position: absolute;
}

.pr-Resizer:hover {
background-color: var(--jp-brand-color1);
}

.pr-ResizerBottomRight {
right: -4px;
bottom: -4px;
cursor: nwse-resize;
}

.pr-ResizerBottomLeft {
left: -4px;
bottom: -4px;
cursor: nesw-resize;
}

.pr-ResizerTopRight {
right: -4px;
top: -4px;
cursor: nesw-resize;
}

.pr-ResizerTopLeft {
left: -4px;
top: -4px;
cursor: nwse-resize;
}

.pr-DashboardWidgetChild {
display: inline-block;
}

.pr-EditableWidget:hover .pr-Resizer {
.pr-EditableWidget:focus .pr-Resizer {
display: block;
}


.pr-Canvas {
pointer-events: none;
}

.pr-Canvas.pr-FreeLayout {
background-image: linear-gradient(45deg, rgba(160,160,160,.15) 25%, transparent 25%), linear-gradient(-45deg, rgba(160,160,160,.15) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(160,160,160,.15) 75%), linear-gradient(-45deg, transparent 75%, rgba(160,160,160,.15) 70%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
background-image:
linear-gradient(to right, #E5E5E5 1px, transparent 1px),
linear-gradient(to bottom, #E5E5E5 1px, transparent 1px);
background-color: white;
background-size: 10px 10px;
}

.pr-Canvas.pr-TiledLayout {
background-image: linear-gradient(45deg, rgba(160,160,160,.15) 25%, transparent 25%), linear-gradient(-45deg, rgba(160,160,160,.15) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgba(160,160,160,.15) 75%), linear-gradient(-45deg, transparent 75%, rgba(160,160,160,.15) 70%);
background-size: 64px 64px;
background-position: 0 0, 0 32px, 32px -32px, -32px 0px;
background-image:
linear-gradient(to right, #E5E5E5 1px, transparent 1px),
linear-gradient(to bottom, #E5E5E5 1px, transparent 1px);
background-color: white;
background-size: 32px 32px;
}

.pr-OnboardingGif {
Expand Down Expand Up @@ -127,5 +143,6 @@ select.pr-ToolbarSelector.jp-mod-styled {

.pr-DragImage {
box-shadow: var(--jp-elevation-z1);
border: var(--jp-border-width) solid var(--jp-brand-color1);
outline: var(--jp-border-width) solid var(--jp-brand-color1);
outline-offset: calc( -1 * var(--jp-border-width));
}

0 comments on commit 67b1213

Please sign in to comment.