Skip to content

Commit 96b840a

Browse files
committed
feat: support add/remove more files
1 parent b76f04c commit 96b840a

File tree

17 files changed

+333
-123
lines changed

17 files changed

+333
-123
lines changed

app/assets/templates/sample.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Sample document
1+
# {FILENAME}
22

33
This document demonstrates Markdown syntax. Professional books and tech documentation are plain text files with this easy markup language. Documatt is the tool for creating beautiful docs, manuals, or books for tech and novel writers.
44

app/assets/templates/sample.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
###############
2-
Sample document
2+
{FILENAME}
33
###############
44

55
.. epigraph:: This document demonstrates reStructuredText syntax. Professional books and tech documentation are plain text files with this easy markup language. Documatt Snippets is an online editor with a preview useful for learning and testing reStructuredText without installing it.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<template>
2+
<div
3+
v-if="canBeDeleted()"
4+
class="tooltip tooltip-right"
5+
data-tip="Delete file"
6+
@click="deleteFile"
7+
>
8+
<Icon name="prime:times" class="block! text-red-500" />
9+
</div>
10+
</template>
11+
12+
<script setup lang="ts">
13+
const previewStore = usePreviewStore();
14+
const props = defineProps<{ filename: string }>();
15+
16+
function canBeDeleted() {
17+
const rootFilename = getRootFilename(previewStore.syntax);
18+
if (props.filename === rootFilename) return false;
19+
return true;
20+
}
21+
22+
function deleteFile() {
23+
const yes = window.confirm(
24+
`Do you really want to delete "${props.filename}"?`,
25+
);
26+
if (yes) {
27+
// delete command breaks reactivity, the recommended approach is to clone new object to notice Vue
28+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
29+
delete previewStore.files[props.filename];
30+
previewStore.files = { ...previewStore.files };
31+
}
32+
}
33+
</script>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<template>
2+
<div v-if="isEntering" class="flex items-center">
3+
<label class="input w-auto">
4+
<input
5+
ref="newFileInputElement"
6+
v-model="newFileStem"
7+
type="text"
8+
placeholder="new file"
9+
class="w-16"
10+
@keyup.enter="addNewFile"
11+
/>
12+
<span class="label">.{{ previewStore.syntax }}</span>
13+
</label>
14+
</div>
15+
16+
<div v-else class="flex items-center">
17+
<div class="tooltip tooltip-right" data-tip="Add new file">
18+
<Icon name="prime:plus" @click="isEntering = true" />
19+
</div>
20+
</div>
21+
</template>
22+
23+
<script setup lang="ts">
24+
const previewStore = usePreviewStore();
25+
26+
const isEntering = ref(false);
27+
const newFileStem = ref("");
28+
const newFileInputElement = useTemplateRef("newFileInputElement");
29+
30+
// Set focus on first render
31+
useFocus(newFileInputElement, { initialValue: true });
32+
33+
// Add file when clicked outside
34+
onClickOutside(newFileInputElement, addNewFile);
35+
36+
function addNewFile() {
37+
const stem = newFileStem.value;
38+
const filename = stem + "." + previewStore.syntax;
39+
40+
// Nothing has been entered, back to NOT entering state
41+
if (!stem.trim()) {
42+
isEntering.value = false;
43+
return;
44+
}
45+
46+
// Exist?
47+
if (filename in previewStore.files) {
48+
alert("Filename already exists");
49+
// Return back to NOT entering state
50+
isEntering.value = false;
51+
return;
52+
}
53+
54+
// Add new blank file with that filename
55+
previewStore.files[filename] = "";
56+
57+
// Return back to NOT entering state
58+
newFileStem.value = "";
59+
isEntering.value = false;
60+
}
61+
</script>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!-- Offer sample doc when doc is blank -->
2+
<template>
3+
<!-- If it's blank -->
4+
<div v-if="isBlank" role="alert" class="alert alert-outline alert-info mb-4">
5+
<span
6+
>Do you want to start with a
7+
<a class="link link-info" @click.prevent="loadSample">sample document</a
8+
>?</span
9+
>
10+
</div>
11+
</template>
12+
13+
<script setup lang="ts">
14+
// Read files to a string variable
15+
// https://vitejs.dev/guide/assets.html#importing-asset-as-string
16+
import sampleRst from "~/assets/templates/sample.rst?raw";
17+
import sampleMd from "~/assets/templates/sample.md?raw";
18+
19+
const props = defineProps<{ filename: string }>();
20+
const previewStore = usePreviewStore();
21+
const uiStore = useUiStore();
22+
23+
const isBlank = computed(() => !previewStore.files[props.filename]?.trim());
24+
25+
function loadSample() {
26+
let body = previewStore.syntax == Syntax.RST ? sampleRst : sampleMd;
27+
body = replacePlaceholders(body, uiStore.activeDoc);
28+
previewStore.files[props.filename] = body;
29+
}
30+
31+
const PLACEHOLDER_FILENAME = "{FILENAME}";
32+
33+
function replacePlaceholders(template: string, filename: string) {
34+
return template.replace(PLACEHOLDER_FILENAME, filename);
35+
}
36+
</script>
Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,6 @@
1-
<template>
2-
<div
3-
v-if="previewStore.rootDocIsBlank"
4-
role="alert"
5-
class="alert alert-outline alert-info mb-4"
6-
>
7-
<span
8-
>Do you want to start with a
9-
<a class="link link-info" @click.prevent="loadSample">sample document</a
10-
>?</span
11-
>
12-
</div>
1+
<!-- Text editor -->
132

