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
diff options
context:
space:
mode:
-rw-r--r--css/prosemirror.scss18
-rw-r--r--package-lock.json17
-rw-r--r--package.json4
-rw-r--r--src/EditorFactory.js5
-rw-r--r--src/mixins/menubar.js8
-rw-r--r--src/nodes/ListItem.js161
-rw-r--r--src/nodes/index.js2
-rw-r--r--src/tests/markdown.spec.js8
8 files changed, 213 insertions, 10 deletions
diff --git a/css/prosemirror.scss b/css/prosemirror.scss
index c16cfe929..cd6c6ea2d 100644
--- a/css/prosemirror.scss
+++ b/css/prosemirror.scss
@@ -24,6 +24,21 @@ div.ProseMirror {
font-size: 14px;
}
+ li label.checkbox-label {
+ width: 100%;
+ display: flex;
+ margin-top: 10px;
+
+ &:before {
+ position: relative;
+ top: 2px;
+ }
+ div.checkbox-wrapper {
+ & > p {
+ margin-top: -1px;
+ }
+ }
+ }
p:first-child,
h1:first-child,
h2:first-child,
@@ -127,7 +142,8 @@ div.ProseMirror {
}
ul, ol {
- padding-left: 14px;
+ padding-left: 10px;
+ margin-left: 10px;
}
ul li {
diff --git a/package-lock.json b/package-lock.json
index 9348493c5..654ac0f04 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11202,6 +11202,11 @@
"uc.micro": "^1.0.5"
}
},
+ "markdown-it-task-lists": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
+ "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA=="
+ },
"markdown-table": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz",
@@ -12893,9 +12898,9 @@
}
},
"prosemirror-inputrules": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.0.4.tgz",
- "integrity": "sha512-RhuBghqUgYWm8ai/P+k1lMl1ZGvt6Cs3Xeur8oN0L1Yy+Z5GmsTp3fT8RVl+vJeGkItEAxAit9Qh7yZxixX7rA==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.1.2.tgz",
+ "integrity": "sha512-Ja5Z3BWestlHYGvtSGqyvxMeB8QEuBjlHM8YnKtLGUXMDp965qdDV4goV8lJb17kIWHk7e7JNj6Catuoa3302g==",
"requires": {
"prosemirror-state": "^1.0.0",
"prosemirror-transform": "^1.0.0"
@@ -12947,9 +12952,9 @@
}
},
"prosemirror-schema-list": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.0.4.tgz",
- "integrity": "sha512-7Y0b6FIG6ATnCcDSLrZfU9yIfOG5Yad3DMNZ9W7GGfMSzdIl0aHExrsIUgviJZjovO2jtLJVbxWGjMR3OrTupA==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.1.2.tgz",
+ "integrity": "sha512-dgM9PwtM4twa5WsgSYMB+J8bwjnR43DAD3L9MsR9rKm/nZR5Y85xcjB7gusVMSsbQ2NomMZF03RE6No6mTnclQ==",
"requires": {
"prosemirror-model": "^1.0.0",
"prosemirror-transform": "^1.0.0"
diff --git a/package.json b/package.json
index b603317cf..e7f92e608 100644
--- a/package.json
+++ b/package.json
@@ -28,9 +28,13 @@
"escape-html": "^1.0.3",
"highlight.js": "^9.16.2",
"markdown-it": "^8.4.2",
+ "markdown-it-task-lists": "^2.1.1",
"nextcloud-vue": "^0.12.7",
"prosemirror-collab": "^1.2.2",
+ "prosemirror-inputrules": "^1.1.2",
"prosemirror-markdown": "^1.4.2",
+ "prosemirror-schema-list": "^1.1.2",
+ "prosemirror-utils": "^0.9.6",
"prosemirror-view": "^1.13.4",
"tiptap": "^1.26.4",
"tiptap-commands": "^1.12.3",
diff --git a/src/EditorFactory.js b/src/EditorFactory.js
index b9acc42dd..9b37cb5c1 100644
--- a/src/EditorFactory.js
+++ b/src/EditorFactory.js
@@ -27,7 +27,6 @@ import {
Link,
BulletList,
OrderedList,
- ListItem,
Blockquote,
CodeBlock,
CodeBlockHighlight,
@@ -36,8 +35,9 @@ import {
Placeholder,
} from 'tiptap-extensions'
import { Strong, Italic, Strike } from './marks'
-import { Image, PlainTextDocument } from './nodes'
+import { Image, PlainTextDocument, ListItem } from './nodes'
import MarkdownIt from 'markdown-it'
+import taskLists from 'markdown-it-task-lists'
import { MarkdownSerializer, defaultMarkdownSerializer } from 'prosemirror-markdown'
@@ -107,6 +107,7 @@ const createEditor = ({ content, onInit, onUpdate, extensions, enableRichEditing
const markdownit = MarkdownIt('commonmark', { html: false, breaks: false })
.enable('strikethrough')
+ .use(taskLists, { enable: true, labelAfter: true })
const SerializeException = function(message) {
this.message = message
diff --git a/src/mixins/menubar.js b/src/mixins/menubar.js
index 482a6e3ba..92b1a3c9b 100644
--- a/src/mixins/menubar.js
+++ b/src/mixins/menubar.js
@@ -124,7 +124,7 @@ export default [
class: 'icon-ul',
isActive: (isActive) => isActive.bullet_list(),
action: (command) => {
- return command.bullet_list()
+ return command.bullet_list_item()
},
},
{
@@ -136,6 +136,12 @@ export default [
},
},
{
+ label: t('text', 'ToDo list'),
+ class: 'icon-checkmark',
+ isActive: (isActive) => isActive.list_item(),
+ action: (command) => command.todo_item(),
+ },
+ {
label: t('text', 'Blockquote'),
class: 'icon-quote',
isActive: (isActive) => isActive.blockquote(),
diff --git a/src/nodes/ListItem.js b/src/nodes/ListItem.js
new file mode 100644
index 000000000..864c69d04
--- /dev/null
+++ b/src/nodes/ListItem.js
@@ -0,0 +1,161 @@
+/*
+ * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
+ *
+ * @author Julius Härtl <jus@bitgrid.net>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * 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 { ListItem as TiptapListItem } from 'tiptap-extensions'
+import { Plugin } from 'tiptap'
+import { toggleList } from 'tiptap-commands'
+import { findParentNode } from 'prosemirror-utils'
+
+const TYPES = {
+ BULLET: 0,
+ CHECKBOX: 1,
+}
+
+export default class ListItem extends TiptapListItem {
+
+ get defaultOptions() {
+ return {
+ nested: true,
+ }
+ }
+
+ get schema() {
+ return {
+ attrs: {
+ done: {
+ default: false,
+ },
+ type: {
+ default: TYPES.BULLET,
+ },
+ },
+ draggable: true,
+ content: 'paragraph block*',
+ toDOM: node => {
+ if (node.attrs.type === TYPES.BULLET) {
+ return ['li', 0]
+ }
+ const checkboxAttributes = { type: 'checkbox', class: 'checkbox' }
+ if (node.attrs.done) {
+ checkboxAttributes.checked = true
+ }
+ return [
+ 'li',
+ [
+ 'input',
+ checkboxAttributes,
+ ],
+ [
+ 'label',
+ { class: 'checkbox-label' },
+ ['div', { class: 'checkbox-wrapper' }, 0],
+ ],
+ ]
+ },
+ parseDOM: [
+ {
+ priority: 100,
+ tag: 'li',
+ getAttrs: el => {
+ const checkbox = el.querySelector('input[type=checkbox]')
+ return { done: checkbox && checkbox.checked, type: checkbox ? TYPES.CHECKBOX : TYPES.BULLET }
+ },
+ },
+ ],
+ toMarkdown: (state, node) => {
+ if (node.attrs.type === TYPES.CHECKBOX) {
+ state.write(`[${node.attrs.done ? 'x' : ' '}] `)
+ }
+ state.renderContent(node)
+ },
+ }
+ }
+
+ commands({ type, schema }) {
+ return {
+ 'bullet_list_item': () => {
+ return (state, dispatch, view) => {
+ return toggleList(schema.nodes.bullet_list, type)(state, dispatch, view)
+ }
+ },
+ 'todo_item': () => {
+ return (state, dispatch, view) => {
+ const schema = state.schema
+ const selection = state.selection
+ const $from = selection.$from
+ const $to = selection.$to
+ const range = $from.blockRange($to)
+ const tr = state.tr
+
+ if (!range) {
+ return false
+ }
+
+ const parentList = findParentNode(function(node) {
+ return node.type === schema.nodes.list_item
+ })(selection)
+
+ tr.setNodeMarkup(parentList.pos, schema.nodes.list_item, { type: parentList.node.attrs.type === TYPES.CHECKBOX ? TYPES.BULLET : TYPES.CHECKBOX })
+
+ if (dispatch) {
+ dispatch(tr)
+ }
+
+ }
+ },
+ }
+ }
+
+ get plugins() {
+ return [
+ new Plugin({
+ props: {
+ handleClick: (view, pos, event) => {
+ const state = view.state
+ const schema = state.schema
+ const selection = state.selection
+ const $from = selection.$from
+ const $to = selection.$to
+ const range = $from.blockRange($to)
+
+ if (!range) {
+ return false
+ }
+
+ const parentList = findParentNode(function(node) {
+ return node.type === schema.nodes.list_item
+ })(selection)
+
+ if (parentList.node.attrs.type !== TYPES.CHECKBOX) {
+ return
+ }
+
+ const tr = state.tr
+ tr.setNodeMarkup(parentList.pos, schema.nodes.list_item, { done: !parentList.node.attrs.done, type: TYPES.CHECKBOX })
+ view.dispatch(tr)
+ },
+ },
+ }),
+ ]
+ }
+
+}
diff --git a/src/nodes/index.js b/src/nodes/index.js
index e0eaf4882..c24428ee7 100644
--- a/src/nodes/index.js
+++ b/src/nodes/index.js
@@ -22,8 +22,10 @@
import Image from './Image'
import PlainTextDocument from './PlainTextDocument'
+import ListItem from './ListItem'
export {
Image,
PlainTextDocument,
+ ListItem,
}
diff --git a/src/tests/markdown.spec.js b/src/tests/markdown.spec.js
index 2c69aa9d3..354665b8a 100644
--- a/src/tests/markdown.spec.js
+++ b/src/tests/markdown.spec.js
@@ -95,4 +95,12 @@ describe('Markdown serializer from html', () => {
test('images', () => {
expect(markdownThroughEditorHtml('<img src="image" alt="description" />')).toBe('![description](image)')
})
+ test('checkboxes', () => {
+ expect(markdownThroughEditor('- [ ] asd')).toBe('* [ ] asd')
+ expect(markdownThroughEditor('- [X] asd')).toBe('* [x] asd')
+ expect(markdownThroughEditorHtml('<ul><li><input type="checkbox" checked /><label>foo</label></li></ul>')).toBe('* [x] foo')
+ expect(markdownThroughEditorHtml('<ul><li><input type="checkbox" /><label>test</label></li></ul>')).toBe('* [ ] test')
+ expect(markdownThroughEditorHtml('<ul><li><input type="checkbox" checked /><div><h2>Test</h2><p><strong>content</strong></p></div></li></ul>')).toBe('* [x] Test\n\n **content**')
+ expect(markdownThroughEditorHtml('<ul><li><input type="checkbox" checked /><p>Test</p><h1>Block level headline</h1></li></ul>')).toBe('* [x] Test\n\n # Block level headline')
+ })
})