UNPKG

@atlassian/aui

Version:

Atlassian User Interface Framework

514 lines (428 loc) 18.1 kB
(function (global, factory) { if (typeof define === "function" && define.amd) { define(['module', 'exports', '../jquery', '../backbone', './class-names', './data-keys', './events', '../i18n', './throbber', '../../../js-vendor/jquery/serializetoobject'], factory); } else if (typeof exports !== "undefined") { factory(module, exports, require('../jquery'), require('../backbone'), require('./class-names'), require('./data-keys'), require('./events'), require('../i18n'), require('./throbber'), require('../../../js-vendor/jquery/serializetoobject')); } else { var mod = { exports: {} }; factory(mod, mod.exports, global.jquery, global.backbone, global.classNames, global.dataKeys, global.events, global.i18n, global.throbber, global.serializetoobject); global.editRow = mod.exports; } })(this, function (module, exports, _jquery, _backbone, _classNames, _dataKeys, _events, _i18n, _throbber) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _jquery2 = _interopRequireDefault(_jquery); var _backbone2 = _interopRequireDefault(_backbone); var _classNames2 = _interopRequireDefault(_classNames); var _dataKeys2 = _interopRequireDefault(_dataKeys); var _events2 = _interopRequireDefault(_events); var _i18n2 = _interopRequireDefault(_i18n); var _throbber2 = _interopRequireDefault(_throbber); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') !== -1; /** * An abstract class that gives the required behaviour for the creating and editing entries. Extend this class and pass * it as the {views.row} property of the options passed to RestfulTable in construction. */ exports.default = _backbone2.default.View.extend({ tagName: 'tr', // delegate events events: { 'focusin': '_focus', 'click': '_focus', 'keyup': '_handleKeyUpEvent' }, /** * @constructor * @param {Object} options */ initialize: function initialize(options) { this.$el = (0, _jquery2.default)(this.el); // faster lookup this._event = _events2.default; this.classNames = _classNames2.default; this.dataKeys = _dataKeys2.default; this.columns = options.columns; this.isCreateRow = options.isCreateRow; this.allowReorder = options.allowReorder; // Allow cancelling an edit with support for setting a new element. this.events['click .' + this.classNames.CANCEL] = '_cancel'; this.delegateEvents(); if (options.isUpdateMode) { this.isUpdateMode = true; } else { this._modelClass = options.model; this.model = new this._modelClass(); } this.fieldFocusSelector = options.fieldFocusSelector; this.bind(this._event.CANCEL, function () { this.disabled = true; }).bind(this._event.SAVE, function (focusUpdated) { if (!this.disabled) { this.submit(focusUpdated); } }).bind(this._event.FOCUS, function (name) { this.focus(name); }).bind(this._event.BLUR, function () { this.$el.removeClass(this.classNames.FOCUSED); this.disable(); }).bind(this._event.SUBMIT_STARTED, function () { this._submitStarted(); }).bind(this._event.SUBMIT_FINISHED, function () { this._submitFinished(); }); }, /** * Renders default cell contents * * @param data */ defaultColumnRenderer: function defaultColumnRenderer(data) { if (data.allowEdit !== false) { return (0, _jquery2.default)("<input type='text' />").addClass('text').attr({ name: data.name, value: data.value }); } else if (data.value) { return document.createTextNode(data.value); } }, /** * Renders drag handle * @return jQuery */ renderDragHandle: function renderDragHandle() { return '<span class="' + this.classNames.DRAG_HANDLE + '"></span></td>'; }, /** * Executes cancel event if ESC is pressed * * @param {Event} e */ _handleKeyUpEvent: function _handleKeyUpEvent(e) { if (e.keyCode === 27) { this.trigger(this._event.CANCEL); } }, /** * Fires cancel event * * @param {Event} e * * @return EditRow */ _cancel: function _cancel(e) { this.trigger(this._event.CANCEL); e.preventDefault(); return this; }, /** * Disables events/fields and adds safe guard against double submitting * * @return EditRow */ _submitStarted: function _submitStarted() { this.submitting = true; this.showLoading().disable().delegateEvents({}); return this; }, /** * Enables events & fields * * @return EditRow */ _submitFinished: function _submitFinished() { this.submitting = false; this.hideLoading().enable().delegateEvents(this.events); return this; }, /** * Handles dom focus event, by only focusing row if it isn't already * * @param {Event} e * * @return EditRow */ _focus: function _focus(e) { if (!this.hasFocus()) { this.trigger(this._event.FOCUS, e.target.name); } return this; }, /** * Returns true if row has focused class * * @return Boolean */ hasFocus: function hasFocus() { return this.$el.hasClass(this.classNames.FOCUSED); }, /** * Focus specified field (by name or id - first argument), first field with an error or first field (DOM order) * * @param name * * @return EditRow */ focus: function focus(name) { var $focus; var $error; this.enable(); if (name) { $focus = this.$el.find(this.fieldFocusSelector(name)); } else { $error = this.$el.find(this.classNames.ERROR + ':first'); if ($error.length === 0) { $focus = this.$el.find(':input:text:first'); } else { $focus = $error.parent().find(':input'); } } this.$el.addClass(this.classNames.FOCUSED); $focus.focus().trigger('select'); return this; }, /** * Disables all fields * * @return EditRow */ disable: function disable() { var $replacementSubmit; var $submit; // firefox does not allow you to submit a form if there are 2 or more submit buttons in a form, even if all but // one is disabled. It also does not let you change the type="submit' to type="button". Therfore he lies the hack. if (isFirefox) { $submit = this.$el.find(':submit'); if ($submit.length) { $replacementSubmit = (0, _jquery2.default)("<input type='submit' class='" + this.classNames.SUBMIT + "' />").addClass($submit.attr('class')).val($submit.val()).data(this.dataKeys.ENABLED_SUBMIT, $submit); $submit.replaceWith($replacementSubmit); } } this.$el.addClass(this.classNames.DISABLED).find(':submit').attr('disabled', 'disabled'); return this; }, /** * Enables all fields * * @return EditRow */ enable: function enable() { var $placeholderSubmit; var $submit; // firefox does not allow you to submit a form if there are 2 or more submit buttons in a form, even if all but // one is disabled. It also does not let you change the type="submit' to type="button". Therfore he lies the hack. if (isFirefox) { $placeholderSubmit = this.$el.find(this.classNames.SUBMIT); $submit = $placeholderSubmit.data(this.dataKeys.ENABLED_SUBMIT); if ($submit && $placeholderSubmit.length) { $placeholderSubmit.replaceWith($submit); } } this.$el.removeClass(this.classNames.DISABLED).find(':submit').removeAttr('disabled'); return this; }, /** * Shows loading indicator * * @return EditRow */ showLoading: function showLoading() { this.$el.addClass(this.classNames.LOADING); return this; }, /** * Hides loading indicator * * @return EditRow */ hideLoading: function hideLoading() { this.$el.removeClass(this.classNames.LOADING); return this; }, /** * If any of the fields have changed * * @return {Boolean} */ hasUpdates: function hasUpdates() { return !!this.mapSubmitParams(this.serializeObject()); }, /** * Serializes the view into model representation. * Default implementation uses simple jQuery plugin to serialize form fields into object * * @return Object */ serializeObject: function serializeObject() { var $el = this.$el; return $el.serializeObject ? $el.serializeObject() : $el.serialize(); }, mapSubmitParams: function mapSubmitParams(params) { return this.model.changedAttributes(params); }, /** * Handle submission of new entries and editing of old. * * @param {Boolean} focusUpdated - flag of whether to focus read-only view after succssful submission * * @return EditRow */ submit: function submit(focusUpdated) { var instance = this; var values; // IE doesnt like it when the focused element is removed if (document.activeElement !== window) { (0, _jquery2.default)(document.activeElement).blur(); } if (this.isUpdateMode) { values = this.mapSubmitParams(this.serializeObject()); // serialize form fields into JSON if (!values) { return instance.trigger(instance._event.CANCEL); } } else { this.model.clear(); values = this.mapSubmitParams(this.serializeObject()); // serialize form fields into JSON } this.trigger(this._event.SUBMIT_STARTED); /* Attempt to add to server model. If fail delegate to createView to render errors etc. Otherwise, add a new model to this._models and render a row to represent it. */ this.model.save(values, { success: function success() { if (instance.isUpdateMode) { instance.trigger(instance._event.UPDATED, instance.model, focusUpdated); } else { instance.trigger(instance._event.CREATED, instance.model.toJSON()); instance.model = new instance._modelClass(); // reset instance.render({ errors: {}, values: {} }); // pulls in instance's model for create row instance.trigger(instance._event.FOCUS); } instance.trigger(instance._event.SUBMIT_FINISHED); }, error: function error(model, data, xhr) { if (xhr.status === 400) { instance.renderErrors(data.errors); instance.trigger(instance._event.VALIDATION_ERROR, data.errors); } instance.trigger(instance._event.SUBMIT_FINISHED); }, silent: true }); return this; }, /** * Render an error message * * @param msg * * @return {jQuery} */ renderError: function renderError(name, msg) { return (0, _jquery2.default)('<div />').attr('data-field', name).addClass(this.classNames.ERROR).text(msg); }, /** * Render and append error messages. The property name will be matched to the input name to determine which cell to * append the error message to. If this does not meet your needs please extend this method. * * @param errors */ renderErrors: function renderErrors(errors) { var instance = this; this.$('.' + this.classNames.ERROR).remove(); // avoid duplicates if (errors) { _jquery2.default.each(errors, function (name, msg) { instance.$el.find("[name='" + name + "']").closest('td').append(instance.renderError(name, msg)); }); } return this; }, /** * Handles rendering of row * * @param {Object} renderData * ... {Object} vales - Values of fields */ render: function render(renderData) { var instance = this; this.$el.empty(); if (this.allowReorder) { (0, _jquery2.default)('<td class="' + this.classNames.ORDER + '" />').append(this.renderDragHandle()).appendTo(instance.$el); } _jquery2.default.each(this.columns, function (i, column) { var contents; var $cell; var value = renderData.values[column.id]; var args = [{ name: column.id, value: value, allowEdit: column.allowEdit }, renderData.values, instance.model]; if (value) { instance.$el.attr('data-' + column.id, value); // helper for webdriver testing } if (instance.isCreateRow && column.createView) { // TODO AUI-1058 - The row's model should be guaranteed to be in the correct state by this point. contents = new column.createView({ model: instance.model }).render(args[0]); } else if (column.editView) { contents = new column.editView({ model: instance.model }).render(args[0]); } else { contents = instance.defaultColumnRenderer.apply(instance, args); } $cell = (0, _jquery2.default)('<td />'); if ((typeof contents === 'undefined' ? 'undefined' : _typeof(contents)) === 'object' && contents.done) { contents.done(function (contents) { $cell.append(contents); }); } else { $cell.append(contents); } if (column.styleClass) { $cell.addClass(column.styleClass); } $cell.appendTo(instance.$el); }); this.$el.append(this.renderOperations(renderData.update, renderData.values)) // add submit/cancel buttons .addClass(this.classNames.ROW + ' ' + this.classNames.EDIT_ROW); this.trigger(this._event.RENDER, this.$el, renderData.values); this.$el.trigger(this._event.CONTENT_REFRESHED, [this.$el]); return this; }, /** * Gets markup for add/update and cancel buttons * * @param {Boolean} update */ renderOperations: function renderOperations(update) { var $operations = (0, _jquery2.default)('<td class="aui-restfultable-operations" />'); if (update) { $operations.append((0, _jquery2.default)('<input class="aui-button" type="submit" />').attr({ accesskey: this.submitAccessKey, value: AJS.I18n.getText('aui.words.update') })).append((0, _jquery2.default)('<a class="aui-button aui-button-link" href="#" />').addClass(this.classNames.CANCEL).text(AJS.I18n.getText('aui.words.cancel')).attr({ accesskey: this.cancelAccessKey })); } else { $operations.append((0, _jquery2.default)('<input class="aui-button" type="submit" />').attr({ accesskey: this.submitAccessKey, value: AJS.I18n.getText('aui.words.add') })); } return $operations.add((0, _jquery2.default)('<td class="aui-restfultable-status" />').append((0, _throbber2.default)())); } }); module.exports = exports['default']; }); //# sourceMappingURL=edit-row.js.map