Skip to content

Instantly share code, notes, and snippets.

@bmarotta
Last active May 28, 2021 15:10
Show Gist options
  • Save bmarotta/4ccd0dcc442adb1dfed1c9b074da2fe3 to your computer and use it in GitHub Desktop.
Save bmarotta/4ccd0dcc442adb1dfed1c9b074da2fe3 to your computer and use it in GitHub Desktop.
<template>
<require from="aurelia-multiselect-autocomplete"></require>
<section>
<h1>Aurelia Multiselect autocomplete</h1>
<aurelia-multiselect-autocomplete items.bind="tags"></aurelia-multiselect-autocomplete>
</section>
</template>
export class App {
constructor(){
this.message = 'Test';
this.defaultValue = model => model.name;
this.tags = ['sky','architecture','animal','travel','water','landscape','building','place','city','wildlife','sea','old','wild','structure','plant','tree','nature','ocean','mammal','tourism','mountain','flower','cute','house','art','river','park','vehicule','summer','fur','palace','landmark','vehicle','garden','sport','forest','spring','church','vascular plant','pet','transportation','beach','grass','tower','food','transport','religion','car','device','bird'];
}
}
.msa-wrapper {
position: relative;
}
.msa-container {
border: 1px solid #ccc;
padding: .2em;
height: 1.6em;
white-space: nowrap;
overflow: none;
}
.msa-search {
position: absolute;
top: 2.1em;
left: 0;
right: 0;
padding: .5em;
border: 1px solid #ccc;
}
.msa-search ul {
list-style: none;
padding: 0;
margin: 0;
}
.msa-selected-item, .msa-input {
display: inline-block;
padding: .2em .5em;
}
.msa-selected-item {
border: 1px solid #888;
border-radius: 1em;
}
.msa-remove-item {
background-color: #888;
color: #fff;
width: 1em;
display: inline-block;
text-align: center;
border-radius: 1em;
height: 1em;
vertical-align: baseline;
line-height: 1em;
margin-left: .3em;
font-weight: bold;
cursor: pointer;
}
.msa-input, .msa-input:active, .msa-input:focus {
display: inline-block;
border: none;
outline: none;
height: 1.25em;
font-size: 1em;
}
.msa-highlight {
text-decoration: underline;
}
.msa-search-item {
cursor: pointer;
}
.msa-search-item-select {
background: #3297FD;
}
<template>
<require from="aurelia-multiselect-autocomplete.css"></require>
<div class="msa-wrapper">
<div class="msa-container" click.delegate="focusElement()">
<div class="msa-selected-item" repeat.for="item of selectedItems">${item}<div class="msa-remove-item" click.delegate="removeItem($index)">×</div></div>
<input ref="inputElem" class="msa-input" type="text" value.bind="inputValue" keydown.trigger='keydown($event)'></input>
</div>
<div class="msa-search" show.bind="searchItems.length > 0">
<ul>
<li repeat.for="searchItem of searchItems" innerHtml.one-way="searchItem.html" click.delegate="itemSelected($index)"
class="msa-search-item ${$index == selectedSearchItem ? 'msa-search-item-select' : ''}"></li>
</ul>
</div>
</div>
</template>
import { bindable, observable } from 'aurelia-framework';
export class AureliaMultiselectAutocomplete {
@bindable selectedItems = [];
@observable searchItems = [];
@bindable items = [];
@observable inputValue = '';
selectedSearchItem = 0;
inputElem;
inputValueChanged() {
console.log('inputValueChanged ' + this.inputValue);
this.searchItems.splice(0, this.searchItems.length);
if (this.inputValue) {
const lowerCased = this.items.filter(n => n != '' && n.toLowerCase && this.selectedItems.indexOf(n) == -1).map(n => n.toLowerCase());
const filtered = lowerCased.filter(n => n.indexOf(this.inputValue) > -1).sort((a,b) => a.indexOf(this.inputValue) - b.indexOf(this.inputValue));
console.log('Items found ' + filtered.length + ' out of ' + this.items.length);
for (let i = 0; i < Math.min(filtered.length,5); i++) {
this.searchItems.push({
value: filtered[i],
html: filtered[i].replace(this.inputValue, '<span class="msa-highlight">' + this.inputValue + '</span>')
});
}
}
if (this.selectedSearchItem > this.searchItems.length) {
this.selectedSearchItem = 0;
}
}
keydown(e) {
if (e.keyCode === 38) { // up key
if(this.selectedSearchItem > 0) {
this.selectedSearchItem = this.selectedSearchItem - 1;
}
return false; // preventDefault
} else if (e.keyCode === 40) { // down key
if(this.selectedSearchItem < this.searchItems.length - 1) {
this.selectedSearchItem = this.selectedSearchItem + 1;
}
return false; // preventDefault
} else if (e.keyCode === 13) { // enter
this.selectCurrent();
return false; // preventDefault
}
return true; // normal input, do not preventDefault
}
itemSelected(index) {
this.selectedSearchItem = index;
return this.selectCurrent();
}
selectCurrent() {
if(this.searchItems.length && this.selectedSearchItem >= 0 && this.selectedSearchItem < this.searchItems.length) {
this.selectedItems.push(this.searchItems[this.selectedSearchItem].value);
this.searchItems.splice(0, this.searchItems.length);
this.inputValue = '';
return false;
}
return true;
}
removeItem(index) {
if(this.selectedItems.length && index >= 0 && index < this.selectedItems.length) {
this.selectedItems.splice(index, 1);
this.searchItems.splice(0, this.searchItems.length);
this.inputValue = '';
return false;
}
return true;
}
focusElement() {
this.inputElem.focus();
}
}
<!doctype html>
<html>
<head>
<title>Aurelia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body aurelia-app>
<h1>Loading...</h1>
<script src="https://jdanyow.github.io/rjs-bundle/node_modules/requirejs/require.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/config.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/aurelia.js"></script>
<script src="https://jdanyow.github.io/rjs-bundle/bundles/babel.js"></script>
<script>
require(['aurelia-bootstrapper']);
</script>
</body>
</html>
export function configure(aurelia) {
aurelia.use.basicConfiguration();
aurelia.start().then(() => aurelia.setRoot());
}
console.log('Hello World!');
/* todo: add styles */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment