diff --git a/src/renderer/components/drawer/drawer.scss b/src/renderer/components/drawer/drawer.scss index e170bcc613..3e3c5765c9 100644 --- a/src/renderer/components/drawer/drawer.scss +++ b/src/renderer/components/drawer/drawer.scss @@ -21,8 +21,6 @@ .Drawer { - --size: 50%; - --full-size: 75%; --spacing: #{$padding * 3}; --icon-focus-color: white; @@ -52,6 +50,7 @@ &.right { top: 0; width: var(--size); + max-width: 95vw; /* Prevent drawer to be bigger than viewport */ } &.top, @@ -85,7 +84,7 @@ font-weight: normal; margin-left: 8px; } - + .drawer-title-text:hover .Icon { opacity: 1; transition: opacity 250ms; @@ -108,10 +107,4 @@ border-bottom: 1px solid var(--borderFaintColor); } } - - @include media("<=900px") { - &.left, &.right { - width: var(--full-size) - } - } } diff --git a/src/renderer/components/drawer/drawer.tsx b/src/renderer/components/drawer/drawer.tsx index 074fad2706..4c93b8cada 100644 --- a/src/renderer/components/drawer/drawer.tsx +++ b/src/renderer/components/drawer/drawer.tsx @@ -24,19 +24,28 @@ import "./drawer.scss"; import React from "react"; import { clipboard } from "electron"; import { createPortal } from "react-dom"; -import { cssNames, noop } from "../../utils"; +import { createStorage, cssNames, noop } from "../../utils"; import { Icon } from "../icon"; import { Animate, AnimateName } from "../animate"; import { history } from "../../navigation"; +import { ResizeDirection, ResizeGrowthDirection, ResizeSide, ResizingAnchor } from "../resizing-anchor"; + +export type DrawerPosition = "top" | "left" | "right" | "bottom"; export interface DrawerProps { open: boolean; title: React.ReactNode; + + /** + * The width or heigh (depending on `position`) of the Drawer. + * + * If not set then the Drawer will be resizable. + */ size?: string; // e.g. 50%, 500px, etc. usePortal?: boolean; className?: string | object; contentClass?: string | object; - position?: "top" | "left" | "right" | "bottom"; + position?: DrawerPosition; animation?: AnimateName; onClose?: () => void; toolbar?: React.ReactNode; @@ -51,10 +60,22 @@ const defaultProps: Partial = { interface State { isCopied: boolean; + width: number; } -export class Drawer extends React.Component { +const resizingAnchorProps = new Map(); +resizingAnchorProps.set("right", [ResizeDirection.HORIZONTAL, ResizeSide.LEADING, ResizeGrowthDirection.RIGHT_TO_LEFT]); +resizingAnchorProps.set("left", [ResizeDirection.HORIZONTAL, ResizeSide.TRAILING, ResizeGrowthDirection.LEFT_TO_RIGHT]); +resizingAnchorProps.set("top", [ResizeDirection.VERTICAL, ResizeSide.TRAILING, ResizeGrowthDirection.TOP_TO_BOTTOM]); +resizingAnchorProps.set("bottom", [ResizeDirection.VERTICAL, ResizeSide.LEADING, ResizeGrowthDirection.BOTTOM_TO_TOP]); + +const defaultDrawerWidth = 725; +const drawerStorage = createStorage("drawer", { + width: defaultDrawerWidth, +}); + +export class Drawer extends React.Component { static defaultProps = defaultProps as object; private mouseDownTarget: HTMLElement; @@ -66,8 +87,9 @@ export class Drawer extends React.Component { this.restoreScrollPos(); }); - public state: State = { + public state = { isCopied: false, + width: drawerStorage.get().width, }; componentDidMount() { @@ -86,6 +108,11 @@ export class Drawer extends React.Component { window.removeEventListener("keydown", this.onEscapeKey); } + resizeWidth = (width: number) => { + this.setState({ width }); + drawerStorage.merge({ width }); + }; + fixUpTripleClick = (ev: MouseEvent) => { // detail: A count of consecutive clicks that happened in a short amount of time if (ev.detail === 3) { @@ -157,33 +184,54 @@ export class Drawer extends React.Component { }; render() { - const { open, position, title, animation, children, toolbar, size, usePortal } = this.props; - let { className, contentClass } = this.props; - const { isCopied } = this.state; - const copyTooltip = isCopied? "Copied!" : "Copy"; - const copyIcon = isCopied? "done" : "content_copy"; + const { className, contentClass, animation, open, position, title, children, toolbar, size, usePortal } = this.props; + const { isCopied, width } = this.state; + const copyTooltip = isCopied ? "Copied!" : "Copy"; + const copyIcon = isCopied ? "done" : "content_copy"; + const canCopyTitle = typeof title === "string" && title.length > 0; + const [direction, placement, growthDirection] = resizingAnchorProps.get(position); + const drawerSize = size || `${width}px`; - className = cssNames("Drawer", className, position); - contentClass = cssNames("drawer-content flex column box grow", contentClass); - const style = size ? { "--size": size } as React.CSSProperties : undefined; const drawer = ( -
this.contentElem = e}> +
this.contentElem = e} + >
{title} - {title && typeof title == "string" && ( + {canCopyTitle && ( this.copyTitle(title)}/> )}
{toolbar}
-
this.scrollElem = e}> +
this.scrollElem = e} + > {children}
+ { + !size && ( + width} + onDrag={this.resizeWidth} + onDoubleClick={() => this.resizeWidth(defaultDrawerWidth)} + minExtent={300} + maxExtent={window.innerWidth * 0.9} + /> + ) + }
); diff --git a/src/renderer/components/resizing-anchor/resizing-anchor.scss b/src/renderer/components/resizing-anchor/resizing-anchor.scss index 149560f187..6720e9f033 100644 --- a/src/renderer/components/resizing-anchor/resizing-anchor.scss +++ b/src/renderer/components/resizing-anchor/resizing-anchor.scss @@ -41,13 +41,20 @@ body.resizing { transition: background 0.2s 0s; } - &:hover { + &:hover, &.resizing { &::after { background: var(--blue); transition: background 0.2s 0.5s; } } + &:hover.wasDragging { + &::after { + background: transparent; + transition: background 0.2s 0s; + } + } + &.disabled { display: none; } @@ -64,16 +71,6 @@ body.resizing { margin-left: 0; } - .resizing & { - $expandedHeight: 200px; - height: $expandedHeight; - margin-top: -$expandedHeight * 0.5; - - &::after { - margin-top: $expandedHeight * 0.5; - } - } - &.trailing { bottom: -$dimension * 0.5; } @@ -85,17 +82,6 @@ body.resizing { cursor: col-resize; width: $dimension; - // Expand hoverable area while resizing to keep highlighting resizer. - // Otherwise, cursor can move far away dropping hover indicator. - .resizing & { - $expandedWidth: 200px; - width: $expandedWidth; - - &.trailing { - right: -$expandedWidth * 0.5; - } - } - &.leading { left: -$dimension * 0.5; } diff --git a/src/renderer/components/resizing-anchor/resizing-anchor.tsx b/src/renderer/components/resizing-anchor/resizing-anchor.tsx index ec564ca219..b33eb4863f 100644 --- a/src/renderer/components/resizing-anchor/resizing-anchor.tsx +++ b/src/renderer/components/resizing-anchor/resizing-anchor.tsx @@ -24,6 +24,7 @@ import React from "react"; import { action, observable, makeObservable } from "mobx"; import _ from "lodash"; import { cssNames, noop } from "../../utils"; +import { observer } from "mobx-react"; export enum ResizeDirection { HORIZONTAL = "horizontal", @@ -162,9 +163,12 @@ function directionDelta(P1: number, P2: number, M: number): number | false { return false; } +@observer export class ResizingAnchor extends React.PureComponent { @observable lastMouseEvent?: MouseEvent; - @observable.ref ref?: React.RefObject; + ref = React.createRef(); + @observable isDragging = false; + @observable wasDragging = false; static defaultProps = { onStart: noop, @@ -192,7 +196,13 @@ export class ResizingAnchor extends React.PureComponent { throw new Error("maxExtent must be >= minExtent"); } - this.ref = React.createRef(); + const cur = props.getCurrentExtent(); + + if (cur > props.maxExtent) { + props.onDrag(props.maxExtent); + } else if (cur < props.minExtent) { + props.onDrag(props.minExtent); + } } componentWillUnmount() { @@ -211,6 +221,7 @@ export class ResizingAnchor extends React.PureComponent { document.addEventListener("mousemove", this.onDrag); document.addEventListener("mouseup", this.onDragEnd); document.body.classList.add(ResizingAnchor.IS_RESIZING); + this.isDragging = true; this.lastMouseEvent = undefined; onStart(); @@ -296,6 +307,10 @@ export class ResizingAnchor extends React.PureComponent { document.removeEventListener("mousemove", this.onDrag); document.removeEventListener("mouseup", this.onDragEnd); document.body.classList.remove(ResizingAnchor.IS_RESIZING); + this.isDragging = false; + this.wasDragging = true; + + setTimeout(() => this.wasDragging = false, 200); }; render() { @@ -303,7 +318,7 @@ export class ResizingAnchor extends React.PureComponent { return
;