Skip to content

Commit

Permalink
User experience tweaks to the organizations adding of users to orgs #…
Browse files Browse the repository at this point in the history
  • Loading branch information
johnmartin committed Sep 18, 2012
1 parent a8514d0 commit dbac398
Show file tree
Hide file tree
Showing 7 changed files with 610 additions and 21 deletions.
118 changes: 113 additions & 5 deletions ckan/public/base/javascript/modules/organization-users.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,127 @@
*/
this.ckan.module('organization-users', function ($, _) {
return {

options: {
index: 0,
users: '',
i18n: {
addTitle: _('Add user')
}
},

initialize: function () {
// ok
this.options.users = this.options.users.split('~~');
// convert all the keep labels into buttons
$('.orginization-remove', this.el).each(function() {
$(this).addClass('btn btn-danger pull-right checkbox');
$('span', this).html('').addClass('icon-remove');
});
$('.organizations-remove', this.el).each(this.initializeRemove);
// ok checkbox change toggle the disabled state for display
this.el.on('change', '.orginization-remove :checkbox', function() {
this.el.on('change', '.organizations-remove :checkbox', function() {
var row = $(this).parents('tr');
row.toggleClass('disabled');
$('input[type=radio]', row).prop('disabled', !$(this).prop('checked'));
$('.btn', row).toggleClass('btn-info');
});
// now init the add
this.initializeAdd();
},

// Initializes the remove button on each table row of the user table
initializeRemove: function() {
$(this).addClass('btn btn-danger pull-right checkbox');
$('span', this).html('').addClass('icon-remove');
},

// Handles the logic of adding users to the table
initializeAdd: function() {
var templateForm = $('#organizations-add-form');
// add the button to the page actions
var add = $('<li><a href="javascript:;" class="btn btn-success" data-toggle="button"><span class="icon-plus-sign"></span> ' + this.i18n('addTitle') + '</a></li>')
.insertAfter('#organizations-back');
$('a', add)
// this is needed for in jQuery event callbacks
.data('me', this)
// ok create a popover from the template
.popover({
title: this.i18n('addTitle'),
content: templateForm.html(),
placement: 'left'
})
// we have to do some of this logic on click because the
// popover only populates the html on first click...
.on('click', this._handlePopoverShow);
// ok let's do a bit of dom tidying
$('.organizations-add', this.el).add(templateForm).remove();
},

// This fired whenever the popover is shown
_handlePopoverShow: function() {
var $this = $(this);
var me = $this.data('me');
// get the popover tooltip
var tip = $this.data('popover').tip();
// now get the popover input
var input = $('input', tip);

// has this been inited before?
if (!input.data('inited')) {
// make the input a autocomplete dropdown
var Module = ckan.module.registry['autocomplete'];
ckan.module.createInstance(Module, input[0]);
// add the org class for styling form
tip.addClass('organization-popover-add');
// add the listener to the button
$('.btn', tip)
.data('me', me)
.data('btn', $this)
.on('click', me._handleAddUserToTable);
// tell everyone that this has been inited now
input.data('inited', true);
}

},

// This actually adds a given username to the users table
_handleAddUserToTable: function() {
var me = $(this).data('me');
// what's the username they want to add?
var username = $('input', $(this).parent()).val();
// ok validate the username they want to add
if (me._validateUsername(username)) {
// setup the template
var templateRow = $('#organizations-add-row')
.html()
.replace(/\[username\]/g, username)
.replace(/\[index\]/g, me.options.index);
// ok add the row to the table
var added = $(templateRow).appendTo($('table tbody', me.el)).hide().fadeIn();
// and init the remove button
$('.organizations-remove', added).each(me.initializeRemove);
// add one to the index
me.options.index++;
// add the username to the checklist
me.options.users.push(username);
// now hide the popover
$(this).data('btn').trigger('click');
}
},

// Validates a given username
_validateUsername: function(username) {
// is there a username?
var username = $.trim(username);
if (!username) {
return false;
}
// is it a username in the table already?
for (var i = 0; i < this.options.users.length; i++) {
if (this.options.users[i] == username) {
return false;
}
}
// everything is fine
return true;
}

}
});
20 changes: 19 additions & 1 deletion ckan/public/base/less/forms.less
Original file line number Diff line number Diff line change
Expand Up @@ -698,10 +698,28 @@ textarea {
color: #FFF !important;
}
}
.orginization-remove.btn {
.organizations-remove.btn {
border-radius: 100px;
input {
display: none;
}
}
.organizations-add {
label {
float: left;
font-weight: normal;
margin-right: 10px;
margin-top: 3px;
}
.select2-container {
margin-top: 0;
}
}
}
.organization-popover-add {
margin-top: 18px;
.select2-container {
width: 190px !important;
margin-right: 10px !important;
}
}
103 changes: 103 additions & 0 deletions ckan/public/base/vendor/bootstrap/js/bootstrap-popover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/* ===========================================================
* bootstrap-popover.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#popovers
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================================================== */


!function ($) {

"use strict"; // jshint ;_;


/* POPOVER PUBLIC CLASS DEFINITION
* =============================== */

var Popover = function (element, options) {
this.init('popover', element, options)
}


/* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
========================================== */

Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {

constructor: Popover

, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
, content = this.getContent()

$tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
$tip.find('.popover-content > *')[this.options.html ? 'html' : 'text'](content)

$tip.removeClass('fade top bottom left right in')
}

, hasContent: function () {
return this.getTitle() || this.getContent()
}

, getContent: function () {
var content
, $e = this.$element
, o = this.options

content = $e.attr('data-content')
|| (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)

return content
}

, tip: function () {
if (!this.$tip) {
this.$tip = $(this.options.template)
}
return this.$tip
}

, destroy: function () {
this.hide().$element.off('.' + this.type).removeData(this.type)
}

})


/* POPOVER PLUGIN DEFINITION
* ======================= */

$.fn.popover = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('popover')
, options = typeof option == 'object' && option
if (!data) $this.data('popover', (data = new Popover(this, options)))
if (typeof option == 'string') data[option]()
})
}

$.fn.popover.Constructor = Popover

$.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
placement: 'right'
, trigger: 'click'
, content: ''
, template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
})

}(window.jQuery);
Loading

0 comments on commit dbac398

Please sign in to comment.