Merge pull request #290 from fantasticit/fix/20250211

fix: fix js error
This commit is contained in:
fantasticit
2025-02-16 13:21:13 +08:00
committed by GitHub
6 changed files with 478 additions and 417 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ const nextConfig = semi({
env: {
SERVER_API_URL: config.client.apiUrl,
COLLABORATION_API_URL: config.client.collaborationUrl,
ENABLE_OSS_S3: config.oss.s3.enable,
ENABLE_OSS_S3: config.oss.s3?.enable,
DNS_PREFETCH: (config.client.dnsPrefetch || '').split(' '),
SITE_URL: config.client.siteUrl,
},
@@ -47,6 +47,28 @@ export const Ad = () => {
const $form = useRef<FormApi>();
const [currentCover, setCurrentCover] = useState('');
const columns = useMemo(() => {
return [
...columns,
{
title: '操作',
dataIndex: 'id',
render: (id) => {
return (
<Button
type="danger"
onClick={() => {
deleteAd(id);
}}
>
删除
</Button>
);
},
},
];
}, [deleteAd]);
const setCover = useCallback((url) => {
$form.current.setValue('cover', url);
setCurrentCover(url);
@@ -111,29 +133,7 @@ export const Ad = () => {
<Form.Input label="链接" field="url" style={{ width: '100%' }} placeholder="请输入跳转链接"></Form.Input>
</Form>
</Modal>
<Table
columns={[
...columns,
{
title: '操作',
dataIndex: 'id',
render: (id) => {
return (
<Button
type="danger"
onClick={() => {
deleteAd(id);
}}
>
删除
</Button>
);
},
},
]}
dataSource={data}
pagination={false}
/>
<Table columns={columns} dataSource={data} pagination={false} />
</div>
)}
/>
@@ -1,341 +1,396 @@
import ReactDOM from 'react-dom';
import type { Editor } from '@tiptap/core';
import { Extension, findParentNodeClosestToPos } from '@tiptap/core';
import { Extension } from '@tiptap/core';
import { safePos } from 'tiptap/prose-utils';
import { ActiveNode, removePossibleTable, selectAncestorNodeByDom } from 'tiptap/prose-utils';
import type { Node } from 'prosemirror-model';
import { Fragment, Slice } from 'prosemirror-model';
import { NodeSelection, Plugin, PluginKey, TextSelection } from 'prosemirror-state';
import type { EditorView } from 'prosemirror-view';
// @ts-expect-error
import { __serializeForClipboard } from 'prosemirror-view';
import {
NodeSelection,
Plugin as PMPlugin,
PluginKey as PMPluginKey,
Selection,
TextSelection,
} from 'prosemirror-state';
import { findParentNodeClosestToPos } from 'prosemirror-utils';
import { __serializeForClipboard, Decoration, DecorationSet, EditorView } from 'prosemirror-view';
function findNodeByUuid(editor: Editor, uuid: string) {
let target = null;
let pos = -1;
const DragablePluginKey = new PMPluginKey('dragable');
export const Dragable = Extension.create({
name: 'dragable',
// @ts-ignore
addProseMirrorPlugins() {
let editorView: EditorView;
let dragHandleDOM: HTMLElement;
let activeNode: ActiveNode | null;
let activeSelection: Selection | null;
let dragging = false;
let mouseleaveTimer = null;
const createDragHandleDOM = () => {
const dom = document.createElement('div');
dom.className = 'dragable';
dom.draggable = true;
dom.setAttribute('data-drag-handle', 'true');
return dom;
};
const showDragHandleDOM = () => {
dragHandleDOM?.classList?.add('show');
dragHandleDOM?.classList?.remove('hide');
};
const hideDragHandleDOM = () => {
dragHandleDOM?.classList?.remove('show');
dragHandleDOM?.classList?.remove('active');
dragHandleDOM?.classList?.add('hide');
};
const renderDragHandleDOM = (view: EditorView, referenceRectDOM: HTMLElement) => {
const root = view.dom.parentElement;
if (!root) return;
const targetNodeRect = referenceRectDOM.getBoundingClientRect();
const rootRect = root.getBoundingClientRect();
const handleRect = dragHandleDOM.getBoundingClientRect();
let offsetX = -5;
if (referenceRectDOM.tagName === 'LI') {
offsetX = referenceRectDOM.getAttribute('data-checked') ? -3 : -16;
if (editor && !editor.isDestroyed) {
editor.state.doc.descendants((node, p) => {
if (node.attrs.uuid === uuid) {
target = node;
pos = p;
return false;
}
});
}
const left = targetNodeRect.left - rootRect.left - handleRect.width + offsetX;
const top = targetNodeRect.top - rootRect.top + handleRect.height / 2 + root.scrollTop;
return { node: target, pos };
}
const offsetLeft = 0;
export interface GlobalDragHandleOptions {
dragHandleWidth: number;
scrollTreshold: number;
dragHandleSelector?: string;
}
dragHandleDOM.style.left = `${left + offsetLeft}px`;
dragHandleDOM.style.top = `${top - 2}px`;
function absoluteRect(node: Element) {
const data = node.getBoundingClientRect();
showDragHandleDOM();
};
return {
top: data.top,
left: data.left,
width: data.width,
};
}
const handleMouseEnter = () => {
if (!activeNode) return null;
function nodeDOMAtCoords(coords: { x: number; y: number }) {
return document
.elementsFromPoint(coords.x, coords.y)
.find(
(elem: Element) =>
elem.parentElement?.matches?.('.ProseMirror') ||
elem.matches(['li', 'p:not(:first-child)', 'pre', 'blockquote', 'h1, h2, h3, h4, h5, h6'].join(', '))
);
}
clearTimeout(mouseleaveTimer);
showDragHandleDOM();
};
function nodePosAtDOM(node: Element, view: EditorView, options: GlobalDragHandleOptions) {
const boundingRect = node.getBoundingClientRect();
const handleMouseLeave = () => {
if (!activeNode) return null;
hideDragHandleDOM();
};
return view.posAtCoords({
left: boundingRect.left + 50 + options.dragHandleWidth,
top: boundingRect.top + 1,
})?.inside;
}
const handleMouseDown = () => {
if (!activeNode) return null;
function calcNodePos(pos: number, view: EditorView) {
const $pos = view.state.doc.resolve(pos);
if ($pos.depth > 1) return $pos.before($pos.depth);
return pos;
}
if (NodeSelection.isSelectable(activeNode.node)) {
const nodeSelection = NodeSelection.create(editorView.state.doc, activeNode.$pos.pos - activeNode.offset);
editorView.dispatch(editorView.state.tr.setSelection(nodeSelection));
editorView.focus();
activeSelection = nodeSelection;
return nodeSelection;
export function DragHandlePlugin(options: GlobalDragHandleOptions & { pluginKey: string; editor: Editor }) {
const editor = options.editor;
let listType = '';
let dragHandleElement: HTMLElement | null = null;
let activeNode: { node: Node; pos: number } | null = null;
const handleDragStart = (event: DragEvent) => {
const view = options.editor.view;
view.focus();
if (!event.dataTransfer) return;
const node = nodeDOMAtCoords({
x: event.clientX + 50 + options.dragHandleWidth,
y: event.clientY,
});
if (!(node instanceof Element)) return;
if (activeNode && NodeSelection.isSelectable(activeNode.node)) {
const selection = NodeSelection.create(view.state.doc, activeNode.pos);
view.dispatch(view.state.tr.setSelection(selection));
} else {
let draggedNodePos = nodePosAtDOM(node, view, options);
if (draggedNodePos == null || draggedNodePos < 0) return;
draggedNodePos = calcNodePos(draggedNodePos, view);
const { from, to } = view.state.selection;
const diff = from - to;
const fromSelectionPos = calcNodePos(from, view);
let differentNodeSelected = false;
const nodePos = view.state.doc.resolve(fromSelectionPos);
// Check if nodePos points to the top level node
if (nodePos.node().type.name === 'doc') {
differentNodeSelected = true;
} else {
const nodeSelection = NodeSelection.create(view.state.doc, nodePos.before());
// Check if the node where the drag event started is part of the current selection
differentNodeSelected = !(
draggedNodePos + 1 >= nodeSelection.$from.pos && draggedNodePos <= nodeSelection.$to.pos
);
}
let selection = view.state.selection;
if (!differentNodeSelected && diff !== 0 && !(view.state.selection instanceof NodeSelection)) {
const endSelection = NodeSelection.create(view.state.doc, to - 1);
selection = TextSelection.create(view.state.doc, draggedNodePos, endSelection.$to.pos);
} else {
selection = NodeSelection.create(view.state.doc, draggedNodePos);
return null;
};
const handleMouseUp = () => {
if (!dragging) return;
dragging = false;
activeSelection = null;
activeNode = null;
};
const handleDragStart = (event) => {
dragging = true;
if (event.dataTransfer && activeSelection) {
const slice = activeSelection.content();
event.dataTransfer.effectAllowed = 'copyMove';
const { dom, text } = __serializeForClipboard(editorView, slice);
event.dataTransfer.clearData();
event.dataTransfer.setData('text/html', dom.innerHTML);
event.dataTransfer.setData('text/plain', text);
event.dataTransfer.setDragImage(activeNode?.el as any, 0, 0);
editorView.dragging = {
slice,
move: true,
};
// select complete table instead of just a row
if ((selection as NodeSelection).node.type.name === 'tableRow') {
const $pos = view.state.doc.resolve(selection.from);
selection = NodeSelection.create(view.state.doc, $pos.before());
}
}
};
view.dispatch(view.state.tr.setSelection(selection));
}
return [
new PMPlugin({
key: DragablePluginKey,
view: (view) => {
if (view.editable) {
dragHandleDOM = createDragHandleDOM();
dragHandleDOM.addEventListener('mouseenter', handleMouseEnter);
dragHandleDOM.addEventListener('mouseleave', handleMouseLeave);
dragHandleDOM.addEventListener('mousedown', handleMouseDown);
dragHandleDOM.addEventListener('mouseup', handleMouseUp);
dragHandleDOM.addEventListener('dragstart', handleDragStart);
view.dom.parentNode?.appendChild(dragHandleDOM);
view.dom.parentNode.style = 'position: relative;';
// If the selected node is a list item, we need to save the type of the wrapping list e.g. OL or UL
if (view.state.selection instanceof NodeSelection && view.state.selection.node.type.name === 'listItem') {
listType = node.parentElement!.tagName.toUpperCase();
}
const slice = view.state.selection.content();
const { dom, text } = __serializeForClipboard(view, slice);
event.dataTransfer.clearData();
event.dataTransfer.setData('text/html', dom.innerHTML);
event.dataTransfer.setData('text/plain', text);
event.dataTransfer.effectAllowed = 'copyMove';
event.dataTransfer.setDragImage(node, 0, 0);
view.dragging = { slice, move: true };
};
const hideDragHandle = () => {
if (dragHandleElement) {
dragHandleElement.classList.add('hide');
}
activeNode = null;
};
const showDragHandle = () => {
if (dragHandleElement) {
dragHandleElement.classList.remove('hide');
}
};
const onDragHandleDrag = (e: DragEvent) => {
hideDragHandle();
const scrollY = window.scrollY;
if (e.clientY < options.scrollTreshold) {
window.scrollTo({ top: scrollY - 30, behavior: 'smooth' });
} else if (window.innerHeight - e.clientY < options.scrollTreshold) {
window.scrollTo({ top: scrollY + 30, behavior: 'smooth' });
}
};
const showDragHandleAtDOM = (node: HTMLElement | undefined) => {
const notDragging = node?.closest('.not-draggable');
if (!(node instanceof Element) || node.matches('ul, ol') || notDragging) {
hideDragHandle();
return;
}
const compStyle = window.getComputedStyle(node);
const parsedLineHeight = Number.parseInt(compStyle.lineHeight, 10);
const lineHeight = Number.isNaN(parsedLineHeight) ? Number.parseInt(compStyle.fontSize) * 1.2 : parsedLineHeight;
const paddingTop = Number.parseInt(compStyle.paddingTop, 10);
const rect = absoluteRect(node);
rect.top += (lineHeight - 24) / 2;
rect.top += paddingTop;
if (node.matches('ul:not([data-type=taskList]) li, ol li')) {
rect.left -= options.dragHandleWidth;
}
rect.width = options.dragHandleWidth;
if (!dragHandleElement) return;
dragHandleElement.style.left = `${rect.left - rect.width - 12}px`;
dragHandleElement.style.top = `${rect.top}px`;
showDragHandle();
};
const onEditorUpdate = () => {
if (!editor.isEditable) {
hideDragHandle();
}
};
// const onSelectionUpdate = () => {
// if (!editor.isEditable)
// return
// const maybeNode = findParentNodeClosestToPos(editor.state.selection.$anchor, node => !node.isInline)
// if (!maybeNode)
// return
// const maybeDOM = findDomRefAtPos(maybeNode.pos, editor.view.domAtPos.bind(editor.view))
// if (maybeDOM) {
// showDragHandleAtDOM(maybeDOM as HTMLElement)
// activeNode = { node: maybeNode.node, pos: maybeNode.pos }
// }
// }
const callShowDraggableMenu = () => {
if (!dragHandleElement) return;
showDragHandle();
};
return new Plugin({
key: new PluginKey(options.pluginKey),
view: (view) => {
dragHandleElement = document.createElement('div');
dragHandleElement.draggable = true;
dragHandleElement.dataset.dragHandle = '';
dragHandleElement.classList.add('drag-handle');
dragHandleElement.addEventListener('dragstart', handleDragStart);
dragHandleElement.addEventListener('drag', onDragHandleDrag);
dragHandleElement.addEventListener('mousedown', callShowDraggableMenu);
dragHandleElement.addEventListener('mouseout', hideDragHandle);
hideDragHandle();
view?.dom?.parentElement?.appendChild(dragHandleElement);
// editor.on('selectionUpdate', onSelectionUpdate)
editor.on('update', onEditorUpdate);
return {
destroy: () => {
dragHandleElement?.removeEventListener('drag', onDragHandleDrag);
dragHandleElement?.removeEventListener('dragstart', handleDragStart);
dragHandleElement?.removeEventListener('mousedown', callShowDraggableMenu);
dragHandleElement?.remove?.();
dragHandleElement = null;
// editor.off('selectionUpdate', onSelectionUpdate)
editor.off('update', onEditorUpdate);
},
};
},
props: {
handleDOMEvents: {
mousemove: (view, event) => {
if (!view.editable) {
return;
}
return {
update(view) {
editorView = view;
},
destroy: () => {
if (!dragHandleDOM) return;
clearTimeout(mouseleaveTimer);
ReactDOM.unmountComponentAtNode(dragHandleDOM);
dragHandleDOM.removeEventListener('mouseenter', handleMouseEnter);
dragHandleDOM.removeEventListener('mouseleave', handleMouseLeave);
dragHandleDOM.removeEventListener('mousedown', handleMouseDown);
dragHandleDOM.removeEventListener('mouseup', handleMouseUp);
dragHandleDOM.removeEventListener('dragstart', handleDragStart);
dragHandleDOM.remove();
},
const coords = {
x: event.clientX + 50 + options.dragHandleWidth,
y: event.clientY,
};
},
props: {
// @ts-ignore
handleDOMEvents: {
drop: (view, event: DragEvent) => {
if (!view.editable || !dragHandleDOM) return false;
if (!activeSelection) return false;
const maybePos = editor.view.posAtCoords({ left: coords.x, top: coords.y });
let dom = nodeDOMAtCoords(coords) as HTMLElement | null;
const eventPos = view.posAtCoords({
left: event.clientX,
top: event.clientY,
});
while (dom && dom.nodeType === 3) {
dom = dom.parentElement;
}
setTimeout(() => {
if (activeSelection) {
[
'ProseMirror-selectednode',
'ProseMirror-selectedblocknode-dragable',
'ProseMirror-selectedblocknode-normal',
].forEach((cls) => {
(view.dom as HTMLElement).querySelectorAll(`.${cls}`).forEach((dom) => dom.classList.remove(cls));
});
const noneSelection = new TextSelection(
view.state.doc.resolve(safePos(view.state, eventPos?.pos ?? 0))
);
view.dispatch(view.state.tr.setSelection(noneSelection));
this.editor.commands.blur();
if (dom?.closest(`[data-node-view-wrapper]`)) {
dom = dom?.closest(`[data-node-view-wrapper]`);
}
activeSelection = null;
activeNode = null;
}
}, 100);
if (dom?.closest(`.columns`)) {
dom = dom?.closest(`.columns`);
}
if (!eventPos) {
return true;
}
if (dom?.closest(`table`)) {
dom = dom?.closest(`table`);
}
const maybeTitle = findParentNodeClosestToPos(
view.state.doc.resolve(safePos(this.editor.state, eventPos.pos)),
(node) => node.type.name === 'title'
);
if (!dom) {
hideDragHandle();
return false;
}
// 不允许在 title 处放置
if (eventPos.pos === 0 || maybeTitle) {
return true;
}
let maybeNode;
if (dragging) {
const tr = removePossibleTable(view, event);
dragging = false;
if (tr) {
view.dispatch(tr);
event.preventDefault();
return true;
}
}
if (dom.getAttribute('data-uuid')) {
maybeNode = findNodeByUuid(editor, dom.getAttribute('data-uuid')!);
}
if (!maybeNode) {
const p = editor.view.posAtDOM(dom, 1);
if (!p || p < 0) {
hideDragHandle();
return false;
},
mousemove: (view, event) => {
if (!view.editable || !dragHandleDOM) return false;
}
const maybeAfter = editor.state.doc.resolve(p)?.nodeBefore;
const coords = { left: event.clientX, top: event.clientY };
const pos = view.posAtCoords(coords);
if (!pos || !pos.pos) return false;
let dom = view.nodeDOM(pos.pos) || view.domAtPos(pos.pos)?.node || event.target;
const maybeTaskItemOrListItem = findParentNodeClosestToPos(view.state.doc.resolve(pos.pos), (node) =>
['taskItem', 'listItem'].some((name) => name === node.type.name)
);
if (!dom) {
if (dragging) return false;
hideDragHandleDOM();
return false;
}
while (dom && dom.nodeType === 3) {
dom = dom.parentElement;
}
// 选中列表项
if (maybeTaskItemOrListItem) {
while (dom && dom.tagName !== 'LI') {
dom = dom.parentElement;
}
}
if (dom.tagName === 'LI') {
if (dom?.parentElement?.childElementCount === 1) {
return false;
}
}
// 不允许选中整个列表
if (dom.tagName === 'UL' || dom.tagName === 'OL') {
return false;
}
try {
let maybeReactRenderer: HTMLElement | null = dom;
while (maybeReactRenderer && !maybeReactRenderer.classList?.contains('react-renderer')) {
maybeReactRenderer = maybeReactRenderer.parentElement;
}
if (maybeReactRenderer && !maybeReactRenderer?.classList?.contains('node-columns')) {
dom = maybeReactRenderer;
}
} catch (e) {
//
}
if (!(dom instanceof Element)) {
if (dragging) return false;
hideDragHandleDOM();
return false;
}
const result = selectAncestorNodeByDom(dom, view);
if (
!result ||
result.node.type.name === 'doc' ||
result.node.type.name === 'title' ||
result.node.type.name === 'tableOfContents'
) {
if (dragging) return false;
hideDragHandleDOM();
return false;
}
activeNode = result;
renderDragHandleDOM(view, result.el);
return false;
},
keydown: () => {
if (!editorView.editable || !dragHandleDOM) return false;
hideDragHandleDOM();
return false;
},
mouseleave: () => {
clearTimeout(mouseleaveTimer);
mouseleaveTimer = setTimeout(() => {
hideDragHandleDOM();
}, 400);
return false;
},
},
},
}),
new PMPlugin({
key: new PMPluginKey('AncestorDragablePluginFocusKey'),
props: {
decorations(state) {
const usingActiveSelection = !!activeSelection;
const selection = state.selection;
if (selection instanceof NodeSelection) {
const { from, to } = selection;
return DecorationSet.create(state.doc, [
Decoration.node(safePos(state, from), safePos(state, to), {
class: usingActiveSelection
? 'ProseMirror-selectedblocknode-dragable'
: 'ProseMirror-selectedblocknode-normal',
}),
]);
if (maybeAfter && !maybeAfter.isInline) {
maybeNode = { node: maybeAfter, pos: p };
}
return DecorationSet.empty;
},
if (!maybeNode && maybePos?.pos) {
maybeNode = findParentNodeClosestToPos(editor.state.doc.resolve(maybePos?.pos), (node) => {
return !node.isInline;
});
}
}
if (!maybeNode) return;
activeNode = { node: maybeNode.node!, pos: maybeNode.pos };
showDragHandleAtDOM(dom as HTMLElement);
},
}),
];
keydown: () => {
hideDragHandle();
},
mousewheel: () => {
hideDragHandle();
},
dragstart: (view) => {
view.dom.classList.add('dragging');
},
drop: (view, event) => {
view.dom.classList.remove('dragging');
hideDragHandle();
let droppedNode: Node | null = null;
const dropPos = view.posAtCoords({
left: event.clientX,
top: event.clientY,
});
if (!dropPos) return;
if (view.state.selection instanceof NodeSelection) {
droppedNode = view.state.selection.node;
}
if (!droppedNode) return;
const resolvedPos = view.state.doc.resolve(dropPos.pos);
const isDroppedInsideList = resolvedPos.parent.type.name === 'listItem';
if (
view.state.selection instanceof NodeSelection &&
view.state.selection.node.type.name === 'listItem' &&
!isDroppedInsideList &&
listType === 'OL'
) {
const text = droppedNode.textContent;
if (!text) return;
const paragraph = view.state.schema.nodes.paragraph?.createAndFill({}, view.state.schema.text(text));
const listItem = view.state.schema.nodes.listItem?.createAndFill({}, paragraph);
const newList = view.state.schema.nodes.orderedList?.createAndFill(null, listItem);
const slice = new Slice(Fragment.from(newList), 0, 0);
view.dragging = { slice, move: event.ctrlKey };
}
},
dragend: (view) => {
view.dom.classList.remove('dragging');
},
},
},
});
}
export const Dragable = Extension.create({
name: 'drag',
addOptions() {
return {
dragHandleWidth: 20,
scrollTreshold: 100,
dragHandleSelector: 'drag-handle',
};
},
addProseMirrorPlugins() {
return [
this.editor.isEditable &&
DragHandlePlugin({
editor: this.editor,
pluginKey: 'drag-handle',
dragHandleWidth: this.options.dragHandleWidth,
scrollTreshold: this.options.scrollTreshold,
dragHandleSelector: this.options.dragHandleSelector,
}),
].filter(Boolean) as Plugin[];
},
});
@@ -27,7 +27,7 @@ const reuploadImageAndUpdateSrc = async (img: HTMLImageElement) => {
}
const blob = await resp.blob();
const url = await uploadFile?.(blob);
const url = await uploadFile?.(blob as File);
img.src = url;
return true;
@@ -150,87 +150,87 @@ export const Paste = Extension.create<IPasteOptions>({
new Plugin({
key: new PluginKey('paste'),
props: {
handlePaste: (view, event: ClipboardEvent) => {
if (view.props.editable && !view.props.editable(view.state)) {
return false;
}
// handlePaste: (view, event: ClipboardEvent) => {
// if (view.props.editable && !view.props.editable(view.state)) {
// return false;
// }
if (!event.clipboardData) return false;
// if (!event.clipboardData) return false;
const files = Array.from(event.clipboardData.files);
const text = event.clipboardData.getData('text/plain');
const html = event.clipboardData.getData('text/html');
const vscode = event.clipboardData.getData('vscode-editor-data');
const node = event.clipboardData.getData('text/node');
const markdownText = event.clipboardData.getData('text/markdown');
const { state, dispatch } = view;
// const files = Array.from(event.clipboardData.files);
// const text = event.clipboardData.getData('text/plain');
// const html = event.clipboardData.getData('text/html');
// const vscode = event.clipboardData.getData('vscode-editor-data');
// const node = event.clipboardData.getData('text/node');
// const markdownText = event.clipboardData.getData('text/markdown');
// const { state, dispatch } = view;
debug(() => {
console.group('paste');
console.log({ text, vscode, node, markdownText, files });
console.log(html);
console.groupEnd();
});
// debug(() => {
// console.group('paste');
// console.log({ text, vscode, node, markdownText, files });
// console.log(html);
// console.groupEnd();
// });
// 直接复制节点
if (node) {
const json = safeJSONParse(node);
const tr = view.state.tr;
const selection = tr.selection;
view.dispatch(tr.insert(selection.from - 1, view.state.schema.nodeFromJSON(json)).scrollIntoView());
return true;
}
// // 直接复制节点
// if (node) {
// const json = safeJSONParse(node);
// const tr = view.state.tr;
// const selection = tr.selection;
// view.dispatch(tr.insert(selection.from - 1, view.state.schema.nodeFromJSON(json)).scrollIntoView());
// return true;
// }
// 链接
if (isValidURL(text)) {
if (!state.selection.empty) {
toggleMark(this.editor.schema.marks.link, { href: text })(state, dispatch);
return true;
}
// // 链接
// if (isValidURL(text)) {
// if (!state.selection.empty) {
// toggleMark(this.editor.schema.marks.link, { href: text })(state, dispatch);
// return true;
// }
const transaction = view.state.tr
.insertText(text, state.selection.from, state.selection.to)
.addMark(
state.selection.from,
state.selection.to + text.length,
state.schema.marks.link.create({ href: text })
);
view.dispatch(transaction);
return true;
}
// const transaction = view.state.tr
// .insertText(text, state.selection.from, state.selection.to)
// .addMark(
// state.selection.from,
// state.selection.to + text.length,
// state.schema.marks.link.create({ href: text })
// );
// view.dispatch(transaction);
// return true;
// }
// 粘贴代码
if (isInCode(view.state)) {
event.preventDefault();
view.dispatch(view.state.tr.insertText(text).scrollIntoView());
return true;
}
// // 粘贴代码
// if (isInCode(view.state)) {
// event.preventDefault();
// view.dispatch(view.state.tr.insertText(text).scrollIntoView());
// return true;
// }
const vscodeMeta = vscode ? JSON.parse(vscode) : undefined;
const pasteCodeLanguage = vscodeMeta?.mode;
// const vscodeMeta = vscode ? JSON.parse(vscode) : undefined;
// const pasteCodeLanguage = vscodeMeta?.mode;
if (html.length > 0) {
return htmlToProsemirror(editor, html);
}
// if (html.length > 0) {
// return htmlToProsemirror(editor, html);
// }
const { markdownToHTML } = extensionThis.options;
// const { markdownToHTML } = extensionThis.options;
if ((markdownText || isMarkdown(text)) && markdownToHTML) {
event.preventDefault();
const html = markdownToHTML(normalizeMarkdown(markdownText || text));
if (html && html.length) return htmlToProsemirror(editor, html, true);
}
// if ((markdownText || isMarkdown(text)) && markdownToHTML) {
// event.preventDefault();
// const html = markdownToHTML(normalizeMarkdown(markdownText || text));
// if (html && html.length) return htmlToProsemirror(editor, html, true);
// }
if (files.length) {
event.preventDefault();
files.forEach((file) => {
handleFileEvent({ editor, file });
});
return true;
}
// if (files.length) {
// event.preventDefault();
// files.forEach((file) => {
// handleFileEvent({ editor, file });
// });
// return true;
// }
return false;
},
// return false;
// },
handleDrop: (view, event: any) => {
if (view.props.editable && !view.props.editable(view.state)) {
return false;
@@ -1,27 +1,31 @@
[data-drag-handle] {
position: absolute;
z-index: 100;
display: inline;
width: 16px;
height: 16px;
cursor: move;
background-color: #fff;
opacity: 0;
transition: opacity 0.3s ease-out;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='16' height='16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='3' y='1' width='3' height='3' rx='1.5' fill='%23111'/%3E%3Crect x='10' y='1' width='3' height='3' rx='1.5' fill='%23111'/%3E%3Crect x='3' y='6' width='3' height='3' rx='1.5' fill='%23111'/%3E%3Crect x='10' y='6' width='3' height='3' rx='1.5' fill='%23111'/%3E%3Crect x='3' y='11' width='3' height='3' rx='1.5' fill='%23111'/%3E%3Crect x='10' y='11' width='3' height='3' rx='1.5' fill='%23111'/%3E%3C/svg%3E");
background-size: contain;
background-position: center 0;
.drag-handle {
position: fixed;
z-index: 50;
width: 1.4rem;
height: 1.4rem;
cursor: grab;
border: 1px solid var(--yunce-border-color);
border-radius: var(--yunce-border-radius);
opacity: 1;
transition: opacity ease-in 0.2s;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' style='fill: rgba(0, 0, 0, 0.5)'%3E%3Cpath d='M3,2 C2.44771525,2 2,1.55228475 2,1 C2,0.44771525 2.44771525,0 3,0 C3.55228475,0 4,0.44771525 4,1 C4,1.55228475 3.55228475,2 3,2 Z M3,6 C2.44771525,6 2,5.55228475 2,5 C2,4.44771525 2.44771525,4 3,4 C3.55228475,4 4,4.44771525 4,5 C4,5.55228475 3.55228475,6 3,6 Z M3,10 C2.44771525,10 2,9.55228475 2,9 C2,8.44771525 2.44771525,8 3,8 C3.55228475,8 4,8.44771525 4,9 C4,9.55228475 3.55228475,10 3,10 Z M7,2 C6.44771525,2 6,1.55228475 6,1 C6,0.44771525 6.44771525,0 7,0 C7.55228475,0 8,0.44771525 8,1 C8,1.55228475 7.55228475,2 7,2 Z M7,6 C6.44771525,6 6,5.55228475 6,5 C6,4.44771525 6.44771525,4 7,4 C7.55228475,4 8,4.44771525 8,5 C8,5.55228475 7.55228475,6 7,6 Z M7,10 C6.44771525,10 6,9.55228475 6,9 C6,8.44771525 6.44771525,8 7,8 C7.55228475,8 8,8.44771525 8,9 C8,9.55228475 7.55228475,10 7,10 Z'%3E%3C/path%3E%3C/svg%3E");
background-size: calc(0.5em + 0.375rem) calc(0.5em + 0.375rem);
background-repeat: no-repeat;
background-position: center;
&.show {
opacity: 0.3;
&:hover {
background-color: var(--yunce-hover-bg-color);
transition: background-color 0.2s;
}
&:hover {
opacity: 1;
}
&:active {
cursor: grabbing;
background-color: var(--yunce-hover-bg-color);
transition: background-color 0.2s;
}
&.hide {
pointer-events: none;
opacity: 0;
}
}
@@ -10,6 +10,8 @@
/* 自定义节点选中 */
--node-selected-border-color: rgb(0 101 255);
--yunce-border-color: #dee0e3;
--yunce-border-radius: 2px;
}
body[theme-mode='dark'] {