Skip to content

Commit

Permalink
Merge pull request #6425 from nextcloud/fix/paragraph-node-view-to-de…
Browse files Browse the repository at this point in the history
…corations

fix(paragraph): migrate preview options to decorations
  • Loading branch information
mejo- authored Sep 19, 2024
2 parents 626b2d0 + 5d61e84 commit b2c45a9
Show file tree
Hide file tree
Showing 12 changed files with 430 additions and 159 deletions.
4 changes: 2 additions & 2 deletions cypress/e2e/nodes/PreviewOptions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ describe('Preview Options', function() {
})

it('should Remove link', function() {
cy.get('.paragraph-content').should('have.text', 'nextcloud.com')
cy.get('p > a').should('have.text', 'nextcloud.com')
cy.get('.action-button__text').contains('Remove link').click()
cy.get('.paragraph-content').should('not.have.text', 'nextcloud.com')
cy.get('p > a').should('not.exist')
})
})
72 changes: 60 additions & 12 deletions src/components/Editor/PreviewOptions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,31 @@
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<div>
<div contenteditable="false" class="preview-options-container">
<NcActions data-text-preview-options="select"
class="preview-options"
@open="$emit('open')">
:open.sync="open"
@open="onOpen">
<template #icon>
<DotsVerticalIcon :size="20" />
</template>
<NcActionCaption :name="t('text', 'Preview options')" />
<NcActionRadio data-text-preview-option="text-only"
close-after-click
name="preview-option"
value="text-only"
:checked="value === 'text-only'"
@change="e => $emit('update:value', e.currentTarget.value)">
:checked="type === 'text-only'"
@change="e => toggle(e.currentTarget.value)">
{{ t('text', 'Text only') }}
</NcActionRadio>
<NcActionRadio data-text-preview-option="link-preview"
close-after-click
name="preview-option"
value="link-preview"
:checked="value === 'link-preview'"
@change="e => $emit('update:value', e.currentTarget.value)">
:checked="type === 'link-preview'"
@change="e => toggle(e.currentTarget.value)">
{{ t('text', 'Show link preview') }}
</NcActionRadio>
<NcActionSeparator />
<NcActionButton @click="e => $emit('update:value', 'delete-preview')">
<NcActionButton close-after-click="true" @click="deleteNode">
<template #icon>
<DeleteIcon :size="20" />
</template>
Expand All @@ -45,6 +44,7 @@ import DeleteIcon from 'vue-material-design-icons/Delete.vue'
export default {
name: 'PreviewOptions',
components: {
DotsVerticalIcon,
NcActions,
Expand All @@ -54,24 +54,72 @@ export default {
NcActionSeparator,
DeleteIcon,
},
props: {
value: {
type: {
type: String,
required: true,
},
offset: {
type: Number,
required: true,
},
nodeSize: {
type: Number,
required: true,
},
$editor: {
type: Object,
required: true,
},
},
data() {
return {
open: false,
}
},
methods: {
onOpen() {
this.$editor.commands.hideLinkBubble()
},
toggle(type) {
this.open = false
const chain = this.$editor.chain().focus()
.setTextSelection(this.offset + 1)
if (type === 'text-only') {
chain.unsetPreview().run()
return
}
chain.setPreview().run()
},
deleteNode() {
this.$editor.commands.deleteRange({
from: this.offset,
to: this.offset + this.nodeSize,
})
},
},
}
</script>

<style lang="scss" scoped>
div[contenteditable=false] {
padding: 0;
margin: 0;
}
div[data-text-preview-options] {
.preview-options-container {
position: absolute;
width: 0 !important;
left: -44px;
top: 50%;
transform: translate(0, -50%);
}
// Inside details, button needs to be shifted further
.details-content div[data-text-preview-options] {
.details-content .preview-options-container {
left: calc(-44px - 24px);
}
Expand Down
6 changes: 4 additions & 2 deletions src/css/prosemirror.scss
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ div.ProseMirror {
padding: .5em 0;
}

p .paragraph-content {
p {
position: relative;
margin-bottom: 1em;
line-height: 150%;
}
Expand Down Expand Up @@ -280,7 +281,8 @@ div.ProseMirror {
position: relative;
padding-left: 3px;

p .paragraph-content {
p {
position: relative;
margin-bottom: 0.5em;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/Callout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default {
.callout__content {
margin-left: 1em;
&:deep(p .paragraph-content) {
&:deep(p) {
&:last-child {
margin-bottom: 0;
}
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/DetailsView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ div.details {
}
}
:deep(.details-content .paragraph-content:last-child) {
:deep(.details-content p:last-child) {
margin-bottom: 0.5em;
}
}
Expand Down
15 changes: 9 additions & 6 deletions src/nodes/Paragraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,10 @@
*/

import TiptapParagraph from '@tiptap/extension-paragraph'
import { VueNodeViewRenderer } from '@tiptap/vue-2'
import ParagraphView from './ParagraphView.vue'
import previewOptions from '../plugins/previewOptions.js'

const Paragraph = TiptapParagraph.extend({

addNodeView() {
return VueNodeViewRenderer(ParagraphView)
},

parseHTML() {
return this.parent().map(rule => Object.assign(rule, { preserveWhitespace: 'full' }))
},
Expand Down Expand Up @@ -40,6 +35,14 @@ const Paragraph = TiptapParagraph.extend({
},
}
},

addProseMirrorPlugins() {
return [
previewOptions({
editor: this.editor,
}),
]
},
})

export default Paragraph
100 changes: 0 additions & 100 deletions src/nodes/ParagraphView.vue

This file was deleted.

46 changes: 12 additions & 34 deletions src/nodes/Preview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
class="preview"
as="div"
contenteditable="false">
<NodeViewContent style="display:none" />
<PreviewOptions v-if="editor.isEditable"
:value.sync="value"
@update:value="changeViewMode" />
<NodeViewContent />
<NcReferenceList :text="node.attrs.href"
:limit="1"
:interactive="!extension.options.isEmbedded"
Expand All @@ -21,44 +18,24 @@
<script>
import { nodeViewProps, NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2'
import { NcReferenceList } from '@nextcloud/vue/dist/Components/NcRichText.js'
import PreviewOptions from '../components/Editor/PreviewOptions.vue'
import { useEditorMixin } from '../components/Editor.provider.js'
export default {
name: 'Preview',
components: {
NodeViewWrapper,
NodeViewContent,
NcReferenceList,
PreviewOptions,
},
mixins: [useEditorMixin],
props: nodeViewProps,
data() {
return {
value: 'link-preview',
}
},
methods: {
changeViewMode(value) {
if (value === 'delete-preview') {
this.deleteNode()
} else if (value === 'text-only') {
this.convertToParagraph()
}
},
convertToParagraph() {
this.$editor.chain()
.focus()
.setTextSelection(this.getPos() + 1)
.unsetPreview()
.run()
},
},
}
</script>

<style lang="scss" scoped>
// Hide the link element inside previews. We cannot hide full node view content
// as the preview options (prosemirror decorations) are inside it.
[data-text-el="preview"] [data-node-view-content] :deep(a) {
display: none;
}
.preview {
position: relative;
Expand All @@ -79,11 +56,12 @@ export default {
}
/* Top align with preview image:
* 7px .preview padding
* + widget margin-top
* - unset center alignment
* - margin-top of widget-default
*/
:deep([data-text-preview-options]) {
top: calc(7px + var(--default-grid-baseline, 4px) * 3);
:deep(.preview-options-container) {
top: unset;
transform: unset;
margin-top: calc(var(--default-grid-baseline, 4px) * 3) !important;
}
</style>
Loading

0 comments on commit b2c45a9

Please sign in to comment.