support multi bubble-menu

This commit is contained in:
fantasticit 2022-11-28 23:01:15 +08:00
parent 5a1a5c7c1a
commit d25ad50117
1 changed files with 44 additions and 1 deletions

View File

@ -27,6 +27,8 @@ export type BubbleMenuViewProps = BubbleMenuPluginProps & {
view: EditorView; view: EditorView;
}; };
const ACTIVE_BUBBLE_MENUS: Instance[] = [];
export class BubbleMenuView { export class BubbleMenuView {
public editor: Editor; public editor: Editor;
@ -178,7 +180,11 @@ export class BubbleMenuView {
const cursorAt = selection.$anchor.pos; const cursorAt = selection.$anchor.pos;
const from = Math.min(...ranges.map((range) => range.$from.pos)); const from = Math.min(...ranges.map((range) => range.$from.pos));
const to = Math.max(...ranges.map((range) => range.$to.pos)); const to = Math.max(...ranges.map((range) => range.$to.pos));
const placement = Math.abs(cursorAt - to) <= Math.abs(cursorAt - from) ? 'bottom' : 'top'; const placement = isNodeSelection(selection)
? 'top'
: Math.abs(cursorAt - to) <= Math.abs(cursorAt - from)
? 'bottom-start'
: 'top-start';
const domAtPos = view.domAtPos(from).node as HTMLElement; const domAtPos = view.domAtPos(from).node as HTMLElement;
const nodeDOM = view.nodeDOM(from) as HTMLElement; const nodeDOM = view.nodeDOM(from) as HTMLElement;
const node = nodeDOM || domAtPos; const node = nodeDOM || domAtPos;
@ -200,7 +206,28 @@ export class BubbleMenuView {
return; return;
} }
const otherBubbleMenus = ACTIVE_BUBBLE_MENUS.filter(
(instance) => instance.id !== this.tippy?.id && instance.popperInstance && instance.popperInstance.state
);
const offsetX = this.tippyOptions?.offset?.[0] ?? 0;
const offsetY = otherBubbleMenus.length
? otherBubbleMenus.reduce((prev, instance, currentIndex, array) => {
const prevY = array[currentIndex - 1]
? array[currentIndex - 1]?.popperInstance?.state?.modifiersData?.popperOffsets?.y ?? 0
: 0;
const currentY = instance?.popperInstance?.state?.modifiersData?.popperOffsets?.y ?? 0;
const currentHeight = instance?.popperInstance?.state?.rects?.popper?.height ?? 40;
if (Math.abs(prevY - currentY) <= currentHeight) {
prev += currentHeight;
}
return prev;
}, 0)
: this.tippyOptions?.offset?.[1] ?? 10;
this.tippy?.setProps({ this.tippy?.setProps({
offset: [offsetX, offsetY],
placement, placement,
getReferenceClientRect: () => { getReferenceClientRect: () => {
let toMountNode = null; let toMountNode = null;
@ -230,15 +257,31 @@ export class BubbleMenuView {
this.show(); this.show();
} }
addActiveBubbleMenu = () => {
const idx = ACTIVE_BUBBLE_MENUS.findIndex((instance) => instance?.id === this.tippy?.id);
if (idx < 0) {
ACTIVE_BUBBLE_MENUS.push(this.tippy);
}
};
removeActiveBubbleMenu = () => {
const idx = ACTIVE_BUBBLE_MENUS.findIndex((instance) => instance?.id === this.tippy?.id);
if (idx > -1) {
ACTIVE_BUBBLE_MENUS.splice(idx, 1);
}
};
show() { show() {
this.addActiveBubbleMenu();
this.tippy?.show(); this.tippy?.show();
} }
hide() { hide() {
this.removeActiveBubbleMenu();
this.tippy?.hide(); this.tippy?.hide();
} }
destroy() { destroy() {
this.removeActiveBubbleMenu();
this.tippy?.destroy(); this.tippy?.destroy();
this.element.removeEventListener('mousedown', this.mousedownHandler, { this.element.removeEventListener('mousedown', this.mousedownHandler, {
capture: true, capture: true,