diff options
author | Ferdinand Thiessen <rpm@fthiessen.de> | 2022-07-01 10:47:52 +0300 |
---|---|---|
committer | Jonas <jonas@freesources.org> | 2022-10-05 12:33:10 +0300 |
commit | 9cf2534ae39b8cfbb01d7d1a6e7918cdf159e5b2 (patch) | |
tree | 485e9575e822aae8f54b629978417ef1059a6ef5 /src | |
parent | 879eaec610f310b3bc4e695f662eb88fe56563eb (diff) |
Feature: Show word count in RichText
Show word count as last element of the overflow menu,
which now will be always shown. The word count
is updated when the menu is openend as the
tiptap word count is not reactive.
Modified ActionList: Added a named slot for adding
elements that should always shown as the last elements.
Signed-off-by: Ferdinand Thiessen <rpm@fthiessen.de>
Diffstat (limited to 'src')
-rw-r--r-- | src/components/Menu/ActionList.vue | 5 | ||||
-rw-r--r-- | src/components/Menu/MenuBar.vue | 54 | ||||
-rw-r--r-- | src/extensions/RichText.js | 32 |
3 files changed, 54 insertions, 37 deletions
diff --git a/src/components/Menu/ActionList.vue b/src/components/Menu/ActionList.vue index 172404730..88142ae0d 100644 --- a/src/components/Menu/ActionList.vue +++ b/src/components/Menu/ActionList.vue @@ -27,9 +27,11 @@ v-bind="state" :container="menuIDSelector" :aria-label="actionEntry.label" + :force-menu="true" :title="actionEntry.label" :data-text-action-entry="actionEntry.key" - :data-text-action-active="activeKey"> + :data-text-action-active="activeKey" + @update:open="(o) => $emit('update:open', o)"> <template #icon> <component :is="icon" :key="iconKey" /> </template> @@ -39,6 +41,7 @@ :action-entry="child" v-on="$listeners" @trigged="onTrigger" /> + <slot name="lastAction" /> </NcActions> </template> diff --git a/src/components/Menu/MenuBar.vue b/src/components/Menu/MenuBar.vue index 2b985cb21..50a505ae1 100644 --- a/src/components/Menu/MenuBar.vue +++ b/src/components/Menu/MenuBar.vue @@ -44,6 +44,15 @@ v-bind="{ actionEntry }" :key="`text-action--${actionEntry.key}`" @call:help="showHelp" /> + <ActionList key="text-action--remain" + :action-entry="hiddenEntries" + @update:open="refreshWordCount" + @call:help="showHelp"> + <template #lastAction> + <NcActionCaption :title="wordCountString" + data-text-action-entry="word-count" /> + </template> + </ActionList> </div> <div class="text-menubar__slot"> <slot /> @@ -52,13 +61,16 @@ </template> <script> +import { NcActionCaption } from '@nextcloud/vue' import { subscribe, unsubscribe } from '@nextcloud/event-bus' +import { translatePlural as n } from '@nextcloud/l10n' import debounce from 'debounce' import HelpModal from '../HelpModal.vue' import actionsFullEntries from './entries.js' import ActionEntry from './ActionEntry.js' import { MENU_ID } from './MenuBar.provider.js' +import ActionList from './ActionList.vue' import { DotsHorizontal } from '../icons.js' import { useEditorMixin, @@ -68,7 +80,12 @@ import { export default { name: 'MenuBar', - components: { ActionEntry, HelpModal }, + components: { + ActionEntry, + ActionList, + HelpModal, + NcActionCaption, + }, mixins: [ useEditorMixin, useIsRichEditorMixin, @@ -99,6 +116,7 @@ export default { isReady: false, isVisible: this.$editor.isFocused, windowWidth: 0, + wordCount: 0, } }, computed: { @@ -122,40 +140,27 @@ export default { return slots - 1 }, visibleEntries() { - const { hiddenEntries, remainAction } = this const list = [...actionsFullEntries].filter(({ priority }) => { // if entry do not have priority, we assume it aways will be visible return priority === undefined || priority <= this.iconsLimit }) - if (hiddenEntries.length === 0) { - return list - } - - if (hiddenEntries.length === 1) { - // put only one entry - list.push(hiddenEntries[0]) - } else { - // add all hidden entries as list of actions - list.push(remainAction) - } - return list }, hiddenEntries() { - return [...actionsFullEntries].filter(({ priority }) => { - // reverse logic from visibleEntries - return priority !== undefined && priority > this.iconsLimit - }) - }, - remainAction() { return { key: 'remain', label: this.t('text', 'Remaining actions'), icon: DotsHorizontal, - children: this.hiddenEntries, + children: [...actionsFullEntries].filter(({ priority }) => { + // reverse logic from visibleEntries + return priority !== undefined && priority > this.iconsLimit + }), } }, + wordCountString() { + return n('text', '%n word', '%n words', this.wordCount) + }, }, mounted() { window.addEventListener('resize', this.getWindowWidth) @@ -225,6 +230,13 @@ export default { hideHelp() { this.displayHelp = false }, + + refreshWordCount(open) { + // characterCount is not reactive so we need this workaround + if (open) { + this.wordCount = this.$editor.storage.characterCount.words() + } + }, }, } </script> diff --git a/src/extensions/RichText.js b/src/extensions/RichText.js index 9d65be624..14fd3ba07 100644 --- a/src/extensions/RichText.js +++ b/src/extensions/RichText.js @@ -1,9 +1,9 @@ -/* +/** * @copyright Copyright (c) 2022 Max <max@nextcloud.com> * * @author Max <max@nextcloud.com> * - * @license GNU AGPL version 3 or any later version + * @license AGPL-3.0-or-later * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,32 +18,33 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * -*/ + */ import { Extension } from '@tiptap/core' /* eslint-disable import/no-named-as-default */ -import Document from '@tiptap/extension-document' -import TipTapParagraph from '@tiptap/extension-paragraph' -import Text from '@tiptap/extension-text' import Blockquote from '@tiptap/extension-blockquote' -import OrderedList from '@tiptap/extension-ordered-list' -import ListItem from '@tiptap/extension-list-item' +import BulletList from './../nodes/BulletList.js' +import Callout from './../nodes/Callouts.js' +import CharacterCount from '@tiptap/extension-character-count' import Code from '@tiptap/extension-code' import CodeBlock from '@tiptap/extension-code-block' -import HorizontalRule from '@tiptap/extension-horizontal-rule' +import Document from '@tiptap/extension-document' import Dropcursor from '@tiptap/extension-dropcursor' import FrontMatter from './../nodes/FrontMatter.js' import HardBreak from './HardBreak.js' +import Heading from '../nodes/Heading/index.js' +import HorizontalRule from '@tiptap/extension-horizontal-rule' +import Image from './../nodes/Image.js' import KeepSyntax from './KeepSyntax.js' +import ListItem from '@tiptap/extension-list-item' +import Mention from './../extensions/Mention.js' +import OrderedList from '@tiptap/extension-ordered-list' import Table from './../nodes/Table.js' -import Image from './../nodes/Image.js' -import Heading from '../nodes/Heading/index.js' -import BulletList from './../nodes/BulletList.js' -import TaskList from './../nodes/TaskList.js' import TaskItem from './../nodes/TaskItem.js' -import Callout from './../nodes/Callouts.js' -import Mention from './../extensions/Mention.js' +import TaskList from './../nodes/TaskList.js' +import Text from '@tiptap/extension-text' +import TipTapParagraph from '@tiptap/extension-paragraph' /* eslint-enable import/no-named-as-default */ import { Strong, Italic, Strike, Link, Underline } from './../marks/index.js' @@ -75,6 +76,7 @@ export default Extension.create({ Italic, Strike, Blockquote, + CharacterCount, Code, CodeBlock, BulletList, |