|
|
@@ -1,32 +1,38 @@
|
|
|
<template>
|
|
|
- <div>
|
|
|
+ <div class="setting-text">
|
|
|
<textarea
|
|
|
ref="text"
|
|
|
class="monospace-font"
|
|
|
- :class="{'has-error': parsedData.error}"
|
|
|
+ :class="{'has-error': error}"
|
|
|
spellcheck="false"
|
|
|
- v-model="value"
|
|
|
+ v-model="text"
|
|
|
:disabled="disabled"
|
|
|
- :title="parsedData.error"
|
|
|
:placeholder="placeholder"
|
|
|
- :rows="rows || calcRows(value)"
|
|
|
+ :rows="rows || calcRows(text)"
|
|
|
@change="onChange"
|
|
|
+ @ctrl-s="onSave"
|
|
|
/>
|
|
|
- <button v-if="hasSave" v-text="i18n('buttonSave')" @click="onSave"
|
|
|
+ <button v-if="hasSave" v-text="saved || i18n('buttonSave')" @click="onSave" :title="ctrlS"
|
|
|
:disabled="disabled || !canSave"/>
|
|
|
<button v-if="hasReset" v-text="i18n('buttonReset')" @click="onReset"
|
|
|
:disabled="disabled || !canReset"/>
|
|
|
- <slot/>
|
|
|
+ <!-- DANGER! Keep the error tag in one line to keep the space which ensures the first word
|
|
|
+ is selected correctly without the preceding button's text on double-click. -->
|
|
|
+ <slot/> <span class="error text-red sep" v-text="error" v-if="json"/>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
+import { i18n } from '@/common';
|
|
|
import { getUnloadSentry } from '@/common/router';
|
|
|
+import { modifiers } from '@violentmonkey/shortcut';
|
|
|
import { deepEqual, objectGet } from '../object';
|
|
|
import options from '../options';
|
|
|
import defaults from '../options-defaults';
|
|
|
import hookSetting from '../hook-setting';
|
|
|
|
|
|
+const ctrlS = modifiers.ctrlcmd === 'm' ? '⌘S' : 'Ctrl-S';
|
|
|
+
|
|
|
export default {
|
|
|
props: {
|
|
|
name: String,
|
|
|
@@ -41,42 +47,47 @@ export default {
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
- value: null,
|
|
|
+ ctrlS,
|
|
|
+ error: null,
|
|
|
placeholder: null,
|
|
|
savedValue: null,
|
|
|
+ saved: '',
|
|
|
+ text: '',
|
|
|
+ value: null,
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
- parsedData() {
|
|
|
- let value;
|
|
|
- let error;
|
|
|
- if (this.json) {
|
|
|
- try {
|
|
|
- value = JSON.parse(this.value);
|
|
|
- } catch (e) {
|
|
|
- error = e.message || e;
|
|
|
- }
|
|
|
- } else {
|
|
|
- value = this.value;
|
|
|
- }
|
|
|
- return { value, error };
|
|
|
- },
|
|
|
isDirty() {
|
|
|
- return !deepEqual(this.parsedData.value, this.savedValue || '');
|
|
|
+ return !deepEqual(this.value, this.savedValue || '');
|
|
|
},
|
|
|
canSave() {
|
|
|
- return !this.parsedData.error && this.isDirty;
|
|
|
+ return !this.error && this.isDirty;
|
|
|
},
|
|
|
canReset() {
|
|
|
- return !deepEqual(this.parsedData.value, this.defaultValue || '');
|
|
|
+ return !deepEqual(this.value, this.defaultValue || '');
|
|
|
},
|
|
|
},
|
|
|
watch: {
|
|
|
isDirty(state) {
|
|
|
this.toggleUnloadSentry(state);
|
|
|
},
|
|
|
- },
|
|
|
- created() {
|
|
|
+ text(str) {
|
|
|
+ let value;
|
|
|
+ let error;
|
|
|
+ if (this.json) {
|
|
|
+ try {
|
|
|
+ value = JSON.parse(str);
|
|
|
+ } catch (e) {
|
|
|
+ error = e.message;
|
|
|
+ }
|
|
|
+ this.error = error;
|
|
|
+ } else {
|
|
|
+ value = str;
|
|
|
+ }
|
|
|
+ this.value = value;
|
|
|
+ this.saved = '';
|
|
|
+ },
|
|
|
+ }, created() {
|
|
|
const handle = this.json
|
|
|
? (value => JSON.stringify(value, null, ' '))
|
|
|
// XXX compatible with old data format
|
|
|
@@ -84,7 +95,7 @@ export default {
|
|
|
const defaultValue = objectGet(defaults, this.name);
|
|
|
this.revoke = hookSetting(this.name, val => {
|
|
|
this.savedValue = val;
|
|
|
- this.value = handle(val);
|
|
|
+ this.text = handle(val);
|
|
|
});
|
|
|
this.defaultValue = defaultValue;
|
|
|
this.placeholder = handle(defaultValue);
|
|
|
@@ -93,7 +104,7 @@ export default {
|
|
|
// The component won't be destroyed on tab change, so the changes are actually kept.
|
|
|
// Here we reset it to make sure the user loses the changes when leaving the settings tab.
|
|
|
// Otherwise the user may be confused about where the changes are after switching back.
|
|
|
- this.value = handle(this.savedValue);
|
|
|
+ this.text = handle(this.savedValue);
|
|
|
});
|
|
|
},
|
|
|
beforeUnmount() {
|
|
|
@@ -106,7 +117,8 @@ export default {
|
|
|
if (!this.hasSave && this.canSave) this.onSave();
|
|
|
},
|
|
|
onSave() {
|
|
|
- options.set(this.name, this.parsedData.value).catch(this.bgError);
|
|
|
+ options.set(this.name, this.value).catch(this.bgError);
|
|
|
+ this.saved = i18n('buttonSaved');
|
|
|
this.$emit('save');
|
|
|
},
|
|
|
onReset() {
|
|
|
@@ -131,3 +143,15 @@ export default {
|
|
|
},
|
|
|
};
|
|
|
</script>
|
|
|
+
|
|
|
+<style>
|
|
|
+.setting-text {
|
|
|
+ > .error {
|
|
|
+ /* We've used .sep so our error text aligns with the buttons, now we need to undo some parts */
|
|
|
+ display: inline;
|
|
|
+ &::after {
|
|
|
+ content: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|