Skip to content

Instantly share code, notes, and snippets.

@jwx
Last active June 27, 2022 19:21
Show Gist options
  • Save jwx/d76c127dffe657913c2982daa0e0b608 to your computer and use it in GitHub Desktop.
Save jwx/d76c127dffe657913c2982daa0e0b608 to your computer and use it in GitHub Desktop.
peasy-ui-grid-menu
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Dumber Gist</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
</head>
<!--
Dumber Gist uses dumber bundler, the default bundle file
is /dist/entry-bundle.js.
The starting module is pointed to "main" (data-main attribute on script)
which is your src/main.ts.
-->
<body>
<script src="/dist/entry-bundle.js" data-main="main"></script>
</body>
</html>
{
"dependencies": {
"peasy-ui": "latest"
}
}
import { UI, UIView } from 'peasy-ui';
import { Menu } from './menu';
import 'styles.css';
window.addEventListener('DOMContentLoaded', (event) => {
main();
});
let menu = null;
function main() {
const model = {
menu: null,
get showButton() { return menu == null },
clicked: () => showMenu(),
}
UI.create(document.body, '<div><button ${ === showButton} ${click @=> clicked}>Show menu</button></div>', model);
setInterval(() => UI.update(), 1000 / 60);
}
function showMenu() {
menu = new Menu(
['one', 'two', 'three', 'four', 'five', 'six'],
(selected) => {
alert('Selected menu option: ' + selected);
menu = null;
});
}
import { UI, UIView } from 'peasy-ui';
class Option {
public element: HTMLElement;
constructor(public menu: any, public name: string) { }
get active() {
return this === this.menu.active ? 'active' : '';
}
}
export class Menu {
public options: Option[] = [];
public active: Option;
public template =
'<div tabindex="1" class="options" ${keyup @=> action} ${ ==> element}>' +
' <div class="option ${option.active}" ${ ==> option.element} ${option <=* options}>${option.name}</div>' +
'</div>'
;
private ui: UIView;
private element: HTMLElement;
public constructor(options: string[], private callback: any) {
this.options = options.map(option => new Option(this, option));
this.active = this.options[0];
this.ui = UI.create(document.body, this.template, this);
this.ui.attached.then(() => this.element.focus());
}
public action(event, model) {
switch (event.key) {
case 'ArrowLeft':
case 'ArrowRight':
case 'ArrowUp':
case 'ArrowDown':
return model.move(event.key.toLowerCase().replace('arrow', ''));
case 'Enter':
const selected = model.active.name;
model.ui.destroy();
model.callback(selected);
return true;
}
}
public move(direction: string) {
const index = this.options.indexOf(this.active);
const columns = this.columns;
switch (direction) {
case 'left':
if (index % columns > 0) {
this.active = this.options[index - 1];
}
break;
case 'right':
if (index % columns < columns - 1 && index < this.options.length - 1) {
this.active = this.options[index + 1];
}
break;
case 'up':
if (index >= columns) {
this.active = this.options[index - columns];
}
break;
case 'down':
if (index + columns < this.options.length) {
this.active = this.options[index + columns];
}
break;
}
return true;
}
public get columns(): number {
const indexNext = this.options.findIndex(option => option.element.offsetTop > this.options[0].element.offsetTop);
return indexNext > -1 ? indexNext : this.options.length;
}
}
.options {
border: 1px solid black;
display: flex;
flex-wrap: wrap;
padding: 5px;
width: 50%;
}
.option {
width: 50px;
padding: 5px;
margin: 5px;
background-color: lightblue;
}
.option.active {
background-color: skyblue;
color: red;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment