Files
warren/frontend/components/viewers/TextEditor.vue

153 lines
4.1 KiB
Vue

<script setup lang="ts">
import { toast } from 'vue-sonner';
import { useTextEditor } from '~/stores/viewers';
const editor = useTextEditor();
const textarea = ref<HTMLTextAreaElement>(
null as unknown as HTMLTextAreaElement
);
const currentLine = ref<number>(1);
const totalLines = computed(
() => editor.data?.editedContent.split('\n').length ?? 0
);
function onOpenUpdate(state: boolean) {
if (!state) {
editor.close();
}
}
async function saveFile() {
if (editor.data == null) {
return;
}
const name = editor.data.entry.name;
const success = await editor.save();
if (success) {
toast.success('Save', {
id: 'TEXT_EDITOR_SAVE_TOAST',
description: `Successfully saved ${name}`,
});
} else {
toast.error('Save', {
id: 'TEXT_EDITOR_SAVE_TOAST',
description: `Failed to save ${name}`,
});
}
}
function onEditorKeyDown(e: KeyboardEvent) {
if (editor.saving) {
e.preventDefault();
return;
}
requestAnimationFrame(updateCurrentLine);
if (!e.ctrlKey || e.key !== 's') {
return;
}
e.preventDefault();
saveFile();
}
function updateCurrentLine() {
if (editor.data == null) {
return;
}
currentLine.value = editor.data.editedContent
.substring(0, textarea.value.selectionStart)
.split('\n').length;
}
function onEditorClick() {
requestAnimationFrame(updateCurrentLine);
}
onMounted(() => {
requestAnimationFrame(updateCurrentLine);
});
</script>
<template>
<Dialog :open="editor.data != null" @update:open="onOpenUpdate">
<DialogContent
v-if="editor.data"
class="flex h-full max-h-[min(98vh,700px)] w-full !max-w-[min(98vw,1000px)] flex-col"
>
<DialogHeader>
<DialogTitle>{{ editor.data.entry.name }}</DialogTitle>
</DialogHeader>
<div v-if="editor.data.content == null">
<span class="text-muted-foreground animate-pulse"
>Loading content...</span
>
</div>
<div v-else class="flex h-full w-full flex-col gap-2">
<div
class="flex h-fit w-full flex-row items-center justify-end gap-1"
>
<Button
title="Reset"
variant="destructive"
size="icon"
:disabled="
editor.data.content === editor.data.editedContent ||
editor.saving
"
@click="() => editor.discardEdits()"
>
<Icon name="lucide:bomb" />
</Button>
<Separator orientation="vertical" class="mx-1" />
<Button
title="Save"
size="icon"
:disabled="
editor.saving ||
editor.data.content === editor.data.editedContent
"
@click="saveFile"
>
<Icon name="lucide:save" />
</Button>
</div>
<textarea
id="editor"
ref="textarea"
v-model="editor.data.editedContent"
class="h-full w-full resize-none overflow-auto rounded-md border p-4 whitespace-pre focus:outline-0"
@keydown="onEditorKeyDown"
@click="onEditorClick"
></textarea>
<div class="flex w-full flex-row justify-end">
<p class="text-muted-foreground">
Line {{ currentLine }} / {{ totalLines }}
</p>
</div>
</div>
</DialogContent>
</Dialog>
</template>
<style>
#editor {
scrollbar-width: 0 !important;
}
#editor::-webkit-scrollbar {
display: none !important;
}
</style>