3+
<template>
144
<textarea
155
ref="textarea"
166
v-model="previewStore.files[props.filename]"
@@ -20,19 +10,9 @@
2010
</template>
2111

2212
<script setup lang="ts">
23-
// Read files to a string variable
24-
// https://vitejs.dev/guide/assets.html#importing-asset-as-string
25-
import sampleRst from "~/assets/templates/sample.rst?raw";
26-
import sampleMd from "~/assets/templates/sample.md?raw";
27-
2813
const props = defineProps<{ filename: string }>();
2914
const previewStore = usePreviewStore();
3015
31-
function loadSample() {
32-
previewStore.files[props.filename] =
33-
previewStore.syntax == Syntax.RST ? sampleRst : sampleMd;
34-
}
35-
3616
// *** Warn dialog before reload ******************************************************
3717
3818
onMounted(() => {

app/components/EditorPane.vue

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
<template>
22
<div class="tabs tabs-border h-full">
3-
<input
4-
type="radio"
5-
:name="tabGroup"
6-
class="tab"
7-
:aria-label="filename"
8-
checked
9-
/>
10-
<div class="tab-content p-3">
11-
<Editor :filename="filename" />
12-
</div>
3+
<!-- Tabs for files except conf.py -->
4+
<template v-for="(_, filename) in previewStore.files" :key="filename">
5+
<template v-if="filename !== 'conf.py'">
6+
<label class="tab group" @click="uiStore.activeDoc = filename">
7+
{{ filename }}
8+
<input
9+
:ref="filename"
10+
type="radio"
11+
name="editor"
12+
:checked="uiStore.activeDoc === filename"
13+
/>
14+
<EditorDeleteFileButton
15+
:filename="filename"
16+
class="hidden! group-hover:block!"
17+
/>
18+
</label>
19+
<div class="tab-content p-3">
20+
<EditorTemplateOffer :filename="filename" />
21+
<EditorText :filename="filename" />
22+
</div>
23+
</template>
24+
</template>
25+
<EditorNewFileButton />
1326
</div>
1427
</template>
1528

1629
<script lang="ts" setup>
17-
const tabGroup = "editor";
1830
const previewStore = usePreviewStore();
19-
const filename = computed(() => getRootFilename(previewStore.syntax));
31+
const uiStore = useUiStore();
2032
</script>

app/components/Footer.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
:href="appConfig.dcomUrl"
99
class="link"
1010
data-rybbit-event="try_documatt_clicked"
11-
>Try Documatt, a free worry-free Sphinx.</a
11+
>Try Documatt, a worry-free Sphinx.</a
1212
>
1313
</span>
1414

app/components/Header.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
alt="Documatt Snippets logo"
1010
class="w-12"
1111
/>
12-
<h1 class="text-primary text-xl font-bold">Sphinx Snippets</h1>
12+
<h1 class="text-primary font-sans text-xl font-bold">Sphinx Snippets</h1>
1313
<h1 class="ml-4 text-sm font-bold">
1414
Try and learn Sphinx with reStructuredText or Markdown
1515
</h1>

app/components/Preview/HtmlTab.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<template>
2+
<PreviewShowSource
3+
:source="previewStore.result?.htmlFiles[uiStore.activeDoc]"
4+
/>
5+
</template>
6+
7+
<script setup lang="ts">
8+
const previewStore = usePreviewStore();
9+
const uiStore = useUiStore();
10+
</script>

0 commit comments

Comments
 (0)