From c4d41b12dc3295161a9efc1595d957a74a7c4bad Mon Sep 17 00:00:00 2001 From: raojinlin Date: Mon, 26 Feb 2024 10:34:21 +0800 Subject: [PATCH] feat: value editor maintains scroll offset after refresh #162 * feat: refresh string value keep scrolltop * fix code styles * delete unused code * feat: Configurable and compatible with keyPath changes * Fix props name format, use kebab-case * Unify coding style --------- Co-authored-by: raojinlin --- .../content_value/ContentEditor.vue | 40 ++++++++++++++++++- .../content_value/ContentEntryEditor.vue | 1 + .../content_value/ContentValueJson.vue | 1 + .../content_value/ContentValueString.vue | 1 + 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/content_value/ContentEditor.vue b/frontend/src/components/content_value/ContentEditor.vue index b4508dc..e2c22cf 100644 --- a/frontend/src/components/content_value/ContentEditor.vue +++ b/frontend/src/components/content_value/ContentEditor.vue @@ -22,10 +22,20 @@ const props = defineProps({ type: Boolean, default: false, }, + keyPath: { + type: String, + default: "", + }, + rememberScroll: { + type: Boolean, + default: true, + } }) const emit = defineEmits(['reset', 'input', 'save']) +const scrollTop = ref(0) + const themeVars = useThemeVars() /** @type {HTMLElement|null} */ const editorRef = ref(null) @@ -88,6 +98,15 @@ onMounted(async () => { emit('save') }) + if (props.rememberScroll) { + editorNode.onDidScrollChange((event) => { + // Update scrolltop when scroll height changes, ie. content changes + if (!event.scrollHeightChanged) { + scrollTop.value = event.scrollTop + } + }) + } + // editorNode.onDidChangeModelLanguageConfiguration(() => { // editorNode?.getAction('editor.action.formatDocument')?.run() // }) @@ -102,14 +121,33 @@ onMounted(async () => { watch( () => props.content, - async (content) => { + async (content, oldContent, onCleanup) => { if (editorNode != null) { 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)) } }, ) +watch( + () => props.keyPath, + () => { + if (editorNode != null) { + scrollTop.value = 0 + editorNode.setScrollTop(0) + } + } +) + watch( () => readonlyValue.value, (readOnly) => { diff --git a/frontend/src/components/content_value/ContentEntryEditor.vue b/frontend/src/components/content_value/ContentEntryEditor.vue index 452df28..ce56563 100644 --- a/frontend/src/components/content_value/ContentEntryEditor.vue +++ b/frontend/src/components/content_value/ContentEntryEditor.vue @@ -175,6 +175,7 @@ const onSave = () => { :border="true" :content="displayValue" :language="viewLanguage" + :key-path="viewAs.field" class="flex-item-expand" @input="onInput" @reset="onInput" diff --git a/frontend/src/components/content_value/ContentValueJson.vue b/frontend/src/components/content_value/ContentValueJson.vue index 978f5d7..4f07954 100644 --- a/frontend/src/components/content_value/ContentValueJson.vue +++ b/frontend/src/components/content_value/ContentValueJson.vue @@ -149,6 +149,7 @@ defineExpose({ v-show="!props.loading" :content="displayValue" :loading="props.loading" + :key-path="props.keyPath" class="flex-item-expand" language="json" style="height: 100%" diff --git a/frontend/src/components/content_value/ContentValueString.vue b/frontend/src/components/content_value/ContentValueString.vue index c023b87..2742d75 100644 --- a/frontend/src/components/content_value/ContentValueString.vue +++ b/frontend/src/components/content_value/ContentValueString.vue @@ -204,6 +204,7 @@ defineExpose({ :content="displayValue" :language="viewLanguage" :loading="props.loading" + :key-path="props.keyPath" class="flex-item-expand" style="height: 100%" @input="onInput"