Stackedit/src/components/modals/TemplatesModal.vue
2022-06-02 07:45:13 +08:00

187 lines
6.5 KiB
Vue

<template>
<modal-inner class="modal__inner-1--templates" aria-label="管理模板">
<div class="modal__content">
<div class="form-entry">
<label class="form-entry__label" for="template">模板</label>
<div class="form-entry__field">
<input v-if="isEditing" id="template" type="text" class="textfield" v-focus @blur="submitEdit()" @keydown.enter="submitEdit()" @keydown.esc.stop="submitEdit(true)" v-model="editingName">
<select v-else id="template" v-model="selectedId" class="textfield">
<option v-for="(template, id) in templates" :key="id" :value="id">
{{ template.name }}
</option>
</select>
</div>
<div class="form-entry__actions flex flex--row flex--end">
<button class="form-entry__button button" @click="create" v-title="'新建模板'">
<icon-file-plus></icon-file-plus>
</button>
<button class="form-entry__button button" @click="copy" v-title="'复制模板'">
<icon-file-multiple></icon-file-multiple>
</button>
<button v-if="!isReadOnly" class="form-entry__button button" @click="isEditing = true" v-title="'重命名模板'">
<icon-pen></icon-pen>
</button>
<button v-if="!isReadOnly" class="form-entry__button button" @click="remove" v-title="'删除模板'">
<icon-delete></icon-delete>
</button>
</div>
</div>
<div class="form-entry">
<label class="form-entry__label">值</label>
<div class="form-entry__field" v-for="(template, id) in templates" :key="id" v-if="id === selectedId">
<code-editor lang="handlebars" :value="template.value" :disabled="isReadOnly" @changed="template.value = $event"></code-editor>
</div>
</div>
<div v-if="!isReadOnly">
<a href="javascript:void(0)" v-if="!showHelpers" @click="showHelpers = true">添加帮助</a>
<div class="form-entry" v-else>
<br>
<label class="form-entry__label">帮助</label>
<div class="form-entry__field" v-for="(template, id) in templates" :key="id" v-if="id === selectedId">
<code-editor lang="javascript" :value="template.helpers" @changed="template.helpers = $event"></code-editor>
</div>
</div>
</div>
</div>
<div class="modal__button-bar">
<button class="button" @click="config.reject()">取消</button>
<button class="button button--resolve" @click="resolve()">确认</button>
</div>
</modal-inner>
</template>
<script>
import { mapGetters } from 'vuex';
import utils from '../../services/utils';
import badgeSvc from '../../services/badgeSvc';
import ModalInner from './common/ModalInner';
import CodeEditor from '../CodeEditor';
import emptyTemplateValue from '../../data/empties/emptyTemplateValue.html';
import emptyTemplateHelpers from '!raw-loader!../../data/empties/emptyTemplateHelpers.js'; // eslint-disable-line
import store from '../../store';
const collator = new Intl.Collator(undefined, { sensitivity: 'base' });
function fillEmptyFields(template) {
if (template.value === '\n') {
template.value = emptyTemplateValue;
}
if (template.helpers === '\n') {
template.helpers = emptyTemplateHelpers;
}
}
export default {
components: {
ModalInner,
CodeEditor,
},
data: () => ({
selectedId: '',
templates: {},
showHelpers: false,
isEditing: false,
editingName: '',
}),
computed: {
...mapGetters('modal', [
'config',
]),
isReadOnly() {
return this.templates[this.selectedId].isAdditional;
},
},
created() {
this.$watch(
() => store.getters['data/allTemplatesById'],
(allTemplatesById) => {
const templates = {};
// Sort templates by name
Object.entries(allTemplatesById)
.sort(([, template1], [, template2]) => collator.compare(template1.name, template2.name))
.forEach(([id, template]) => {
const templateClone = utils.deepCopy(template);
fillEmptyFields(templateClone);
templates[id] = templateClone;
});
this.templates = templates;
this.selectedId = this.config.selectedId;
if (!templates[this.selectedId]) {
[this.selectedId] = Object.keys(templates);
}
this.isEditing = false;
},
{ immediate: true },
);
this.$watch('selectedId', (selectedId) => {
const template = this.templates[selectedId];
this.showHelpers = template.helpers !== emptyTemplateHelpers;
this.editingName = template.name;
}, { immediate: true });
},
methods: {
create() {
const template = {
name: 'New template',
value: '\n',
helpers: '\n',
};
fillEmptyFields(template);
this.selectedId = utils.uid();
this.templates[this.selectedId] = template;
this.isEditing = true;
},
copy() {
const template = utils.deepCopy(this.templates[this.selectedId]);
template.name += ' copy';
delete template.isAdditional;
this.selectedId = utils.uid();
this.templates[this.selectedId] = template;
this.isEditing = true;
},
remove() {
delete this.templates[this.selectedId];
[this.selectedId] = Object.keys(this.templates);
},
submitEdit(cancel) {
const template = this.templates[this.selectedId];
if (!cancel && this.editingName) {
template.name = utils.sanitizeName(this.editingName);
} else {
this.editingName = template.name;
}
setTimeout(() => { // For the form-entry to get the blur event
this.isEditing = false;
}, 1);
},
async resolve() {
const oldTemplateIds = Object.keys(store.getters['data/templatesById']);
await store.dispatch('data/setTemplatesById', this.templates);
const newTemplateIds = Object.keys(store.getters['data/templatesById']);
const createdCount = newTemplateIds
.filter(id => !oldTemplateIds.includes(id))
.length;
const removedCount = oldTemplateIds
.filter(id => !newTemplateIds.includes(id))
.length;
if (createdCount) {
badgeSvc.addBadge('addTemplate');
}
if (removedCount) {
badgeSvc.addBadge('removeTemplate');
}
this.config.resolve({
templates: this.templates,
selectedId: this.selectedId,
});
},
},
};
</script>
<style lang="scss">
.modal__inner-1.modal__inner-1--templates {
max-width: 600px;
}
</style>