Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
fix(FloatingButtons): group smartpicker button and drag handle together
- The button group follows the cursor now

- The button group is top-aligned to hovered paragraph node

- The drag handle button has cursor type `grab`

- Not displayed on mobile and full width view

Also removes block margin from link previews. We already have margin

between paragraphs and it makes the floating buttons look off otherwise.

Fixes: #7272

Fixes: #7604

Signed-off-by: Jonas <[email protected]>

[skip ci]
  • Loading branch information
mejo- authored and backportbot[bot] committed Oct 22, 2025
commit 612359e740b80d53529bcd99d69167cd0116c7dc
36 changes: 8 additions & 28 deletions src/components/Editor/ContentContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,7 @@
<EditorOutline />
</div>
<slot />
<DragHandle :editor="editor" class="drag-handle--button">
<NcButton type="tertiary-no-background" size="normal">
<template #icon>
<DragVerticalIcon :size="20" />
</template>
</NcButton>
</DragHandle>
<FloatingButtons v-if="!isMobile && !isFullWidth" />
<EditorContent
role="document"
class="editor__content text-editor__content"
Expand All @@ -30,27 +24,27 @@
</template>

<script>
import NcButton from '@nextcloud/vue/components/NcButton'
import { DragHandle } from '@tiptap/extension-drag-handle-vue-2'
import { useIsMobile } from '@nextcloud/vue/composables/useIsMobile'
import { EditorContent } from '@tiptap/vue-2'
import DragVerticalIcon from 'vue-material-design-icons/DragVertical.vue'
import { useEditor } from '../../composables/useEditor.ts'
import { useEditorWidth } from '../../composables/useEditorWidth.ts'
import EditorOutline from './EditorOutline.vue'
import FloatingButtons from './FloatingButtons.vue'
import { useOutlineStateMixin } from './Wrapper.provider.js'

export default {
name: 'ContentContainer',
components: {
EditorContent,
EditorOutline,
NcButton,
DragHandle,
DragVerticalIcon,
FloatingButtons,
},
mixins: [useOutlineStateMixin],
setup() {
const isMobile = useIsMobile()
const { editor } = useEditor()
return { editor }
const { isFullWidth } = useEditorWidth()
return { editor, isMobile, isFullWidth }
},
computed: {
showOutline() {
Expand All @@ -72,12 +66,6 @@ export default {
}
}

.ie {
.editor__content:deep(.ProseMirror) {
padding-top: 50px;
}
}

.text-editor__content-wrapper {
--side-width: calc((100% - var(--text-editor-max-width)) / 2);
display: grid;
Expand All @@ -104,12 +92,4 @@ export default {
}
}
}

.drag-handle--button {
color: var(--color-maxcontrast);
position: absolute;
left: -60px;
transform: translate(0, -20%);
padding-right: 24px;
}
</style>
112 changes: 112 additions & 0 deletions src/components/Editor/FloatingButtons.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<!--
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<template>
<DragHandle
:editor="editor"
class="floating-buttons"
:class="{ heading: isHeadingNode }"
:on-node-change="onNodeChange">
<NcButton
type="tertiary-no-background"
size="small"
:title="t('text', 'Insert below')"
@click="onOpenSmartPicker">
<template #icon>
<PlusIcon :size="16" />
</template>
</NcButton>
<NcButton
type="tertiary-no-background"
size="small"
class="drag-button"
:title="t('text', 'Click for options, hold to drag')">
<template #icon>
<DragVerticalIcon :size="16" />
</template>
</NcButton>
</DragHandle>
</template>

<script>
import { t } from '@nextcloud/l10n'
import NcButton from '@nextcloud/vue/components/NcButton'
import { DragHandle } from '@tiptap/extension-drag-handle-vue-2'
import DragVerticalIcon from 'vue-material-design-icons/DragVertical.vue'
import PlusIcon from 'vue-material-design-icons/Plus.vue'
import { useEditor } from '../../composables/useEditor.ts'

export default {
name: 'FloatingButtons',

components: {
DragHandle,
DragVerticalIcon,
NcButton,
PlusIcon,
},

setup() {
const { editor } = useEditor()
return { editor }
},

data() {
return {
node: null,
pos: -1,
}
},

computed: {
isHeadingNode() {
return this.node?.type === this.editor.schema.nodes.heading
},
},

methods: {
onNodeChange({ node, pos }) {
this.node = node
this.pos = pos
},
onOpenSmartPicker() {
if (!this.node || this.pos === -1) {
return
}

// Node has no children or just text children and no text content
const { schema } = this.editor
const emptyNode =
this.node.textContent.trim() === ''
&& (this.node.children.length === 0
|| this.node.children.every((n) => n.type === schema.nodes.text))

// Insert at the end of the node
const pos = emptyNode ? this.pos + 1 : this.pos + this.node.nodeSize
this.editor.chain().insertContentAt(pos, '/').focus().run()
},
t,
},
}
</script>

<style scoped lang="scss">
.floating-buttons {
display: flex;

&.heading {
margin-right: 16px;
}
}

.drag-button {
cursor: grab;

:deep(span),
:deep(svg) {
cursor: grab !important;
}
}
</style>
48 changes: 0 additions & 48 deletions src/components/Editor/SmartPickerMenu.vue

This file was deleted.

1 change: 1 addition & 0 deletions src/nodes/Preview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default {

:deep(div.widgets--list a.widget-default) {
color: var(--color-main-text);
margin: 0;
padding: 0;
text-decoration: none;
max-width: calc(100vw - 156px);
Expand Down
Loading