perf: value editor maintains scroll offset after refresh #162

This commit is contained in:
Lykin 2024-02-26 11:38:12 +08:00
parent c4d41b12dc
commit ad2c4c432b
3 changed files with 41 additions and 31 deletions

View File

@ -3,6 +3,7 @@ import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import * as monaco from 'monaco-editor' import * as monaco from 'monaco-editor'
import usePreferencesStore from 'stores/preferences.js' import usePreferencesStore from 'stores/preferences.js'
import { useThemeVars } from 'naive-ui' import { useThemeVars } from 'naive-ui'
import { isEmpty } from 'lodash'
const props = defineProps({ const props = defineProps({
content: { content: {
@ -22,25 +23,35 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
keyPath: { offsetKey: {
type: String, type: String,
default: "", default: '',
}, },
rememberScroll: { keepOffset: {
type: Boolean, type: Boolean,
default: true, default: false,
} },
}) })
const emit = defineEmits(['reset', 'input', 'save']) const emit = defineEmits(['reset', 'input', 'save'])
const scrollTop = ref(0)
const themeVars = useThemeVars() const themeVars = useThemeVars()
/** @type {HTMLElement|null} */ /** @type {HTMLElement|null} */
const editorRef = ref(null) const editorRef = ref(null)
/** @type monaco.editor.IStandaloneCodeEditor */ /** @type monaco.editor.IStandaloneCodeEditor */
let editorNode = null let editorNode = null
const scrollOffset = { top: 0, left: 0 }
const updateScroll = () => {
if (editorNode != null) {
if (props.keepOffset && !isEmpty(props.offsetKey)) {
editorNode.setScrollPosition({ scrollTop: scrollOffset.top, scrollLeft: scrollOffset.left })
} else {
// reset offset if not needed
editorNode.setScrollPosition({ scrollTop: 0, scrollLeft: 0 })
}
}
}
const destroyEditor = () => { const destroyEditor = () => {
if (editorNode != null && editorNode.dispose != null) { if (editorNode != null && editorNode.dispose != null) {
@ -98,14 +109,17 @@ onMounted(async () => {
emit('save') emit('save')
}) })
if (props.rememberScroll) { editorNode.onDidScrollChange((event) => {
editorNode.onDidScrollChange((event) => { // save scroll offset when changes, ie. content changes
// Update scrolltop when scroll height changes, ie. content changes if (props.keepOffset && !event.scrollHeightChanged) {
if (!event.scrollHeightChanged) { scrollOffset.top = event.scrollTop
scrollTop.value = event.scrollTop scrollOffset.left = event.scrollLeft
} }
}) })
}
editorNode.onDidLayoutChange((event) => {
updateScroll()
})
// editorNode.onDidChangeModelLanguageConfiguration(() => { // editorNode.onDidChangeModelLanguageConfiguration(() => {
// editorNode?.getAction('editor.action.formatDocument')?.run() // editorNode?.getAction('editor.action.formatDocument')?.run()
@ -121,31 +135,25 @@ onMounted(async () => {
watch( watch(
() => props.content, () => props.content,
async (content, oldContent, onCleanup) => { async (content) => {
if (editorNode != null) { if (editorNode != null) {
editorNode.setValue(content) editorNode.setValue(content)
const disposable = editorNode.onDidLayoutChange(() => {
if (props.rememberScroll && scrollTop.value > 0) {
editorNode.setScrollTop(scrollTop.value)
}
});
onCleanup(() => disposable.dispose())
await nextTick(() => emit('reset', content)) await nextTick(() => emit('reset', content))
updateScroll()
} }
}, },
) )
watch( watch(
() => props.keyPath, () => props.offsetKey,
() => { () => {
// reset scroll offset when key changed
if (editorNode != null) { if (editorNode != null) {
scrollTop.value = 0 scrollOffset.top = 0
editorNode.setScrollTop(0) scrollOffset.left = 0
editorNode.setScrollPosition({ scrollTop: 0, scrollLeft: 0 })
} }
} },
) )
watch( watch(

View File

@ -149,7 +149,8 @@ defineExpose({
v-show="!props.loading" v-show="!props.loading"
:content="displayValue" :content="displayValue"
:loading="props.loading" :loading="props.loading"
:key-path="props.keyPath" :offset-key="props.keyPath"
keep-offset
class="flex-item-expand" class="flex-item-expand"
language="json" language="json"
style="height: 100%" style="height: 100%"

View File

@ -204,7 +204,8 @@ defineExpose({
:content="displayValue" :content="displayValue"
:language="viewLanguage" :language="viewLanguage"
:loading="props.loading" :loading="props.loading"
:key-path="props.keyPath" :offset-key="props.keyPath"
keep-offset
class="flex-item-expand" class="flex-item-expand"
style="height: 100%" style="height: 100%"
@input="onInput" @input="onInput"