void;
-}> = ({ content, description, children, onClick }) => {
+}> = ({ content, description, children, placement = "top", onClick }) => {
const title = (
);
return (
-
+
{children}
);
diff --git a/packages/mini-markdown-editor/src/utils/handle-scroll.ts b/packages/mini-markdown-editor/src/utils/handle-scroll.ts
index 1c1d35f96e745113534abb41390efb781adea3e3..96e42c230512a3137c7b446a20e6213bc5d938f6 100644
--- a/packages/mini-markdown-editor/src/utils/handle-scroll.ts
+++ b/packages/mini-markdown-editor/src/utils/handle-scroll.ts
@@ -14,6 +14,8 @@ class ScrollSynchronizer {
// 滚动动画的配置参数
private static readonly SCROLL_ANIMATION_DURATION = 100; // ms
private static readonly MIN_SCROLL_DISTANCE = 10; // px
+ // 添加底部滚动阈值
+ private static readonly BOTTOM_THRESHOLD = 1; // px
// 计算编辑器和预览区域高度的对应关系
private computeHeightMapping({ previewView, editorView }: InstancesType): void {
@@ -44,6 +46,13 @@ class ScrollSynchronizer {
);
if (!scrollElement || !targetElement) return;
+ // 顶部边界检查
+ if (scrollElement.scrollTop <= 0) {
+ targetElement.scrollTop = 0;
+ return;
+ }
+
+ // 底部边界检查
if (this.isScrolledToBottom(scrollElement)) {
this.scrollToBottom(targetElement);
return;
@@ -96,7 +105,22 @@ class ScrollSynchronizer {
// 判断是否滚动到底部
private isScrolledToBottom(element: Element): boolean {
- return element.scrollTop >= element.scrollHeight - element.clientHeight;
+ return (
+ element.scrollTop + element.clientHeight + ScrollSynchronizer.BOTTOM_THRESHOLD >=
+ element.scrollHeight
+ );
+ }
+
+ // 滚动到顶部
+ private scrollToTop(editorView: EditorView, previewView: HTMLElement): void {
+ editorView.scrollDOM.scrollTo({
+ top: 0,
+ behavior: "smooth",
+ });
+ previewView.scrollTo({
+ top: 0,
+ behavior: "smooth",
+ });
}
// 滚动到底部
@@ -173,24 +197,36 @@ class ScrollSynchronizer {
}
const ratio = Math.max(0, Math.min(1, (scrollTop - sourceList[index]) / sourceDistance));
- const targetScrollTop = targetList[index] + targetDistance * ratio;
+ let targetScrollTop = targetList[index] + targetDistance * ratio;
+
+ //* 边界修正
+ if (targetScrollTop < 0) targetScrollTop = 0; // 顶部边界保护
+ const maxScroll = targetList[targetList.length - 1];
+ if (targetScrollTop > maxScroll) targetScrollTop = maxScroll; // 底部边界保护
return { ratio, targetScrollTop };
}
// 处理编辑器滚动
public handleEditorScroll(editorView: EditorView, previewView: HTMLElement | null): void {
- if (!previewView || false) return;
+ if (!previewView) return;
this.computeHeightMapping({ previewView, editorView });
this.synchronizeScroll("editor", { editorView, previewView });
}
// 处理预览区滚动
public handlePreviewScroll(previewView: HTMLElement | null, editorView: EditorView): void {
- if (!previewView || false) return;
+ if (!previewView) return;
this.computeHeightMapping({ previewView, editorView });
this.synchronizeScroll("preview", { editorView, previewView });
}
+
+ // 处理双区域滚动到顶部
+ public handleScrollTop(editorView: EditorView, previewView: HTMLElement): void {
+ if (!previewView) return;
+ this.computeHeightMapping({ previewView, editorView });
+ this.scrollToTop(editorView, previewView);
+ }
}
//? 可选导出
@@ -203,3 +239,7 @@ export const handleEditorScroll = ({ editorView, previewView }: InstancesType):
export const handlePreviewScroll = ({ editorView, previewView }: InstancesType): void => {
scrollSynchronizer.handlePreviewScroll(previewView, editorView);
};
+
+export const handleScrollTop = ({ editorView, previewView }: InstancesType): void => {
+ scrollSynchronizer.handleScrollTop(editorView, previewView);
+};
diff --git a/packages/mini-markdown-editor/src/utils/insert-content.ts b/packages/mini-markdown-editor/src/utils/insert-content.ts
index ce74506248bc7d1fadf18da578945cae93e07df4..8012209d11ebdd3fc427499a9db4611da2a5a53f 100644
--- a/packages/mini-markdown-editor/src/utils/insert-content.ts
+++ b/packages/mini-markdown-editor/src/utils/insert-content.ts
@@ -37,6 +37,7 @@ class InsertContent {
});
}
+ // 模拟输入
public insertTextAtCursor(content: string) {
const view = this.editorView;
if (!view) return;