|
|
@@ -6,39 +6,39 @@ import * as monaco from 'monaco-editor'
|
|
|
import { IconButton } from '@repo/ui'
|
|
|
|
|
|
const props = withDefaults(
|
|
|
- defineProps<{
|
|
|
- allowFullscreen?: boolean
|
|
|
- autoToggleTheme?: boolean
|
|
|
- bordered?: boolean
|
|
|
- config?: editor.IStandaloneEditorConstructionOptions
|
|
|
- language?: string
|
|
|
- lineNumbers?: 'off' | 'on'
|
|
|
- modelValue?: any
|
|
|
- readOnly?: boolean
|
|
|
- theme?: 'hc-black' | 'vs-dark' | 'vs-light'
|
|
|
- valueFormat?: string
|
|
|
- height?: string
|
|
|
- appendTo?: string | HTMLElement
|
|
|
- }>(),
|
|
|
- {
|
|
|
- allowFullscreen: true,
|
|
|
- config: () => ({
|
|
|
- minimap: {
|
|
|
- enabled: false
|
|
|
- },
|
|
|
- selectOnLineNumbers: true
|
|
|
- }),
|
|
|
- autoToggleTheme: true,
|
|
|
- language: 'json',
|
|
|
- lineNumbers: 'on',
|
|
|
- readOnly: false,
|
|
|
- theme: 'vs-light',
|
|
|
- valueFormat: 'string',
|
|
|
- height: '120px'
|
|
|
- }
|
|
|
+ defineProps<{
|
|
|
+ allowFullscreen?: boolean
|
|
|
+ autoToggleTheme?: boolean
|
|
|
+ bordered?: boolean
|
|
|
+ config?: editor.IStandaloneEditorConstructionOptions
|
|
|
+ language?: string
|
|
|
+ lineNumbers?: 'off' | 'on'
|
|
|
+ modelValue?: any
|
|
|
+ readOnly?: boolean
|
|
|
+ theme?: 'hc-black' | 'vs-dark' | 'vs-light'
|
|
|
+ valueFormat?: string
|
|
|
+ height?: string
|
|
|
+ appendTo?: string | HTMLElement
|
|
|
+ }>(),
|
|
|
+ {
|
|
|
+ allowFullscreen: true,
|
|
|
+ config: () => ({
|
|
|
+ minimap: {
|
|
|
+ enabled: false
|
|
|
+ },
|
|
|
+ selectOnLineNumbers: true
|
|
|
+ }),
|
|
|
+ autoToggleTheme: true,
|
|
|
+ language: 'json',
|
|
|
+ lineNumbers: 'on',
|
|
|
+ readOnly: false,
|
|
|
+ theme: 'vs-light',
|
|
|
+ valueFormat: 'string',
|
|
|
+ height: '120px'
|
|
|
+ }
|
|
|
)
|
|
|
|
|
|
-// todo 动态切换主题
|
|
|
+// 动态切换主题
|
|
|
const theme = ref('dark')
|
|
|
|
|
|
const emit = defineEmits(['update:modelValue', 'update:language'])
|
|
|
@@ -47,7 +47,7 @@ const isFullScreen = ref(false)
|
|
|
|
|
|
const fullScreenStyle = `position: fixed;
|
|
|
top: 0;
|
|
|
- left: 0;
|
|
|
+ left: 0;
|
|
|
right: 0;
|
|
|
bottom: 0;
|
|
|
z-index: 2999;`
|
|
|
@@ -61,7 +61,7 @@ let monacoEditor: monaco.editor.IStandaloneCodeEditor | null = null
|
|
|
* @param text
|
|
|
*/
|
|
|
function setValue(text: string) {
|
|
|
- monacoEditor?.setValue(text || '')
|
|
|
+ monacoEditor?.setValue(text || '')
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -69,124 +69,118 @@ function setValue(text: string) {
|
|
|
* @param text
|
|
|
*/
|
|
|
function insertText(text: string) {
|
|
|
- // 获取光标位置
|
|
|
- const position = monacoEditor?.getPosition()
|
|
|
- // 未获取到光标位置信息
|
|
|
- if (!position) {
|
|
|
- return
|
|
|
- }
|
|
|
- // 插入
|
|
|
- monacoEditor?.executeEdits('', [
|
|
|
- {
|
|
|
- range: new monaco.Range(
|
|
|
- position.lineNumber,
|
|
|
- position.column,
|
|
|
- position.lineNumber,
|
|
|
- position.column
|
|
|
- ),
|
|
|
- text
|
|
|
- }
|
|
|
- ])
|
|
|
- // 设置新的光标位置
|
|
|
- monacoEditor?.setPosition({
|
|
|
- ...position,
|
|
|
- column: position.column + text.length
|
|
|
- })
|
|
|
- // 重新聚焦
|
|
|
- monacoEditor?.focus()
|
|
|
+ // 获取光标位置
|
|
|
+ const position = monacoEditor?.getPosition()
|
|
|
+ // 未获取到光标位置信息
|
|
|
+ if (!position) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 插入
|
|
|
+ monacoEditor?.executeEdits('', [
|
|
|
+ {
|
|
|
+ range: new monaco.Range(
|
|
|
+ position.lineNumber,
|
|
|
+ position.column,
|
|
|
+ position.lineNumber,
|
|
|
+ position.column
|
|
|
+ ),
|
|
|
+ text
|
|
|
+ }
|
|
|
+ ])
|
|
|
+ // 设置新的光标位置
|
|
|
+ monacoEditor?.setPosition({
|
|
|
+ ...position,
|
|
|
+ column: position.column + text.length
|
|
|
+ })
|
|
|
+ // 重新聚焦
|
|
|
+ monacoEditor?.focus()
|
|
|
}
|
|
|
|
|
|
onMounted(() => {
|
|
|
- monacoEditor = monaco.editor.create(editContainer.value as HTMLElement, {
|
|
|
- value: getValue(),
|
|
|
- ...props.config,
|
|
|
- automaticLayout: true,
|
|
|
- language: props.language,
|
|
|
- lineNumbers: props.lineNumbers,
|
|
|
- readOnly: props.readOnly,
|
|
|
- scrollBeyondLastLine: false,
|
|
|
- theme: props.theme
|
|
|
- })
|
|
|
-
|
|
|
- function handleToggleTheme() {
|
|
|
- if (theme.value === 'dark') {
|
|
|
- monaco.editor.setTheme('vs-dark')
|
|
|
- } else {
|
|
|
- monaco.editor.setTheme('vs-light')
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 自动切换主题
|
|
|
- if (props.autoToggleTheme) {
|
|
|
- watch(
|
|
|
- () => theme,
|
|
|
- () => {
|
|
|
- nextTick(() => handleToggleTheme())
|
|
|
- },
|
|
|
- {
|
|
|
- immediate: true
|
|
|
- }
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- // 获取值
|
|
|
- function getValue() {
|
|
|
- // valueFormat 为json 格式,需要转换处理
|
|
|
- if (props.valueFormat === 'json' && props.modelValue) {
|
|
|
- return JSON.stringify(props.modelValue, null, 2)
|
|
|
- }
|
|
|
- return props.modelValue ?? ''
|
|
|
- }
|
|
|
-
|
|
|
- // 监听值变化
|
|
|
- monacoEditor.onDidChangeModelContent(() => {
|
|
|
- const currenValue = monacoEditor?.getValue()
|
|
|
-
|
|
|
- // valueFormat 为json 格式,需要转换处理
|
|
|
- if (props.valueFormat === 'json' && currenValue) {
|
|
|
- emit('update:modelValue', JSON.parse(currenValue))
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- emit('update:modelValue', currenValue ?? '')
|
|
|
- })
|
|
|
+ monacoEditor = monaco.editor.create(editContainer.value as HTMLElement, {
|
|
|
+ value: getValue(),
|
|
|
+ ...props.config,
|
|
|
+ automaticLayout: true,
|
|
|
+ language: props.language,
|
|
|
+ lineNumbers: props.lineNumbers,
|
|
|
+ readOnly: props.readOnly,
|
|
|
+ scrollBeyondLastLine: false,
|
|
|
+ theme: props.theme
|
|
|
+ })
|
|
|
+
|
|
|
+ function handleToggleTheme() {
|
|
|
+ if (theme.value === 'dark') {
|
|
|
+ monaco.editor.setTheme('vs-dark')
|
|
|
+ } else {
|
|
|
+ monaco.editor.setTheme('vs-light')
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 自动切换主题
|
|
|
+ if (props.autoToggleTheme) {
|
|
|
+ watch(
|
|
|
+ () => theme,
|
|
|
+ () => {
|
|
|
+ nextTick(() => handleToggleTheme())
|
|
|
+ },
|
|
|
+ {
|
|
|
+ immediate: true
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取值
|
|
|
+ function getValue() {
|
|
|
+ // valueFormat 为json 格式,需要转换处理
|
|
|
+ if (props.valueFormat === 'json' && props.modelValue) {
|
|
|
+ return JSON.stringify(props.modelValue, null, 2)
|
|
|
+ }
|
|
|
+ return props.modelValue ?? ''
|
|
|
+ }
|
|
|
+
|
|
|
+ // 监听值变化
|
|
|
+ monacoEditor.onDidChangeModelContent(() => {
|
|
|
+ const currenValue = monacoEditor?.getValue()
|
|
|
+
|
|
|
+ // valueFormat 为json 格式,需要转换处理
|
|
|
+ if (props.valueFormat === 'json' && currenValue) {
|
|
|
+ emit('update:modelValue', JSON.parse(currenValue))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ emit('update:modelValue', currenValue ?? '')
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
defineExpose({
|
|
|
- insertText,
|
|
|
- setValue
|
|
|
+ insertText,
|
|
|
+ setValue
|
|
|
})
|
|
|
</script>
|
|
|
<template>
|
|
|
- <Teleport :disabled="!appendTo" :to="appendTo">
|
|
|
- <div
|
|
|
- ref="editContainer"
|
|
|
- :class="{ bordered: props.bordered }"
|
|
|
- :style="isFullScreen ? fullScreenStyle : { height: props.height }"
|
|
|
- class="code-editor w-full h-full relative"
|
|
|
- >
|
|
|
- <div
|
|
|
- class="z-999 text-$epic-text-helper absolute right-4 top-2 cursor-pointer text-xl"
|
|
|
- @click="isFullScreen = !isFullScreen"
|
|
|
- v-if="props.allowFullscreen"
|
|
|
- >
|
|
|
- <IconButton v-if="!isFullScreen" icon="lucide:fullscreen" link />
|
|
|
- <IconButton v-else icon="material-symbols-light:fullscreen-exit-rounded" link />
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </Teleport>
|
|
|
+ <Teleport :disabled="!appendTo" :to="appendTo">
|
|
|
+ <div ref="editContainer" :class="{ bordered: props.bordered }"
|
|
|
+ :style="isFullScreen ? fullScreenStyle : { height: props.height }"
|
|
|
+ class="code-editor w-full h-full relative">
|
|
|
+ <div class="z-999 text-$epic-text-helper absolute right-4 top-2 cursor-pointer text-xl"
|
|
|
+ @click="isFullScreen = !isFullScreen" v-if="props.allowFullscreen">
|
|
|
+ <IconButton v-if="!isFullScreen" icon="lucide:fullscreen" link />
|
|
|
+ <IconButton v-else icon="material-symbols-light:fullscreen-exit-rounded" link />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Teleport>
|
|
|
</template>
|
|
|
<style lang="less" scoped>
|
|
|
.code-editor {
|
|
|
- width: 100%;
|
|
|
- min-height: 150px;
|
|
|
+ width: 100%;
|
|
|
+ min-height: 150px;
|
|
|
|
|
|
- :deep(.monaco-editor) {
|
|
|
- height: 100%;
|
|
|
- }
|
|
|
+ :deep(.monaco-editor) {
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
|
|
|
- &.bordered {
|
|
|
- border: 1px solid var(--epic-border-color);
|
|
|
- }
|
|
|
+ &.bordered {
|
|
|
+ border: 1px solid var(--epic-border-color);
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|