mirror of
https://github.com/ueberdosis/tiptap.git
synced 2025-01-19 06:43:02 +08:00
add global drag handle experiment
This commit is contained in:
parent
0a238d9c12
commit
24d2f38021
158
docs/src/demos/Experiments/GlobalDragHandle/DragHandle.js
Normal file
158
docs/src/demos/Experiments/GlobalDragHandle/DragHandle.js
Normal file
@ -0,0 +1,158 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { NodeSelection, Plugin } from 'prosemirror-state'
|
||||
import { serializeForClipboard } from 'prosemirror-view/src/clipboard'
|
||||
|
||||
function removeNode(node) {
|
||||
node.parentNode.removeChild(node)
|
||||
}
|
||||
|
||||
function absoluteRect(node) {
|
||||
const data = node.getBoundingClientRect()
|
||||
|
||||
return {
|
||||
top: data.top,
|
||||
left: data.left,
|
||||
width: data.width,
|
||||
}
|
||||
}
|
||||
|
||||
export default Extension.create({
|
||||
addProseMirrorPlugins() {
|
||||
function blockPosAtCoords(coords, view) {
|
||||
const pos = view.posAtCoords(coords)
|
||||
let node = view.domAtPos(pos.pos)
|
||||
|
||||
node = node.node
|
||||
|
||||
while (node && node.parentNode) {
|
||||
if (node.parentNode?.classList?.contains('ProseMirror')) { // todo
|
||||
break
|
||||
}
|
||||
|
||||
node = node.parentNode
|
||||
}
|
||||
|
||||
if (node && node.nodeType === 1) {
|
||||
const desc = view.docView.nearestDesc(node, true)
|
||||
|
||||
if (!(!desc || desc === view.docView)) {
|
||||
return desc.posBefore
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function dragStart(e, view) {
|
||||
view.composing = true
|
||||
|
||||
if (!e.dataTransfer) {
|
||||
return
|
||||
}
|
||||
|
||||
const coords = { left: e.clientX + 50, top: e.clientY }
|
||||
const pos = blockPosAtCoords(coords, view)
|
||||
|
||||
if (pos != null) {
|
||||
view.dispatch(view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos)))
|
||||
|
||||
const slice = view.state.selection.content()
|
||||
const { dom, text } = serializeForClipboard(view, slice)
|
||||
|
||||
e.dataTransfer.clearData()
|
||||
e.dataTransfer.setData('text/html', dom.innerHTML)
|
||||
e.dataTransfer.setData('text/plain', text)
|
||||
|
||||
const el = document.querySelector('.ProseMirror-selectednode')
|
||||
e.dataTransfer?.setDragImage(el, 0, 0)
|
||||
|
||||
view.dragging = { slice, move: true }
|
||||
}
|
||||
}
|
||||
|
||||
let dropElement
|
||||
const WIDTH = 28
|
||||
|
||||
return [
|
||||
new Plugin({
|
||||
view(editorView) {
|
||||
const element = document.createElement('div')
|
||||
|
||||
element.draggable = 'true'
|
||||
element.classList.add('global-drag-handle')
|
||||
element.addEventListener('dragstart', e => dragStart(e, editorView))
|
||||
dropElement = element
|
||||
document.body.appendChild(dropElement)
|
||||
|
||||
return {
|
||||
// update(view, prevState) {
|
||||
// },
|
||||
destroy() {
|
||||
removeNode(dropElement)
|
||||
dropElement = null
|
||||
},
|
||||
}
|
||||
},
|
||||
props: {
|
||||
handleDrop(view, event, slice, moved) {
|
||||
if (moved) {
|
||||
// setTimeout(() => {
|
||||
// console.log('remove selection')
|
||||
// view.dispatch(view.state.tr.deleteSelection())
|
||||
// }, 50)
|
||||
}
|
||||
},
|
||||
// handlePaste() {
|
||||
// alert(2)
|
||||
// },
|
||||
handleDOMEvents: {
|
||||
// drop(view, event) {
|
||||
// setTimeout(() => {
|
||||
// const node = document.querySelector('.ProseMirror-hideselection')
|
||||
// if (node) {
|
||||
// node.classList.remove('ProseMirror-hideselection')
|
||||
// }
|
||||
// }, 50)
|
||||
// },
|
||||
mousemove(view, event) {
|
||||
const coords = {
|
||||
left: event.clientX + WIDTH + 50,
|
||||
top: event.clientY,
|
||||
}
|
||||
const pos = view.posAtCoords(coords)
|
||||
|
||||
if (pos) {
|
||||
let node = view.domAtPos(pos?.pos)
|
||||
|
||||
if (node) {
|
||||
node = node.node
|
||||
while (node && node.parentNode) {
|
||||
if (node.parentNode?.classList?.contains('ProseMirror')) { // todo
|
||||
break
|
||||
}
|
||||
node = node.parentNode
|
||||
}
|
||||
|
||||
if (node instanceof Element) {
|
||||
const cstyle = window.getComputedStyle(node)
|
||||
const lineHeight = parseInt(cstyle.lineHeight, 10)
|
||||
// const top = parseInt(cstyle.marginTop, 10) + parseInt(cstyle.paddingTop, 10)
|
||||
const top = 0
|
||||
const rect = absoluteRect(node)
|
||||
const win = node.ownerDocument.defaultView
|
||||
|
||||
rect.top += win.pageYOffset + ((lineHeight - 24) / 2) + top
|
||||
rect.left += win.pageXOffset
|
||||
rect.width = `${WIDTH}px`
|
||||
|
||||
dropElement.style.left = `${-WIDTH + rect.left}px`
|
||||
dropElement.style.top = `${rect.top}px`
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
133
docs/src/demos/Experiments/GlobalDragHandle/index.vue
Normal file
133
docs/src/demos/Experiments/GlobalDragHandle/index.vue
Normal file
@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div v-if="editor">
|
||||
<editor-content :editor="editor" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Editor, EditorContent, defaultExtensions } from '@tiptap/vue-starter-kit'
|
||||
import DragHandle from './DragHandle.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EditorContent,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.editor = new Editor({
|
||||
extensions: [
|
||||
...defaultExtensions(),
|
||||
DragHandle,
|
||||
],
|
||||
content: `
|
||||
<p>paragraph 1</p>
|
||||
<p>paragraph 2</p>
|
||||
<p>paragraph 3</p>
|
||||
<ul>
|
||||
<li>list item 1</li>
|
||||
<li>list item 2</li>
|
||||
</ul>
|
||||
<pre>code</pre>
|
||||
`,
|
||||
onUpdate: () => {
|
||||
console.log(this.editor.getHTML())
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.editor.destroy()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.global-drag-handle {
|
||||
position: absolute;
|
||||
|
||||
&::after {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1rem;
|
||||
height: 1.25rem;
|
||||
content: '⠿';
|
||||
font-weight: 700;
|
||||
cursor: grab;
|
||||
background:#0D0D0D10;
|
||||
color: #0D0D0D50;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep {
|
||||
.ProseMirror {
|
||||
padding: 0 1rem;
|
||||
|
||||
> * + * {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: rgba(#616161, 0.1);
|
||||
color: #616161;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #0D0D0D;
|
||||
color: #FFF;
|
||||
font-family: 'JetBrainsMono', monospace;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
code {
|
||||
color: inherit;
|
||||
background: none;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding-left: 1rem;
|
||||
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 2px solid rgba(#0D0D0D, 0.1);
|
||||
margin: 2rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ProseMirror-selectednode {
|
||||
outline: 2px solid #70CFF8;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -4,6 +4,7 @@ Congratulations! You’ve found our playground with a list of experiments. Be aw
|
||||
## New
|
||||
* [Linter](/experiments/linter)
|
||||
* [Multiple editors](/experiments/multiple-editors)
|
||||
* [Global drag handle](/experiments/global-drag-handle)
|
||||
* [@tiptap/extension-slash-command?](/experiments/commands)
|
||||
* [@tiptap/extension-iframe?](/experiments/embeds)
|
||||
* [@tiptap/extension-toggle-list?](/experiments/details)
|
||||
|
5
docs/src/docPages/experiments/global-drag-handle.md
Normal file
5
docs/src/docPages/experiments/global-drag-handle.md
Normal file
@ -0,0 +1,5 @@
|
||||
# GlobalDragHandle
|
||||
|
||||
⚠️ Experiment
|
||||
|
||||
<demo name="Experiments/GlobalDragHandle" />
|
Loading…
Reference in New Issue
Block a user