diff --git a/lib/app-layout/style.scss b/lib/app-layout/style.scss index f7eb175d4..f89bb2583 100644 --- a/lib/app-layout/style.scss +++ b/lib/app-layout/style.scss @@ -53,19 +53,6 @@ } } - // Fade content when toolbars are visible - &.is-showing-note-info { - opacity: $fade-alpha; - transition: $anim; - pointer-events: none; - - @media only screen and (max-width: $single-column) { - .app-layout__note-column { - opacity: $fade-alpha; - } - } - } - &.is-note-open { .app-layout__source-column { transition: opacity 0.2s ease-in-out; diff --git a/lib/app.tsx b/lib/app.tsx index c64e4beaf..0109b3fe5 100644 --- a/lib/app.tsx +++ b/lib/app.tsx @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import 'focus-visible/dist/focus-visible.js'; import NoteInfo from './note-info'; +import NoteActions from './note-actions'; import NavigationBar from './navigation-bar'; import AppLayout from './app-layout'; import DevBadge from './components/dev-badge'; @@ -38,6 +39,7 @@ type StateProps = { showAlternateLoginPrompt: boolean; showEmailVerification: boolean; showNavigation: boolean; + showNoteActions: boolean; showNoteInfo: boolean; showRevisions: boolean; theme: 'light' | 'dark'; @@ -182,6 +184,7 @@ class AppComponent extends Component { showAlternateLoginPrompt, showEmailVerification, showNavigation, + showNoteActions, showNoteInfo, showRevisions, theme, @@ -193,7 +196,6 @@ class AppComponent extends Component { }); const mainClasses = classNames('simplenote-app', { - 'note-info-open': showNoteInfo, 'navigation-open': showNavigation, 'is-electron': isElectron, 'is-macos': isElectron && isMac, @@ -212,6 +214,7 @@ class AppComponent extends Component { {showNavigation && } {showNoteInfo && } + {showNoteActions && } @@ -228,6 +231,7 @@ const mapStateToProps: S.MapState = (state) => ({ showAlternateLoginPrompt: state.ui.showAlternateLoginPrompt, showEmailVerification: selectors.shouldShowEmailVerification(state), showNavigation: state.ui.showNavigation, + showNoteActions: state.ui.showNoteActions, showNoteInfo: state.ui.showNoteInfo, showRevisions: state.ui.showRevisions, theme: selectors.getTheme(state), diff --git a/lib/components/clipboard-button/index.tsx b/lib/components/clipboard-button/index.tsx index 88724d6e1..4ff5126cd 100644 --- a/lib/components/clipboard-button/index.tsx +++ b/lib/components/clipboard-button/index.tsx @@ -1,8 +1,13 @@ import React, { useEffect, useRef, useState } from 'react'; -import PropTypes from 'prop-types'; import Clipboard from 'clipboard'; -function ClipboardButton({ text }) { +type Props = { + container?: React.RefObject; + linkText: string; + text: string; +}; + +function ClipboardButton({ container, linkText, text }: Props) { const buttonRef = useRef(); const textCallback = useRef(); const successCallback = useRef(); @@ -30,6 +35,7 @@ function ClipboardButton({ text }) { // create the `Clipboard` object on mount and destroy on unmount useEffect(() => { const clipboard = new Clipboard(buttonRef.current, { + container: container?.current || undefined, text: () => textCallback.current(), }); clipboard.on('success', () => successCallback.current()); @@ -39,14 +45,9 @@ function ClipboardButton({ text }) { return ( ); } -ClipboardButton.propTypes = { - disbaled: PropTypes.bool, - text: PropTypes.string, -}; - export default ClipboardButton; diff --git a/lib/dialogs/share/index.tsx b/lib/dialogs/share/index.tsx index badb19099..8768c1e23 100644 --- a/lib/dialogs/share/index.tsx +++ b/lib/dialogs/share/index.tsx @@ -1,21 +1,16 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; -import { includes, isEmpty } from 'lodash'; import MD5 from 'md5.js'; -import ClipboardButton from '../../components/clipboard-button'; import isEmailTag from '../../utils/is-email-tag'; import Dialog from '../../dialog'; import TabPanels from '../../components/tab-panels'; import PanelTitle from '../../components/panel-title'; -import ToggleControl from '../../controls/toggle'; import actions from '../../state/actions'; import * as S from '../../state'; import * as T from '../../types'; -const shareTabs = ['collaborate', 'publish']; - type StateProps = { settings: S.State['settings']; noteId: T.EntityId; @@ -26,20 +21,12 @@ type DispatchProps = { addCollaborator: (noteId: T.EntityId, collaborator: T.TagName) => any; closeDialog: () => any; editNote: (noteId: T.EntityId, changes: Partial) => any; - publishNote: (noteId: T.EntityId, shouldPublish: boolean) => any; removeCollaborator: (noteId: T.EntityId, collaborator: T.TagName) => any; }; type Props = StateProps & DispatchProps; export class ShareDialog extends Component { - onTogglePublished = (shouldPublish: boolean) => { - this.props.publishNote(this.props.noteId, shouldPublish); - }; - - getPublishURL = (url) => - isEmpty(url) ? undefined : `http://simp.ly/p/${url}`; - onAddCollaborator = (event) => { const { noteId } = this.props; const collaborator = this.collaboratorElement.value.trim(); @@ -80,15 +67,12 @@ export class ShareDialog extends Component { }; render() { - const { closeDialog, note } = this.props; - const data = note || {}; - const isPublished = includes(data.systemTags, 'published'); - const publishURL = this.getPublishURL(data.publishURL); + const { closeDialog } = this.props; return ( - - -
+ +
+

