Skip to content

Instantly share code, notes, and snippets.

@neverkas
Created November 13, 2014 21:03
Show Gist options
  • Save neverkas/56e5436ed1eb6c7b2a53 to your computer and use it in GitHub Desktop.
Save neverkas/56e5436ed1eb6c7b2a53 to your computer and use it in GitHub Desktop.
Ember Starter Kit // source http://emberjs.jsbin.com/fudava
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/2.1.0/normalize.css">
<style id="jsbin-css">
/* Put your CSS here */
html, body {
margin: 20px;
}
/* for js bin */
ul {margin: 0px; padding:0px;}
li {list-style-type: none}
.notice {color:red;}
.selected {background: #D1C903; color:white; padding: 5px; margin: 0px;}
.is_visible {display: block;}
.is_hidden {display: none;}
.content_section {position: relative; float: left; width: 100%; margin: 20px 0px;}
/* Selects */
label {width: 100%;display: block;padding: 0px 0px 5px 0px;}
.select_wrap{margin-bottom:0px;width:205px;margin-right:5px;position: relative;float: left;}
.select_wrap.disabled,label.disabled{opacity:0.4;-ms-filter:alpha(opacity=40);}
.ie8 .select_wrap.disabled span.select,.ie8 .select_wrap.disabled span.select_toggle{background-color:#f5f5f5;}
/* Ember Custom Select Component */
.multi_select_component input {width: 195px;padding: 3px;}
.multi_select_component .selected_option {position: relative;float: left;overflow: hidden;white-space: nowrap;background: #FFF;width: 193px;height: 14px;border: 1px solid #BBB;padding: 3px 5px 6px;}
.multi_select_component .selected_option span.select_toggle{display:block;right:0px;width:18px;border: none; cursor: pointer;}
.multi_select_component .selected_option span.select_toggle .toggle{border:4px solid transparent;border-top:4px solid #0095D8;width:0px;height:0px;position:absolute;top:9px;left:5px;display:block;}
.multi_select_component .select_options {background: #FFF;border: 1px solid #BBB; position: fixed;z-index: 20;padding: 0px 0px;}
.multi_select_component .select_options .option {white-space: nowrap;padding: 3px 5px 3px;width: 100%;min-width: 203px;box-sizing: border-box;height: 22px; cursor: pointer;}
.multi_select_component .select_options .option:hover, .multi_select_component .select_options .option.is_active {background: #0095D8; color:#fff;}
.multi_select_component .focus_wrap {position: absolute;width: 232px;height: 231px;right: 0px;top: 0px;}
.multi_select_component .list_options {position: relative;border-bottom: 1px solid #CCC; background: #fff; width: 100%;}
.multi_select_component .available_options {position: relative; max-height: 200px; overflow-y:scroll;overflow-x: hidden;}
.multi_select_component .selected_options {position: relative; border-top: 1px solid #CCC; max-height: 200px; overflow-y:scroll;overflow-x: hidden;}
.multi_select_component .selected_options .option.is_active {background: #FFA500; color:#fff;}
.multi_select_component span.select_toggle {display: block;right: 1px;width: 18px;border: none;cursor: pointer; top: 2px;height: 23px;position: absolute; bottom:0; }
.multi_select_component span.select_toggle .toggle {border: 4px solid rgba(0, 0, 0, 0);border-top: 4px solid #0095D8;width: 0px;height: 0px;position: absolute;top: 9px;left: 5px;display: block;}
.ie8 input {border: 1px solid #bbb;}
.ie8 .multi_select_component span.select_toggle {height: 21px; right: 1px;}
.mac.chrome .multi_select_component span.select_toggle {height: 22px;}
</style>
</head>
<body>
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
{{multi-select label='Ember Multi Select' availableOptions=optionsForSelect selectedOptions=controllerSelectedOptions allSelectedProperty=allOptionsAreSelected }}
<section class='content_section'>
My Currently Selected Options
<ul>
{{#each option in controllerSelectedOptions }}
<li class='selected'>{{option.optionName}} - {{option.optionValue}}</li>
{{/each}}
</ul>
</section>
<section class='content_section notice'>
{{#if allOptionsAreSelected }}
All options have been selected
{{/if}}
</section>
</script>
<script type="text/x-handlebars" data-template-name="components/multi-select">
<label>
{{ label }}
{{#if isRequired }}{{required}}{{/if}}
{{#if isInvalid }}<span class='icon small_icon field_error'>Required</span>{{/if}}
</label>
<section class="select_wrap" >
{{input value=filterInput type='text' placeholder=placeholderText}}
<span class="select_toggle" {{action focusInput on='click'}}><span class="toggle"></span></span>
<ul {{bind-attr class='optionsVisible:is_visible:is_hidden :select_options'}}>
<section class='list_options'>
<li class='option' {{action selectAll this on='click'}}>All</li>
<li class='option' {{action clearAll this on='click'}}>Clear</li>
</section>
<section class="available_options">
{{#each filteredOptions }}
<li {{bind-attr class='isActive:is_active isHidden:is_hidden :option'}} {{action optionIsSelected this on='click'}}>{{ optionName }}</li>
{{/each}}
</section>
{{#if selectedOptions }}
<section class="selected_options">
{{#each selectedOptions }}
<li class='is_active option' {{action removeFromSelectedList this on='click'}}>{{ optionName }}</li>
{{/each}}
</section>
{{/if}}
</ul>
</section>
</script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="http://builds.emberjs.com/handlebars-1.0.0.js"></script>
<script src="http://builds.emberjs.com/tags/v1.1.2/ember.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery-scrollTo/1.4.6/jquery.scrollTo.min.js"></script>
<script id="jsbin-javascript">
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexController = Ember.Controller.extend({
optionsForSelect: [
{optionName: 'George Washington', optionValue: 'gw'},
{optionName: 'John Adams', optionValue: 'ja'},
{optionName: 'Thomas Jefferson', optionValue: 'tj'},
{optionName: 'James Madison', optionValue: 'jm'},
{optionName: 'James Monroe', optionValue: 'jmon'},
{optionName: 'John Quincy Adams', optionValue: 'jqa'}],
controllerSelectedOptions: [],
allOptionsAreSelected: false
});
App.MultiSelectComponent = Ember.Component.extend({
classNames: ['multi_select_component', 'field'],
optionsVisible: false,
selectedAction: null,
focusAction: null,
requiredProperty: null,
isRequired: false,
isInvalid: false,
currentFocusedOption: null,
placeholderText: function () {
if (Ember.isEmpty(this.get('selectedOptions'))) {
return 'Select one or more';
} else if (this.get('selectedOptions').length === 1) {
return Ember.get(this.get('selectedOptions').objectAt(0), 'optionName');
} else {
return 'Multiple values selected';
}
} .property('selectedOptions.length'),
filterInput: function () {
return this.get('placeholderText');
} .property('placeholderText'),
filteredOptions: function () {
var filterValue = this.get('filterInput'),
completeList = this.get('availableOptions'),
regex = new RegExp(this.get('filterInput'), 'i'),
filterList = null;
if (!filterValue || filterValue === this.get('placeholderText')) {
return this.get('availableOptions');
}
return completeList.filter(function (item) {
return item.optionName.match(regex);
});
} .property('filterInput', 'availableOptions.@each'),
resetOptionsList: function () {
if (this.get('selectedOptions.length') === 0) {
this.resetActiveStates();
this.resetHiddenStates();
this.resetFilters();
this.set('allSelectedProperty', false);
}
} .observes('selectedOptions.length'),
nonAlphaNumericInput: function (event) {
if (
(event.keyCode >= 48 && event.keyCode <= 57) || (event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 96 && event.keyCode <= 105) || (event.keyCode == 46 || event.keyCode == 32 || event.keyCode == 8 || event.keyCode == 9 || event.keyCode == 27 || event.keyCode == 13 || event.keyCode == 189 || event.keyCode == 190) || (event.keyCode >= 35 && event.keyCode <= 40)
) {
return false;
} else {
event.preventDefault();
return true;
}
},
focusIn: function () {
if (this.get('optionsVisible') === true) {
return false;
}
this.set('filterInput', null);
this.set('optionsVisible', true);
this.setOptionsPlacement();
},
keyDown: function (e) {
var arrowKey = e.keyCode === 38 ? -1 : e.keyCode === 40 ? 1 : 0, // up or down
enterKey = e.keyCode === 13;
// Dont allow certain keyCodes that break the regex filter
if (this.nonAlphaNumericInput(e)) {
e.preventDefault();
}
if (arrowKey) {
this.send('changeSelection', {
direction: arrowKey
});
}
if (enterKey) {
this.send('enterSelection');
}
},
mouseLeave: function () {
this.set('optionsVisible', false);
this.$('input').blur();
this.resetFilters();
},
resetFocus: function () {
// Need to blur focus because IE is AWESOME!!
this.$('input').blur();
this.$('input').focus();
},
resetFilters: function () {
this.set('currentFocusedOption', null);
this.set('filterInput', this.get('placeholderText'));
},
resetActiveStates: function () {
this.get('filteredOptions').setEach('isActive', false);
},
resetHiddenStates: function () {
this.get('availableOptions').setEach('isHidden', false);
},
toggleAllSelected: function () {
if (this.get('selectedOptions.length') === this.get('availableOptions.length')) {
this.set('allSelectedProperty', true);
} else {
this.set('allSelectedProperty', false);
}
}.observes('selectedOptions.length'),
getCurrentIndex: function (direction) {
var currentObject = this.get('currentFocusedOption'),
optionsList = this.get('filteredOptions'),
optionsCount = this.get('filteredOptions').get('length');
if (Ember.isEmpty(currentObject)) {
return optionsCount - 1;
} else {
return optionsList.indexOf(currentObject) + direction == -1 ? optionsCount + direction : (optionsList.indexOf(currentObject) + direction) % optionsCount;
}
},
nextVisibleObject: function (idx, direction) {
var optionsList = this.get('filteredOptions'),
optionsCount = this.get('filteredOptions').get('length'),
currentObject = optionsList.objectAt(idx);
if (Ember.get(currentObject, 'isHidden')) {
idx = idx + direction == -1 ? optionsCount + direction : (optionsList.indexOf(currentObject) + direction) % optionsCount;
return this.nextVisibleObject(idx, direction);
} else {
return currentObject;
}
},
setActiveObject: function (selectedObject) {
this.resetActiveStates();
Ember.set(selectedObject, 'isActive', true);
this.set('currentFocusedOption', selectedObject);
this.scrollToActiveObject();
},
scrollToActiveObject: function () {
$('.available_options').scrollTo('.is_active', 50, {
offset: -150
});
},
setOptionsPlacement: function () {
var container = this.$(),
inputElement = container.find('input'),
filters = container.find('.select_options'),
targetPosition = inputElement.offset().top + inputElement.outerHeight();
filters.css({
top: targetPosition
});
},
actions: {
selectAll: function () {
this.set('selectedOptions', []);
this.get('selectedOptions').pushObjects(this.get('availableOptions'));
this.get('availableOptions').forEach(function (option) {
Ember.set(option, 'isHidden', true);
});
this.resetActiveStates();
this.set('allSelectedProperty', true);
},
clearAll: function () {
this.set('selectedOptions', []);
this.resetFocus();
},
removeFromSelectedList: function (option) {
this.get('selectedOptions').removeObject(option);
Ember.set(option, 'isHidden', false);
this.set('allSelectedProperty', false);
this.resetFilters();
this.resetFocus();
},
enterSelection: function () {
var currentSelected = this.get('currentFocusedOption');
if (Ember.isEmpty(currentSelected)) {
return false;
} else {
this.send('optionIsSelected', currentSelected);
}
},
changeSelection: function (params) {
if (Ember.typeOf(params) === 'object' && params.hasOwnProperty('direction')) {
this.setActiveObject(this.nextVisibleObject(this.getCurrentIndex(params.direction), params.direction));
}
},
optionIsSelected: function (option) {
if (this.get('selectedOptions').contains(option)) {
return false;
}
this.get('selectedOptions').pushObject(option);
Ember.set(option, 'isHidden', true);
Ember.set(option, 'isActive', false);
this.resetFilters();
this.set('filterInput', null);
this.resetFocus();
},
focusInput: function () {
this.resetFocus();
}
}
});
</script>
<script id="jsbin-source-css" type="text/css">/* Put your CSS here */
html, body {
margin: 20px;
}
/* for js bin */
ul {margin: 0px; padding:0px;}
li {list-style-type: none}
.notice {color:red;}
.selected {background: #D1C903; color:white; padding: 5px; margin: 0px;}
.is_visible {display: block;}
.is_hidden {display: none;}
.content_section {position: relative; float: left; width: 100%; margin: 20px 0px;}
/* Selects */
label {width: 100%;display: block;padding: 0px 0px 5px 0px;}
.select_wrap{margin-bottom:0px;width:205px;margin-right:5px;position: relative;float: left;}
.select_wrap.disabled,label.disabled{opacity:0.4;-ms-filter:alpha(opacity=40);}
.ie8 .select_wrap.disabled span.select,.ie8 .select_wrap.disabled span.select_toggle{background-color:#f5f5f5;}
/* Ember Custom Select Component */
.multi_select_component input {width: 195px;padding: 3px;}
.multi_select_component .selected_option {position: relative;float: left;overflow: hidden;white-space: nowrap;background: #FFF;width: 193px;height: 14px;border: 1px solid #BBB;padding: 3px 5px 6px;}
.multi_select_component .selected_option span.select_toggle{display:block;right:0px;width:18px;border: none; cursor: pointer;}
.multi_select_component .selected_option span.select_toggle .toggle{border:4px solid transparent;border-top:4px solid #0095D8;width:0px;height:0px;position:absolute;top:9px;left:5px;display:block;}
.multi_select_component .select_options {background: #FFF;border: 1px solid #BBB; position: fixed;z-index: 20;padding: 0px 0px;}
.multi_select_component .select_options .option {white-space: nowrap;padding: 3px 5px 3px;width: 100%;min-width: 203px;box-sizing: border-box;height: 22px; cursor: pointer;}
.multi_select_component .select_options .option:hover, .multi_select_component .select_options .option.is_active {background: #0095D8; color:#fff;}
.multi_select_component .focus_wrap {position: absolute;width: 232px;height: 231px;right: 0px;top: 0px;}
.multi_select_component .list_options {position: relative;border-bottom: 1px solid #CCC; background: #fff; width: 100%;}
.multi_select_component .available_options {position: relative; max-height: 200px; overflow-y:scroll;overflow-x: hidden;}
.multi_select_component .selected_options {position: relative; border-top: 1px solid #CCC; max-height: 200px; overflow-y:scroll;overflow-x: hidden;}
.multi_select_component .selected_options .option.is_active {background: #FFA500; color:#fff;}
.multi_select_component span.select_toggle {display: block;right: 1px;width: 18px;border: none;cursor: pointer; top: 2px;height: 23px;position: absolute; bottom:0; }
.multi_select_component span.select_toggle .toggle {border: 4px solid rgba(0, 0, 0, 0);border-top: 4px solid #0095D8;width: 0px;height: 0px;position: absolute;top: 9px;left: 5px;display: block;}
.ie8 input {border: 1px solid #bbb;}
.ie8 .multi_select_component span.select_toggle {height: 21px; right: 1px;}
.mac.chrome .multi_select_component span.select_toggle {height: 22px;}</script>
<script id="jsbin-source-javascript" type="text/javascript">App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexController = Ember.Controller.extend({
optionsForSelect: [
{optionName: 'George Washington', optionValue: 'gw'},
{optionName: 'John Adams', optionValue: 'ja'},
{optionName: 'Thomas Jefferson', optionValue: 'tj'},
{optionName: 'James Madison', optionValue: 'jm'},
{optionName: 'James Monroe', optionValue: 'jmon'},
{optionName: 'John Quincy Adams', optionValue: 'jqa'}],
controllerSelectedOptions: [],
allOptionsAreSelected: false
});
App.MultiSelectComponent = Ember.Component.extend({
classNames: ['multi_select_component', 'field'],
optionsVisible: false,
selectedAction: null,
focusAction: null,
requiredProperty: null,
isRequired: false,
isInvalid: false,
currentFocusedOption: null,
placeholderText: function () {
if (Ember.isEmpty(this.get('selectedOptions'))) {
return 'Select one or more';
} else if (this.get('selectedOptions').length === 1) {
return Ember.get(this.get('selectedOptions').objectAt(0), 'optionName');
} else {
return 'Multiple values selected';
}
} .property('selectedOptions.length'),
filterInput: function () {
return this.get('placeholderText');
} .property('placeholderText'),
filteredOptions: function () {
var filterValue = this.get('filterInput'),
completeList = this.get('availableOptions'),
regex = new RegExp(this.get('filterInput'), 'i'),
filterList = null;
if (!filterValue || filterValue === this.get('placeholderText')) {
return this.get('availableOptions');
}
return completeList.filter(function (item) {
return item.optionName.match(regex);
});
} .property('filterInput', 'availableOptions.@each'),
resetOptionsList: function () {
if (this.get('selectedOptions.length') === 0) {
this.resetActiveStates();
this.resetHiddenStates();
this.resetFilters();
this.set('allSelectedProperty', false);
}
} .observes('selectedOptions.length'),
nonAlphaNumericInput: function (event) {
if (
(event.keyCode >= 48 && event.keyCode <= 57) || (event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 96 && event.keyCode <= 105) || (event.keyCode == 46 || event.keyCode == 32 || event.keyCode == 8 || event.keyCode == 9 || event.keyCode == 27 || event.keyCode == 13 || event.keyCode == 189 || event.keyCode == 190) || (event.keyCode >= 35 && event.keyCode <= 40)
) {
return false;
} else {
event.preventDefault();
return true;
}
},
focusIn: function () {
if (this.get('optionsVisible') === true) {
return false;
}
this.set('filterInput', null);
this.set('optionsVisible', true);
this.setOptionsPlacement();
},
keyDown: function (e) {
var arrowKey = e.keyCode === 38 ? -1 : e.keyCode === 40 ? 1 : 0, // up or down
enterKey = e.keyCode === 13;
// Dont allow certain keyCodes that break the regex filter
if (this.nonAlphaNumericInput(e)) {
e.preventDefault();
}
if (arrowKey) {
this.send('changeSelection', {
direction: arrowKey
});
}
if (enterKey) {
this.send('enterSelection');
}
},
mouseLeave: function () {
this.set('optionsVisible', false);
this.$('input').blur();
this.resetFilters();
},
resetFocus: function () {
// Need to blur focus because IE is AWESOME!!
this.$('input').blur();
this.$('input').focus();
},
resetFilters: function () {
this.set('currentFocusedOption', null);
this.set('filterInput', this.get('placeholderText'));
},
resetActiveStates: function () {
this.get('filteredOptions').setEach('isActive', false);
},
resetHiddenStates: function () {
this.get('availableOptions').setEach('isHidden', false);
},
toggleAllSelected: function () {
if (this.get('selectedOptions.length') === this.get('availableOptions.length')) {
this.set('allSelectedProperty', true);
} else {
this.set('allSelectedProperty', false);
}
}.observes('selectedOptions.length'),
getCurrentIndex: function (direction) {
var currentObject = this.get('currentFocusedOption'),
optionsList = this.get('filteredOptions'),
optionsCount = this.get('filteredOptions').get('length');
if (Ember.isEmpty(currentObject)) {
return optionsCount - 1;
} else {
return optionsList.indexOf(currentObject) + direction == -1 ? optionsCount + direction : (optionsList.indexOf(currentObject) + direction) % optionsCount;
}
},
nextVisibleObject: function (idx, direction) {
var optionsList = this.get('filteredOptions'),
optionsCount = this.get('filteredOptions').get('length'),
currentObject = optionsList.objectAt(idx);
if (Ember.get(currentObject, 'isHidden')) {
idx = idx + direction == -1 ? optionsCount + direction : (optionsList.indexOf(currentObject) + direction) % optionsCount;
return this.nextVisibleObject(idx, direction);
} else {
return currentObject;
}
},
setActiveObject: function (selectedObject) {
this.resetActiveStates();
Ember.set(selectedObject, 'isActive', true);
this.set('currentFocusedOption', selectedObject);
this.scrollToActiveObject();
},
scrollToActiveObject: function () {
$('.available_options').scrollTo('.is_active', 50, {
offset: -150
});
},
setOptionsPlacement: function () {
var container = this.$(),
inputElement = container.find('input'),
filters = container.find('.select_options'),
targetPosition = inputElement.offset().top + inputElement.outerHeight();
filters.css({
top: targetPosition
});
},
actions: {
selectAll: function () {
this.set('selectedOptions', []);
this.get('selectedOptions').pushObjects(this.get('availableOptions'));
this.get('availableOptions').forEach(function (option) {
Ember.set(option, 'isHidden', true);
});
this.resetActiveStates();
this.set('allSelectedProperty', true);
},
clearAll: function () {
this.set('selectedOptions', []);
this.resetFocus();
},
removeFromSelectedList: function (option) {
this.get('selectedOptions').removeObject(option);
Ember.set(option, 'isHidden', false);
this.set('allSelectedProperty', false);
this.resetFilters();
this.resetFocus();
},
enterSelection: function () {
var currentSelected = this.get('currentFocusedOption');
if (Ember.isEmpty(currentSelected)) {
return false;
} else {
this.send('optionIsSelected', currentSelected);
}
},
changeSelection: function (params) {
if (Ember.typeOf(params) === 'object' && params.hasOwnProperty('direction')) {
this.setActiveObject(this.nextVisibleObject(this.getCurrentIndex(params.direction), params.direction));
}
},
optionIsSelected: function (option) {
if (this.get('selectedOptions').contains(option)) {
return false;
}
this.get('selectedOptions').pushObject(option);
Ember.set(option, 'isHidden', true);
Ember.set(option, 'isActive', false);
this.resetFilters();
this.set('filterInput', null);
this.resetFocus();
},
focusInput: function () {
this.resetFocus();
}
}
});</script></body>
</html>
/* Put your CSS here */
html, body {
margin: 20px;
}
/* for js bin */
ul {margin: 0px; padding:0px;}
li {list-style-type: none}
.notice {color:red;}
.selected {background: #D1C903; color:white; padding: 5px; margin: 0px;}
.is_visible {display: block;}
.is_hidden {display: none;}
.content_section {position: relative; float: left; width: 100%; margin: 20px 0px;}
/* Selects */
label {width: 100%;display: block;padding: 0px 0px 5px 0px;}
.select_wrap{margin-bottom:0px;width:205px;margin-right:5px;position: relative;float: left;}
.select_wrap.disabled,label.disabled{opacity:0.4;-ms-filter:alpha(opacity=40);}
.ie8 .select_wrap.disabled span.select,.ie8 .select_wrap.disabled span.select_toggle{background-color:#f5f5f5;}
/* Ember Custom Select Component */
.multi_select_component input {width: 195px;padding: 3px;}
.multi_select_component .selected_option {position: relative;float: left;overflow: hidden;white-space: nowrap;background: #FFF;width: 193px;height: 14px;border: 1px solid #BBB;padding: 3px 5px 6px;}
.multi_select_component .selected_option span.select_toggle{display:block;right:0px;width:18px;border: none; cursor: pointer;}
.multi_select_component .selected_option span.select_toggle .toggle{border:4px solid transparent;border-top:4px solid #0095D8;width:0px;height:0px;position:absolute;top:9px;left:5px;display:block;}
.multi_select_component .select_options {background: #FFF;border: 1px solid #BBB; position: fixed;z-index: 20;padding: 0px 0px;}
.multi_select_component .select_options .option {white-space: nowrap;padding: 3px 5px 3px;width: 100%;min-width: 203px;box-sizing: border-box;height: 22px; cursor: pointer;}
.multi_select_component .select_options .option:hover, .multi_select_component .select_options .option.is_active {background: #0095D8; color:#fff;}
.multi_select_component .focus_wrap {position: absolute;width: 232px;height: 231px;right: 0px;top: 0px;}
.multi_select_component .list_options {position: relative;border-bottom: 1px solid #CCC; background: #fff; width: 100%;}
.multi_select_component .available_options {position: relative; max-height: 200px; overflow-y:scroll;overflow-x: hidden;}
.multi_select_component .selected_options {position: relative; border-top: 1px solid #CCC; max-height: 200px; overflow-y:scroll;overflow-x: hidden;}
.multi_select_component .selected_options .option.is_active {background: #FFA500; color:#fff;}
.multi_select_component span.select_toggle {display: block;right: 1px;width: 18px;border: none;cursor: pointer; top: 2px;height: 23px;position: absolute; bottom:0; }
.multi_select_component span.select_toggle .toggle {border: 4px solid rgba(0, 0, 0, 0);border-top: 4px solid #0095D8;width: 0px;height: 0px;position: absolute;top: 9px;left: 5px;display: block;}
.ie8 input {border: 1px solid #bbb;}
.ie8 .multi_select_component span.select_toggle {height: 21px; right: 1px;}
.mac.chrome .multi_select_component span.select_toggle {height: 22px;}
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexController = Ember.Controller.extend({
optionsForSelect: [
{optionName: 'George Washington', optionValue: 'gw'},
{optionName: 'John Adams', optionValue: 'ja'},
{optionName: 'Thomas Jefferson', optionValue: 'tj'},
{optionName: 'James Madison', optionValue: 'jm'},
{optionName: 'James Monroe', optionValue: 'jmon'},
{optionName: 'John Quincy Adams', optionValue: 'jqa'}],
controllerSelectedOptions: [],
allOptionsAreSelected: false
});
App.MultiSelectComponent = Ember.Component.extend({
classNames: ['multi_select_component', 'field'],
optionsVisible: false,
selectedAction: null,
focusAction: null,
requiredProperty: null,
isRequired: false,
isInvalid: false,
currentFocusedOption: null,
placeholderText: function () {
if (Ember.isEmpty(this.get('selectedOptions'))) {
return 'Select one or more';
} else if (this.get('selectedOptions').length === 1) {
return Ember.get(this.get('selectedOptions').objectAt(0), 'optionName');
} else {
return 'Multiple values selected';
}
} .property('selectedOptions.length'),
filterInput: function () {
return this.get('placeholderText');
} .property('placeholderText'),
filteredOptions: function () {
var filterValue = this.get('filterInput'),
completeList = this.get('availableOptions'),
regex = new RegExp(this.get('filterInput'), 'i'),
filterList = null;
if (!filterValue || filterValue === this.get('placeholderText')) {
return this.get('availableOptions');
}
return completeList.filter(function (item) {
return item.optionName.match(regex);
});
} .property('filterInput', 'availableOptions.@each'),
resetOptionsList: function () {
if (this.get('selectedOptions.length') === 0) {
this.resetActiveStates();
this.resetHiddenStates();
this.resetFilters();
this.set('allSelectedProperty', false);
}
} .observes('selectedOptions.length'),
nonAlphaNumericInput: function (event) {
if (
(event.keyCode >= 48 && event.keyCode <= 57) || (event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 96 && event.keyCode <= 105) || (event.keyCode == 46 || event.keyCode == 32 || event.keyCode == 8 || event.keyCode == 9 || event.keyCode == 27 || event.keyCode == 13 || event.keyCode == 189 || event.keyCode == 190) || (event.keyCode >= 35 && event.keyCode <= 40)
) {
return false;
} else {
event.preventDefault();
return true;
}
},
focusIn: function () {
if (this.get('optionsVisible') === true) {
return false;
}
this.set('filterInput', null);
this.set('optionsVisible', true);
this.setOptionsPlacement();
},
keyDown: function (e) {
var arrowKey = e.keyCode === 38 ? -1 : e.keyCode === 40 ? 1 : 0, // up or down
enterKey = e.keyCode === 13;
// Dont allow certain keyCodes that break the regex filter
if (this.nonAlphaNumericInput(e)) {
e.preventDefault();
}
if (arrowKey) {
this.send('changeSelection', {
direction: arrowKey
});
}
if (enterKey) {
this.send('enterSelection');
}
},
mouseLeave: function () {
this.set('optionsVisible', false);
this.$('input').blur();
this.resetFilters();
},
resetFocus: function () {
// Need to blur focus because IE is AWESOME!!
this.$('input').blur();
this.$('input').focus();
},
resetFilters: function () {
this.set('currentFocusedOption', null);
this.set('filterInput', this.get('placeholderText'));
},
resetActiveStates: function () {
this.get('filteredOptions').setEach('isActive', false);
},
resetHiddenStates: function () {
this.get('availableOptions').setEach('isHidden', false);
},
toggleAllSelected: function () {
if (this.get('selectedOptions.length') === this.get('availableOptions.length')) {
this.set('allSelectedProperty', true);
} else {
this.set('allSelectedProperty', false);
}
}.observes('selectedOptions.length'),
getCurrentIndex: function (direction) {
var currentObject = this.get('currentFocusedOption'),
optionsList = this.get('filteredOptions'),
optionsCount = this.get('filteredOptions').get('length');
if (Ember.isEmpty(currentObject)) {
return optionsCount - 1;
} else {
return optionsList.indexOf(currentObject) + direction == -1 ? optionsCount + direction : (optionsList.indexOf(currentObject) + direction) % optionsCount;
}
},
nextVisibleObject: function (idx, direction) {
var optionsList = this.get('filteredOptions'),
optionsCount = this.get('filteredOptions').get('length'),
currentObject = optionsList.objectAt(idx);
if (Ember.get(currentObject, 'isHidden')) {
idx = idx + direction == -1 ? optionsCount + direction : (optionsList.indexOf(currentObject) + direction) % optionsCount;
return this.nextVisibleObject(idx, direction);
} else {
return currentObject;
}
},
setActiveObject: function (selectedObject) {
this.resetActiveStates();
Ember.set(selectedObject, 'isActive', true);
this.set('currentFocusedOption', selectedObject);
this.scrollToActiveObject();
},
scrollToActiveObject: function () {
$('.available_options').scrollTo('.is_active', 50, {
offset: -150
});
},
setOptionsPlacement: function () {
var container = this.$(),
inputElement = container.find('input'),
filters = container.find('.select_options'),
targetPosition = inputElement.offset().top + inputElement.outerHeight();
filters.css({
top: targetPosition
});
},
actions: {
selectAll: function () {
this.set('selectedOptions', []);
this.get('selectedOptions').pushObjects(this.get('availableOptions'));
this.get('availableOptions').forEach(function (option) {
Ember.set(option, 'isHidden', true);
});
this.resetActiveStates();
this.set('allSelectedProperty', true);
},
clearAll: function () {
this.set('selectedOptions', []);
this.resetFocus();
},
removeFromSelectedList: function (option) {
this.get('selectedOptions').removeObject(option);
Ember.set(option, 'isHidden', false);
this.set('allSelectedProperty', false);
this.resetFilters();
this.resetFocus();
},
enterSelection: function () {
var currentSelected = this.get('currentFocusedOption');
if (Ember.isEmpty(currentSelected)) {
return false;
} else {
this.send('optionIsSelected', currentSelected);
}
},
changeSelection: function (params) {
if (Ember.typeOf(params) === 'object' && params.hasOwnProperty('direction')) {
this.setActiveObject(this.nextVisibleObject(this.getCurrentIndex(params.direction), params.direction));
}
},
optionIsSelected: function (option) {
if (this.get('selectedOptions').contains(option)) {
return false;
}
this.get('selectedOptions').pushObject(option);
Ember.set(option, 'isHidden', true);
Ember.set(option, 'isActive', false);
this.resetFilters();
this.set('filterInput', null);
this.resetFocus();
},
focusInput: function () {
this.resetFocus();
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment