зеркало из https://github.com/nextcloud/spreed.git
245 строки
7.1 KiB
JavaScript
245 строки
7.1 KiB
JavaScript
/* global Marionette, Handlebars */
|
|
|
|
/**
|
|
*
|
|
* @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com)
|
|
*
|
|
* @license GNU AGPL version 3 or any later version
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
(function(OCA, Marionette, Handlebars) {
|
|
'use strict';
|
|
|
|
OCA.SpreedMe = OCA.SpreedMe || {};
|
|
OCA.SpreedMe.Views = OCA.SpreedMe.Views || {};
|
|
|
|
var TEMPLATE =
|
|
'<div class="label-wrapper">' +
|
|
' <{{labelTagName}} class="label">{{text}}</{{labelTagName}}>' +
|
|
' {{#if editionEnabled}}' +
|
|
' <div class="edit-button"><span class="icon button icon-rename" {{#if buttonTitle}} title="{{buttonTitle}}" {{/if}}></span></div>' +
|
|
' {{/if}}' +
|
|
'</div>' +
|
|
'{{#if editionEnabled}}' +
|
|
' <div class="input-wrapper hidden-important">' +
|
|
' <input class="username" {{#if inputMaxLength}} maxlength="{{inputMaxLength}}" {{/if}} type="text" value="{{inputValue}}" {{#if inputPlaceholder}} placeholder="{{inputPlaceholder}}" {{/if}}>'+
|
|
' <input type="submit" value="" class="icon icon-confirm confirm-button"></div>'+
|
|
' </div>' +
|
|
'{{/if}}';
|
|
|
|
/**
|
|
* View for an editable text label.
|
|
*
|
|
* In its main state, an EditableTextLabel shows text in a label (an HTML
|
|
* element that can contain a line of text, like "<h1>" or "<p>"). The text
|
|
* comes from an attribute in a Backbone model and is automatically updated
|
|
* when the attribute changes.
|
|
*
|
|
* It also provides an edition state in which a text input field replaces
|
|
* the label, making possible to edit and save the attribute of the model.
|
|
* The EditableTextLabel can be make read-only by calling
|
|
* "disableEdition()", or read-write by calling "enableEdition()".
|
|
*
|
|
* The EditableTextLabel works on a single attribute of a model; they must
|
|
* be set in the constructor using the "model" and "modelAttribute" options
|
|
* (the first is the Backbone model to get the attribute from, the second is
|
|
* the name of the attribute). The "modelSaveOptions" option can be set if
|
|
* needed to control the options passed to "Model.save", and
|
|
* "extraClassNames", "labelTagName", "labelPlaceholder", "inputMaxLength",
|
|
* "inputPlaceholder" and "buttonTitle" can be used to customize some
|
|
* elements of the view.
|
|
*
|
|
* After initialization, and once the view has been rendered, the
|
|
* "modelAttribute" and "labelPlaceholder" options can be updated using the
|
|
* "setModelAttribute" and "setLabelPlaceholder" methods.
|
|
*/
|
|
var EditableTextLabel = Marionette.View.extend({
|
|
|
|
className: function() {
|
|
return 'editable-text-label' + (this.getOption('extraClassNames')? ' ' + this.getOption('extraClassNames') : '');
|
|
},
|
|
|
|
labelTagName: 'p',
|
|
|
|
buttonTitle: t('spreed', 'Edit'),
|
|
|
|
ui: {
|
|
labelWrapper: '.label-wrapper',
|
|
label: '.label',
|
|
editButton: '.edit-button',
|
|
inputWrapper: '.input-wrapper',
|
|
input: 'input.username',
|
|
confirmButton: '.confirm-button',
|
|
},
|
|
|
|
events: {
|
|
'click @ui.editButton': 'showInput',
|
|
'keyup @ui.input': 'handleInputKeyUp',
|
|
'click @ui.confirmButton': 'confirmEdit',
|
|
},
|
|
|
|
modelEvents: function() {
|
|
var modelEvents = {};
|
|
modelEvents['change:' + this.modelAttribute] = 'updateText';
|
|
|
|
return modelEvents;
|
|
},
|
|
|
|
template: Handlebars.compile(TEMPLATE),
|
|
|
|
templateContext: function() {
|
|
return {
|
|
text: this._getText(),
|
|
|
|
editionEnabled: this._editionEnabled,
|
|
|
|
labelTagName: this.getOption('labelTagName'),
|
|
inputMaxLength: this.getOption('inputMaxLength'),
|
|
// The text of the label is not used as input value as it could
|
|
// contain a placeholder text.
|
|
inputValue: this.model.get(this.modelAttribute),
|
|
inputPlaceholder: this.getOption('inputPlaceholder'),
|
|
buttonTitle: this.getOption('buttonTitle')
|
|
};
|
|
},
|
|
|
|
initialize: function(options) {
|
|
this.mergeOptions(options, ['model', 'modelAttribute', 'modelSaveOptions', 'labelPlaceholder']);
|
|
|
|
this._editionEnabled = true;
|
|
},
|
|
|
|
setModelAttribute: function(modelAttribute) {
|
|
if (this.modelAttribute === modelAttribute) {
|
|
return;
|
|
}
|
|
|
|
var modelEvents = _.result(this, 'modelEvents');
|
|
this.unbindEvents(this.model, modelEvents);
|
|
|
|
this.modelAttribute = modelAttribute;
|
|
|
|
modelEvents = _.result(this, 'modelEvents');
|
|
this.bindEvents(this.model, modelEvents);
|
|
|
|
this.updateText();
|
|
this.hideInput();
|
|
},
|
|
|
|
setLabelPlaceholder: function(labelPlaceholder) {
|
|
if (this.labelPlaceholder === labelPlaceholder) {
|
|
return;
|
|
}
|
|
|
|
this.labelPlaceholder = labelPlaceholder;
|
|
|
|
this.updateText();
|
|
},
|
|
|
|
enableEdition: function() {
|
|
if (this._editionEnabled) {
|
|
return;
|
|
}
|
|
|
|
this._editionEnabled = true;
|
|
|
|
this.render();
|
|
},
|
|
|
|
disableEdition: function() {
|
|
if (!this._editionEnabled) {
|
|
return;
|
|
}
|
|
|
|
this._editionEnabled = false;
|
|
|
|
this.render();
|
|
},
|
|
|
|
_getText: function() {
|
|
return this.model.get(this.modelAttribute) || this.labelPlaceholder || '';
|
|
},
|
|
|
|
updateText: function() {
|
|
this.getUI('label').text(this._getText());
|
|
},
|
|
|
|
showInput: function() {
|
|
this.getUI('input').val(this.model.get(this.modelAttribute));
|
|
|
|
this.getUI('inputWrapper').removeClass('hidden-important');
|
|
this.getUI('labelWrapper').addClass('hidden-important');
|
|
|
|
this.getUI('input').focus();
|
|
},
|
|
|
|
hideInput: function() {
|
|
this.getUI('labelWrapper').removeClass('hidden-important');
|
|
this.getUI('inputWrapper').addClass('hidden-important');
|
|
},
|
|
|
|
handleInputKeyUp: function(event) {
|
|
if (event.keyCode === 13) {
|
|
// Enter
|
|
this.confirmEdit();
|
|
} else if (event.keyCode === 27) {
|
|
// ESC
|
|
this.hideInput();
|
|
}
|
|
},
|
|
|
|
confirmEdit: function() {
|
|
var newText = this.getUI('input').val().trim();
|
|
|
|
if (newText === this.model.get(this.modelAttribute)) {
|
|
this.hideInput();
|
|
|
|
return;
|
|
}
|
|
|
|
// TODO This should show the error message instead of just hiding
|
|
// the input without changes.
|
|
var hideInputOnValidationError = function(/*model, error*/) {
|
|
this.hideInput();
|
|
}.bind(this);
|
|
this.model.listenToOnce(this.model, 'invalid', hideInputOnValidationError);
|
|
|
|
var options = _.clone(this.modelSaveOptions || {});
|
|
options.success = _.bind(function() {
|
|
this.model.stopListening(this.model, 'invalid', hideInputOnValidationError);
|
|
|
|
this.hideInput();
|
|
|
|
if (this.modelSaveOptions && _.isFunction(this.modelSaveOptions.success)) {
|
|
this.modelSaveOptions.success.apply(this, arguments);
|
|
}
|
|
}, this);
|
|
options.error = _.bind(function() {
|
|
this.model.stopListening(this.model, 'invalid', hideInputOnValidationError);
|
|
|
|
this.hideInput();
|
|
}, this);
|
|
|
|
this.model.save(this.modelAttribute, newText, options);
|
|
},
|
|
|
|
});
|
|
|
|
OCA.SpreedMe.Views.EditableTextLabel = EditableTextLabel;
|
|
|
|
})(OCA, Marionette, Handlebars);
|