comindware.ui
Version:
Comindware Core UI provides the basic components like editors, lists, dropdowns, popups that we so desperately need while creating Marionette-based single-page applications.
266 lines (231 loc) • 9.81 kB
JavaScript
/**
* Developer: Stepan Burguchev
* Date: 10/13/2014
* Copyright: 2009-2016 Comindware®
* All Rights Reserved
* Published under the MIT license
*/
'use strict';
import template from './templates/textAreaEditor.hbs';
import BaseItemEditorView from './base/BaseItemEditorView';
import LocalizationService from '../../services/LocalizationService';
import { Handlebars, keypress } from 'lib';
import { keyCode, helpers, htmlHelpers } from 'utils';
import formRepository from '../formRepository';
const changeMode = {
blur: 'blur',
keydown: 'keydown'
};
const size = {
auto: 'auto',
fixed: 'fixed'
};
const defaultOptions = function() {
return {
changeMode: changeMode.blur,
size: size.auto,
emptyPlaceholder: LocalizationService.get('CORE.FORM.EDITORS.TEXTAREAEDITOR.PLACEHOLDER'),
readonlyPlaceholder: LocalizationService.get('CORE.FORM.EDITORS.TEXTAREAEDITOR.READONLYPLACEHOLDER'),
disablePlaceholder: LocalizationService.get('CORE.FORM.EDITORS.TEXTAREAEDITOR.DISABLEPLACEHOLDER'),
maxLength: null,
height: null,
minHeight: 2,
maxHeight: null,
showTitle: true
};
};
/**
* @name TextAreaEditorView
* @memberof module:core.form.editors
* @class Multiline text editor. Supported data type: <code>String</code>.
* @extends module:core.form.editors.base.BaseEditorView
* @param {Object} options Options object. All the properties of {@link module:core.form.editors.base.BaseEditorView BaseEditorView} class are also supported.
* @param {Number|null} [options.maxLength=null] The maximum number of characters. Not limited if <code>null</code>.
* @param {String} [options.changeMode='blur'] Determines the moment the editor's value is updated:<ul>
* <li><code>'keydown'</code> - on key press.</li>
* <li><code>'blur'</code> - on focus out.</li></ul>
* @param {String} [options.size='auto'] Determines the strategy to compute the editor's height:<ul>
* <li><code>'auto'</code> - determined by the content (withing the range [<code>minHeight</code>, <code>maxHeight</code>]).</li>
* <li><code>'fixed'</code> - fixed, determined by <code>height</code> option.</li></ul>
* @param {String} [options.emptyPlaceholder='Field is empty'] Empty text placeholder.
* @param {String} [options.readonlyPlaceholder='Field is readonly'] Текст placeholder, отображаемый
* в случае если эдитор имеет флаг <code>readonly</code>.
* @param {String} [options.disablePlaceholder='Field is disabled'] Текст placeholder, отображаемый
* в случае если эдитор имеет флаг <code>enabled: false</code>.
* @param {Number} [options.height=null] The height of the editor (in rows) when its size is fixed.
* @param {Number} [options.minHeight=2] The minimum height of the editor (in rows).
* @param {Number} [options.maxHeight=30] The maximum height of the editor (in rows).
* @param {Boolean} {options.showTitle=true} Whether to show title attribute.
* */
formRepository.editors.TextArea = BaseItemEditorView.extend(/** @lends module:core.form.editors.TextAreaEditorView.prototype */{
initialize(options) {
const defaults = defaultOptions();
if (options.schema) {
_.extend(this.options, defaults, _.pick(options.schema, _.keys(defaults)));
} else {
_.extend(this.options, defaults, _.pick(options || {}, _.keys(defaults)));
}
this.placeholder = this.options.emptyPlaceholder;
},
focusElement: '.js-textarea',
className: 'editor editor_textarea',
ui: {
textarea: '.js-textarea'
},
events: {
change: '__change',
'input @ui.textarea': '__input',
'keyup @ui.textarea': '__keyup'
},
template: Handlebars.compile(template),
templateHelpers() {
return this.options;
},
onRender() {
// Keyboard shortcuts listener
if (this.keyListener) {
this.keyListener.reset();
}
this.keyListener = new keypress.Listener(this.ui.textarea[0]);
},
/**
* Позволяет добавить callback-функцию на ввод определенной клавиши или комбинации клавиш. Использует метод simple_combo плагина
* [Keypress](https://dmauro.github.io/Keypress/).
* @param {String} key Комбинация клавиш или несколько комбинаций, разделенных запятыми.
* Полный список с названиями клавиш указан в исходном файле плагина:
* [keypress.coffee](https://github.com/dmauro/Keypress/blob/master/keypress.coffee#L750-912).
* @param {String} callback Callback-функция, вызываемая по срабатыванию комбо.
* */
addKeyboardListener(key, callback) {
if (!this.keyListener) {
helpers.throwInvalidOperationError('You must apply keyboard listener after \'render\' event has happened.');
}
const keys = key.split(',');
_.each(keys, function(k) {
this.keyListener.simple_combo(k, callback);
}, this);
},
onShow() {
const value = this.getValue() || '';
this.ui.textarea.val(value);
if (this.options.showTitle) {
this.$el.prop('title', value);
}
switch (this.options.size) {
case size.auto:
this.ui.textarea.attr('rows', this.options.minHeight);
if (this.options.maxHeight) {
const maxHeight = parseInt(this.ui.textarea.css('line-height'), 10) * this.options.maxHeight;
this.ui.textarea.css('maxHeight', maxHeight);
}
if (!htmlHelpers.isElementInDom(this.el)) {
helpers.throwInvalidOperationError('Auto-sized TextAreaEditor MUST be in DOM while rendering (bad height computing otherwise).');
}
this.ui.textarea.autosize({ append: '' });
break;
case size.fixed:
this.ui.textarea.attr('rows', this.options.height);
break;
default:
helpers.throwArgumentError('Invalid `size parameter`.');
}
},
setPermissions(enabled, readonly) {
BaseItemEditorView.prototype.setPermissions.call(this, enabled, readonly);
this.setPlaceholder();
},
setPlaceholder() {
if (!this.getEnabled()) {
this.placeholder = this.options.disablePlaceholder;
} else if (this.getReadonly()) {
this.placeholder = this.options.readonlyPlaceholder;
} else {
this.placeholder = this.options.emptyPlaceholder;
}
this.ui.textarea.prop('placeholder', this.placeholder);
},
__setEnabled(enabled) {
//noinspection Eslint
BaseItemEditorView.prototype.__setEnabled.call(this, enabled);
this.ui.textarea.prop('disabled', !enabled);
},
__setReadonly(readonly) {
//noinspection Eslint
BaseItemEditorView.prototype.__setReadonly.call(this, readonly);
if (this.getEnabled()) {
this.ui.textarea.prop('readonly', readonly);
this.ui.textarea.prop('tabindex', readonly ? -1 : 0);
}
},
__value(value, updateUi, triggerChange) {
if (this.value === value) {
return;
}
this.value = value;
if (this.options.showTitle) {
this.$el.prop('title', value);
}
if (updateUi) {
this.ui.textarea.val(value);
}
if (triggerChange) {
this.__triggerChange();
}
},
/**
* Метод позволяет установить позицию курсора.
* @param {Number} position Новая позиция курсора.
* */
setCaretPos(position) {
this.ui.textarea.setSelection(position, position);
},
setValue(value) {
this.__value(value, true, false);
},
__change() {
this.__triggerInput();
this.__value(this.ui.textarea.val(), false, true);
},
__input() {
this.__triggerInput();
if (this.options.changeMode === changeMode.keydown) {
this.__value(this.ui.textarea.val(), false, true);
}
},
__keyup(e) {
if ([
keyCode.LEFT,
keyCode.RIGHT,
keyCode.HOME,
keyCode.END
].indexOf(e.keyCode) === -1) {
return;
}
const caret = this.ui.textarea.getSelection();
if (this.oldCaret && this.oldCaret.start === caret.start && this.oldCaret.end === caret.end) {
return;
}
this.oldCaret = caret;
const text = this.ui.textarea.val();
this.trigger('caretChange', text, caret);
},
__triggerInput() {
const text = this.ui.textarea.val();
if (this.oldText === text) {
return;
}
this.oldText = text;
const caret = this.ui.textarea.getSelection();
this.trigger('input', text, {
start: caret.start,
end: caret.end
});
},
/**
* Focuses the editor's input and selects all the text in it.
* */
select() {
this.ui.textarea.select();
}
});
export default formRepository.editors.TextArea;