Skip to content
This repository has been archived by the owner on Nov 29, 2023. It is now read-only.

feat: add global drag and drop function to the editor #48

Merged
merged 28 commits into from Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
bf11888
wip: draggable
LIlGG Sep 13, 2023
834135d
feat: node draggable
LIlGG Sep 13, 2023
1d3a518
wip: add global drag and drop function to the editor
LIlGG Sep 14, 2023
4f3b20c
Merge branch 'main' of github.com:halo-sigs/richtext-editor into feat…
LIlGG Sep 14, 2023
7f11e02
wip: add global drag and drop function to the editor
LIlGG Sep 14, 2023
4b5ad7a
wip: add global drag and drop function to the editor
LIlGG Sep 14, 2023
8f882cc
wip: add global drag and drop function to the editor
LIlGG Sep 14, 2023
3962102
add global drag and drop function to the editor
LIlGG Sep 15, 2023
85c7684
add global drag and drop function to the editor
LIlGG Sep 15, 2023
8b5e63a
WIP: update docs
LIlGG Sep 15, 2023
4c514bf
Merge branch 'main' of github.com:halo-sigs/richtext-editor into feat…
LIlGG Sep 18, 2023
7b4c5f4
update docs
LIlGG Sep 18, 2023
2069192
Merge branch 'main' of github.com:halo-sigs/richtext-editor into feat…
LIlGG Sep 18, 2023
3d9ad73
Merge branch 'main' of github.com:halo-sigs/richtext-editor into feat…
LIlGG Sep 19, 2023
cebec3d
chore: optimize the method of obtaining the parent node
LIlGG Sep 20, 2023
36c2bfa
chore: optimize the method of obtaining the parent node
LIlGG Sep 21, 2023
5b57707
Merge branch 'main' of github.com:halo-sigs/richtext-editor into feat…
LIlGG Sep 21, 2023
5fe6bac
chore: optimize the method of obtaining the parent node
LIlGG Sep 22, 2023
86086da
chore: optimize the method of obtaining the parent node
LIlGG Sep 22, 2023
10b5ca5
chore: optimize the method of obtaining the parent node
LIlGG Sep 22, 2023
2123c22
chore: optimize the method of obtaining the parent node
LIlGG Sep 22, 2023
c234741
chore: optimize the method of obtaining the parent node
LIlGG Sep 25, 2023
b58ccc1
Merge branch 'main' of github.com:halo-sigs/richtext-editor into feat…
LIlGG Sep 25, 2023
038586c
chore: optimize the method of obtaining the parent node
LIlGG Sep 25, 2023
e05224a
update draggableItem type
LIlGG Sep 25, 2023
358dd1e
Merge branch 'main' of github.com:halo-sigs/richtext-editor into feat…
LIlGG Sep 25, 2023
1976b0e
fix paragraph
LIlGG Sep 25, 2023
c603390
fix bubble
LIlGG Sep 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
580 changes: 343 additions & 237 deletions docs/extension.md

Large diffs are not rendered by default.

