Skip to content

Commit

Permalink
+ Now, increasing the select box code by 5%, THAT I can accept, if it…
Browse files Browse the repository at this point in the history
… allows me to add support for the 'multiple' parameter in dropdowns! Seriously. (sbox.js, index.css)

@ An interesting feature that I cooked up and isn't part of the 'official' HTML description of <select multiple>: just add a class named 'single' to any option that you want to be 'exclusive', i.e. clicking it will automatically 'unclick' all other items. I've never used so many 'single quotes' in a sentence before.

@ There are still a few rough edges: multiple selected items can be rendered with multiple .selected classes when opening a select box; also, there is currently no keyboard support for a 'multiple' dropdown, you can only open and close it without a pointer.
  • Loading branch information
Nao committed Aug 16, 2014
1 parent 903402e commit 755ae02
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 25 deletions.
72 changes: 47 additions & 25 deletions core/javascript/sbox.js
Expand Up @@ -81,7 +81,7 @@

// Cache all sb items
$items = $dd.children().not('.optgroup');
setSelected($orig_item = $items.filter('.selected'));
setSelected($orig_item = $items.filter('.selected,:has(input:checked)').first());

$dd.children().first().addClass('first');
$dd.children().last().addClass('last');
Expand Down Expand Up @@ -155,25 +155,25 @@
// you can use the magic word: <option data-hide>
var visible = $option.attr('data-hide') !== '';

return $('<div id="sbo' + ++unique + '" role="option">')
return $('<div id="sbo' + ++unique + '" role="option" class="pitem">')
.data('orig', $option)
.data('value', $option.attr('value') || '')
.attr('aria-disabled', !!$option.is(':disabled'))
.toggleClass('disabled', !visible || $option.is(':disabled,.hr'))
.toggleClass('selected', $option.is(':selected'))
.toggleClass('selected', $option.is(':selected') && (!$orig.attr('multiple') || $option.hasClass('single')))
.toggle(visible)
.append(
$('<div class="item">')
.attr('style', $option.attr('style') || '')
.addClass($option.attr('class'))
.append(optionFormat($option))
.append(optionFormat($option, !$orig.attr('multiple') || $option.hasClass('single') ? '' : '<input type="checkbox" onclick="this.checked = !this.checked;"' + ($option.is(':selected') ? ' checked' : '') + '>'))
);
},

// Formatting for the display
optionFormat = function ($dom)
optionFormat = function ($dom, extra)
{
return '<div class="text">' + (($dom.text ? $dom.text().split('|').join('</div><div class="details">') : $dom + '') || '&nbsp;') + '</div>';
return '<div class="text">' + (extra || '') + (($dom.text ? $dom.text().split('|').join('</div><div class="details">') : $dom + '') || '&nbsp;') + '</div>';
},

// Hide and reset dropdown markup
Expand All @@ -185,9 +185,8 @@
$display.blur();
$sb.removeClass('open');
$dd.removeClass('has_bar');
$dd
.animate(is_opera ? { opacity: 'toggle' } : { opacity: 'toggle', height: 'toggle' }, instantClose == 1 ? 0 : 100)
.attr('aria-hidden', true);
$dd.animate(is_opera ? { opacity: 'toggle' } : { opacity: 'toggle', height: 'toggle' }, instantClose == 1 ? 0 : 100);
$dd.attr('aria-hidden', true);
$dd.find('.scrollbar').remove();
$dd.find('.overview').contents().unwrap().unwrap();
}
Expand Down Expand Up @@ -297,15 +296,15 @@
},

// When the user selects an item in any manner
selectItem = function ($item, no_open)
selectItem = function ($item, no_open, is_clicking)
{
var $newtex = $item.find('.text'), $oritex = $display.find('.text'), oriwi = $oritex.width(), newwi;

// If we're selecting an item and the box is closed, open it.
if (!no_open && !$sb.hasClass('open'))
openSB();

setSelected($item);
setSelected($item, is_clicking);

// Update the title attr and the display markup
$oritex
Expand All @@ -317,7 +316,7 @@
$oritex.stop(true, true).width(oriwi).delay(100).animate({ width: newwi });
},

setSelected = function ($item)
setSelected = function ($item, is_clicking)
{
// If the select box has just been rebuilt, reset its selection.
// Good to know: !$items.has($item).length is 60 times slower.
Expand All @@ -326,9 +325,17 @@
if (!$item[0].parentNode === $dd[0])
$item = $orig_item;

// Change the selection to this item
$selected = $item.addClass('selected');
$items.not($selected).removeClass('selected');
// Change the selection to the first selected item in the list
if ($orig.attr('multiple') && !$item.has('>.single').length)
$selected = $items.filter('.selected,:has(input:checked)').first();
else
{
// Change the selection to this item
$selected = $item.addClass('selected');
$items.not($selected).removeClass('selected');
if (is_clicking)
$items.not($selected).find('input').prop('checked', false);
}
$sb.attr('aria-activedescendant', $selected.attr('id'));
},

Expand All @@ -338,26 +345,41 @@
has_changed = $orig.val() !== $selected.data('value') && !$selected.hasClass('disabled');

// Update the original <select>
$orig.find('option')[0].selected = false;
$selected.data('orig')[0].selected = true;
if ($orig.attr('multiple'))
$items.each(function () {
if ($(this).data('orig')[0].selected != $(this).find('input').prop('checked'))
$(this).data('orig')[0].selected = $(this).find('input').prop('checked') || $(this).hasClass('selected');
});
else
{
$orig.find('option')[0].selected = false;
$selected.data('orig')[0].selected = true;
}

$orig_item = $selected;
},

// When the user explicitly clicks an item
clickSBItem = function (e)
{
if (e.which == 1)
if (e.which == 1 && (!$orig.attr('multiple') || $(this).has('>.single').length))
{
selectItem($(this));
selectItem($(this), false, true);
updateOriginal();
closeAndUnbind();
focusSB();

if (has_changed)
{
$orig.triggerHandler('change');
has_changed = false;
}
}
else if (e.which == 1)
{
$items.filter('.selected').removeClass('selected');
$(this).find('input').prop('checked', !$(this).find('input').prop('checked'));
setSelected($(this));
updateOriginal();
}
if (has_changed)
{
$orig.triggerHandler('change');
has_changed = false;
}

return false;
Expand Down
8 changes: 8 additions & 0 deletions core/skins/index.css
Expand Up @@ -647,6 +647,9 @@ select.sb
padding: @is (mobile, 12px 8px, 4px 6px)
text-decoration: none

.pitem:hover
background-color: #eee

.selected .item
background-color: #eee

Expand Down Expand Up @@ -701,6 +704,11 @@ select.sb
background: #e8ece8
background: linear-gradient(90deg, #ddd, #e8ece8, #ccc)

/* Multiple selections get a checkbox. */
input[type=checkbox]
margin: 0 8px 0 0
vertical-align: -2px

#sidebar .sbox
max-width: 200px

Expand Down

0 comments on commit 755ae02

Please sign in to comment.