Add an email address of another Simplenote user to share a note. @@ -147,54 +131,7 @@ export class ShareDialog extends Component {

- -
-
-
- -
-

- Ready to share your note with the world? Anyone with the public - link will be able to view the latest version. -

-
- {isPublished && ( -
- Public link -
-
- (this.publishUrlElement = e)} - className="settings-item-text-input transparent-input" - placeholder={ - isPublished ? 'Publishing note…' : 'Note not published' - } - value={publishURL} - readOnly={true} - spellCheck={false} - /> -
- {publishURL && } -
-
-
- {publishURL &&

Note published!

} -
- )} -
- +
); } diff --git a/lib/icons/ellipsis-outline.tsx b/lib/icons/ellipsis-outline.tsx new file mode 100644 index 000000000..fc4d242dd --- /dev/null +++ b/lib/icons/ellipsis-outline.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +export default function EllipsisOutlineIcon() { + return ( + + + + + ); +} diff --git a/lib/navigation-bar/style.scss b/lib/navigation-bar/style.scss index 8100ed1ce..c0f6ede71 100644 --- a/lib/navigation-bar/style.scss +++ b/lib/navigation-bar/style.scss @@ -13,7 +13,7 @@ } .is-macos .navigation-bar { - padding-top: 70px; + padding-top: $toolbar-height + 26px; } .navigation-bar__folders { diff --git a/lib/note-actions/index.tsx b/lib/note-actions/index.tsx new file mode 100644 index 000000000..3a9ee4461 --- /dev/null +++ b/lib/note-actions/index.tsx @@ -0,0 +1,248 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import classNames from 'classnames'; +import FocusTrap from 'focus-trap-react'; +import { includes, isEmpty } from 'lodash'; + +import ClipboardButton from '../components/clipboard-button'; +import CheckboxControl from '../controls/checkbox'; +import getNoteTitleAndPreview from '../utils/note-utils'; +import Spinner from '../components/spinner'; + +import actions from '../state/actions'; + +import * as S from '../state'; +import * as T from '../types'; + +type StateProps = { + hasRevisions: boolean; + isMarkdown: boolean; + isPinned: boolean; + noteId: T.EntityId; + note: T.Note; +}; + +type DispatchProps = { + markdownNote: (noteId: T.EntityId, shouldEnableMarkdown: boolean) => any; + onFocusTrapDeactivate: () => any; + pinNote: (noteId: T.EntityId, shouldPin: boolean) => any; + publishNote: (noteId: T.EntityId, shouldPublish: boolean) => any; + shareNote: () => any; + toggleRevisions: () => any; + trashNote: () => any; +}; + +type Props = StateProps & DispatchProps; + +export class NoteActions extends Component { + static displayName = 'NoteActions'; + isMounted = false; + containerRef = React.createRef(); + + componentDidMount() { + this.isMounted = true; + } + + componentWillUnmount() { + this.isMounted = false; + } + + handleFocusTrapDeactivate = () => { + const { onFocusTrapDeactivate } = this.props; + + if (this.isMounted) { + // Bit of a delay so that clicking the note actios toolbar will toggle the view properly. + setTimeout(() => onFocusTrapDeactivate(), 200); + } + }; + + getNoteLink = (note: T.Note, noteId: T.EntityId) => { + const { title } = getNoteTitleAndPreview(note); + return `[${title}](simplenote://note/${noteId})`; + }; + + getPublishURL = (url: string | undefined) => { + return isEmpty(url) ? null : `http://simp.ly/p/${url}`; + }; + + render() { + const { hasRevisions, isMarkdown, isPinned, noteId, note } = this.props; + const isPublished = includes(note.systemTags, 'published'); + const publishURL = this.getPublishURL(note.publishURL); + const noteLink = this.getNoteLink(note, noteId); + + return ( + +
+
+ + + + +
+ +
+ + {hasRevisions && ( +
+ +
+ )} + {hasRevisions || ( +
+ + History (unavailable) + +
+ )} +
+
+ +
+ {isPublished && publishURL ? ( + + ) : isPublished && !publishURL ? ( + <> + Copy Link + + + ) : ( + Copy Link + )} +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ ); + } + + pinNote = (shouldPin: boolean) => + this.props.pinNote(this.props.noteId, shouldPin); + + publishNote = (shouldPublish: boolean) => + this.props.publishNote(this.props.noteId, shouldPublish); + + markdownNote = (shouldEnableMarkdown: boolean) => + this.props.markdownNote(this.props.noteId, shouldEnableMarkdown); +} + +const mapStateToProps: S.MapState = ({ + data, + ui: { openedNote }, +}) => { + const note = data.notes.get(openedNote); + + return { + noteId: openedNote, + note: note, + hasRevisions: !!data.noteRevisions.get(openedNote)?.size, + isMarkdown: note?.systemTags.includes('markdown'), + isPinned: note?.systemTags.includes('pinned'), + }; +}; + +const mapDispatchToProps: S.MapDispatch = { + markdownNote: actions.data.markdownNote, + onFocusTrapDeactivate: actions.ui.closeNoteActions, + pinNote: actions.data.pinNote, + publishNote: actions.data.publishNote, + shareNote: () => actions.ui.showDialog('SHARE'), + toggleRevisions: actions.ui.toggleRevisions, + trashNote: actions.ui.trashOpenNote, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(NoteActions); diff --git a/lib/note-actions/style.scss b/lib/note-actions/style.scss new file mode 100644 index 000000000..67f04d7f0 --- /dev/null +++ b/lib/note-actions/style.scss @@ -0,0 +1,134 @@ +$checkbox-sprite: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0OCA0OCI+PHJlY3QgeD0iMCIgZmlsbD0ibm9uZSIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ii8+PHBhdGggZmlsbD0iIzY0Njk3MCIgZD0iTTE5IDVMMTkgNXYxNEg1VjVIMTlNMTkgM0g1QzMuODk1IDMgMyAzLjg5NSAzIDV2MTRjMCAxLjEwNSAwLjg5NSAyIDIgMmgxNGMxLjEwNSAwIDItMC44OTUgMi0yVjVDMjEgMy44OTUgMjAuMTA1IDMgMTkgM3pNNDMgM0gyOWMtMS4xMDUgMC0yIDAuODk1LTIgMnYxNGMwIDEuMTA1IDAuODk1IDIgMiAyaDE0YzEuMTA1IDAgMi0wLjg5NSAyLTJWNUM0NSAzLjg5NSA0NC4xMDUgMyA0MyAzek0zNC4xNCAxNi44NWwtNC4zOC00LjM3IDEuNDItMS40MiAzIDMgNi42My02LjYzIDEuNDEgMS40MkwzNC4xNCAxNi44NXoiLz48cGF0aCBmaWxsPSIjOGM4Zjk0IiBkPSJNMTkgMjlMMTkgMjl2MTRINVYyOUgxOU0xOSAyN0g1Yy0xLjEwNSAwLTIgMC44OTUtMiAydjE0YzAgMS4xMDUgMC44OTUgMiAyIDJoMTRjMS4xMDUgMCAyLTAuODk1IDItMlYyOUMyMSAyNy44OTUgMjAuMTA1IDI3IDE5IDI3ek00MyAyN0gyOWMtMS4xMDUgMC0yIDAuODk1LTIgMnYxNGMwIDEuMTA1IDAuODk1IDIgMiAyaDE0YzEuMTA1IDAgMi0wLjg5NSAyLTJWMjlDNDUgMjcuODk1IDQ0LjEwNSAyNyA0MyAyN3pNMzQuMTQgNDAuODVsLTQuMzgtNC4zNyAxLjQyLTEuNDIgMyAzIDYuNjMtNi42MyAxLjQxIDEuNDJMMzQuMTQgNDAuODV6Ii8+PC9zdmc+'; + +.is-macos .note-actions { + top: $toolbar-height + 16px + 26px; +} + +.note-actions { + width: 200px; + position: absolute; + right: 16px; + top: $toolbar-height + 16px; + border: 1px solid; + border-radius: 6px; + box-shadow: 0 3px 6px 0 rgba($studio-black, 0.2); + display: flex; + flex-direction: column; + overflow: auto; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; + + .note-actions-panel { + flex: none; + border-bottom: 1px solid; + padding: 4px 0; + + &:last-child { + border: none; + } + } + + .note-actions-item { + display: flex; + align-items: center; + padding: 5px 16px; + height: 28px; + cursor: pointer; + + button { + padding: 0; + font-weight: normal; + color: inherit; + width: 100%; + text-align: left; + } + + &:hover { + background-color: $studio-gray-0; + } + } + + .note-actions-item-disabled { + color: $studio-gray-5; + &:hover { + background-color: transparent; + } + + .note-actions-disabled { + flex: 1 1 auto; + } + } + + .note-actions-item-text { + flex: 1 1 auto; + } + + .note-actions-internal-link .note-actions-name { + display: inline-block; + padding: 5px 10px; + } + + .note-actions-trash { + color: $studio-red-50; + } + + .note-actions-item-control .checkbox-control { + bottom: 3px; + + input { + cursor: pointer; + } + } + + .note-actions-item-control .checkbox-control .checkbox-control-base, + .note-actions-item-control .checkbox-control .checkbox-control-checked { + background-image: url($checkbox-sprite); + background-repeat: no-repeat; + background-size: 36px 36px; + border-radius: 0; + border: 0; + fill: $studio-gray-50; + } + .note-actions-item-control .checkbox-control .checkbox-control-base { + background-position: 0 0; + } + .note-actions-item-control .checkbox-control .checkbox-control-checked { + background-color: transparent; + background-position: -18px 0; + } + + .spinner__circle { + color: $studio-gray-30; + } +} + +.theme-dark { + .spinner__circle { + color: $studio-gray-50; + } + .note-actions.theme-color-bg { + background-color: $studio-gray-90; + } + + .note-actions-trash { + color: $studio-red-30; + } + + .note-actions-item-disabled { + color: $studio-gray-50; + } + + .note-actions-item:hover { + background-color: $studio-gray-80; + } + + .note-actions-item-control .checkbox-control .checkbox-control-base, + .note-actions-item-control .checkbox-control .checkbox-control-checked { + background-position: 0 -18px; + fill: $studio-gray-30; + } + + .note-actions-item-control .checkbox-control .checkbox-control-checked { + background-position: -18px -18px; + } +} diff --git a/lib/note-info/index.tsx b/lib/note-info/index.tsx index eaea8519c..019217eb0 100644 --- a/lib/note-info/index.tsx +++ b/lib/note-info/index.tsx @@ -1,33 +1,25 @@ import React, { Component } from 'react'; +import Modal from 'react-modal'; import { connect } from 'react-redux'; -import onClickOutside from 'react-onclickoutside'; -import { includes, isEmpty } from 'lodash'; -import format from 'date-fns/format'; -import ClipboardButton from '../components/clipboard-button'; import LastSyncTime from '../components/last-sync-time'; -import PanelTitle from '../components/panel-title'; -import ToggleControl from '../controls/toggle'; -import CrossIcon from '../icons/cross'; -import getNoteTitleAndPreview from '../utils/note-utils'; +import SmallCrossIcon from '../icons/cross-small'; import References from './references'; import actions from '../state/actions'; +import { getTheme } from '../state/selectors'; import * as S from '../state'; import * as T from '../types'; type StateProps = { - isMarkdown: boolean; - isPinned: boolean; noteId: T.EntityId; note: T.Note; + theme: string; }; type DispatchProps = { - markdownNote: (noteId: T.EntityId, shouldEnableMarkdown: boolean) => any; - onOutsideClick: () => any; - pinNote: (noteId: T.EntityId, shouldPin: boolean) => any; + onModalClose: () => any; }; type Props = StateProps & DispatchProps; @@ -35,45 +27,48 @@ type Props = StateProps & DispatchProps; export class NoteInfo extends Component { static displayName = 'NoteInfo'; - handleClickOutside = this.props.onOutsideClick; - - getNoteLink = (note: T.Note, noteId: T.EntityId) => { - const { title } = getNoteTitleAndPreview(note); - return `[${title}](simplenote://note/${noteId})`; - }; - - getPublishURL = (url: string | undefined) => { - return isEmpty(url) ? null : `http://simp.ly/p/${url}`; - }; - render() { - const { isMarkdown, isPinned, noteId, note } = this.props; - const isPublished = includes(note.systemTags, 'published'); + const { noteId, note, onModalClose, theme } = this.props; const creationDate = note.creationDate * 1000; const modificationDate = note.modificationDate ? note.modificationDate * 1000 : null; - const publishURL = this.getPublishURL(note.publishURL); - const noteLink = this.getNoteLink(note, noteId); return ( -
-
-
- Info + +
+
+

Document

+

+ + Last synced + + + + +

{modificationDate && (

- Modified -
+ Modified

- Created -
+ Created

- Last sync -
+ Words - - -
-

-

- - - {wordCount(note.content)} words + {wordCount(note.content)}

- - {characterCount(note.content)} characters - - -

-
-
- -
-
- -
- {isPublished && ( -
- - Public link -
-

- {publishURL} -

- -
-
-
- )} -
- - Internal link -
-

{`simplenote://note/${noteId}`}

- -
-
+

-
+ ); } - - pinNote = (shouldPin: boolean) => - this.props.pinNote(this.props.noteId, shouldPin); - - markdownNote = (shouldEnableMarkdown: boolean) => - this.props.markdownNote(this.props.noteId, shouldEnableMarkdown); } // https://github.com/RadLikeWhoa/Countable @@ -227,27 +144,18 @@ function characterCount(content: string) { ); } -const mapStateToProps: S.MapState = ({ - data, - ui: { openedNote }, -}) => { - const note = data.notes.get(openedNote); +const mapStateToProps: S.MapState = (state) => { + const note = state.data.notes.get(state.ui.openedNote); return { - noteId: openedNote, + noteId: state.ui.openedNote, note: note, - isMarkdown: note?.systemTags.includes('markdown'), - isPinned: note?.systemTags.includes('pinned'), + theme: getTheme(state), }; }; const mapDispatchToProps: S.MapDispatch = { - markdownNote: actions.data.markdownNote, - onOutsideClick: actions.ui.toggleNoteInfo, - pinNote: actions.data.pinNote, + onModalClose: actions.ui.toggleNoteInfo, }; -export default connect( - mapStateToProps, - mapDispatchToProps -)(onClickOutside(NoteInfo)); +export default connect(mapStateToProps, mapDispatchToProps)(NoteInfo); diff --git a/lib/note-info/references.tsx b/lib/note-info/references.tsx index e6e968a55..60a7fbc2a 100644 --- a/lib/note-info/references.tsx +++ b/lib/note-info/references.tsx @@ -20,13 +20,13 @@ export const References: FunctionComponent = ({ references }) => { } return ( -
- - References +
+
+
Referenced In
{references.map((noteId) => ( ))} - +
); }; diff --git a/lib/note-info/style.scss b/lib/note-info/style.scss index 712ca2214..4e84364b0 100644 --- a/lib/note-info/style.scss +++ b/lib/note-info/style.scss @@ -1,21 +1,14 @@ .note-info { display: flex; flex-direction: column; - position: absolute; - top: 0; - left: 100%; - width: $note-info-width; - height: 100%; - border-left: 1px solid; - overflow: auto; - overflow-x: hidden; + width: 360px; + user-select: text; -webkit-overflow-scrolling: touch; - background: white; .note-info-panel { - flex: none; - padding: 20px; + flex: 0 0 auto; border-bottom: 1px solid; + padding-bottom: 8px; &:last-child { border: none; @@ -24,12 +17,18 @@ .note-info-header { display: flex; + border-bottom: 1px solid; + padding: 12px 8px 12px 16px; + margin-bottom: 8px; flex-direction: row; align-items: center; + height: 56px; h2 { flex-grow: 1; margin-bottom: 0; + text-transform: none; + font-size: 16px; } button { @@ -39,69 +38,75 @@ } .note-info-item { - display: flex; - align-items: center; - margin-bottom: 2.25em; + margin: 0; + padding: 4px 0; + height: 28px; - &:last-child { - margin: 0; + .note-info-detail { + float: right; + padding-right: 16px; } } .note-info-item-text { flex: 1 1 auto; + padding-left: 16px; } - .note-info-item-control { - flex: none; - width: 44px; - text-align: right; - } - - .note-info-detail { - a { - color: $studio-simplenote-blue-50; + .note-references { + max-height: 200px; + overflow-y: auto; + margin-bottom: 35px; + padding-bottom: 0; + + div.note-info-name { + height: 28px; + padding-left: 16px; + line-height: 28px; + margin-top: 8px; } - } - - .note-info-copy { - display: flex; - flex-direction: row; - .note-info-link-text { - border: none; - overflow: hidden; - padding: 0; - text-overflow: ellipsis; - white-space: nowrap; - } - - button { - flex: 1 0 auto; + .note-info-item { + height: auto; + padding-bottom: 0; } } .reference-link { display: block; - padding: 5px; text-align: left; text-decoration: none; width: 100%; + height: 48px; + padding-left: 16px; + padding-right: 16px; span { display: block; margin: 0; + font-size: 13px; + } + + &:hover { + background-color: $studio-gray-0; } .reference-title { - font-style: italic; + font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + font-size: 14px; } } } -.theme-dark .note-info-detail a { - color: $studio-simplenote-blue-30; +.theme-dark { + .note-info { + .reference-link { + &:hover { + background-color: $studio-gray-80; + } + } + } } diff --git a/lib/note-toolbar/index.tsx b/lib/note-toolbar/index.tsx index b889d6269..80b3a14b2 100644 --- a/lib/note-toolbar/index.tsx +++ b/lib/note-toolbar/index.tsx @@ -4,15 +4,13 @@ import { CmdOrCtrl } from '../utils/platform'; import BackIcon from '../icons/back'; import ChecklistIcon from '../icons/check-list'; +import EllipsisOutlineIcon from '../icons/ellipsis-outline'; import IconButton from '../icon-button'; import InfoIcon from '../icons/info'; import NewNoteIcon from '../icons/new-note'; import PreviewIcon from '../icons/preview'; import PreviewStopIcon from '../icons/preview-stop'; -import RevisionsIcon from '../icons/revisions'; -import ShareIcon from '../icons/share'; import SidebarIcon from '../icons/sidebar'; -import TrashIcon from '../icons/trash'; import actions from '../state/actions'; import * as S from '../state'; @@ -20,7 +18,6 @@ import * as T from '../types'; type StateProps = { editMode: boolean; - hasRevisions: boolean; isOffline: boolean; markdownEnabled: boolean; note: T.Note | null; @@ -30,13 +27,11 @@ type DispatchProps = { deleteNoteForever: () => any; newNote: () => any; restoreNote: () => any; - shareNote: () => any; toggleEditMode: () => any; toggleFocusMode: () => any; + toggleNoteActions: () => any; toggleNoteInfo: () => any; toggleNoteList: () => any; - toggleRevisions: () => any; - trashNote: () => any; }; type Props = DispatchProps & StateProps & React.HTMLProps; @@ -60,10 +55,10 @@ export class NoteToolbar extends Component { const { editMode, newNote, - hasRevisions, isOffline, markdownEnabled, note, + toggleNoteActions, toggleNoteInfo, } = this.props; @@ -96,13 +91,6 @@ export class NoteToolbar extends Component {
{isOffline &&
OFFLINE
}
-
- } - onClick={() => window.dispatchEvent(new Event('toggleChecklist'))} - title={`Insert Checklist • ${CmdOrCtrl}+Shift+C`} - /> -
{markdownEnabled && (
{ )}
} - onClick={this.props.toggleRevisions} - title={hasRevisions ? 'History' : 'History (unavailable)'} - /> -
-
- } - onClick={this.props.shareNote} - title="Share" + icon={} + onClick={() => window.dispatchEvent(new Event('toggleChecklist'))} + title={`Insert Checklist • ${CmdOrCtrl}+Shift+C`} />
} - onClick={this.props.trashNote} - title="Trash" + icon={} + onClick={toggleNoteInfo} + title="Info" />
} - onClick={toggleNoteInfo} - title="Info" + icon={} + onClick={toggleNoteActions} + title="Actions" />
@@ -193,7 +173,6 @@ const mapStateToProps: S.MapState = ({ return { editMode, - hasRevisions: !!data.noteRevisions.get(openedNote)?.size, isOffline: connectionStatus === 'offline', markdownEnabled: note?.systemTags.includes('markdown') || false, note, @@ -204,13 +183,11 @@ const mapDispatchToProps: S.MapDispatch = { deleteNoteForever: actions.ui.deleteOpenNoteForever, newNote: actions.ui.createNote, restoreNote: actions.ui.restoreOpenNote, - shareNote: () => actions.ui.showDialog('SHARE'), toggleEditMode: actions.ui.toggleEditMode, toggleFocusMode: actions.settings.toggleFocusMode, + toggleNoteActions: actions.ui.toggleNoteActions, toggleNoteInfo: actions.ui.toggleNoteInfo, toggleNoteList: actions.ui.toggleNoteList, - toggleRevisions: actions.ui.toggleRevisions, - trashNote: actions.ui.trashOpenNote, }; export default connect(mapStateToProps, mapDispatchToProps)(NoteToolbar); diff --git a/lib/state/action-types.ts b/lib/state/action-types.ts index aadf2ed2b..87952e9b1 100644 --- a/lib/state/action-types.ts +++ b/lib/state/action-types.ts @@ -137,6 +137,8 @@ export type ToggleFocusMode = Action<'TOGGLE_FOCUS_MODE'>; export type ToggleKeyboardShortcuts = Action<'KEYBOARD_SHORTCUTS_TOGGLE'>; export type ToggleNavigation = Action<'NAVIGATION_TOGGLE'>; export type ToggleNoteList = Action<'NOTE_LIST_TOGGLE'>; +export type ToggleNoteActions = Action<'NOTE_ACTIONS_TOGGLE'>; +export type CloseNoteActions = Action<'NOTE_ACTIONS_CLOSE'>; export type ToggleNoteInfo = Action<'NOTE_INFO_TOGGLE'>; export type ToggleRevisions = Action<'REVISIONS_TOGGLE'>; export type ToggleSimperiumConnectionStatus = Action< @@ -337,6 +339,7 @@ export type ActionType = | AddNoteTag | ChangeConnectionStatus | CloseNote + | CloseNoteActions | CloseDialog | CloseRevision | CloseWindow @@ -423,6 +426,7 @@ export type ActionType = | ToggleKeyboardShortcuts | ToggleNavigation | ToggleNoteList + | ToggleNoteActions | ToggleNoteInfo | ToggleRevisions | ToggleSimperiumConnectionStatus diff --git a/lib/state/ui/actions.ts b/lib/state/ui/actions.ts index 60be5f875..b9d19538b 100644 --- a/lib/state/ui/actions.ts +++ b/lib/state/ui/actions.ts @@ -9,6 +9,10 @@ export const closeNote: A.ActionCreator = () => ({ type: 'CLOSE_NOTE', }); +export const closeNoteActions: A.ActionCreator = () => ({ + type: 'NOTE_ACTIONS_CLOSE', +}); + export const closeWindow: A.ActionCreator = () => ({ type: 'CLOSE_WINDOW', }); @@ -147,6 +151,10 @@ export const toggleNoteList: A.ActionCreator = () => ({ type: 'NOTE_LIST_TOGGLE', }); +export const toggleNoteActions: A.ActionCreator = () => ({ + type: 'NOTE_ACTIONS_TOGGLE', +}); + export const toggleNoteInfo: A.ActionCreator = () => ({ type: 'NOTE_INFO_TOGGLE', }); diff --git a/lib/state/ui/reducer.ts b/lib/state/ui/reducer.ts index fed92cc97..fa3174c79 100644 --- a/lib/state/ui/reducer.ts +++ b/lib/state/ui/reducer.ts @@ -322,12 +322,32 @@ const simperiumConnected: A.Reducer = (state = false, action) => ? action.simperiumConnected : state; +const showNoteActions: A.Reducer = (state = false, action) => { + switch (action.type) { + case 'NOTE_ACTIONS_TOGGLE': + return !state; + + case 'NAVIGATION_TOGGLE': + case 'NOTE_ACTIONS_CLOSE': + case 'NOTE_INFO_TOGGLE': + case 'REVISIONS_TOGGLE': + case 'SELECT_NOTE': + case 'SHOW_DIALOG': + case 'TRASH_NOTE': + return false; + + default: + return state; + } +}; + const showNoteInfo: A.Reducer = (state = false, action) => { switch (action.type) { case 'NOTE_INFO_TOGGLE': return !state; case 'NAVIGATION_TOGGLE': + case 'NOTE_ACTIONS_TOGGLE': case 'SELECT_NOTE': return false; @@ -398,6 +418,7 @@ export default combineReducers({ selectedSearchMatchIndex, showAlternateLoginPrompt, showNavigation, + showNoteActions, showNoteInfo, showNoteList, showRevisions, diff --git a/scss/_components.scss b/scss/_components.scss index 8eb88a520..ea63476e4 100644 --- a/scss/_components.scss +++ b/scss/_components.scss @@ -31,6 +31,7 @@ @import 'icons/style'; @import 'navigation-bar/style'; @import 'navigation-bar/item/style'; +@import 'note-actions/style'; @import 'note-detail/style'; @import 'note-editor/style'; @import 'note-info/style'; diff --git a/scss/_general.scss b/scss/_general.scss index bd2c58ced..1a7a6327e 100644 --- a/scss/_general.scss +++ b/scss/_general.scss @@ -60,12 +60,6 @@ optgroup { position: relative; width: 100%; height: 100%; - transform: translateX(0); - transition: transform 200ms ease-in-out; - - &.note-info-open { - transform: translateX(-$note-info-width); - } } .is-macos { diff --git a/scss/_variables.scss b/scss/_variables.scss index b96e59103..6c6026f8d 100644 --- a/scss/_variables.scss +++ b/scss/_variables.scss @@ -16,7 +16,6 @@ $focus-outline: 4px auto $studio-simplenote-blue-5; // Global Measurements $navigation-bar-width: 260px; -$note-info-width: 320px; $note-list-width: 380px; $max-content-width: 650px; $toolbar-height: 44px;