diff --git a/packages/mini-markdown-editor/src/components/Status/index.tsx b/packages/mini-markdown-editor/src/components/Status/index.tsx index 81c84a7196e62e4470b679b0e433f78491190f9b..14be80eda7056f83d137466ae3094caac4ecec7c 100644 --- a/packages/mini-markdown-editor/src/components/Status/index.tsx +++ b/packages/mini-markdown-editor/src/components/Status/index.tsx @@ -3,6 +3,7 @@ import styled from "styled-components"; import { useEditorContentStore } from "@/store/editor"; import { Checkbox } from "antd"; import type { CheckboxProps } from "antd"; +import { handleScrollTop } from "@/utils/handle-scroll"; const StatusWrapper = styled.div` width: 100%; @@ -43,17 +44,25 @@ const Status: FC<{ isSyncScroll: boolean; updateSyncScrollStatus: (val: boolean) isSyncScroll, updateSyncScrollStatus, }) => { - const content = useEditorContentStore((state) => state.content); + const { content, editorView, previewView } = useEditorContentStore(); const contentNum = useMemo(() => { return content.replace(/[\s\n]/g, "").length; }, [content]); - // 状态改变处理函数 + // 同步滚动状态改变处理函数 const handleSyncScrollChange: CheckboxProps["onChange"] = (e) => { updateSyncScrollStatus(e.target.checked); }; + // 滚动到顶部 + const handleAreaScrollTop = () => { + if (editorView && previewView) { + handleScrollTop({ editorView, previewView }); + } + // TODO: 处理同步滚动后的操作 + }; + return (
字数: {contentNum}
@@ -61,7 +70,9 @@ const Status: FC<{ isSyncScroll: boolean; updateSyncScrollStatus: (val: boolean) 同步滚动 -
滚动到顶部
+
+ 滚动到顶部 +
); diff --git a/packages/mini-markdown-editor/src/components/Toolbar/FullScreen.tsx b/packages/mini-markdown-editor/src/components/Toolbar/FullScreen.tsx index ef8c666e6271bb441f6e51966e4616de3ad6431b..fe02de0c5c6efe7fc76b0da7c6cb626c38bb9316 100644 --- a/packages/mini-markdown-editor/src/components/Toolbar/FullScreen.tsx +++ b/packages/mini-markdown-editor/src/components/Toolbar/FullScreen.tsx @@ -5,17 +5,16 @@ import { useToolbarStore } from "@/store/toolbar"; import { Hotkey } from "@/common/hotkeys"; const FullScreen = () => { - const isFullScreen = useToolbarStore((state) => state.isFullScreen); - const setIsFullScreen = useToolbarStore((state) => state.setIsFullScreen); + const { isFullScreen, setIsFullScreen } = useToolbarStore(); return ( <> setIsFullScreen(!isFullScreen)} > - {/* {"全屏"} */}
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;