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);