Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/text.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFerdinand Thiessen <rpm@fthiessen.de>2022-07-01 10:47:52 +0300
committerJonas <jonas@freesources.org>2022-10-05 12:33:10 +0300
commit9cf2534ae39b8cfbb01d7d1a6e7918cdf159e5b2 (patch)
tree485e9575e822aae8f54b629978417ef1059a6ef5 /src
parent879eaec610f310b3bc4e695f662eb88fe56563eb (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.vue5
-rw-r--r--src/components/Menu/MenuBar.vue54
-rw-r--r--src/extensions/RichText.js32
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,