source: main/trunk/model-interfaces-dev/atea/macron-restoration/src/components/DirectInput.vue@ 35950

Last change on this file since 35950 was 35950, checked in by cstephen, 2 years ago

Fix localStorage-backed properties

File size: 8.0 KB
Line 
1<script>
2import { mapState } from "vuex";
3import { saveAs } from "file-saver"
4import { log } from "../js/Util"
5import MacronRestorationModule from "../js/MacronRestorationModule"
6import { SnackController } from "./Snackbar.vue"
7
8const macroniser = new MacronRestorationModule();
9
10export default {
11 name: "DirectInput",
12 data() {
13 return {
14 macroniseWaitTimeout: null,
15 waitingToMacronise: false,
16 errorState: false,
17 preserveExistingMacrons: true,
18 enableSpellcheck: false,
19 showMacronisedWords: true,
20 normaliseLinebreaks: false
21 }
22 },
23 computed: {
24 canDownload() {
25 return this.restored.length > 0 && !this.waitingToMacronise;
26 },
27 input: {
28 get() {
29 return this.$store.state.directInput;
30 },
31 set(newValue) {
32 this.$store.commit("setDirectInput", newValue);
33 this.startMacronisationWait();
34 }
35 },
36 restored: {
37 get() {
38 return this.$store.state.directOutput;
39 },
40 set(newValue) {
41 this.$store.commit("setDirectOutput", newValue);
42 }
43 },
44 htmlFormattedRestored() {
45 let restored = "";
46
47 for (const word of this.restored) {
48 if (word.w) {
49 if (word[0] !== "-") {
50 restored += " ";
51 }
52
53 if (word.macronised && this.showMacronisedWords) {
54 restored += `<mark class="highlight">${word.w}</mark>`;
55 }
56 else {
57 restored += word.w;
58 }
59 }
60
61 if (word.linebreaks) {
62 restored += this.normaliseLinebreak(word.linebreaks);
63 }
64 }
65
66 return restored.trim();
67 },
68 formattedRestored() {
69 let restored = "";
70
71 for (const word of this.restored) {
72 if (word.w) {
73 if (word[0] !== "-") {
74 restored += " ";
75 }
76
77 restored += word.w;
78 }
79
80 if (word.linebreaks) {
81 restored += this.normaliseLinebreak(word.linebreaks);
82 }
83 }
84
85 return restored.trim();
86 },
87 ...mapState({
88 translations: state => state.translations
89 })
90 },
91 watch: {
92 preserveExistingMacrons(newValue) {
93 localStorage.setItem("preserveExistingMacrons", newValue);
94 },
95 enableSpellcheck(newValue) {
96 localStorage.setItem("enableSpellcheck", newValue);
97 },
98 showMacronisedWords(newValue) {
99 localStorage.setItem("showMacronisedWords", newValue);
100 },
101 normaliseLinebreaks(newValue) {
102 localStorage.setItem("normaliseLinebreaks", newValue);
103 }
104 },
105 methods: {
106 /**
107 * Handles input on an element, and grows its height so that the text is always visible.
108 * @param {InputEvent} event The input event.
109 */
110 onTextInput(event) {
111 event.target.height = "auto";
112 event.target.height = event.target.scrollHeight;
113 },
114 startMacronisationWait() {
115 if (this.input === null || this.input === "") {
116 return;
117 }
118
119 this.waitingToMacronise = true;
120
121 if (this.macroniseWaitTimeout !== null) {
122 clearTimeout(this.macroniseWaitTimeout);
123 }
124
125 this.macroniseWaitTimeout = setTimeout(this.updateMacronisedText, 1000);
126 },
127 async updateMacronisedText() {
128 this.errorState = false;
129
130 try {
131 this.restored = await macroniser.directMacronisation(this.input, this.preserveExistingMacrons);
132 this.waitingToMacronise = false;
133 }
134 catch (ex) {
135 this.errorState = true;
136 log(ex, "warn");
137 }
138 },
139
140 downloadAsText() {
141 const blob = new Blob([ this.formattedRestored ], { type: "text/plain;charset=utf-8" });
142 saveAs(blob, "restored.txt");
143 },
144 async copyToClipboard() {
145 await navigator.clipboard.writeText(this.formattedRestored);
146 SnackController.addSnack(this.translations.get("DirectInput_CopySuccess"));
147 },
148
149 /**
150 * Normalises a linebreak.
151 * @param {String} linebreakCount The linebreak sequence.
152 */
153 normaliseLinebreak(linebreakCount) {
154 if (!this.normaliseLinebreaks) {
155 return this.repeatString("\n", linebreakCount);
156 }
157
158 linebreakCount = Math.min(2, linebreakCount);
159 return this.repeatString("\n", linebreakCount);
160 },
161 repeatString(string, count) {
162 let result = "";
163
164 for (let i = 0; i < count; i++) {
165 result += string;
166 }
167
168 return result;
169 }
170 },
171 beforeMount() {
172 // The null check is used to default this setting to true
173 this.preserveExistingMacrons = (localStorage.getItem("preserveExistingMacrons") ?? "true") === "true";
174 this.enableSpellcheck = localStorage.getItem("enableSpellcheck") === "true";
175 this.showMacronisedWords = (localStorage.getItem("showMacronisedWords") ?? "true") === "true";
176 this.normaliseLinebreaks = localStorage.getItem("normaliseLinebreaks") === "true";
177 }
178}
179</script>
180
181<template>
182<div class="root">
183 <textarea class="text-input input-area" @input="onTextInput" autocomplete="off"
184 v-model="input" :placeholder="translations.get('DirectInput_InputPlaceholder')"
185 :spellcheck="enableSpellcheck ? 'yes' : 'no'" />
186
187 <div class="text-container">
188 <span v-if="errorState" class="material-icons mdi-m error-text">error</span>
189 <span v-if="errorState" class="error-text">{{ translations.get('DirectInput_UnknownError') }}</span>
190
191 <span class="preserveWhitespace" v-html="htmlFormattedRestored" />
192 </div>
193
194 <div class="flex">
195 <input type="checkbox" id="i-preserve-existing-macrons" v-model="preserveExistingMacrons" @input="startMacronisationWait" />
196 <label for="i-preserve-existing-macrons">{{ translations.get('DirectInput_PreserveExistingMacrons') }}</label>
197 </div>
198
199 <div class="flex">
200 <input type="checkbox" id="i-show-macronised-words" v-model="showMacronisedWords" :disabled="restored.length === 0" />
201 <label for="i-show-macronised-words">{{ translations.get('DirectInput_ShowMacronisedWords') }}</label>
202 </div>
203
204 <div class="flex">
205 <input type="checkbox" id="i-enable-spellcheck" v-model="enableSpellcheck" />
206 <label for="i-enable-spellcheck">{{ translations.get('DirectInput_EnableSpellcheck') }}</label>
207 </div>
208
209 <div class="flex">
210 <input type="checkbox" id="i-show-macronised-words" v-model="normaliseLinebreaks" :disabled="restored.length === 0" />
211 <label for="i-show-macronised-words">{{ translations.get('DirectInput_NormaliseLinebreaks') }}</label>
212 </div>
213
214 <button class="btn-primary right-column" :disabled="!canDownload" @click="copyToClipboard">
215 {{ translations.get('DirectInput_CopyToClipboard') }}
216 </button>
217
218 <button class="btn-primary right-column" :disabled="!canDownload" @click="downloadAsText">
219 {{ translations.get('DirectInput_Download') }}
220 </button>
221</div>
222</template>
223
224<style scoped lang="scss">
225.root {
226 display: grid;
227 grid-template-columns: 1fr 1fr;
228 gap: 1em;
229}
230
231.preserveWhitespace {
232 white-space: pre-line;
233}
234
235.input-area {
236 resize: vertical;
237 min-height: 4em;
238}
239
240.flex {
241 display: flex;
242 align-items: center;
243 gap: 0.5em;
244}
245
246.left-column {
247 grid-column: 1;
248}
249
250.right-column {
251 grid-column: 2;
252}
253
254.highlight {
255 background-color: var(--highlighted-word-bg);
256}
257
258.error-text {
259 color: rgb(185, 3, 3);
260}
261</style>
Note: See TracBrowser for help on using the repository browser.