perf: value editor maintains scroll offset after refresh #162
This commit is contained in:
parent
c4d41b12dc
commit
ad2c4c432b
|
@ -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(
|
||||||
|
|
|
@ -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%"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue