From 1f1ed55ce0f4222b421b64671b4bfb21b6fe1e18 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Wed, 5 Feb 2025 20:36:41 +0800 Subject: [PATCH 1/4] =?UTF-8?q?refactor(mini-markdown-editor):=20=E7=B2=BE?= =?UTF-8?q?=E7=AE=80=E5=85=A8=E5=B1=8F=E6=98=BE=E7=A4=BA=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=8C=E6=89=A9=E5=B1=95tooltip=E7=BB=84=E4=BB=B6=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Toolbar/FullScreen.tsx | 5 ++--- .../src/components/base/IconTooltip.tsx | 6 ++++-- packages/mini-markdown-editor/src/utils/insert-content.ts | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/mini-markdown-editor/src/components/Toolbar/FullScreen.tsx b/packages/mini-markdown-editor/src/components/Toolbar/FullScreen.tsx index ef8c666..fe02de0 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/insert-content.ts b/packages/mini-markdown-editor/src/utils/insert-content.ts index ce74506..8012209 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; -- Gitee From ebf45bedd9fbebc2eb84af178fc60f12a3b8bb1a Mon Sep 17 00:00:00 2001 From: tabzzz Date: Wed, 5 Feb 2025 21:21:15 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(mini-markdown-editor):=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E7=8A=B6=E6=80=81=E6=A0=8F=E6=BB=9A=E5=8A=A8=E5=88=B0?= =?UTF-8?q?=E9=A1=B6=E9=83=A8=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Status/index.tsx | 17 ++++++++++++--- .../src/utils/handle-scroll.ts | 21 +++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/mini-markdown-editor/src/components/Status/index.tsx b/packages/mini-markdown-editor/src/components/Status/index.tsx index 81c84a7..14be80e 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/utils/handle-scroll.ts b/packages/mini-markdown-editor/src/utils/handle-scroll.ts index 1c1d35f..0439191 100644 --- a/packages/mini-markdown-editor/src/utils/handle-scroll.ts +++ b/packages/mini-markdown-editor/src/utils/handle-scroll.ts @@ -99,6 +99,12 @@ class ScrollSynchronizer { return element.scrollTop >= element.scrollHeight - element.clientHeight; } + // 滚动到顶部 + private scrollToTop(editorView: EditorView, previewView: HTMLElement): void { + editorView.scrollDOM.scrollTop = 0; + previewView.scrollTop = 0; + } + // 滚动到底部 private scrollToBottom(targetElement: Element): void { const targetScrollTop = targetElement.scrollHeight - targetElement.clientHeight; @@ -180,17 +186,24 @@ class ScrollSynchronizer { // 处理编辑器滚动 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 | null): void { + if (!previewView) return; + this.computeHeightMapping({ previewView, editorView }); + this.scrollToTop(editorView, previewView); + } } //? 可选导出 @@ -203,3 +216,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); +}; -- Gitee From 0313c1074b42a190ac01c08643398993cd63a79a Mon Sep 17 00:00:00 2001 From: tabzzz Date: Wed, 5 Feb 2025 22:44:50 +0800 Subject: [PATCH 3/4] =?UTF-8?q?chore(mini-markdown-editor):=20=E4=B8=BA?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E5=88=B0=E9=A1=B6=E9=83=A8=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=B9=B3=E6=BB=91=E6=BB=9A=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mini-markdown-editor/src/utils/handle-scroll.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/mini-markdown-editor/src/utils/handle-scroll.ts b/packages/mini-markdown-editor/src/utils/handle-scroll.ts index 0439191..4406d61 100644 --- a/packages/mini-markdown-editor/src/utils/handle-scroll.ts +++ b/packages/mini-markdown-editor/src/utils/handle-scroll.ts @@ -101,8 +101,14 @@ class ScrollSynchronizer { // 滚动到顶部 private scrollToTop(editorView: EditorView, previewView: HTMLElement): void { - editorView.scrollDOM.scrollTop = 0; - previewView.scrollTop = 0; + editorView.scrollDOM.scrollTo({ + top: 0, + behavior: "smooth", + }); + previewView.scrollTo({ + top: 0, + behavior: "smooth", + }); } // 滚动到底部 @@ -199,7 +205,7 @@ class ScrollSynchronizer { } // 处理双区域滚动到顶部 - public handleScrollTop(editorView: EditorView, previewView: HTMLElement | null): void { + public handleScrollTop(editorView: EditorView, previewView: HTMLElement): void { if (!previewView) return; this.computeHeightMapping({ previewView, editorView }); this.scrollToTop(editorView, previewView); -- Gitee From 826b7931b29aec24d2727a8afe4afe7e3a27530d Mon Sep 17 00:00:00 2001 From: tabzzz Date: Wed, 5 Feb 2025 23:15:52 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix(mini-markdown-editor):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=90=8C=E6=AD=A5=E6=BB=9A=E5=8A=A8=E6=97=B6=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E9=A1=B6=E9=83=A8/=E5=BA=95=E9=83=A8=E4=B8=8D?= =?UTF-8?q?=E5=87=86=E7=A1=AE=E7=9A=84bug=EF=BC=8C=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E9=83=A8=E5=88=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/utils/handle-scroll.ts | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/mini-markdown-editor/src/utils/handle-scroll.ts b/packages/mini-markdown-editor/src/utils/handle-scroll.ts index 4406d61..96e42c2 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,10 @@ class ScrollSynchronizer { // 判断是否滚动到底部 private isScrolledToBottom(element: Element): boolean { - return element.scrollTop >= element.scrollHeight - element.clientHeight; + return ( + element.scrollTop + element.clientHeight + ScrollSynchronizer.BOTTOM_THRESHOLD >= + element.scrollHeight + ); } // 滚动到顶部 @@ -185,7 +197,12 @@ 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 }; } -- Gitee