From 4ecbf6acd3962c6e55fa3cb7525d0baa1731e84e Mon Sep 17 00:00:00 2001 From: Luka Trovic Date: Wed, 22 Jun 2022 10:58:19 +0200 Subject: [PATCH 1/3] feat: move picker, insert image into the overflow menu Signed-off-by: Luka Trovic --- src/components/Menu/MenuBar.vue | 2 ++ src/components/Menu/entries.js | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Menu/MenuBar.vue b/src/components/Menu/MenuBar.vue index e2b66036184..b33195c4ae6 100644 --- a/src/components/Menu/MenuBar.vue +++ b/src/components/Menu/MenuBar.vue @@ -261,6 +261,8 @@ export default { @media (max-width: 660px) { .text-menubar__entries { margin-left: 0; + position: fixed; + top: calc(100vh - 100px); } } } diff --git a/src/components/Menu/entries.js b/src/components/Menu/entries.js index 9af3dd04a71..bf9aa63417e 100644 --- a/src/components/Menu/entries.js +++ b/src/components/Menu/entries.js @@ -301,14 +301,14 @@ export default [ action: (command, emojiObject = {}) => { return command.emoji(emojiObject) }, - priority: 4, + priority: 19, }, { key: 'insert-image', label: t('text', 'Insert image'), icon: Images, component: ActionImageUpload, - priority: 2, + priority: 18, }, { key: 'formatting-help', From 44eba0f233f4b54078daf70fbc921fa0b65054be Mon Sep 17 00:00:00 2001 From: Luka Trovic Date: Wed, 29 Jun 2022 19:20:24 +0200 Subject: [PATCH 2/3] feat: split menu entries into mobileEntries.js Signed-off-by: Luka Trovic --- src/components/Menu/MenuBar.vue | 42 +++- src/components/Menu/mobileEntries.js | 353 +++++++++++++++++++++++++++ 2 files changed, 391 insertions(+), 4 deletions(-) create mode 100644 src/components/Menu/mobileEntries.js diff --git a/src/components/Menu/MenuBar.vue b/src/components/Menu/MenuBar.vue index b33195c4ae6..4ca6f47fcc6 100644 --- a/src/components/Menu/MenuBar.vue +++ b/src/components/Menu/MenuBar.vue @@ -33,10 +33,26 @@
- + +
@@ -50,6 +66,7 @@ import debounce from 'debounce' import HelpModal from '../HelpModal.vue' import actionsFullEntries from './entries.js' +import mobileEntries from './mobileEntries.js' import ActionEntry from './ActionEntry.js' import { DotsHorizontal } from '../icons.js' import { @@ -57,6 +74,7 @@ import { useIsRichEditorMixin, useIsRichWorkspaceMixin, } from '../EditorWrapper.provider.js' +import isMobile from '../../mixins/isMobile.js' export default { name: 'MenuBar', @@ -65,6 +83,7 @@ export default { useEditorMixin, useIsRichEditorMixin, useIsRichWorkspaceMixin, + isMobile, ], props: { autohide: { @@ -128,6 +147,12 @@ export default { return priority !== undefined && priority > this.iconsLimit }) }, + topEntries() { + return [...mobileEntries].filter(({ position }) => position === 'top') + }, + bottomEntries() { + return [...mobileEntries].filter(({ position }) => position === 'bottom') + }, remainAction() { return { key: 'remain', @@ -261,8 +286,17 @@ export default { @media (max-width: 660px) { .text-menubar__entries { margin-left: 0; + } + + .top { + display: flex; + } + + .bottom { + display: flex; position: fixed; top: calc(100vh - 100px); + left: 0; } } } diff --git a/src/components/Menu/mobileEntries.js b/src/components/Menu/mobileEntries.js new file mode 100644 index 00000000000..1a2a28a4123 --- /dev/null +++ b/src/components/Menu/mobileEntries.js @@ -0,0 +1,353 @@ +/* + * @copyright Copyright (c) 2019 Julius Härtl + * + * @author Julius Härtl + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +import { + Undo, + Redo, + FormatBold, + FormatItalic, + FormatUnderline, + FormatStrikethrough, + FormatHeader1, + FormatHeader2, + FormatHeader3, + FormatHeader4, + FormatHeader5, + FormatHeader6, + FormatListNumbered, + FormatListBulleted, + FormatListCheckbox, + FormatQuote, + Info, + Positive, + Warn, + Danger, + CodeTags, + Table, + Emoticon, + Help, + Images, +} from '../icons.js' +import EmojiPickerAction from './EmojiPickerAction.vue' +import ActionImageUpload from './ActionImageUpload.vue' + +export default [ + { + key: 'undo', + label: t('text', 'Undo'), + keyChar: 'z', + keyModifiers: ['ctrl'], + icon: Undo, + action: (command) => command.undo(), + priority: 5, + position: 'top', + }, + { + key: 'redo', + label: t('text', 'Redo'), + keyChar: 'y', + keyModifiers: ['ctrl'], + icon: Redo, + action: (command) => command.redo(), + priority: 11, + position: 'top', + }, + { + key: 'style', + label: t('text', 'Style'), + keyModifiers: ['ctrl', 'shift'], + visible: false, + icon: FormatBold, + isActive: 'styling', + children: [ + { + key: 'bold', + label: t('text', 'Bold'), + keyChar: 'b', + keyModifiers: ['ctrl'], + icon: FormatBold, + isActive: 'strong', + action: (command) => { + return command.toggleBold() + }, + priority: 6, + position: 'top', + }, + { + key: 'italic', + label: t('text', 'Italic'), + keyChar: 'i', + keyModifiers: ['ctrl'], + icon: FormatItalic, + isActive: 'em', + action: (command) => { + return command.toggleItalic() + }, + priority: 7, + position: 'top', + }, + { + key: 'underline', + label: t('text', 'Underline'), + keyChar: 'u', + keyModifiers: ['ctrl'], + icon: FormatUnderline, + isActive: 'underline', + action: (command) => { + return command.toggleUnderline() + }, + priority: 14, + position: 'top', + }, + { + key: 'strikethrough', + label: t('text', 'Strikethrough'), + keyChar: 'd', + keyModifiers: ['ctrl'], + icon: FormatStrikethrough, + isActive: 'strike', + action: (command) => { + return command.toggleStrike() + }, + priority: 15, + position: 'top', + }, + ], + priority: 1, + position: 'top', + }, + { + key: 'headings', + label: t('text', 'Headings'), + keyChar: '1…6', + keyModifiers: ['ctrl', 'shift'], + visible: false, + icon: FormatHeader1, + isActive: 'heading', + children: [ + { + key: 'headings-h1', + label: t('text', 'Heading 1'), + icon: FormatHeader1, + isActive: ['heading', { level: 1 }], + action: (command) => { + return command.toggleHeading({ level: 1 }) + }, + }, + { + key: 'headings-h2', + label: t('text', 'Heading 2'), + icon: FormatHeader2, + isActive: ['heading', { level: 2 }], + action: (command) => { + return command.toggleHeading({ level: 2 }) + }, + }, + { + key: 'headings-h3', + label: t('text', 'Heading 3'), + icon: FormatHeader3, + isActive: ['heading', { level: 3 }], + action: (command) => { + return command.toggleHeading({ level: 3 }) + }, + }, + { + key: 'headings-h4', + label: t('text', 'Heading 4'), + isActive: ['heading', { level: 4 }], + icon: FormatHeader4, + action: (command) => { + return command.toggleHeading({ level: 4 }) + }, + }, + { + key: 'headings-h5', + label: t('text', 'Heading 5'), + isActive: ['heading', { level: 5 }], + icon: FormatHeader5, + action: (command) => { + return command.toggleHeading({ level: 5 }) + }, + }, + { + key: 'headings-h6', + label: t('text', 'Heading 6'), + isActive: ['heading', { level: 6 }], + icon: FormatHeader6, + action: (command) => { + return command.toggleHeading({ level: 6 }) + }, + }, + ], + priority: 1, + position: 'top', + }, + { + key: 'lists', + label: t('text', 'Lists'), + keyChar: '1…6', + keyModifiers: ['ctrl', 'shift'], + visible: false, + icon: FormatListBulleted, + isActive: ['bulletList', 'orderedList', 'taskList'], + children: [ + { + key: 'unordered-list', + label: t('text', 'Unordered list'), + icon: FormatListBulleted, + isActive: 'bulletList', + keyChar: '8', + keyModifiers: ['ctrl', 'shift'], + action: (command) => { + return command.toggleBulletList() + }, + }, + { + key: 'ordered-list', + label: t('text', 'Ordered list'), + icon: FormatListNumbered, + isActive: 'orderedList', + keyChar: '9', + keyModifiers: ['ctrl', 'shift'], + action: (command) => { + return command.toggleOrderedList() + }, + }, + { + key: 'task-list', + label: t('text', 'To-Do list'), + icon: FormatListCheckbox, + isActive: 'taskList', + action: (command) => command.toggleTaskList(), + }, + ], + priority: 1, + position: 'top', + }, + { + key: 'blocks', + label: t('text', 'Blocks'), + keyModifiers: ['ctrl', 'shift'], + visible: false, + icon: FormatQuote, + isActive: 'blocks', + children: [ + { + key: 'blockquote', + label: t('text', 'Blockquote'), + keyChar: '>', + keyModifiers: ['ctrl'], + isActive: 'blockquote', + icon: FormatQuote, + action: (command) => { + return command.toggleBlockquote() + }, + }, + { + key: 'callout-info', + label: t('text', 'Info'), + isActive: ['callout', { type: 'info' }], + icon: Info, + action: (command) => { + return command.toggleCallout({ type: 'info' }) + }, + }, + { + key: 'callout-success', + label: t('text', 'Success'), + isActive: ['callout', { type: 'success' }], + icon: Positive, + action: (command) => { + return command.toggleCallout({ type: 'success' }) + }, + }, + { + key: 'callout-warn', + label: t('text', 'Warning'), + isActive: ['callout', { type: 'warn' }], + icon: Warn, + action: (command) => { + return command.toggleCallout({ type: 'warn' }) + }, + }, + { + key: 'callout-error', + label: t('text', 'Danger'), + isActive: ['callout', { type: 'error' }], + icon: Danger, + action: (command) => { + return command.toggleCallout({ type: 'error' }) + }, + }, + { + key: 'code-block', + label: t('text', 'Code block'), + isActive: 'codeBlock', + icon: CodeTags, + action: (command) => { + return command.toggleCodeBlock() + }, + }, + ], + priority: 1, + position: 'bottom', + }, + { + key: 'table', + label: t('text', 'Table'), + isActive: 'table', + icon: Table, + action: (command) => { + return command.insertTable() + }, + priority: 16, + position: 'bottom', + }, + { + key: 'emoji-picker', + label: t('text', 'Insert emoji'), + icon: Emoticon, + component: EmojiPickerAction, + action: (command, emojiObject = {}) => { + return command.emoji(emojiObject) + }, + priority: 4, + position: 'bottom', + }, + { + key: 'insert-image', + label: t('text', 'Insert image'), + icon: Images, + component: ActionImageUpload, + priority: 2, + position: 'bottom', + }, + { + key: 'formatting-help', + label: t('text', 'Formatting help'), + icon: Help, + click: (view) => view.$emit('call:help'), + priority: 17, + position: 'bottom', + }, +] From 051bb73f8aeff5e7e8be24bb93de17a598cfa43f Mon Sep 17 00:00:00 2001 From: Luka Trovic Date: Wed, 6 Jul 2022 14:28:00 +0200 Subject: [PATCH 3/3] feat: reorganize toolbar and use icon for saved indicator Signed-off-by: Luka Trovic --- src/components/EditorWrapper.vue | 10 +- src/components/Menu/ActionEntry.scss | 6 + src/components/Menu/MenuBar.vue | 13 +- src/components/Menu/mobileEntries.js | 237 +++++++++++++-------------- 4 files changed, 132 insertions(+), 134 deletions(-) diff --git a/src/components/EditorWrapper.vue b/src/components/EditorWrapper.vue index 1ec1d0cb1a2..de517e6c3d5 100644 --- a/src/components/EditorWrapper.vue +++ b/src/components/EditorWrapper.vue @@ -54,7 +54,7 @@ :autohide="autohide" :loaded.sync="menubarLoaded">
-
+
import(/* webpackChunkName: "editor-guest" */'./GuestNameDialog.vue'), SessionList: () => import(/* webpackChunkName: "editor-collab" */'./SessionList.vue'), Lock, + ContentSave, }, directives: { Tooltip, @@ -267,6 +269,12 @@ export default { } return this.dirtyStateIndicator ? t('text', 'Saving …') : t('text', 'Saved') }, + savedColor() { + if (this.hasConnectionIssue) { + return 'var(--color-error)' + } + return this.dirtyStateIndicator ? 'var(--color-background-dark)' : 'var(--color-primary)' + }, lastSavedStatusClass() { return this.syncError && this.lastSavedString !== '' ? 'error' : '' }, diff --git a/src/components/Menu/ActionEntry.scss b/src/components/Menu/ActionEntry.scss index 643d2c45201..3a669685898 100644 --- a/src/components/Menu/ActionEntry.scss +++ b/src/components/Menu/ActionEntry.scss @@ -72,3 +72,9 @@ } } } + +@media (max-width: 660px) { + .emoji-popover { + top: calc(-55vh) !important; + } +} diff --git a/src/components/Menu/MenuBar.vue b/src/components/Menu/MenuBar.vue index 4ca6f47fcc6..0d37083e6d0 100644 --- a/src/components/Menu/MenuBar.vue +++ b/src/components/Menu/MenuBar.vue @@ -40,13 +40,13 @@ @call:help="showHelp" />