diff --git a/res/menu/context.en.yml b/res/menu/context.en.yml index ffd6ac7773..e07e22e869 100644 --- a/res/menu/context.en.yml +++ b/res/menu/context.en.yml @@ -168,6 +168,16 @@ en: command: 'app:restore-item' - label: 'Permanently Delete Selected Items' command: 'app:destroy-item' + item-view: &item-view + - label: View Layout + id: 'item-view-layout' + submenu: + - label: 'Stacked' + id: 'stacked' + type: checkbox + - label: 'Side by side' + id: 'side-by-side' + type: checkbox trash: &trash - label: 'Permanently Delete Items' command: 'app:destroy-item' @@ -199,6 +209,7 @@ en: item-bulk-list: *item-bulk-list item-deleted: *item-deleted item-bulk-deleted: *item-bulk-deleted + item-view: *item-view trash: *trash photo: *photo selection: *selection @@ -223,6 +234,7 @@ en: item-bulk-list: *item-bulk-list item-deleted: *item-deleted item-bulk-deleted: *item-bulk-deleted + item-view: *item-view trash: *trash photo: *photo selection: *selection diff --git a/res/strings/renderer.en.yml b/res/strings/renderer.en.yml index 56e1f56570..b66ff029ad 100644 --- a/res/strings/renderer.en.yml +++ b/res/strings/renderer.en.yml @@ -97,7 +97,7 @@ en: template: Default Template debug: Enable Developer Mode dup: - label: During Import + label: During import option: skip: Skip duplicate photos import: Accept duplicate photos @@ -113,7 +113,11 @@ en: option: fit: Zoom photos to fit fill: Zoom photos to fill - + layout: + label: Item View layout + option: + stacked: Stacked + side-by-side: Side by side project: label: Project template: diff --git a/src/browser/menu.js b/src/browser/menu.js index e9af90c35d..096af9e2ce 100644 --- a/src/browser/menu.js +++ b/src/browser/menu.js @@ -251,6 +251,18 @@ class Menu { id: target.id, mode: li.mode }) })) + break + } + case 'item-view-layout': { + const { target } = params[0] + item.submenu = item.submenu.map(li => ({ + ...li, + checked: li.id === target.layout, + click: this.responder('app:settings-persist', win, { + layout: li.id + }) + })) + break } } @@ -347,13 +359,15 @@ class ContextMenu extends Menu { scopes.selection = [...scopes.photo, 'selection'] scopes.notes = [...scopes.global] scopes.note = [...scopes.notes, 'note'] - scopes.notepad = [...scopes.global, 'notepad'] scopes['item-bulk'] = [...scopes.items, 'item-bulk'] scopes['item-list'] = [...scopes.items, 'item-list', 'item'] scopes['item-bulk-list'] = [...scopes.items, 'item-bulk-list', 'item-bulk'] scopes['item-deleted'] = [...scopes.global, 'item-deleted'] scopes['item-bulk-deleted'] = [...scopes.global, 'item-bulk-deleted'] scopes['item-tag'] = [...scopes.global, 'item-tag'] + scopes['item-view'] = [...scopes.global, 'item-view'] + scopes.notepad = [...scopes['item-view'], 'notepad'] + scopes.esper = [...scopes['item-view']] } module.exports = { diff --git a/src/browser/tropy.js b/src/browser/tropy.js index 3401cb934f..3033d7a9a2 100644 --- a/src/browser/tropy.js +++ b/src/browser/tropy.js @@ -469,6 +469,9 @@ class Tropy extends EventEmitter { this.on('app:writing-mode', (win, { id, mode }) => this.dispatch(act.notepad.update({ [id]: { mode } }), win)) + this.on('app:settings-persist', (win, payload) => + this.dispatch(act.settings.persist(payload), win)) + this.on('app:toggle-menu-bar', win => { if (win.isMenuBarAutoHide()) { win.setAutoHideMenuBar(false) diff --git a/src/components/editor/container.js b/src/components/editor/container.js index 861c7aa281..f3328d9fca 100644 --- a/src/components/editor/container.js +++ b/src/components/editor/container.js @@ -1,7 +1,6 @@ 'use strict' const React = require('react') -const { Component } = React const { func, bool, object, number, string } = require('prop-types') const { EditorToolbar } = require('./toolbar') const { EditorState } = require('prosemirror-state') @@ -16,7 +15,7 @@ const { get, noop, restrict } = require('../../common/util') const { SASS: { EDITOR } } = require('../../constants') -class Editor extends Component { +class Editor extends React.Component { constructor(props) { super(props) this.state = { @@ -126,10 +125,10 @@ class Editor extends Component { } render() { - const { isDisabled, placeholder, tabIndex } = this.props - const { hasViewFocus } = this.state - const state = this.getEditorState() - const showPlaceholder = placeholder != null && this.isBlank(state.doc) + let { isDisabled, isDraggable, placeholder, tabIndex } = this.props + let { hasViewFocus } = this.state + let state = this.getEditorState() + let showPlaceholder = placeholder != null && this.isBlank(state.doc) return (
- {!isDisabled && + {(isDraggable || !isDisabled) && @@ -164,6 +164,7 @@ class Editor extends Component { static propTypes = { isDisabled: bool, + isDraggable: bool, keymap: object.isRequired, mode: string.isRequired, onBlur: func.isRequired, diff --git a/src/components/editor/toolbar.js b/src/components/editor/toolbar.js index ee3fb31406..c045354716 100644 --- a/src/components/editor/toolbar.js +++ b/src/components/editor/toolbar.js @@ -1,8 +1,7 @@ 'use strict' const React = require('react') -const { PureComponent } = React -const { func, instanceOf } = require('prop-types') +const { bool, func, instanceOf } = require('prop-types') const { Toolbar, ToolbarContext, ToolGroup } = require('../toolbar') const { Button } = require('../button') const { EditorState } = require('prosemirror-state') @@ -29,7 +28,7 @@ const { } = require('../icons') -class EditorToolbar extends PureComponent { +class EditorToolbar extends React.PureComponent { constructor(props) { super(props) @@ -162,7 +161,7 @@ class EditorToolbar extends PureComponent { render() { return ( - + @@ -229,12 +228,10 @@ class EditorToolbar extends PureComponent { } static propTypes = { + isDraggable: bool, state: instanceOf(EditorState), onCommand: func.isRequired } - - static defaultProps = { - } } module.exports = { diff --git a/src/components/esper/esper.js b/src/components/esper/esper.js index 9f5c9e968a..4331a9b0c0 100644 --- a/src/components/esper/esper.js +++ b/src/components/esper/esper.js @@ -669,6 +669,12 @@ class Esper extends React.PureComponent { this.container = container } + handleContextMenu = (event) => { + if (!this.isDisabled) { + this.props.onContextMenu(event, 'esper') + } + } + handleMouseDown = () => { if (document.activeElement !== this.container) { this.container.focus() @@ -694,6 +700,7 @@ class Esper extends React.PureComponent { tabIndex={tabIndex} className={cx(this.classes)} onBlur={this.handleBlur} + onContextMenu={this.handleContextMenu} onMouseDown={this.handleMouseDown} onKeyDown={this.handleKeyDown} onKeyUp={this.handleKeyUp}> @@ -761,6 +768,7 @@ class Esper extends React.PureComponent { maxZoom: number.isRequired, minZoom: number.isRequired, mode: string.isRequired, + onContextMenu: func.isRequired, onChange: func.isRequired, onPhotoError: func.isRequired, onSelect: func.isRequired, diff --git a/src/components/item/container.js b/src/components/item/container.js index fb217b02d8..0f4fa18138 100644 --- a/src/components/item/container.js +++ b/src/components/item/container.js @@ -6,7 +6,8 @@ const { BufferedResizable } = require('../resizable') const { Esper } = require('../esper') const { NotePad } = require('../note') const act = require('../../actions') -const { SASS: { ESPER } } = require('../../constants') +const cx = require('classnames') +const { SASS: { ESPER }, ITEM: { LAYOUT } } = require('../../constants') const { arrayOf, bool, func, number, object, shape, string @@ -22,10 +23,49 @@ const { class ItemContainer extends React.PureComponent { + get dimension() { + return (this.props.settings.layout === LAYOUT.SIDE_BY_SIDE) ? + 'width' : 'height' + } + + get size() { + switch (this.props.settings.layout) { + case LAYOUT.STACKED: + return this.props.esper.height + case LAYOUT.SIDE_BY_SIDE: + return this.props.esper.width + default: + return 100 + } + } + + get isNotePadDraggable() { + return ARGS.frameless && + this.props.settings.layout === LAYOUT.SIDE_BY_SIDE + } + + get hasOverlayToolbars() { + return this.props.settings.overlayToolbars && + this.props.settings.layout !== LAYOUT.SIDE_BY_SIDE + } + + getResizableProps(layout = this.props.settings.layout) { + return layout === LAYOUT.SIDE_BY_SIDE ? + { edge: 'right', margin: ESPER.MIN_WIDTH, min: ESPER.MIN_WIDTH } : + { edge: 'bottom', margin: ESPER.MIN_HEIGHT, min: ESPER.MIN_HEIGHT } + } + setNotePad = (notepad) => { this.notepad = notepad } + handleContextMenu = (event, scope = 'item-view', opts = {}) => { + this.props.onContextMenu(event, scope, { + layout: this.props.settings.layout, + ...opts + }) + } + handleEsperChange = ({ photo, selection, image, esper }) => { if (esper != null) { this.props.onUiUpdate({ esper }) @@ -41,24 +81,26 @@ class ItemContainer extends React.PureComponent { } } - handleEsperResize = (height) => { - this.props.onUiUpdate({ esper: { height } }) + handleEsperResize = (value) => { + this.props.onUiUpdate({ + esper: { [this.dimension]: value } + }) } render() { return ( -
+
+ value={this.size} + onChange={this.handleEsperResize}> + onContextMenu={this.handleContextMenu}/>
) } @@ -91,6 +135,7 @@ class ItemContainer extends React.PureComponent { cache: string.isRequired, esper: shape({ height: number.isRequired, + width: number.isRequired, panel: bool.isRequired, tool: string.isRequired }).isRequired, diff --git a/src/components/note/pad.js b/src/components/note/pad.js index 52d8b8b7c1..ad7a59df6d 100644 --- a/src/components/note/pad.js +++ b/src/components/note/pad.js @@ -1,14 +1,13 @@ 'use strict' const React = require('react') -const { PureComponent } = React const { bool, func, number, object, shape, string } = require('prop-types') const { Editor } = require('../editor') const { TABS } = require('../../constants') const cx = require('classnames') -class NotePad extends PureComponent { +class NotePad extends React.PureComponent { get classes() { return ['note-pad', this.props.mode, { 'no-wrap': !this.props.wrap, @@ -66,6 +65,7 @@ class NotePad extends PureComponent { mode={this.props.mode} placeholder="notepad.placeholder" isDisabled={this.isDisabled} + isDraggable={this.props.isDraggable} tabIndex={this.props.tabIndex} onBlur={this.handleEditorBlur} onChange={this.handleChange}/> @@ -75,6 +75,7 @@ class NotePad extends PureComponent { static propTypes = { isDisabled: bool, + isDraggable: bool, isItemOpen: bool, keymap: object.isRequired, note: shape({ diff --git a/src/components/prefs/app.js b/src/components/prefs/app.js index 9955f03b19..d56717c5c8 100644 --- a/src/components/prefs/app.js +++ b/src/components/prefs/app.js @@ -5,7 +5,7 @@ const { PureComponent } = React const { array, arrayOf, bool, func, shape, string } = require('prop-types') const { TemplateSelect } = require('../template/select') const { ipcRenderer: ipc } = require('electron') -const { ESPER } = require('../../constants') +const { ESPER, ITEM } = require('../../constants') const { FormElement, @@ -97,6 +97,12 @@ class AppPrefs extends PureComponent { value={this.props.settings.zoomMode} options={this.props.zoomModes} onChange={this.props.onSettingsUpdate}/> +
.resizable { - // Temporary insurance to make sure the notepad is visible when upgrading to 1.1.2 - // See #221 - max-height: calc(100% - 38px); + &.stacked { + > .resizable { + // Temporary insurance to make sure the notepad is visible when upgrading to 1.1.2 + // See #221 + max-height: calc(100% - 38px); + } + } + + &.side-by-side { + flex-direction: row; } } diff --git a/src/stylesheets/components/note/_pad.scss b/src/stylesheets/components/note/_pad.scss index b6d70d02b8..f5df24fe9b 100644 --- a/src/stylesheets/components/note/_pad.scss +++ b/src/stylesheets/components/note/_pad.scss @@ -7,4 +7,8 @@ display: flex; flex-direction: column; contain: strict; + + .side-by-side & { + padding-left: $space-min; + } } diff --git a/src/stylesheets/themes/_dark.scss b/src/stylesheets/themes/_dark.scss index f85065ab88..6fefb5c744 100644 --- a/src/stylesheets/themes/_dark.scss +++ b/src/stylesheets/themes/_dark.scss @@ -260,6 +260,8 @@ $icon-spin-color: var(--brand-primary); $icon-warning: var(--red-64); +$icon-esper-bg: var(--shade-0); + // Project $project-darken-color: var(--shade-0-20); $project-heading-color: var(--shade-8); diff --git a/src/stylesheets/themes/_light.scss b/src/stylesheets/themes/_light.scss index 4886994007..432bafe54b 100644 --- a/src/stylesheets/themes/_light.scss +++ b/src/stylesheets/themes/_light.scss @@ -90,6 +90,7 @@ $shade-10: #000; // Black #{--shade-10-24}: rgba($shade-10, 0.24); #{--shade-10-60}: rgba($shade-10, 0.6); #{--shade-10-65}: rgba($shade-10, 0.65); + #{--shade-10-14}: rgba($shade-10, 0.14); } @@ -254,6 +255,8 @@ $icon-spin-color: var(--brand-primary); $icon-warning: var(--red); +$icon-esper-bg: var(--shade-10-14); + // Project $project-darken-color: var(--shade-10-10); $project-heading-color: var(--shade-8);