Fix editor state issues (#173)
* Add MarkdownEditor component with default value and disabled option * Add editorThemeCompartment for customizing editor theme
This commit is contained in:
parent
f6eff090e7
commit
1940c6e1ba
@ -7,6 +7,10 @@ const description1 = ref(null);
|
||||
const description2 = ref(null);
|
||||
const description3 = ref(null);
|
||||
|
||||
const description4 = ref("Hello, world! This is a **bold** statement.");
|
||||
|
||||
const isDisabled = ref(false);
|
||||
|
||||
const onImageUpload = (file) => {
|
||||
return URL.createObjectURL(file).replace("blob:", "");
|
||||
};
|
||||
@ -79,3 +83,34 @@ const description = ref(null)
|
||||
|
||||
<MarkdownEditor v-model="description" :heading-buttons="false" />
|
||||
```
|
||||
|
||||
## With default value
|
||||
<DemoContainer>
|
||||
<MarkdownEditor v-model="description4" />
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const description = ref("Hello, world! This is a **bold** statement.");
|
||||
</script>
|
||||
|
||||
<MardownEditor v-model="description" />
|
||||
```
|
||||
|
||||
## Disabled
|
||||
<DemoContainer>
|
||||
<Toggle v-model="isDisabled" label="Disabled" />
|
||||
<MarkdownEditor v-model="description" :disabled="isDisabled" />
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const description = ref(null);
|
||||
</script>
|
||||
|
||||
<MardownEditor v-model="description" disabled />
|
||||
```
|
||||
|
||||
@ -250,7 +250,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="previewMode">
|
||||
<div v-else>
|
||||
<div class="markdown-body-wrapper">
|
||||
<div
|
||||
style="width: 100%"
|
||||
@ -265,7 +265,7 @@
|
||||
<script setup lang="ts">
|
||||
import { type Component, computed, ref, onMounted, onBeforeUnmount, toRef, watch } from 'vue'
|
||||
|
||||
import { EditorState } from '@codemirror/state'
|
||||
import { Compartment, EditorState } from '@codemirror/state'
|
||||
import { EditorView, keymap, placeholder as cm_placeholder } from '@codemirror/view'
|
||||
import { markdown } from '@codemirror/lang-markdown'
|
||||
import { indentWithTab, historyKeymap, history } from '@codemirror/commands'
|
||||
@ -327,6 +327,8 @@ const props = withDefaults(
|
||||
|
||||
const editorRef = ref<HTMLDivElement>()
|
||||
let editor: EditorView | null = null
|
||||
let isDisabledCompartment: Compartment | null = null
|
||||
let editorThemeCompartment: Compartment | null = null
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
@ -337,8 +339,10 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
editorThemeCompartment = new Compartment()
|
||||
|
||||
const theme = EditorView.theme({
|
||||
// in defualts.scss there's references to .cm-content and such to inherit global styles
|
||||
// in defaults.scss there's references to .cm-content and such to inherit global styles
|
||||
'.cm-content': {
|
||||
marginBlockEnd: '0.5rem',
|
||||
padding: '0.5rem',
|
||||
@ -355,6 +359,10 @@ onMounted(() => {
|
||||
},
|
||||
})
|
||||
|
||||
isDisabledCompartment = new Compartment()
|
||||
|
||||
const disabledCompartment = EditorState.readOnly.of(props.disabled)
|
||||
|
||||
const eventHandlers = EditorView.domEventHandlers({
|
||||
paste: (ev, view) => {
|
||||
const { clipboardData } = ev
|
||||
@ -425,7 +433,6 @@ onMounted(() => {
|
||||
|
||||
const editorState = EditorState.create({
|
||||
extensions: [
|
||||
theme,
|
||||
eventHandlers,
|
||||
updateListener,
|
||||
keymap.of([indentWithTab]),
|
||||
@ -437,13 +444,24 @@ onMounted(() => {
|
||||
keymap.of(historyKeymap),
|
||||
cm_placeholder(props.placeholder || ''),
|
||||
inputFilter,
|
||||
isDisabledCompartment.of(disabledCompartment),
|
||||
editorThemeCompartment.of(theme),
|
||||
],
|
||||
})
|
||||
|
||||
editor = new EditorView({
|
||||
state: editorState,
|
||||
parent: editorRef.value,
|
||||
doc: props.modelValue,
|
||||
doc: props.modelValue ?? '', // This doesn't work for some reason
|
||||
})
|
||||
|
||||
// set editor content to props.modelValue
|
||||
editor?.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editor.state.doc.length,
|
||||
insert: props.modelValue,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@ -541,18 +559,70 @@ const BUTTONS: ButtonGroupMap = {
|
||||
},
|
||||
}
|
||||
|
||||
const currentValue = toRef(props, 'modelValue')
|
||||
watch(currentValue, (newValue) => {
|
||||
if (editor) {
|
||||
editor.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editor.state.doc.length,
|
||||
insert: newValue,
|
||||
},
|
||||
})
|
||||
watch(
|
||||
() => props.disabled,
|
||||
(newValue) => {
|
||||
if (editor) {
|
||||
if (isDisabledCompartment) {
|
||||
editor.dispatch({
|
||||
effects: [isDisabledCompartment.reconfigure(EditorState.readOnly.of(newValue))],
|
||||
})
|
||||
}
|
||||
|
||||
if (editorThemeCompartment) {
|
||||
editor.dispatch({
|
||||
effects: [
|
||||
editorThemeCompartment.reconfigure(
|
||||
EditorView.theme({
|
||||
// in defaults.scss there's references to .cm-content and such to inherit global styles
|
||||
'.cm-content': {
|
||||
marginBlockEnd: '0.5rem',
|
||||
padding: '0.5rem',
|
||||
minHeight: '200px',
|
||||
caretColor: 'var(--color-contrast)',
|
||||
width: '100%',
|
||||
overflowX: 'scroll',
|
||||
maxHeight: props.maxHeight ? `${props.maxHeight}px` : 'unset',
|
||||
overflowY: 'scroll',
|
||||
|
||||
opacity: newValue ? 0.6 : 1,
|
||||
pointerEvents: newValue ? 'none' : 'all',
|
||||
cursor: newValue ? 'not-allowed' : 'auto',
|
||||
},
|
||||
'.cm-scroller': {
|
||||
height: '100%',
|
||||
overflow: 'visible',
|
||||
},
|
||||
})
|
||||
),
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const currentValue = toRef(props, 'modelValue')
|
||||
watch(
|
||||
currentValue,
|
||||
(newValue) => {
|
||||
if (editor && newValue !== editor.state.doc.toString()) {
|
||||
editor.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: editor.state.doc.length,
|
||||
insert: newValue,
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
)
|
||||
|
||||
const updateCurrentValue = (newValue: string) => {
|
||||
emit('update:modelValue', newValue)
|
||||
@ -873,4 +943,10 @@ function openVideoModal() {
|
||||
justify-content: start;
|
||||
}
|
||||
}
|
||||
|
||||
.cm-disabled {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user