Skip to content

Instantly share code, notes, and snippets.

@AquilaSands
Last active August 17, 2017 18:14
Show Gist options
  • Save AquilaSands/637e979531abfc2449e1131beed68ead to your computer and use it in GitHub Desktop.
Save AquilaSands/637e979531abfc2449e1131beed68ead to your computer and use it in GitHub Desktop.
Aurelia Gist Custom Attribute with View
<template>
<require from="./picker"></require>
<h1>Custom Attribute with a View</h1>
<button picker="value.bind:buttonValue; items.bind:buttonList">Click me</button>
<p>The value of button is: ${buttonValue}</p>
<input type="text" picker="value.bind:inputValue; items.bind:inputList">
<p>the value of input is: ${inputValue}</p>
</template>
import { bindable } from 'aurelia-framework';
export class App {
@bindable inputValue;
@bindable buttonValue;
@bindable inputList = [
"Chocolate",
"Vanilla",
"Orange"
]
@bindable buttonList = [
"Jump",
"Run",
"Skip",
"Walk"
]
message = 'Hello World!';
}
<!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>
.picker {
background-color: #fff;
box-shadow: 0px 0px 10px 2px rgba(40,40,40,0.25);
border: 1px solid #e4e4e4;
}
.picker > ul {
list-style: none;
margin: 0px;
padding: 0px;
}
.picker > ul > li {
line-height: 1.5em;
cursor: pointer;
padding: 0.5em
}
.picker > ul > li:hover, .picker > ul > li.active {
background: #c7c7c7;
}
<template>
<require from="./picker.css"></require>
<div class="picker">
<ul>
<li repeat.for="item of items" click.trigger="pick(item)" class="${value === item ? 'active' : ''}">${item}</li>
</ul>
</div>
</template>
import { inject, bindable, Container, ViewEngine, View, bindingMode} from 'aurelia-framework';
import { DOM } from 'aurelia-pal';
@inject(Element, Container, ViewEngine)
export class PickerCustomAttribute {
show = false;
mouseupListener;
focusListener;
divElement;
@bindable({ defaultBindingMode: bindingMode.twoWay }) value;
@bindable({ defaultBindingMode: bindingMode.twoWay }) items;
constructor(element, container, viewEngine) {
this.element = element;
this.container = container;
this.viewEngine = viewEngine;
this.mouseupListener = e => this.handleMouseUp(e);
this.focusListener = e => this.handleFocus(e);
}
attached() {
if (this.isInputElement()) {
this.element.addEventListener('focus', this.focusListener);
this.element.addEventListener('blur', this.focusListener);
} else {
this.element.addEventListener('click', this.mouseupListener);
}
}
detached() {
if (this.isInputElement) {
this.element.removeEventListener('focus', this.focusListener);
this.element.removeEventListener('blur', this.focusListener);
} else {
this.element.removeEventListner('click', this.mouseupListener);
}
document.removeEventListener('mouseup', this.mouseupListener);
}
pick(item) {
this.value = item;
if (this.isInputElement()) {
//Since we're not binding the value on the input element we update it here.
this.element.value = item;
}
this.removePicker();
}
handleMouseUp(e) {
if (!this.isInputElement() && !this.show) {
this.createPicker();
}
if (this.show) {
/*
Remove picker if click is outside of it's client rectangle.
*/
let containerRect = this.divElement.getBoundingClientRect();
let elementRect = this.element.getBoundingClientRect();
let inContainerRect = e.clientX > containerRect.left && e.clientX < containerRect.right && e.clientY > containerRect.top && e.clientY < containerRect.bottom;
let inElementRect = e.clientX > elementRect.left && e.clientX < elementRect.right && e.clientY > elementRect.top && e.clientY < elementRect.bottom;
if (!inContainerRect && !inElementRect) {
this.removePicker();
}
}
}
handleFocus(e) {
if (e.type === 'focus' && !this.show) {
this.createPicker();
}
if (e.type === 'blur') {
if (this.isInputElement() && this.element.value !== this.value && typeof this.value !== "undefined") {
this.element.value = this.value
}
}
}
isInputElement() {
return this.element.nodeType === 1 && this.element.tagName.toLowerCase() == 'input';
}
createPicker() {
this.viewEngine.loadViewFactory('./picker.html').then(factory => {
const childContainer = this.container.createChild();
const view = factory.create(childContainer);
view.bind(this);
this.addElement(view)
if (this.isInputElement)
document.addEventListener('mouseup', this.mouseupListener);
this.show = true;
})
}
removePicker() {
const body = DOM.querySelectorAll('body')[0];
body.removeChild(this.divElement);
if (this.isInputElement)
document.removeEventListener('mouseup', this.mouseupListener);
this.show = false;
}
addElement(view) {
const body = DOM.querySelectorAll('body')[0];
this.divElement = DOM.createElement('div');
view.appendNodesTo(this.divElement);
const elementRect = this.element.getBoundingClientRect();
const left = elementRect.left + window.scrollX;
const height = this.divElement.getBoundingClientRect().height;
var top = elementRect.top + elementRect.height;
top = ((top+height) < window.innerHeight) ? top + window.scrollY : (elementRect.top - height + window.scrollY);
this.divElement.style.top = top + 'px';
this.divElement.style.left = left + 'px';
this.divElement.style.position = 'absolute';
this.divElement.style.zIndex = '2001';
body.insertBefore(this.divElement, body.firstChild);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment