UNPKG

@atlassian/aui

Version:

Atlassian User Interface library

321 lines (283 loc) 9.26 kB
import $ from '../jquery'; import Backbone from 'backbone'; import { I18n } from '../i18n'; import classNames from './class-names'; import dataKeys from './data-keys'; import events from './event-names'; import { appendStatusSpinner, removeStatusSpinner } from './spinner'; /** * An abstract class that gives the required behaviour for RestfulTable rows. * Extend this class and pass it as the {views.row} property of the options passed to RestfulTable in construction. */ export default Backbone.View.extend({ tagName: 'tr', events: { 'click .aui-restfultable-editable': 'edit', }, initialize: function (options) { options = options || {}; this._event = events; this.classNames = classNames; this.dataKeys = dataKeys; this.columns = options.columns; this.allowEdit = options.allowEdit; this.allowDelete = options.allowDelete; if (!this.events['click .aui-restfultable-editable']) { throw new Error( 'It appears you have overridden the events property. To add events you will need to use' + 'a work around. https://github.com/documentcloud/backbone/issues/244' ); } this.index = options.index || 0; this.deleteConfirmationCallback = options.deleteConfirmationCallback; this.allowReorder = options.allowReorder; this.$el = $(this.el); this.on(this._event.CANCEL, () => (this.disabled = true)) .on(this._event.FOCUS, (field) => this.focus(field)) .on(this._event.BLUR, () => this.unfocus()) .on(this._event.MODAL, () => this.$el.addClass(this.classNames.ACTIVE)) .on(this._event.MODELESS, () => this.$el.removeClass(this.classNames.ACTIVE)); }, /** * Renders drag handle * * @return jQuery */ renderDragHandle: function () { return '<span class="' + this.classNames.DRAG_HANDLE + '"></span></td>'; }, /** * Renders default cell contents * * @param data * * @return {undefiend, String} */ defaultColumnRenderer: function (data) { if (data.value) { return document.createTextNode(data.value.toString()); } }, /** * Save changed attributes back to server and re-render * * @param attr * * @return {Row} */ sync: function (attr) { var instance = this; this.model.addExpand(attr); this.showLoading(); this.model.save(attr, { success: function () { instance.hideLoading().render(); instance.trigger(instance._event.UPDATED); }, error: function () { instance.hideLoading(); }, }); return this; }, /** * Get model from server and re-render * * @return {Row} */ refresh: function (success, error) { var instance = this; this.showLoading(); this.model.fetch({ success: function () { instance.hideLoading().render(); if (success) { success.apply(this, arguments); } }, error: function () { instance.hideLoading(); if (error) { error.apply(this, arguments); } }, }); return this; }, /** * Returns true if row has focused class * * @return Boolean */ hasFocus: function () { return this.$el.hasClass(this.classNames.FOCUSED); }, /** * Adds focus class (Item has been recently updated) * * @return Row */ focus: function () { $(this.el).addClass(this.classNames.FOCUSED); return this; }, /** * Removes focus class * * @return Row */ unfocus: function () { $(this.el).removeClass(this.classNames.FOCUSED); return this; }, /** * Adds loading class (to show server activity) * * @return Row */ showLoading: function () { appendStatusSpinner(this.$el); return this; }, /** * Hides loading class (to show server activity) * * @return Row */ hideLoading: function () { removeStatusSpinner(this.$el); return this; }, /** * Switches row into edit mode * * @param e */ edit: function (e) { var field; if ($(e.target).is('.' + this.classNames.EDITABLE)) { field = $(e.target).attr('data-field-name'); } else { field = $(e.target) .closest('.' + this.classNames.EDITABLE) .attr('data-field-name'); } this.trigger(this._event.ROW_EDIT, field); return this; }, /** * Can be overriden to add custom options. * * @returns {jQuery} */ renderOperations: function () { var instance = this; if (this.allowDelete !== false) { return $("<a href='#' class='aui-button' />") .addClass(this.classNames.DELETE) .text(I18n.getText('aui.words.delete')) .on('click', function (e) { e.preventDefault(); instance.destroy(); }); } }, /** * Removes entry from table. * * @returns {undefined} */ destroy: function () { if (this.deleteConfirmationCallback) { let promise = this.deleteConfirmationCallback(this.model.toJSON()); if (promise && promise.then) { promise.then( () => this.model.destroy(), () => {} ); } else { throw new Error('deleteConfirmationCallback needs to return a Promise'); } } else { this.model.destroy(); } }, /** * Renders a generic edit row. You probably want to override this in a sub class. * * @return Row */ render: function () { var instance = this; var renderData = this.model.toJSON(); var $opsCell = $("<td class='aui-restfultable-operations' />").append( this.renderOperations({}, renderData) ); var $throbberCell = $(`<td class="${this.classNames.STATUS}" />`); // restore state this.$el .removeClass( this.classNames.DISABLED + ' ' + this.classNames.FOCUSED + ' ' + this.classNames.EDIT_ROW ) .addClass(this.classNames.READ_ONLY) .empty(); if (this.allowReorder) { $('<td class="' + this.classNames.ORDER + '" />') .append(this.renderDragHandle()) .appendTo(instance.$el); } this.$el.attr('data-id', this.model.id); // helper for webdriver testing $.each(this.columns, function (i, column) { var contents; var $cell = $('<td />'); var value = renderData[column.id]; var fieldName = column.fieldName || column.id; var args = [ { name: fieldName, value: value, allowEdit: column.allowEdit }, renderData, instance.model, ]; if (value) { instance.$el.attr('data-' + column.id, value); // helper for webdriver testing } if (column.readView) { contents = new column.readView({ model: instance.model, }).render(args[0]); } else { contents = instance.defaultColumnRenderer.apply(instance, args); } if (instance.allowEdit !== false && column.allowEdit !== false) { var $editableRegion = $('<span />') .addClass(instance.classNames.EDITABLE) .append('<span class="aui-icon aui-icon-small aui-iconfont-edit"></span>') .append(contents) .attr('data-field-name', fieldName); $cell = $('<td />').append($editableRegion).appendTo(instance.$el); if (!contents || !$.trim(contents)) { $cell.addClass(instance.classNames.NO_VALUE); $editableRegion.html( $('<em />').text(this.emptyText || I18n.getText('aui.enter.value')) ); } } else { $cell.append(contents); } if (column.styleClass) { $cell.addClass(column.styleClass); } $cell.appendTo(instance.$el); }); this.$el .append($opsCell) .append($throbberCell) .addClass(this.classNames.ROW + ' ' + this.classNames.READ_ONLY); this.trigger(this._event.RENDER, this.$el, renderData); this.$el.trigger(this._event.CONTENT_REFRESHED, [this.$el]); return this; }, });