Skip to content

Commit

Permalink
feat(menu): Put list items in submenu, add indentation options
Browse files Browse the repository at this point in the history
Fixes: #2438
Contributes to: #2836

Signed-off-by: Jonas <[email protected]>
  • Loading branch information
mejo- authored and backportbot[bot] committed Sep 20, 2024
1 parent 8cc58a0 commit e567603
Show file tree
Hide file tree
Showing 12 changed files with 142 additions and 36 deletions.
20 changes: 18 additions & 2 deletions cypress/e2e/workspace.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ describe('Workspace', function() {
['unordered-list', 'ul'],
['ordered-list', 'ol'],
['task-list', 'ul.contains-task-list'],
].forEach(([button, tag]) => testButton(button, tag, 'List me'))
].forEach(([button, tag]) => testListButton(button, tag, 'List me'))
})

it('takes README.md into account', function() {
Expand Down Expand Up @@ -302,7 +302,23 @@ const openSidebar = filename => {
}

/**
*
* @param {string} button Name of the button to click.
* @param {string} tag Html tag expected to be toggled.
* @param {string} content Content expected in the element.
*/
function testListButton(button, tag, content) {
cy.getSubmenuEntry('lists', button)
.should('not.have.class', 'is-active')
.click()
cy.getContent()
.find(`${tag}`)
.should('contain', content)
cy.getSubmenuEntry('lists', button)
.should('have.class', 'is-active')
.click()
}

/**
* @param {string} button Name of the button to click.
* @param {string} tag Html tag expected to be toggled.
* @param {string} content Content expected in the element.
Expand Down
3 changes: 2 additions & 1 deletion src/components/Menu/ActionListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<template>
<NextcloudVueNcActionButton class="entry-single-action entry-action entry-action-item"
:title="listItemTooltip || undefined"
:class="state.class"
:disabled="state.disabled"
:aria-keyshortcuts="keyshortcuts || undefined"
Expand Down Expand Up @@ -52,7 +53,7 @@ export default {
} else {
// Some actions run themselves.
// others still need to have .run() called upon them.
actionEntry.action(this.$editor.chain().focus())?.run()
actionEntry.action(this.$editor.chain().focus(), this.$editor)?.run()
}
this.$nextTick(() => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Menu/ActionSingle.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default {
} else {
// Some actions run themselves.
// others still need to have .run() called upon them.
actionEntry.action(this.$editor.chain().focus())?.run()
actionEntry.action(this.$editor.chain().focus(), this.$editor)?.run()
}
this.$nextTick(() => {
Expand Down
5 changes: 5 additions & 0 deletions src/components/Menu/BaseActionEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ const BaseActionEntry = {
getKeys(this.$isMobile, this.actionEntry),
].join(' ')
},
listItemTooltip() {
return [
getKeys(this.$isMobile, this.actionEntry),
].join(' ')
},
},
watch: {
/** Handle tabindex for menu toolbar */
Expand Down
111 changes: 81 additions & 30 deletions src/components/Menu/entries.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
FormatHeader4,
FormatHeader5,
FormatHeader6,
FormatIndentDecrease,
FormatIndentIncrease,
FormatListNumbered,
FormatListBulleted,
FormatListCheckbox,
Expand Down Expand Up @@ -126,6 +128,8 @@ export default [
{
key: 'headings-h1',
label: t('text', 'Heading 1'),
keyChar: '1',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
icon: FormatHeader1,
isActive: ['heading', { level: 1 }],
action: (command) => {
Expand All @@ -135,6 +139,8 @@ export default [
{
key: 'headings-h2',
label: t('text', 'Heading 2'),
keyChar: '2',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
icon: FormatHeader2,
isActive: ['heading', { level: 2 }],
action: (command) => {
Expand All @@ -144,6 +150,8 @@ export default [
{
key: 'headings-h3',
label: t('text', 'Heading 3'),
keyChar: '3',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
icon: FormatHeader3,
isActive: ['heading', { level: 3 }],
action: (command) => {
Expand All @@ -153,6 +161,8 @@ export default [
{
key: 'headings-h4',
label: t('text', 'Heading 4'),
keyChar: '4',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
isActive: ['heading', { level: 4 }],
icon: FormatHeader4,
action: (command) => {
Expand All @@ -162,6 +172,8 @@ export default [
{
key: 'headings-h5',
label: t('text', 'Heading 5'),
keyChar: '5',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
isActive: ['heading', { level: 5 }],
icon: FormatHeader5,
action: (command) => {
Expand All @@ -171,6 +183,8 @@ export default [
{
key: 'headings-h6',
label: t('text', 'Heading 6'),
keyChar: '6',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
isActive: ['heading', { level: 6 }],
icon: FormatHeader6,
action: (command) => {
Expand Down Expand Up @@ -201,38 +215,75 @@ export default [
priority: 1,
},
{
key: 'unordered-list',
label: t('text', 'Unordered list'),
keyChar: '8',
key: 'lists',
label: t('text', 'Lists'),
keyChar: '7…9',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
isActive: 'bulletList',
isActive: [{ isList: true }],
icon: FormatListBulleted,
action: (command) => {
return command.toggleBulletList()
},
priority: 9,
},
{
key: 'ordered-list',
label: t('text', 'Ordered list'),
keyChar: '7',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
isActive: 'orderedList',
icon: FormatListNumbered,
action: (command) => {
return command.toggleOrderedList()
},
priority: 10,
},
{
key: 'task-list',
label: t('text', 'To-Do list'),
keyChar: '9',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
isActive: 'taskList',
icon: FormatListCheckbox,
action: (command) => command.toggleTaskList(),
priority: 11,
children: [
{
key: 'unordered-list',
label: t('text', 'Unordered list'),
keyChar: '8',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
isActive: 'bulletList',
icon: FormatListBulleted,
action: (command) => {
return command.toggleBulletList()
},
},
{
key: 'ordered-list',
label: t('text', 'Ordered list'),
keyChar: '7',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
isActive: 'orderedList',
icon: FormatListNumbered,
action: (command) => {
return command.toggleOrderedList()
},
},
{
key: 'task-list',
label: t('text', 'To-Do list'),
keyChar: '9',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
isActive: 'taskList',
icon: FormatListCheckbox,
action: (command) => command.toggleTaskList(),
},
{
key: 'lists-separator',
isSeparator: true,
},
{
key: 'list-indent-increase',
label: t('text', 'Increase indentation'),
keyChar: 'Tab',
icon: FormatIndentIncrease,
action: (command, editor = null) => {
if (editor && editor.isActive('taskItem')) {
return command.sinkListItem('taskItem')
}
return command.sinkListItem('listItem')
},
},
{
key: 'list-indent-decrease',
label: t('text', 'Decrease indentation'),
keyChar: 'Tab',
keyModifiers: [MODIFIERS.Shift],
icon: FormatIndentDecrease,
action: (command, editor = null) => {
if (editor && editor.isActive('taskItem')) {
return command.liftListItem('taskItem')
}
return command.liftListItem('listItem')
},
},
],
priority: 3,
},
{
key: 'insert-link',
Expand Down
2 changes: 1 addition & 1 deletion src/components/Menu/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const getKeys = (isMobile, { keyChar, keyModifiers }) => {
}

const isDisabled = (actionEntry, $editor) => {
return actionEntry.action && !actionEntry.action($editor.can())
return actionEntry.action && !actionEntry.action($editor.can(), $editor)
}

const getIsActive = ({ isActive }, $editor) => {
Expand Down
1 change: 1 addition & 0 deletions src/components/Suggestion/LinkPicker/suggestions.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const formattingSuggestions = (query) => {
return sortImportantFirst(
[
...menuEntries.find(e => e.key === 'headings').children,
...menuEntries.find(e => e.key === 'lists').children,
...menuEntries.filter(e => e.action && !filterOut(e)),
...menuEntries.find(e => e.key === 'callouts').children,
{
Expand Down
4 changes: 4 additions & 0 deletions src/components/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import MDI_FormatHeader3 from 'vue-material-design-icons/FormatHeader3.vue'
import MDI_FormatHeader4 from 'vue-material-design-icons/FormatHeader4.vue'
import MDI_FormatHeader5 from 'vue-material-design-icons/FormatHeader5.vue'
import MDI_FormatHeader6 from 'vue-material-design-icons/FormatHeader6.vue'
import MDI_FormatIndentDecrease from 'vue-material-design-icons/FormatIndentDecrease.vue'
import MDI_FormatIndentIncrease from 'vue-material-design-icons/FormatIndentIncrease.vue'
import MDI_FormatItalic from 'vue-material-design-icons/FormatItalic.vue'
import MDI_FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue'
import MDI_FormatListCheckbox from 'vue-material-design-icons/FormatListCheckbox.vue'
Expand Down Expand Up @@ -104,6 +106,8 @@ export const FormatHeader3 = makeIcon(MDI_FormatHeader3)
export const FormatHeader4 = makeIcon(MDI_FormatHeader4)
export const FormatHeader5 = makeIcon(MDI_FormatHeader5)
export const FormatHeader6 = makeIcon(MDI_FormatHeader6)
export const FormatIndentDecrease = makeIcon(MDI_FormatIndentDecrease)
export const FormatIndentIncrease = makeIcon(MDI_FormatIndentIncrease)
export const FormatItalic = makeIcon(MDI_FormatItalic)
export const FormatListBulleted = makeIcon(MDI_FormatListBulleted)
export const FormatListCheckbox = makeIcon(MDI_FormatListCheckbox)
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/RichText.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import ListItem from '@tiptap/extension-list-item'
import Markdown from './../extensions/Markdown.js'
import Mention from './../extensions/Mention.js'
import Search from './../extensions/Search.js'
import OrderedList from '@tiptap/extension-ordered-list'
import OrderedList from './../nodes/OrderedList.js'
import Paragraph from './../nodes/Paragraph.js'
import Placeholder from '@tiptap/extension-placeholder'
import Preview from './../nodes/Preview.js'
Expand Down
4 changes: 4 additions & 0 deletions src/nodes/BulletList.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ const BulletList = TiptapBulletList.extend({
addAttributes() {
return {
...this.parent?.(),
isList: {
default: true,
rendered: false,
},
bullet: {
default: '-',
rendered: false,
Expand Down
20 changes: 20 additions & 0 deletions src/nodes/OrderedList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import TiptapOrderedList from '@tiptap/extension-ordered-list'

const OrderedList = TiptapOrderedList.extend({
addAttributes() {
return {
...this.parent?.(),
isList: {
default: true,
rendered: false,
},
}
},
})

export default OrderedList
4 changes: 4 additions & 0 deletions src/nodes/TaskList.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ const TaskList = TiptapTaskList.extend({
addAttributes() {
return {
...this.parent?.(),
isList: {
default: true,
rendered: false,
},
bullet: {
default: '-',
rendered: false,
Expand Down

0 comments on commit e567603

Please sign in to comment.