157 lines
3.8 KiB
Vue
157 lines
3.8 KiB
Vue
<script lang="ts">
|
|
import type { Editor, EditorConfiguration, EditorChange } from 'codemirror';
|
|
import { changeEnd } from 'codemirror';
|
|
import { fromTextArea } from 'hypermd';
|
|
|
|
import '#shared/hypermd.extend';
|
|
|
|
function onChange(cm: Editor, change: EditorChange)
|
|
{
|
|
if (changeEnd(change).line == cm.lastLine())
|
|
updateBottomMargin(cm);
|
|
}
|
|
|
|
|
|
function updateBottomMargin(cm: Editor)
|
|
{
|
|
let padding = "";
|
|
if (cm.lineCount() > 1)
|
|
{
|
|
//@ts-ignore
|
|
let totalH = cm.display.scroller.clientHeight - 30, lastLineH = cm.getLineHandle(cm.lastLine()).height;
|
|
padding = (totalH / 2 - lastLineH) + "px";
|
|
}
|
|
|
|
if (cm.state.scrollPastEndPadding != padding)
|
|
{
|
|
cm.state.scrollPastEndPadding = padding;
|
|
cm.display.lineSpace.parentNode.style.paddingBottom = padding;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<script setup lang="ts">
|
|
|
|
const { placeholder, autofocus = false, gutters = true, format = 'd-any' } = defineProps<{
|
|
placeholder?: string
|
|
autofocus?: boolean
|
|
gutters?: boolean
|
|
format?: 'hypermd' | 'd-any'
|
|
}>();
|
|
const model = defineModel<string>();
|
|
const editor = ref<Editor>();
|
|
|
|
const input = useTemplateRef('input');
|
|
|
|
onMounted(() => {
|
|
if(input.value)
|
|
{
|
|
const e = editor.value = fromTextArea(input.value, {
|
|
mode: {
|
|
name: format,
|
|
hashtag: true,
|
|
toc: false,
|
|
math: false,
|
|
orgModeMarkup: false,
|
|
tokenTypeOverrides: {
|
|
hr: "line-HyperMD-hr line-background-HyperMD-hr-bg hr",
|
|
list1: "list-1",
|
|
list2: "list-2",
|
|
list3: "list-3",
|
|
code: "inline-code",
|
|
hashtag: "hashtag meta"
|
|
}
|
|
},
|
|
spellcheck: true,
|
|
autofocus: autofocus,
|
|
lineNumbers: false,
|
|
showCursorWhenSelecting: true,
|
|
indentUnit: 4,
|
|
autoCloseBrackets: true,
|
|
foldGutter: gutters,
|
|
theme: 'custom'
|
|
} as EditorConfiguration);
|
|
|
|
e.setValue(model.value ?? '');
|
|
updateBottomMargin(e);
|
|
e.on('change', onChange);
|
|
e.on('change', (cm: Editor, change: EditorChange) => model.value = cm.getValue());
|
|
e.on('refresh', updateBottomMargin);
|
|
}
|
|
});
|
|
|
|
watchEffect(() => {
|
|
const value = editor.value?.getValue();
|
|
if (editor.value && model.value !== value) {
|
|
editor.value.setValue(model.value ?? '');
|
|
editor.value.clearHistory();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div :class="{ 'cancel-gutters': !gutters }" class="flex flex-1 w-full justify-stretch items-stretch !font-sans !text-base">
|
|
<textarea ref="input" class="hidden"></textarea>
|
|
</div>
|
|
</template>
|
|
|
|
<style>
|
|
.CodeMirror
|
|
{
|
|
@apply bg-transparent;
|
|
@apply flex-1 h-full;
|
|
@apply font-sans;
|
|
|
|
@apply text-light-100 dark:text-dark-100;
|
|
}
|
|
.cancel-gutters .CodeMirror-gutters
|
|
{
|
|
@apply hidden;
|
|
}
|
|
.cancel-gutters .CodeMirror-sizer
|
|
{
|
|
@apply ms-2;
|
|
}
|
|
.CodeMirror-gutters
|
|
{
|
|
@apply bg-transparent;
|
|
@apply border-transparent;
|
|
}
|
|
.CodeMirror-gutter-wrapper
|
|
{
|
|
@apply absolute top-0 bottom-0;
|
|
@apply flex justify-center items-center;
|
|
}
|
|
.CodeMirror-foldmarker
|
|
{
|
|
@apply text-light-100;
|
|
@apply dark:text-dark-100;
|
|
@apply ps-3;
|
|
text-shadow: none;
|
|
}
|
|
.hmd-inactive-line .cm-formatting-header, .hmd-inactive-line .cm-formatting-link, .hmd-inactive-line .cm-link-has-alias, .hmd-inactive-line .cm-link-alias-pipe
|
|
{
|
|
@apply hidden;
|
|
}
|
|
.CodeMirror-line
|
|
{
|
|
@apply text-base;
|
|
}
|
|
.CodeMirror-cursor
|
|
{
|
|
@apply border-light-100 dark:border-dark-100;
|
|
}
|
|
.CodeMirror-selected
|
|
{
|
|
@apply bg-light-35 dark:bg-dark-35;
|
|
}
|
|
.HyperMD-list-line-1 {
|
|
@apply !ps-0;
|
|
}
|
|
.HyperMD-list-line-2 {
|
|
@apply !ps-6;
|
|
}
|
|
.HyperMD-list-line-3 {
|
|
@apply !ps-12;
|
|
}
|
|
</style> |