diff --git a/package-lock.json b/package-lock.json index d0158b768..d166fe18b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3857,7 +3857,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -11820,6 +11819,14 @@ "type-check": "~0.3.2" } }, + "linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "requires": { + "uc.micro": "^1.0.1" + } + }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -12049,6 +12056,30 @@ "integrity": "sha512-lbRZ2mE3Q9RtLjxZBZ9+IMl68DKIXaVAhwvwn9pmjnPLS0h/6kyBMgNhqi1xFJ/2yv6cSyv0jbiZavZv93JkkA==", "dev": true }, + "markdown-it": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", + "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", + "requires": { + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "dependencies": { + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" + } + } + }, + "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.2", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.2.tgz", @@ -12107,6 +12138,11 @@ "unist-util-visit": "^1.1.0" } }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" + }, "mem": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", @@ -15881,8 +15917,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.1", @@ -17320,6 +17355,11 @@ "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.1.0.tgz", "integrity": "sha512-W3kLbx+ML9PBl5Bzso/lTvVxk4BCveSNAtQeht59FEtxCdGThmn6wSHA4Xq3eQYAK24NHdisMM4JmsK0GFy/pg==" }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, "unherit": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.1.tgz", diff --git a/package.json b/package.json index d9b638347..8debbbfa5 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,8 @@ "@nextcloud/vue": "^1.5.0", "blueimp-md5": "^2.13.0", "dompurify": "^2.0.8", + "markdown-it": "^10.0.0", + "markdown-it-task-lists": "^2.1.1", "moment": "^2.24.0", "nextcloud-vue-collections": "^0.7.2", "p-queue": "^6.3.0", diff --git a/src/components/card/CardSidebar.vue b/src/components/card/CardSidebar.vue index 7335d32ec..8f7acf539 100644 --- a/src/components/card/CardSidebar.vue +++ b/src/components/card/CardSidebar.vue @@ -127,9 +127,22 @@ href="https://deck.readthedocs.io/en/latest/Markdown/" target="_blank" class="icon icon-info" /> + + + {{ t('deck', 'Edit description') }} + + + {{ t('deck', 'View description') }} + + - - + @@ -168,6 +181,11 @@ import { CollectionList } from 'nextcloud-vue-collections' import CardSidebarTabAttachments from './CardSidebarTabAttachments' import CardSidebarTabComments from './CardSidebarTabComments' import CardSidebarTabActivity from './CardSidebarTabActivity' +import MarkdownIt from 'markdown-it' +import MarkdownItTaskLists from 'markdown-it-task-lists' + +const markdownIt = new MarkdownIt() +markdownIt.use(MarkdownItTaskLists, { enabled: true, label: true, labelAfter: true }) const capabilities = window.OC.getCapabilities() @@ -202,8 +220,10 @@ export default { addedLabelToCard: null, copiedCard: null, allLabels: null, - desc: null, + saving: false, + markdownIt: null, + descriptionEditing: false, mdeConfig: { autoDownloadFontAwesome: false, spellChecker: false, @@ -265,26 +285,13 @@ export default { this.saving = false }, }, + renderedDescription() { + return markdownIt.render(this.copiedCard.description) + }, }, watch: { - 'currentCard': { - immediate: true, - handler() { - if (!this.currentCard) { - return - } - this.copiedCard = JSON.parse(JSON.stringify(this.currentCard)) - this.allLabels = this.currentCard.labels - - if (this.currentCard.assignedUsers && this.currentCard.assignedUsers.length > 0) { - this.assignedUsers = this.currentCard.assignedUsers.map((item) => item.participant) - } else { - this.assignedUsers = [] - } - - this.desc = this.currentCard.description - this.updateRelativeTimestamps() - }, + currentCard() { + this.initialize() }, }, created() { @@ -293,7 +300,57 @@ export default { destroyed() { clearInterval(this.updateRelativeTimestamps) }, + mounted() { + this.initialize() + }, methods: { + initialize() { + if (!this.currentCard) { + return + } + + this.copiedCard = JSON.parse(JSON.stringify(this.currentCard)) + this.allLabels = this.currentCard.labels + + if (this.currentCard.assignedUsers && this.currentCard.assignedUsers.length > 0) { + this.assignedUsers = this.currentCard.assignedUsers.map((item) => item.participant) + } else { + this.assignedUsers = [] + } + + this.desc = this.currentCard.description + this.updateRelativeTimestamps() + }, + showEditor() { + if (!this.canEdit) { + return + } + this.descriptionEditing = true + }, + hideEditor() { + this.descriptionEditing = false + }, + clickedPreview(e) { + if (e.target.getAttribute('type') === 'checkbox') { + const clickedIndex = [...document.querySelector('#description-preview').querySelectorAll('input')].findIndex((li) => li.id === e.target.id) + const reg = /\[(X|\s|_|-)\]/ig + let nth = 0 + const updatedDescription = this.copiedCard.description.replace(reg, (match, i, original) => { + let result = match + if ('' + nth++ === '' + clickedIndex) { + if (match.match(/^\[\s\]/i)) { + result = match.replace(/\[\s\]/i, '[x]') + } + if (match.match(/^\[x\]/i)) { + result = match.replace(/\[x\]/i, '[ ]') + } + return result + } + return match + }) + this.updateDescription(updatedDescription) + } + }, updateRelativeTimestamps() { this.lastModifiedRelative = OC.Util.relativeModifiedDate(this.currentCard.lastModified * 1000) this.lastCreatedRemative = OC.Util.relativeModifiedDate(this.currentCard.createdAt * 1000) @@ -377,6 +434,8 @@ export default { @import "~easymde/dist/easymde.min.css"; .vue-easymde, .CodeMirror { border: none; + margin: 0; + padding: 0; } .editor-preview, .editor-statusbar { @@ -398,11 +457,16 @@ export default { .icon-info { display: inline-block; - width: 16px; + width: 32px; height: 16px; float: right; opacity: .7; } + + .icon-toggle, .icon-rename { + float: right; + margin-top: -14px; + } } aside::v-deep section { @@ -469,4 +533,13 @@ export default { .multiselect.multiselect--active::v-deep .multiselect__tags-wrap { z-index: 0; } + + #description-preview { + min-height: 100px; + + &::v-deep input { + min-height: auto; + } + } +