Skip to content

Instantly share code, notes, and snippets.

@jdanyow
Last active August 19, 2017 02:38
Show Gist options
  • Save jdanyow/a78d15898660218b8438e34dcc31f390 to your computer and use it in GitHub Desktop.
Save jdanyow/a78d15898660218b8438e34dcc31f390 to your computer and use it in GitHub Desktop.
Aurelia Radio Select
<template>
<require from="./radio-select"></require>
<style>
form > label {
display: block;
margin-top: 10px;
}
</style>
<form>
<label>
First Name:
<input>
</label>
<label id="color1">
Color 1:
<radio-select items-source.bind="colors"
value.bind="color1"
aria-labelledby="color1"
focus.trigger="logFocus('color1')"
blur.trigger="logBlur('color1')"
ref="color1RadioSelect">
</radio-select>
</label>
<label id="color2">
Color 2:
<radio-select items-source.bind="colors"
value.bind="color2"
aria-labelledby="color1"
focus.trigger="logFocus('color2')"
blur.trigger="logBlur('color2')">
<em css="color: ${item}">
${item}
</em>
</radio-select>
</label>
<label>
Last Name:
<input>
</label>
</form>
<button type="button" click.delegate="color1RadioSelect.focus()">Focus Color 1</button>
<button type="button" mouseover.delegate="color1RadioSelect.blur()">Blur Color 1 (on mouse over of this button)</button>
</template>
export class App {
colors = ['red', 'green', 'blue'];
color1 = 'green';
color2 = 'red';
logFocus(event) {
console.log('focus', event);
}
logBlur(event) {
console.log('blur', event);
}
}
<!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>
import {DOM} from 'aurelia-pal';
import {TaskQueue} from 'aurelia-task-queue';
/**
* A component made up of multiple focusable elements.
* - exposes focus and blur methods
* - normalizes child element focus and blur events and fires focus/blur at the component element level.
*/
export class MultiInputComponent {
element;
focused = false;
constructor(element, taskQueue) {
this.element = element;
this.taskQueue = taskQueue;
element.focus = this.focus;
element.blur = this.blur;
}
bind() {
this.element.addEventListener('focus', this.handleFocusEvent, true);
this.element.addEventListener('blur', this.handleFocusEvent, true);
}
unbind() {
this.element.removeEventListener('focus', this.handleFocusEvent);
this.element.removeEventListener('blur', this.handleFocusEvent);
}
focus = () => {
// todo:
// - selector needs to handle other types of focusable elements.
// - should not attempt to focus hidden or disabled elements.
const input = this.element.querySelector('input');
input.focus();
this.syncFocus();
}
blur = () => {
// todo: selector needs to handle other types of focusable elements
const input = this.element.querySelector('input:focus');
if (!input) {
return;
}
input.blur();
this.syncFocus();
}
handleFocusEvent = event => {
if (!event.isTrusted) {
return;
}
this.taskQueue.queueMicroTask(this.syncFocus);
}
syncFocus = () => {
const focused = this.element.querySelector(':focus');
if (this.focused === focused) {
return;
}
this.focused = focused;
this.element.dispatchEvent(DOM.createCustomEvent(focused ? 'focus' : 'blur'));
}
}
<template>
<label repeat.for="item of itemsSource">
<input type="radio" name.one-time="name" model.bind="item" checked.bind="value">
<template replaceable part="item-template">${item}</template>
</label>
</template>
import {bindable, processContent} from 'aurelia-templating';
import {bindingMode} from 'aurelia-binding';
import {inject} from 'aurelia-dependency-injection';
import {TaskQueue} from 'aurelia-task-queue';
import {makePartReplacementFromContent} from './template-transforms';
import {MultiInputComponent} from './multi-input-component';
let id = 0;
@inject(Element, TaskQueue)
@processContent(makePartReplacementFromContent)
export class RadioSelect extends MultiInputComponent{
@bindable itemsSource = null;
@bindable({ defaultBindingMode: bindingMode.twoWay }) value = null;
name = `radio-select-${id++}`;
constructor(element, taskQueue) {
super(element, taskQueue);
this.element.setAttribute('role', 'radiogroup');
}
}
import {FEATURE} from 'aurelia-pal';
export function makePartReplacementFromContent(viewCompiler, viewResources, element, behaviorInstruction) {
const content = element.firstElementChild;
if (content) {
// create the <template>
const template = document.createElement('template');
// support browsers that do not have a real <template> element implementation (IE)
FEATURE.ensureHTMLTemplateElement(template);
// indicate the part this <template> replaces.
template.setAttribute('replace-part', 'item-template');
// replace the element's content with the <template>
element.insertBefore(template, content);
element.removeChild(content);
template.content.appendChild(content);
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment