Skip to content

Commit 0576d45

Browse files
committed
feat(menu): Put list items in submenu, add indention options
Fixes: #2438 Contributes to: #2836 Signed-off-by: Jonas <[email protected]>
1 parent 3109ff0 commit 0576d45

File tree

11 files changed

+134
-36
lines changed

11 files changed

+134
-36
lines changed

cypress/e2e/workspace.spec.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ describe('Workspace', function() {
112112
['unordered-list', 'ul'],
113113
['ordered-list', 'ol'],
114114
['task-list', 'ul.contains-task-list'],
115-
].forEach(([button, tag]) => testButton(button, tag, 'List me'))
115+
].forEach(([button, tag]) => testListButton(button, tag, 'List me'))
116116
})
117117

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

304304
/**
305-
*
305+
* @param {string} button Name of the button to click.
306+
* @param {string} tag Html tag expected to be toggled.
307+
* @param {string} content Content expected in the element.
308+
*/
309+
function testListButton(button, tag, content) {
310+
cy.getSubmenuEntry('lists', button)
311+
.should('not.have.class', 'is-active')
312+
.click()
313+
cy.getContent()
314+
.find(`${tag}`)
315+
.should('contain', content)
316+
cy.getSubmenuEntry('lists', button)
317+
.should('have.class', 'is-active')
318+
.click()
319+
}
320+
321+
/**
306322
* @param {string} button Name of the button to click.
307323
* @param {string} tag Html tag expected to be toggled.
308324
* @param {string} content Content expected in the element.

src/components/Menu/ActionListItem.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
<template>
77
<NextcloudVueNcActionButton class="entry-single-action entry-action entry-action-item"
8+
:title="listItemTooltip || undefined"
89
:class="state.class"
910
:disabled="state.disabled"
1011
:aria-keyshortcuts="keyshortcuts || undefined"
@@ -52,7 +53,7 @@ export default {
5253
} else {
5354
// Some actions run themselves.
5455
// others still need to have .run() called upon them.
55-
actionEntry.action(this.$editor.chain().focus())?.run()
56+
actionEntry.action(this.$editor.chain().focus(), this.$editor)?.run()
5657
}
5758

5859
this.$nextTick(() => {

src/components/Menu/ActionSingle.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export default {
5858
} else {
5959
// Some actions run themselves.
6060
// others still need to have .run() called upon them.
61-
actionEntry.action(this.$editor.chain().focus())?.run()
61+
actionEntry.action(this.$editor.chain().focus(), this.$editor)?.run()
6262
}
6363

6464
this.$nextTick(() => {

src/components/Menu/BaseActionEntry.js

+5
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ const BaseActionEntry = {
5454
getKeys(this.$isMobile, this.actionEntry),
5555
].join(' ')
5656
},
57+
listItemTooltip() {
58+
return [
59+
getKeys(this.$isMobile, this.actionEntry),
60+
].join(' ')
61+
},
5762
},
5863
watch: {
5964
/** Handle tabindex for menu toolbar */

src/components/Menu/entries.js

+77-30
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
FormatHeader4,
2020
FormatHeader5,
2121
FormatHeader6,
22+
FormatIndentDecrease,
23+
FormatIndentIncrease,
2224
FormatListNumbered,
2325
FormatListBulleted,
2426
FormatListCheckbox,
@@ -127,6 +129,8 @@ export default [
127129
{
128130
key: 'headings-h1',
129131
label: t('text', 'Heading 1'),
132+
keyChar: '1',
133+
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
130134
icon: FormatHeader1,
131135
isActive: ['heading', { level: 1 }],
132136
action: (command) => {
@@ -136,6 +140,8 @@ export default [
136140
{
137141
key: 'headings-h2',
138142
label: t('text', 'Heading 2'),
143+
keyChar: '2',
144+
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
139145
icon: FormatHeader2,
140146
isActive: ['heading', { level: 2 }],
141147
action: (command) => {
@@ -145,6 +151,8 @@ export default [
145151
{
146152
key: 'headings-h3',
147153
label: t('text', 'Heading 3'),
154+
keyChar: '3',
155+
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
148156
icon: FormatHeader3,
149157
isActive: ['heading', { level: 3 }],
150158
action: (command) => {
@@ -154,6 +162,8 @@ export default [
154162
{
155163
key: 'headings-h4',
156164
label: t('text', 'Heading 4'),
165+
keyChar: '4',
166+
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
157167
isActive: ['heading', { level: 4 }],
158168
icon: FormatHeader4,
159169
action: (command) => {
@@ -163,6 +173,8 @@ export default [
163173
{
164174
key: 'headings-h5',
165175
label: t('text', 'Heading 5'),
176+
keyChar: '5',
177+
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
166178
isActive: ['heading', { level: 5 }],
167179
icon: FormatHeader5,
168180
action: (command) => {
@@ -172,6 +184,8 @@ export default [
172184
{
173185
key: 'headings-h6',
174186
label: t('text', 'Heading 6'),
187+
keyChar: '6',
188+
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
175189
isActive: ['heading', { level: 6 }],
176190
icon: FormatHeader6,
177191
action: (command) => {
@@ -202,38 +216,71 @@ export default [
202216
priority: 1,
203217
},
204218
{
205-
key: 'unordered-list',
206-
label: t('text', 'Unordered list'),
207-
keyChar: '8',
219+
key: 'lists',
220+
label: t('text', 'Lists'),
221+
keyChar: '7…9',
208222
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
209-
isActive: 'bulletList',
223+
isActive: [{ isList: true }],
210224
icon: FormatListBulleted,
211-
action: (command) => {
212-
return command.toggleBulletList()
213-
},
214-
priority: 9,
215-
},
216-
{
217-
key: 'ordered-list',
218-
label: t('text', 'Ordered list'),
219-
keyChar: '7',
220-
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
221-
isActive: 'orderedList',
222-
icon: FormatListNumbered,
223-
action: (command) => {
224-
return command.toggleOrderedList()
225-
},
226-
priority: 10,
227-
},
228-
{
229-
key: 'task-list',
230-
label: t('text', 'To-Do list'),
231-
keyChar: '9',
232-
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
233-
isActive: 'taskList',
234-
icon: FormatListCheckbox,
235-
action: (command) => command.toggleTaskList(),
236-
priority: 11,
225+
children: [
226+
{
227+
key: 'unordered-list',
228+
label: t('text', 'Unordered list'),
229+
keyChar: '8',
230+
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
231+
isActive: 'bulletList',
232+
icon: FormatListBulleted,
233+
action: (command) => {
234+
return command.toggleBulletList()
235+
},
236+
},
237+
{
238+
key: 'ordered-list',
239+
label: t('text', 'Ordered list'),
240+
keyChar: '7',
241+
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
242+
isActive: 'orderedList',
243+
icon: FormatListNumbered,
244+
action: (command) => {
245+
return command.toggleOrderedList()
246+
},
247+
},
248+
{
249+
key: 'task-list',
250+
label: t('text', 'To-Do list'),
251+
keyChar: '9',
252+
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
253+
isActive: 'taskList',
254+
icon: FormatListCheckbox,
255+
action: (command) => command.toggleTaskList(),
256+
},
257+
{
258+
key: 'list-indent-increase',
259+
label: t('text', 'Increase indention'),
260+
keyChar: 'Tab',
261+
icon: FormatIndentIncrease,
262+
action: (command, editor = null) => {
263+
if (editor && editor.isActive('taskItem')) {
264+
return command.sinkListItem('taskItem')
265+
}
266+
return command.sinkListItem('listItem')
267+
},
268+
},
269+
{
270+
key: 'list-indent-decrease',
271+
label: t('text', 'Decrease indention'),
272+
keyChar: 'Tab',
273+
keyModifiers: [MODIFIERS.Shift],
274+
icon: FormatIndentDecrease,
275+
action: (command, editor = null) => {
276+
if (editor && editor.isActive('taskItem')) {
277+
return command.liftListItem('taskItem')
278+
}
279+
return command.liftListItem('listItem')
280+
},
281+
},
282+
],
283+
priority: 3,
237284
},
238285
{
239286
key: 'insert-link',

src/components/Menu/utils.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const getKeys = (isMobile, { keyChar, keyModifiers }) => {
3636
}
3737

3838
const isDisabled = (actionEntry, $editor) => {
39-
return actionEntry.action && !actionEntry.action($editor.can())
39+
return actionEntry.action && !actionEntry.action($editor.can(), $editor)
4040
}
4141

4242
const getIsActive = ({ isActive }, $editor) => {

src/components/icons.js

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import MDI_FormatHeader3 from 'vue-material-design-icons/FormatHeader3.vue'
2626
import MDI_FormatHeader4 from 'vue-material-design-icons/FormatHeader4.vue'
2727
import MDI_FormatHeader5 from 'vue-material-design-icons/FormatHeader5.vue'
2828
import MDI_FormatHeader6 from 'vue-material-design-icons/FormatHeader6.vue'
29+
import MDI_FormatIndentDecrease from 'vue-material-design-icons/FormatIndentDecrease.vue'
30+
import MDI_FormatIndentIncrease from 'vue-material-design-icons/FormatIndentIncrease.vue'
2931
import MDI_FormatItalic from 'vue-material-design-icons/FormatItalic.vue'
3032
import MDI_FormatListBulleted from 'vue-material-design-icons/FormatListBulleted.vue'
3133
import MDI_FormatListCheckbox from 'vue-material-design-icons/FormatListCheckbox.vue'
@@ -105,6 +107,8 @@ export const FormatHeader3 = makeIcon(MDI_FormatHeader3)
105107
export const FormatHeader4 = makeIcon(MDI_FormatHeader4)
106108
export const FormatHeader5 = makeIcon(MDI_FormatHeader5)
107109
export const FormatHeader6 = makeIcon(MDI_FormatHeader6)
110+
export const FormatIndentDecrease = makeIcon(MDI_FormatIndentDecrease)
111+
export const FormatIndentIncrease = makeIcon(MDI_FormatIndentIncrease)
108112
export const FormatItalic = makeIcon(MDI_FormatItalic)
109113
export const FormatListBulleted = makeIcon(MDI_FormatListBulleted)
110114
export const FormatListCheckbox = makeIcon(MDI_FormatListCheckbox)

src/extensions/RichText.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import ListItem from '@tiptap/extension-list-item'
3333
import Markdown from './../extensions/Markdown.js'
3434
import Mention from './../extensions/Mention.js'
3535
import Search from './../extensions/Search.js'
36-
import OrderedList from '@tiptap/extension-ordered-list'
36+
import OrderedList from './../nodes/OrderedList.js'
3737
import Paragraph from './../nodes/Paragraph.js'
3838
import Placeholder from '@tiptap/extension-placeholder'
3939
import Preview from './../nodes/Preview.js'

src/nodes/BulletList.js

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ const BulletList = TiptapBulletList.extend({
1919
addAttributes() {
2020
return {
2121
...this.parent?.(),
22+
isList: {
23+
default: true,
24+
},
2225
bullet: {
2326
default: '-',
2427
rendered: false,

src/nodes/OrderedList.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import TiptapOrderedList from '@tiptap/extension-ordered-list'
7+
8+
const OrderedList = TiptapOrderedList.extend({
9+
addAttributes() {
10+
return {
11+
...this.parent?.(),
12+
isList: {
13+
default: true,
14+
},
15+
}
16+
},
17+
})
18+
19+
export default OrderedList

src/nodes/TaskList.js

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ const TaskList = TiptapTaskList.extend({
2222
addAttributes() {
2323
return {
2424
...this.parent?.(),
25+
isList: {
26+
default: true,
27+
},
2528
bullet: {
2629
default: '-',
2730
rendered: false,

0 commit comments

Comments
 (0)