文件
mynode/packages/editor/ProseMirror/plugins/linkTooltipPlugin.ts
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

95 行
3.0 KiB
TypeScript

import { EditorState, Plugin } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import schema from '../schema';
import { getEditorApi } from './joplinEditorApiPlugin';
import { EditorEventType } from '../../events';
import { OnEventCallback } from '../../types';
import jumpToHash from '../utils/jumpToHash';
// This plugin is similar to https://prosemirror.net/examples/tooltip/
class LinkTooltip {
private tooltip_: HTMLElement;
private tooltipContent_: HTMLButtonElement;
private onEditorEvent_: OnEventCallback|null = null;
public constructor(view: EditorView) {
this.tooltip_ = document.createElement('div');
this.tooltip_.classList.add('link-tooltip', '-hidden');
this.tooltipContent_ = document.createElement('button');
this.tooltipContent_.classList.add('link');
this.tooltipContent_.role = 'link';
this.tooltip_.appendChild(this.tooltipContent_);
view.dom.parentElement.appendChild(this.tooltip_);
this.update(view, null);
}
public update(view: EditorView, lastState: EditorState) {
const state = view.state;
this.onEditorEvent_ = getEditorApi(state).onEvent;
const sameSelection = lastState && state.selection.eq(lastState.selection);
const sameDoc = lastState && state.doc.eq(lastState.doc);
if (sameSelection && sameDoc) {
return;
}
const getMarksNearSelection = () => {
let marks = state.selection.$from.marks();
const parentStart = state.selection.$from.posAtIndex(0);
const indexBefore = state.selection.from - 1;
if (indexBefore >= parentStart) {
const beforeSelection = state.doc.resolve(indexBefore);
const marksJustBeforeSelection = beforeSelection.marks();
marks = marks.concat(marksJustBeforeSelection);
}
return marks;
};
const linkMark = getMarksNearSelection().find(mark => mark.type === schema.marks.link);
const show = state.selection.empty && linkMark;
if (!show) {
this.tooltip_.classList.add('-hidden');
this.tooltipContent_.onclick = () => {};
} else {
this.tooltipContent_.textContent = linkMark.attrs.href;
this.tooltipContent_.onclick = () => {
const href = linkMark.attrs.href;
if (href.startsWith('#')) {
const command = jumpToHash(href.substring(1));
command(view.state, view.dispatch, view);
} else {
this.onEditorEvent_({
kind: EditorEventType.FollowLink,
link: linkMark.attrs.href,
});
}
};
this.tooltip_.classList.remove('-hidden');
const position = view.coordsAtPos(state.selection.from);
// Fall back to document.body to support testing environments:
const parentBox = (this.tooltip_.offsetParent ?? document.body).getBoundingClientRect();
const tooltipBox = this.tooltip_.getBoundingClientRect();
this.tooltip_.style.top = `${position.top - parentBox.top + tooltipBox.height}px`;
this.tooltip_.style.left = `${Math.max(position.left - parentBox.left - tooltipBox.width / 2, 0)}px`;
}
}
public destroy() {
this.tooltip_.remove();
}
}
const linkTooltipPlugin = new Plugin({
view: view => new LinkTooltip(view),
});
export default linkTooltipPlugin;