Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions cypress/e2e/MenuBar.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { initUserAndFiles, randHash } from '../utils/index.js'

const randUser = randHash()
const fileName = 'test.md'

describe('Test the rich text editor menu bar', function() {
before(() => initUserAndFiles(randUser, fileName))

beforeEach(function() {
cy.login(randUser, 'password', {
onBeforeLoad(win) {
cy.stub(win, 'open')
.as('winOpen')

},
})

cy.openFile(fileName)
})

describe('word count', function() {
/**
*
*/
function getWordCount() {
return cy.get('.popover .open').get('[data-text-action-entry="word-count"]')
}

beforeEach(cy.clearContent)
it('empty file', () => {
cy.getFile(fileName)
.then($el => {
cy.getActionEntry('remain')
.click()
getWordCount()
.should('include.text', '0 words')
})
})

it('single word', () => {
cy.getFile(fileName)
.then($el => {
cy.getContent()
.type(' Hello ')
cy.getActionEntry('remain')
.click()
getWordCount()
.should('include.text', '1 word')
})
})

it('multiple words', () => {
cy.getFile(fileName)
.then($el => {
cy.getContent()
.type('Hello \nworld')
cy.getActionEntry('remain')
.click()
getWordCount()
.should('include.text', '2 word')
})
})
})
})
4 changes: 2 additions & 2 deletions js/editor-rich.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/editor-rich.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/editor.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/editor.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/files-modal.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion js/files-modal.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions js/text-files.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-files.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-public.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-public.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-text.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-text.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/text-viewer.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/text-viewer.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/vendors.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/vendors.js.map

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@tiptap/extension-blockquote": "^2.0.0-beta.29",
"@tiptap/extension-bold": "^2.0.0-beta.28",
"@tiptap/extension-bullet-list": "^2.0.0-beta.29",
"@tiptap/extension-character-count": "^2.0.0-beta.30",
"@tiptap/extension-code": "^2.0.0-beta.28",
"@tiptap/extension-code-block": "^2.0.0-beta.41",
"@tiptap/extension-code-block-lowlight": "^2.0.0-beta.72",
Expand Down
5 changes: 4 additions & 1 deletion src/components/Menu/ActionList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
<Actions v-tooltip="tooltip"
class="entry-list-action entry-action"
v-bind="state"
: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>
Expand All @@ -35,6 +37,7 @@
is-item
:action-entry="child"
@trigged="onTrigger" />
<slot name="lastAction" />
</Actions>
</template>

Expand Down
50 changes: 29 additions & 21 deletions src/components/Menu/MenuBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,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>
<ActionCaption :title="wordCountString"
data-text-action-entry="word-count" />
</template>
</ActionList>
</div>
<div class="text-menubar__slot">
<slot />
Expand All @@ -45,12 +54,16 @@
</template>

<script>
import ActionCaption from '@nextcloud/vue/dist/Components/ActionCaption'

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 ActionList from './ActionList.vue'
import { DotsHorizontal } from '../icons.js'
import {
useEditorMixin,
Expand All @@ -60,7 +73,7 @@ import {

export default {
name: 'MenuBar',
components: { ActionEntry, HelpModal },
components: { ActionEntry, ActionList, ActionCaption, HelpModal },
mixins: [
useEditorMixin,
useIsRichEditorMixin,
Expand All @@ -79,6 +92,7 @@ export default {
isReady: false,
isVisible: this.$editor.isFocused,
windowWidth: 0,
wordCount: 0,
}
},
computed: {
Expand All @@ -102,40 +116,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)
Expand Down Expand Up @@ -205,6 +206,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>
Expand Down
Loading