134 lines
4.6 KiB
JavaScript
134 lines
4.6 KiB
JavaScript
import autosize from 'autosize';
|
|
import $ from 'jquery';
|
|
import { isEmpty } from 'lodash';
|
|
import GfmAutoComplete, { defaultAutocompleteConfig } from 'ee_else_ce/gfm_auto_complete';
|
|
import { disableButtonIfEmptyField } from '~/lib/utils/common_utils';
|
|
import dropzoneInput from './dropzone_input';
|
|
import { addMarkdownListeners, removeMarkdownListeners } from './lib/utils/text_markdown';
|
|
|
|
export default class GLForm {
|
|
/**
|
|
* Create a GLForm
|
|
*
|
|
* @param {jQuery} form Root element of the GLForm
|
|
* @param {Object} enableGFM Which autocomplete features should be enabled?
|
|
* @param {Boolean} forceNew If true, treat the element as a **new** form even if `gfm-form` class already exists.
|
|
* @param {Object} gfmDataSources The paths of the autocomplete data sources to use for GfmAutoComplete
|
|
* By default, the backend embeds these in the global object gl.GfmAutocomplete.dataSources.
|
|
* Use this param to override them.
|
|
*/
|
|
constructor(form, enableGFM = {}, forceNew = false, gfmDataSources = {}) {
|
|
this.form = form;
|
|
this.textarea = this.form.find('textarea.js-gfm-input');
|
|
this.enableGFM = { ...defaultAutocompleteConfig, ...enableGFM };
|
|
|
|
// Disable autocomplete for keywords which do not have dataSources available
|
|
let dataSources = (gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources) || {};
|
|
|
|
if (!isEmpty(gfmDataSources)) {
|
|
dataSources = gfmDataSources;
|
|
}
|
|
|
|
Object.keys(this.enableGFM).forEach((item) => {
|
|
if (item !== 'emojis' && !dataSources[item]) {
|
|
this.enableGFM[item] = false;
|
|
}
|
|
});
|
|
|
|
// Before we start, we should clean up any previous data for this form
|
|
this.destroy();
|
|
// Set up the form
|
|
this.setupForm(dataSources, forceNew);
|
|
this.form.data('glForm', this);
|
|
}
|
|
|
|
destroy() {
|
|
// Clean form listeners
|
|
this.clearEventListeners();
|
|
if (this.autoComplete) {
|
|
this.autoComplete.destroy();
|
|
}
|
|
if (this.formDropzone) {
|
|
this.formDropzone.destroy();
|
|
}
|
|
|
|
this.form.data('glForm', null);
|
|
}
|
|
|
|
setupForm(dataSources, forceNew = false) {
|
|
const isNewForm = this.form.is(':not(.gfm-form)') || forceNew;
|
|
this.form.removeClass('js-new-note-form');
|
|
if (isNewForm) {
|
|
this.form.find('.div-dropzone').remove();
|
|
this.form.addClass('gfm-form');
|
|
// remove notify commit author checkbox for non-commit notes
|
|
disableButtonIfEmptyField(
|
|
this.form.find('.js-note-text'),
|
|
this.form.find('.js-comment-button, .js-note-new-discussion'),
|
|
);
|
|
this.autoComplete = new GfmAutoComplete(dataSources);
|
|
this.autoComplete.setup(this.form.find('.js-gfm-input'), this.enableGFM);
|
|
this.formDropzone = dropzoneInput(this.form, { parallelUploads: 1 });
|
|
|
|
if (this.form.is(':not(.js-no-autosize)')) {
|
|
autosize(this.textarea);
|
|
}
|
|
}
|
|
// form and textarea event listeners
|
|
this.addEventListeners();
|
|
addMarkdownListeners(this.form);
|
|
this.form.show();
|
|
if (this.isAutosizeable) this.setupAutosize();
|
|
if (this.textarea.data('autofocus') === true) this.textarea.focus();
|
|
}
|
|
|
|
setupAutosize() {
|
|
// eslint-disable-next-line @gitlab/no-global-event-off
|
|
this.textarea.off('autosize:resized').on('autosize:resized', this.setHeightData.bind(this));
|
|
|
|
// eslint-disable-next-line @gitlab/no-global-event-off
|
|
this.textarea.off('mouseup.autosize').on('mouseup.autosize', this.destroyAutosize.bind(this));
|
|
|
|
setTimeout(() => {
|
|
autosize(this.textarea);
|
|
this.textarea.css('resize', 'vertical');
|
|
}, 0);
|
|
}
|
|
|
|
setHeightData() {
|
|
this.textarea.data('height', this.textarea.outerHeight());
|
|
}
|
|
|
|
destroyAutosize() {
|
|
const outerHeight = this.textarea.outerHeight();
|
|
|
|
if (this.textarea.data('height') === outerHeight) return;
|
|
|
|
autosize.destroy(this.textarea);
|
|
|
|
this.textarea.data('height', outerHeight);
|
|
this.textarea.outerHeight(outerHeight);
|
|
this.textarea.css('max-height', window.outerHeight);
|
|
}
|
|
|
|
clearEventListeners() {
|
|
// eslint-disable-next-line @gitlab/no-global-event-off
|
|
this.textarea.off('focus');
|
|
// eslint-disable-next-line @gitlab/no-global-event-off
|
|
this.textarea.off('blur');
|
|
removeMarkdownListeners(this.form);
|
|
}
|
|
|
|
addEventListeners() {
|
|
this.textarea.on('focus', function focusTextArea() {
|
|
$(this).closest('.md-area').addClass('is-focused');
|
|
});
|
|
this.textarea.on('blur', function blurTextArea() {
|
|
$(this).closest('.md-area').removeClass('is-focused');
|
|
});
|
|
}
|
|
|
|
get supportsQuickActions() {
|
|
return Boolean(this.textarea.data('supports-quick-actions'));
|
|
}
|
|
}
|