scinote-web/app/javascript/vue/shared/tinymce.vue

184 lines
5.7 KiB
Vue
Raw Normal View History

2022-05-10 19:28:09 +08:00
<template>
2022-08-04 17:06:25 +08:00
<div class="tinymce-wrapper">
<div class="tinymce-container" :class="{ 'error': error }">
<form class="tiny-mce-editor" role="form" :action="updateUrl" accept-charset="UTF-8" data-remote="true" method="post">
<input type="hidden" name="_method" value="patch">
<div class="hidden tinymce-cancel-button tox-mbtn" tabindex="-1">
<button type="button" tabindex="-1">
2023-06-08 23:33:50 +08:00
<span class="sn-icon sn-icon-close"></span>
<span class="mce-txt">{{ i18n.t('general.cancel') }}</span>
</button>
</div>
<div class="hidden tinymce-save-button tox-mbtn" tabindex="-1">
<button type="button" tabindex="-1" >
2023-06-15 21:12:51 +08:00
<span class="sn-icon sn-icon-check"></span>
<span class="mce-txt">{{ i18n.t('general.save') }}</span>
</button>
</div>
<div class="hidden tinymce-status-badge pull-right">
<i class="fas fa-check-circle"></i>
<span>{{ i18n.t('tiny_mce.saved_label') }}</span>
</div>
<div :id="`${objectType}_view_${objectId}`"
@click="initTinymce"
v-html="value_html"
class="ql-editor tinymce-view"
:data-placeholder="placeholder"
:data-tinymce-init="`tinymce-${objectType}-description-${objectId}`">
</div>
<div class="flex tinymce-editor-container">
<textarea :id="`${objectType}_textarea_${objectId}`"
class="form-control hidden"
autocomplete="off"
:data-tinymce-object="`tinymce-${objectType}-description-${objectId}`"
:data-object-type="objectType"
:data-object-id="objectId"
:data-last-updated="lastUpdated * 1000"
:data-tinymce-asset-path="this.getStaticUrl('tiny-mce-assets-url')"
:placeholder="placeholder"
:value="value"
cols="120"
rows="10"
:name="fieldName"
aria-hidden="true">
</textarea>
2022-12-22 17:37:47 +08:00
<input type="hidden" class="tiny-mce-images" name="tiny_mce_images" value="[]">
</div>
</form>
</div>
2022-08-04 17:06:25 +08:00
<div v-if="active && error" class="tinymce-error">
{{ error }}
</div>
2022-05-10 19:28:09 +08:00
</div>
</template>
<script>
import UtilsMixin from '../mixins/utils.js';
2022-08-04 17:06:25 +08:00
2022-05-10 19:28:09 +08:00
export default {
name: 'Tinymce',
props: {
value: String,
value_html: String,
placeholder: String,
updateUrl: String,
objectType: String,
objectId: Number,
fieldName: String,
lastUpdated: Number,
inEditMode: Boolean,
assignableMyModuleId: Number,
characterLimit: {
type: Number,
default: null
}
},
data() {
return {
characterCount: 0,
2022-08-04 17:06:25 +08:00
blurEventHandler: null,
active: false
}
2022-05-10 19:28:09 +08:00
},
2022-08-04 17:06:25 +08:00
mixins: [ UtilsMixin ],
2022-05-10 19:28:09 +08:00
watch: {
inEditMode() {
if (this.inEditMode) {
2022-08-04 17:06:25 +08:00
this.initTinymce();
} else {
this.wrapTables();
2022-05-10 19:28:09 +08:00
}
this.initCodeHighlight();
},
characterCount() {
if (this.editorInstance()) {
this.editorInstance().blurDisabled = this.error != false ;
}
}
},
computed: {
error() {
if(this.characterLimit && this.characterCount > this.characterLimit) {
return(
this.i18n.t('errors.general_text_too_long')
);
}
return false
2022-05-10 19:28:09 +08:00
}
},
mounted() {
if (this.inEditMode) {
this.initTinymce();
} else {
this.wrapTables();
}
this.initCodeHighlight();
},
2022-05-10 19:28:09 +08:00
methods: {
initTinymce(e) {
2022-05-10 19:28:09 +08:00
let textArea = `#${this.objectType}_textarea_${this.objectId}`;
if (this.active) return
if (e && $(e.target).prop("tagName") === 'A') return
if (e && $(e.target).hasClass('atwho-user-popover')) return
if (e && $(e.target).hasClass('record-info-link')) return
if (e && $(e.target).parent().hasClass('atwho-inserted')) return
TinyMCE.init(textArea, {
onSaveCallback: (data) => {
if (data.data) {
this.$emit('update', data.data)
}
this.$emit('editingDisabled');
this.wrapTables();
this.initCodeHighlight();
},
afterInitCallback: () => {
this.active = true;
this.initCharacterCount();
this.$emit('editingEnabled');
},
placeholder: this.placeholder,
assignableMyModuleId: this.assignableMyModuleId
2022-05-10 19:28:09 +08:00
}
)
2022-05-10 19:28:09 +08:00
},
getStaticUrl(name) {
return $(`meta[name=\'${name}\']`).attr('content');
},
wrapTables() {
2022-12-14 21:32:23 +08:00
this.$nextTick(() => {
TinyMCE.wrapTables($(this.$el).find('.tinymce-view'));
2022-12-14 21:32:23 +08:00
});
},
initCharacterCount() {
if (!this.editorInstance()) return;
2022-08-04 17:06:25 +08:00
this.characterCount = this.editorInstance().plugins.wordcount.body.getCharacterCount();
this.editorInstance().on('input change paste keydown', (e) => {
e.currentTarget && (this.characterCount = this.editorInstance().plugins.wordcount.body.getCharacterCount());
});
this.editorInstance().on('remove', () => this.active = false);
2022-08-04 17:06:25 +08:00
// clear error on cancel
$(this.editorInstance().container).find('.tinymce-cancel-button').on('click', ()=> {
this.characterCount = 0;
});
},
editorInstance() {
return tinyMCE.activeEditor;
},
initCodeHighlight() {
this.$nextTick(() => {
Prism.highlightAllUnder(this.$el);
});
2022-05-10 19:28:09 +08:00
}
}
}
2022-05-11 17:18:40 +08:00
</script>