From 4b23da52398d6982bfe6cdd0a4ecb49fe96194b9 Mon Sep 17 00:00:00 2001 From: Afiq Date: Thu, 7 Aug 2025 19:38:28 +0800 Subject: [PATCH] Refactor RsCodeMirror Component for Enhanced Language and Theme Handling - Introduced helper functions to manage language and theme extensions, improving code organization and readability. - Implemented dynamic initialization of extensions based on selected language and theme, ensuring accurate syntax highlighting. - Added fullscreen functionality with keyboard shortcuts for a better user experience, including visual feedback for fullscreen mode. - Enhanced template structure and styles for improved responsiveness and usability, particularly in fullscreen mode. - Updated event handling for keyboard shortcuts to streamline code formatting and fullscreen toggling. - Ensured proper cleanup of body overflow styles on component unmount to maintain layout integrity. --- components/RsCodeMirror.vue | 341 +++++++++++++++++++++++++++++------- 1 file changed, 276 insertions(+), 65 deletions(-) diff --git a/components/RsCodeMirror.vue b/components/RsCodeMirror.vue index 46a5519..d4907bc 100644 --- a/components/RsCodeMirror.vue +++ b/components/RsCodeMirror.vue @@ -89,29 +89,62 @@ const dropdownThemes = ref([ const value = ref(props.modelValue); const extensions = ref([]); -if (props.language == "vue") { + +// Helper function to get theme extension +const getThemeExtension = (themeVal) => { + switch (themeVal) { + case "oneDark": + return oneDark; + case "amy": + return amy; + case "ayuLight": + return ayuLight; + case "barf": + return barf; + case "cobalt": + return cobalt; + case "dracula": + return dracula; + case "clouds": + default: + return clouds; + } +}; + +// Helper function to get language extension +const getLanguageExtension = () => { + switch (props.language) { + case "vue": + return vue(); + case "css": + case "scss": + return css(); + case "html": + return css(); // Use CSS for HTML highlighting + case "javascript": + case "js": + default: + return javascript(); + } +}; + +// Initialize extensions with proper theme +const initializeExtensions = () => { + const currentTheme = props.theme || editorTheme.value || "oneDark"; + const themeExtension = getThemeExtension(currentTheme); + const languageExtension = getLanguageExtension(); + extensions.value = [ - vue(), - oneDark, + languageExtension, + themeExtension, autocompletion(), indentUnit.of(" "), indentOnInput(), ]; -} else if (props.language == "javascript") { - extensions.value = [ - javascript(), - oneDark, - autocompletion(), - indentUnit.of(" "), - indentOnInput(), - ]; -} else if (props.language == "css") { - extensions.value = [ - css(), - oneDark, - autocompletion(), - ]; -} +}; + +// Initialize extensions on component creation +initializeExtensions(); const totalLines = ref(0); const totalLength = ref(0); @@ -124,35 +157,45 @@ const handleReady = (payload) => { totalLength.value = view.value.state.doc.length; }; +// Watch for theme changes watch( - () => editorTheme.value, - (themeVal) => { - const themeExtension = - themeVal === "oneDark" - ? oneDark - : themeVal === "amy" - ? amy - : themeVal === "ayuLight" - ? ayuLight - : themeVal === "barf" - ? barf - : themeVal === "cobalt" - ? cobalt - : themeVal === "dracula" - ? dracula - : clouds; - - if (props.language == "vue") { + () => props.theme || editorTheme.value, + (newTheme) => { + if (newTheme) { + const themeExtension = getThemeExtension(newTheme); + const languageExtension = getLanguageExtension(); + extensions.value = [ - vue(), + languageExtension, themeExtension, autocompletion(), indentUnit.of(" "), indentOnInput(), ]; - } else { + } + }, + { immediate: true } +); + +// Watch for language changes +watch( + () => props.language, + () => { + initializeExtensions(); + } +); + +// Watch for editorTheme store changes +watch( + () => editorTheme.value, + (newTheme) => { + if (!props.theme && newTheme) { + // Only update if no explicit theme prop is provided + const themeExtension = getThemeExtension(newTheme); + const languageExtension = getLanguageExtension(); + extensions.value = [ - javascript(), + languageExtension, themeExtension, autocompletion(), indentUnit.of(" "), @@ -244,12 +287,49 @@ const formatCurrentCode = async () => { const debouncedFormatCode = useDebounceFn(formatCurrentCode, 300); +// Fullscreen functionality +const isFullscreen = ref(false); +const fullscreenContainer = ref(null); + +const toggleFullscreen = () => { + isFullscreen.value = !isFullscreen.value; + + if (isFullscreen.value) { + document.body.style.overflow = 'hidden'; + // Focus the editor after entering fullscreen + nextTick(() => { + if (view.value) { + view.value.focus(); + } + }); + } else { + document.body.style.overflow = ''; + } +}; + +const exitFullscreen = () => { + if (isFullscreen.value) { + isFullscreen.value = false; + document.body.style.overflow = ''; + } +}; + const handleKeyDown = (e) => { // Press Shift + Alt + F to format code if (e.shiftKey && e.altKey && e.key === "F") { e.preventDefault(); debouncedFormatCode(); } + // Press F11 or Ctrl+Shift+F to toggle fullscreen + if (e.key === "F11" || (e.ctrlKey && e.shiftKey && e.key === "F")) { + e.preventDefault(); + toggleFullscreen(); + } + // Press Escape to exit fullscreen + if (e.key === "Escape" && isFullscreen.value) { + e.preventDefault(); + exitFullscreen(); + } }; onMounted(() => { @@ -259,6 +339,10 @@ onMounted(() => { onUnmounted(() => { window.removeEventListener("keydown", handleKeyDown); + // Ensure body overflow is reset + if (isFullscreen.value) { + document.body.style.overflow = ''; + } }); // Add this watch effect after the value ref declaration @@ -274,38 +358,70 @@ watch( + +