fix: prevent code input content from being obscured in fullscreen mode (#7599)

#### What type of PR is this?

/area ui
/kind bug
/milestone 2.21.x

#### What this PR does / why we need it:

Prevent code input content from being obscured in fullscreen mode

#### Which issue(s) this PR fixes:

Fixes https://github.com/halo-dev/halo/issues/7574

#### Does this PR introduce a user-facing change?

```release-note
修复代码输入框在全屏时,底部内容被遮挡的问题。
```
pull/7613/head
Ryan Wang 2025-07-04 12:31:41 +08:00 committed by GitHub
parent a76e64dcda
commit a6322fa023
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 10 deletions

View File

@ -6,7 +6,7 @@ import { json } from "@codemirror/lang-json";
import { LanguageSupport, StreamLanguage } from "@codemirror/language"; import { LanguageSupport, StreamLanguage } from "@codemirror/language";
import { yaml } from "@codemirror/legacy-modes/mode/yaml"; import { yaml } from "@codemirror/legacy-modes/mode/yaml";
import type { EditorStateConfig } from "@codemirror/state"; import type { EditorStateConfig } from "@codemirror/state";
import { EditorState } from "@codemirror/state"; import { Compartment, EditorState } from "@codemirror/state";
import { EditorView } from "@codemirror/view"; import { EditorView } from "@codemirror/view";
import { basicSetup } from "codemirror"; import { basicSetup } from "codemirror";
import { onBeforeUnmount, onMounted, shallowRef, watch } from "vue"; import { onBeforeUnmount, onMounted, shallowRef, watch } from "vue";
@ -42,17 +42,21 @@ const emit = defineEmits<{
(e: "change", value: string): void; (e: "change", value: string): void;
}>(); }>();
const customTheme = EditorView.theme({
"&": {
height: props.height,
width: "100%",
},
});
const wrapper = shallowRef<HTMLDivElement>(); const wrapper = shallowRef<HTMLDivElement>();
const cmState = shallowRef<EditorState>(); const cmState = shallowRef<EditorState>();
const cmView = shallowRef<EditorView>(); const cmView = shallowRef<EditorView>();
const themeCompartment = new Compartment();
const createCustomTheme = (height: string) => {
return EditorView.theme({
"&": {
height,
width: "100%",
},
});
};
const createCmEditor = () => { const createCmEditor = () => {
const language = const language =
typeof props.language === "string" typeof props.language === "string"
@ -62,7 +66,7 @@ const createCmEditor = () => {
let extensions = [ let extensions = [
basicSetup, basicSetup,
EditorView.lineWrapping, EditorView.lineWrapping,
customTheme, themeCompartment.of(createCustomTheme(props.height)),
language, language,
EditorView.updateListener.of((viewUpdate) => { EditorView.updateListener.of((viewUpdate) => {
if (viewUpdate.docChanged) { if (viewUpdate.docChanged) {
@ -106,6 +110,17 @@ onMounted(() => {
} }
} }
); );
watch(
() => props.height,
(newHeight) => {
if (cmView.value) {
cmView.value.dispatch({
effects: themeCompartment.reconfigure(createCustomTheme(newHeight)),
});
}
}
);
}); });
// Destroy codemirror editor when component unmounts // Destroy codemirror editor when component unmounts

View File

@ -31,6 +31,15 @@ useEventListener(codeInputWrapperRef, "keydown", (e: KeyboardEvent) => {
fullscreen.value = false; fullscreen.value = false;
} }
}); });
const editorHeight = computed(() => {
if (fullscreen.value) {
// VPageHeader height is 3.5rem
return "calc(100vh - 3.5rem)";
}
return "100%";
});
</script> </script>
<template> <template>
@ -52,7 +61,7 @@ useEventListener(codeInputWrapperRef, "keydown", (e: KeyboardEvent) => {
<VCodemirror <VCodemirror
:model-value="props.context._value" :model-value="props.context._value"
v-bind="context.attrs" v-bind="context.attrs"
height="100%" :height="editorHeight"
:language="language" :language="language"
class="block w-full" class="block w-full"
@change="onChange" @change="onChange"
@ -62,6 +71,7 @@ useEventListener(codeInputWrapperRef, "keydown", (e: KeyboardEvent) => {
v-if="!fullscreen" v-if="!fullscreen"
v-tooltip="$t('core.formkit.code.fullscreen.enter')" v-tooltip="$t('core.formkit.code.fullscreen.enter')"
class="absolute bottom-2 right-2 inline-flex cursor-pointer items-center justify-center rounded-full bg-primary p-1.5 text-white opacity-0 transition-all hover:!opacity-90 hover:shadow group-hover:opacity-100" class="absolute bottom-2 right-2 inline-flex cursor-pointer items-center justify-center rounded-full bg-primary p-1.5 text-white opacity-0 transition-all hover:!opacity-90 hover:shadow group-hover:opacity-100"
type="button"
@click="fullscreen = true" @click="fullscreen = true"
> >
<RiFullscreenLine class="text-xs" /> <RiFullscreenLine class="text-xs" />