Binary file added docs/extension.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion example/src/App.vue
Expand Up @@ -43,6 +43,7 @@ import {
RichTextEditor,
useEditor,
ExtensionIndent,
ExtensionDraggable,
ExtensionColumns,
ExtensionColumn,
ExtensionNodeSelected,
Expand All @@ -59,7 +60,11 @@ const editor = useEditor({
ExtensionBulletList,
ExtensionCode,
ExtensionDocument,
ExtensionDropcursor,
ExtensionDropcursor.configure({
width: 2,
class: "dropcursor",
color: "skyblue",
}),
ExtensionGapcursor,
ExtensionHardBreak,
ExtensionHeading,
Expand Down Expand Up @@ -102,6 +107,7 @@ const editor = useEditor({
ExtensionColor,
ExtensionFontSize,
ExtensionIndent,
ExtensionDraggable,
ExtensionColumns,
ExtensionColumn,
ExtensionNodeSelected,
Expand Down
37 changes: 17 additions & 20 deletions packages/editor/src/components/bubble/BubbleMenuPlugin.ts
Expand Up @@ -8,11 +8,14 @@ import { EditorState, Plugin, PluginKey } from "@tiptap/pm/state";
import type { EditorView } from "@tiptap/pm/view";
import tippy, { type Instance, type Props, sticky } from "tippy.js";

export interface TippyOptionProps extends Props {
fixed?: boolean;
}
export interface BubbleMenuPluginProps {
pluginKey: PluginKey | string;
editor: Editor;
element: HTMLElement;
tippyOptions?: Partial<Props>;
tippyOptions?: Partial<TippyOptionProps>;
updateDelay?: number;
shouldShow?:
| ((props: {
Expand Down Expand Up @@ -44,9 +47,9 @@ export class BubbleMenuView {

public preventHide = false;

public tippy: Instance | undefined;
public tippy: Instance<TippyOptionProps> | undefined;

public tippyOptions?: Partial<Props>;
public tippyOptions?: Partial<TippyOptionProps>;

public getRenderContainer?: BubbleMenuPluginProps["getRenderContainer"];

Expand Down Expand Up @@ -97,8 +100,6 @@ export class BubbleMenuView {
capture: true,
});
this.view.dom.addEventListener("dragstart", this.dragstartHandler);
// this.editor.on("focus", this.focusHandler);
// this.editor.on('blur', this.blurHandler);
this.tippyOptions = tippyOptions || {};
// Detaches menu content from its current parent
this.element.remove();
Expand All @@ -113,11 +114,6 @@ export class BubbleMenuView {
this.hide();
};

// focusHandler = () => {
// // we use `setTimeout` to make sure `selection` is already updated
// setTimeout(() => this.update(this.editor.view));
// };

blurHandler = ({ event }: { event: FocusEvent }) => {
if (this.preventHide) {
this.preventHide = false;
Expand Down Expand Up @@ -157,7 +153,7 @@ export class BubbleMenuView {
content: this.element,
interactive: true,
trigger: "manual",
placement: "top",
placement: "bottom-start",
hideOnClick: "toggle",
plugins: [sticky],
...Object.assign(
Expand All @@ -169,6 +165,7 @@ export class BubbleMenuView {
moveTransition: "transform 0.2s ease-in-out",
}
: {}),
fixed: true,
},
this.tippyOptions
),
Expand Down Expand Up @@ -203,24 +200,21 @@ export class BubbleMenuView {
const from = Math.min(...ranges.map((range) => range.$from.pos));
const to = Math.max(...ranges.map((range) => range.$to.pos));
// prevent the menu from being obscured
const tippyParentNode = this.tippy?.popper.parentNode;
const siblings =
tippyParentNode?.querySelectorAll("[data-tippy-root]") ?? [];
const placement = this.tippyOptions?.placement
? this.tippyOptions?.placement
: isNodeSelection(selection)
? siblings.length > 1
? ACTIVE_BUBBLE_MENUS.length > 1
? "bottom"
: "top"
: this.tippy.props.fixed
? "bottom-start"
: Math.abs(cursorAt - to) <= Math.abs(cursorAt - from)
? siblings.length > 1
? "top-start"
: "bottom-start"
? "bottom-start"
: "top-start";

const domAtPos = view.domAtPos(from).node as HTMLElement;
const nodeDOM = view.nodeDOM(from) as HTMLElement;
const node = nodeDOM || domAtPos;

const shouldShow =
this.editor.isEditable &&
this.shouldShow?.({
Expand All @@ -246,7 +240,7 @@ export class BubbleMenuView {
);
const offset = this.tippyOptions?.offset as [number, number];
const offsetX = offset?.[0] ?? 0;
const offsetY = otherBubbleMenus.length
let offsetY = otherBubbleMenus.length
? otherBubbleMenus.reduce((prev, instance, currentIndex, array) => {
const prevY = array[currentIndex - 1]
? array[currentIndex - 1]?.popperInstance?.state?.modifiersData
Expand All @@ -264,6 +258,9 @@ export class BubbleMenuView {
return prev;
}, 0)
: offset?.[1] ?? 10;
if (!offsetY) {
offsetY = 10;
}
this.tippy?.setProps({
offset: [offsetX, offsetY],
placement,
Expand Down
16 changes: 16 additions & 0 deletions packages/editor/src/extensions/audio/index.ts
Expand Up @@ -269,6 +269,22 @@ const Audio = Node.create<ExtensionOptions>({
],
};
},
getDraggable() {
return {
getRenderContainer({ dom }) {
let container = dom;
while (
container &&
!container.hasAttribute("data-node-view-wrapper")
) {
container = container.parentElement;
}
return {
el: container,
};
},
};
},
};
},
});
Expand Down
16 changes: 16 additions & 0 deletions packages/editor/src/extensions/blockquote/index.ts
Expand Up @@ -28,6 +28,22 @@ const Blockquote = TiptapBlockquote.extend<
},
};
},
getDraggable() {
return {
getRenderContainer({ dom }) {
let element: HTMLElement | null = dom;
while (element && element.parentElement) {
if (element.tagName === "BLOCKQUOTE") {
break;
}
element = element.parentElement;
}
return {
el: element,
};
},
};
},
};
},
});
Expand Down
16 changes: 16 additions & 0 deletions packages/editor/src/extensions/bullet-list/index.ts
Expand Up @@ -38,6 +38,22 @@ const BulletList = TiptapBulletList.extend<
},
};
},
getDraggable() {
return {
getRenderContainer({ dom }) {
let container = dom;
while (container && !(container.tagName === "LI")) {
container = container.parentElement as HTMLElement;
}
return {
el: container,
dragDomOffset: {
x: -12,
},
};
},
};
},
};
},
addExtensions() {
Expand Down
39 changes: 26 additions & 13 deletions packages/editor/src/extensions/code-block/code-block.ts
Expand Up @@ -74,6 +74,22 @@ const updateIndent = (tr: Transaction, type: IndentType): Transaction => {
return tr;
};

const getRenderContainer = (node: HTMLElement) => {
let container = node;
// 文本节点
if (container.nodeName === "#text") {
container = node.parentElement as HTMLElement;
}
while (
container &&
container.classList &&
!container.classList.contains("code-node")
) {
container = container.parentElement as HTMLElement;
}
return container;
};

export default CodeBlockLowlight.extend<
CustomCodeBlockLowlightOptions & CodeBlockLowlightOptions
>({
Expand Down Expand Up @@ -195,19 +211,7 @@ export default CodeBlockLowlight.extend<
return isActive(state, CodeBlockLowlight.name);
},
getRenderContainer: (node: HTMLElement) => {
let container = node;
// 文本节点
if (container.nodeName === "#text") {
container = node.parentElement as HTMLElement;
}
while (
container &&
container.classList &&
!container.classList.contains("code-node")
) {
container = container.parentElement as HTMLElement;
}
return container;
return getRenderContainer(node);
},
items: [
{
Expand All @@ -222,6 +226,15 @@ export default CodeBlockLowlight.extend<
],
};
},
getDraggable() {
return {
getRenderContainer({ dom }: { dom: HTMLElement }) {
return {
el: getRenderContainer(dom),
};
},
};
},
};
},
});
21 changes: 19 additions & 2 deletions packages/editor/src/extensions/columns/columns.ts
Expand Up @@ -19,6 +19,7 @@ import MdiDeleteForeverOutline from "~icons/mdi/delete-forever-outline?color=red
import { i18n } from "@/locales";
import { deleteNode } from "@/utils";
import MdiCollage from "~icons/mdi/collage";
import { ExtensionOptions } from "@/types";

declare module "@tiptap/core" {
interface Commands<ReturnType> {
Expand Down Expand Up @@ -168,7 +169,7 @@ const gotoCol = (state: EditorState, dispatch: any, type: GotoColType) => {
return false;
};

const Columns = Node.create({
const Columns = Node.create<ExtensionOptions>({
name: "columns",
group: "block",
priority: 10,
Expand All @@ -179,7 +180,6 @@ const Columns = Node.create({

addOptions() {
return {
...this.parent?.(),
HTMLAttributes: {
class: "columns",
},
Expand Down Expand Up @@ -295,6 +295,23 @@ const Columns = Node.create({
],
};
},
getDraggable() {
return {
getRenderContainer({ dom }) {
let container = dom;
while (container && !container.classList.contains("columns")) {
container = container.parentElement as HTMLElement;
}
return {
el: container,
dragDomOffset: {
y: -5,
},
};
},
allowPropagationDownward: true,
};
},
};
},

Expand Down