From b0185d0886be6f64889c5c35ba52afa1f8aa4438 Mon Sep 17 00:00:00 2001 From: d3m1d0v Date: Tue, 30 Jul 2024 12:53:16 +0300 Subject: [PATCH] fix(bundle): scroll to current line after moving cursor in markup mode --- demo/Playground.scss | 14 ++++ demo/Playground.tsx | 149 ++++++++++++++++++++++++++----------------- src/bundle/Editor.ts | 25 ++++++-- 3 files changed, 123 insertions(+), 65 deletions(-) diff --git a/demo/Playground.scss b/demo/Playground.scss index e24193f3..a1d8b89c 100644 --- a/demo/Playground.scss +++ b/demo/Playground.scss @@ -42,4 +42,18 @@ top: 8px; left: 8px; } + + &__actions { + display: flex; + gap: 8px; + } + + &__move-to-line { + display: flex; + column-gap: 4px; + } + + &__move-to-line-input { + width: 56px; + } } diff --git a/demo/Playground.tsx b/demo/Playground.tsx index daa43914..dbe8db5f 100644 --- a/demo/Playground.tsx +++ b/demo/Playground.tsx @@ -1,4 +1,4 @@ -import React, {CSSProperties, useCallback, useEffect} from 'react'; +import React, {CSSProperties, useCallback, useEffect, useState} from 'react'; import sanitize from '@diplodoc/transform/lib/sanitize'; import {Button, DropdownMenu} from '@gravity-ui/uikit'; @@ -8,6 +8,7 @@ import { MarkdownEditorMode, MarkdownEditorView, MarkupString, + NumberInput, RenderPreview, logger, markupToolbarConfigs, @@ -218,64 +219,75 @@ export const Playground = React.memo((props) => { Markdown Editor Playground {VERSION} - - isEmpty: {String(mdEditor.isEmpty())} - - } - > - { - mdEditor.clear(); - mdEditor.focus(); - }} - /> - { - mdEditor.append('> append'); - mdEditor.focus(); - }} - /> - { - mdEditor.prepend('> prepend'); - mdEditor.focus(); - }} - /> - { - mdEditor.replace('> replace'); - mdEditor.focus(); - }} - /> - { - mdEditor.moveCursor('start'); - mdEditor.focus(); - }} - /> - { - mdEditor.moveCursor('end'); - mdEditor.focus(); - }} - /> - { - mdEditor.moveCursor({line: 115}); - mdEditor.focus(); - }} - /> - +
+ + isEmpty: {String(mdEditor.isEmpty())} + + } + > + { + mdEditor.clear(); + mdEditor.focus(); + }} + /> + { + mdEditor.append('> append'); + mdEditor.focus(); + }} + /> + { + mdEditor.prepend('> prepend'); + mdEditor.focus(); + }} + /> + { + mdEditor.replace('> replace'); + mdEditor.focus(); + }} + /> + { + mdEditor.moveCursor('start'); + mdEditor.focus(); + }} + /> + { + mdEditor.moveCursor('end'); + mdEditor.focus(); + }} + /> + { + mdEditor.moveCursor({line: 115}); + mdEditor.focus(); + }} + /> + + {mdEditor.currentMode === 'markup' && ( + { + if (typeof line !== 'number' || Number.isNaN(line)) return; + mdEditor.moveCursor({line}); + mdEditor.focus(); + }} + /> + )} +

@@ -304,3 +316,22 @@ export const Playground = React.memo((props) => { }); Playground.displayName = 'Playground'; + +function MoveToLine({onClick}: {onClick: (value: number | undefined) => void}) { + const [line, setLine] = useState(0); + + return ( +
+ + +
+ ); +} diff --git a/src/bundle/Editor.ts b/src/bundle/Editor.ts index 758896e8..80c4362f 100644 --- a/src/bundle/Editor.ts +++ b/src/bundle/Editor.ts @@ -401,7 +401,15 @@ export class EditorImpl extends SafeEventEmitter implements EditorI return this.currentEditor.append(markup); } - moveCursor(position: 'start' | 'end' | {line: number}): void { + moveCursor( + position: + | 'start' + | 'end' + | { + /** 0-based line number */ + line: number; + }, + ): void { if (typeof position === 'object') { return this.moveCursorToLine(position.line); } @@ -409,16 +417,21 @@ export class EditorImpl extends SafeEventEmitter implements EditorI return this.currentEditor.moveCursor(position); } - private moveCursorToLine(line: number): void { + private moveCursorToLine(/** 0-based line number */ line: number): void { const mode = this.currentMode; switch (mode) { case 'markup': { - const lineNumber = line + 1; const view = this.markupEditor.cm; - if (lineNumber > 0 && lineNumber <= view.state.doc.lines) { - view.dispatch({selection: {anchor: view.state.doc.line(lineNumber).from}}); - } + + let cmLine = line + 1; // lines in codemirror is 1-based + cmLine = Math.max(cmLine, 1); + cmLine = Math.min(cmLine, view.state.doc.lines); + + view.dispatch({ + scrollIntoView: true, + selection: {anchor: view.state.doc.line(cmLine).from}, + }); break; }