Update framework
This commit is contained in:
1
framework/ui/index.d.ts
vendored
Normal file
1
framework/ui/index.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export * from './menu/index';
|
2
framework/ui/index.js
Normal file
2
framework/ui/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './menu/index';
|
||||
// export * as Text from './text';
|
39
framework/ui/menu/index.d.ts
vendored
Normal file
39
framework/ui/menu/index.d.ts
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import { BaseItem } from './items/base-item';
|
||||
import { SoundSet } from './interfaces/sound-set';
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
export declare class Menu extends EventEmitter {
|
||||
private title;
|
||||
private menuItems;
|
||||
private soundSet;
|
||||
private defaultAction;
|
||||
private cancelAction;
|
||||
private titleContainer;
|
||||
private currentItem;
|
||||
private currentIndex;
|
||||
private container;
|
||||
private element;
|
||||
private DOMNodes;
|
||||
private soundManager;
|
||||
private keyboardManager;
|
||||
constructor(title?: string, menuItems?: BaseItem[], soundSet?: SoundSet, defaultAction?: string, cancelAction?: string);
|
||||
private init;
|
||||
addItem(item: BaseItem): this;
|
||||
setTitle(title: string): this;
|
||||
setSoundSet(soundSet: SoundSet): this;
|
||||
setDefaultAction(id: string): this;
|
||||
setCancelAction(id: string): this;
|
||||
run(element: HTMLElement): Promise<any>;
|
||||
close(): void;
|
||||
private appendToContainer;
|
||||
private handleItemUpdate;
|
||||
private onItemFocus;
|
||||
focusNext(): void;
|
||||
focusPrevious(): void;
|
||||
private focusCurrentIndex;
|
||||
getCurrentFocus(): BaseItem;
|
||||
getContainer(): HTMLElement;
|
||||
clickDefaultAction(): void;
|
||||
clickCancelAction(): void;
|
||||
private compile;
|
||||
}
|
||||
export * from './items';
|
145
framework/ui/menu/index.js
Normal file
145
framework/ui/menu/index.js
Normal file
@@ -0,0 +1,145 @@
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
import { SoundManager } from './sound-manager';
|
||||
import { KeyboardManager } from './keyboard-manager';
|
||||
export class Menu extends EventEmitter {
|
||||
constructor(title = 'Menu', menuItems = [], soundSet = null, defaultAction = null, cancelAction = null) {
|
||||
super();
|
||||
this.title = title;
|
||||
this.menuItems = menuItems;
|
||||
this.soundSet = soundSet;
|
||||
this.defaultAction = defaultAction;
|
||||
this.cancelAction = cancelAction;
|
||||
this.currentIndex = 0;
|
||||
this.DOMNodes = [];
|
||||
this.currentIndex = 0;
|
||||
this.currentItem = null;
|
||||
this.soundManager = new SoundManager(soundSet);
|
||||
this.keyboardManager = new KeyboardManager(this);
|
||||
this.init();
|
||||
}
|
||||
init() {
|
||||
this.menuItems[this.currentIndex] &&
|
||||
this.menuItems[this.currentIndex].focus();
|
||||
this.emit('init');
|
||||
}
|
||||
addItem(item) {
|
||||
this.menuItems.push(item);
|
||||
this.emit('item.add', item);
|
||||
return this;
|
||||
}
|
||||
setTitle(title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
setSoundSet(soundSet) {
|
||||
this.soundSet = soundSet;
|
||||
this.soundManager.setSoundSet(this.soundSet);
|
||||
return this;
|
||||
}
|
||||
setDefaultAction(id) {
|
||||
this.defaultAction = id;
|
||||
return this;
|
||||
}
|
||||
setCancelAction(id) {
|
||||
this.cancelAction = id;
|
||||
return this;
|
||||
}
|
||||
run(element) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.element = element;
|
||||
this.container = document.createElement('div');
|
||||
this.titleContainer = document.createElement('h1');
|
||||
this.titleContainer.textContent = this.title;
|
||||
this.container.appendChild(this.titleContainer);
|
||||
this.menuItems.forEach((item) => {
|
||||
this.appendToContainer(item.getDOMNode());
|
||||
item.on('update', this.handleItemUpdate.bind(this));
|
||||
item.on('focus', this.onItemFocus.bind(this));
|
||||
item.on('choose', (event) => {
|
||||
const menuMap = this.compile();
|
||||
this.soundManager.handleSound('choose');
|
||||
this.emit('choose', menuMap);
|
||||
resolve(menuMap);
|
||||
});
|
||||
});
|
||||
element.appendChild(this.container);
|
||||
this.soundManager.handleSound('open');
|
||||
this.keyboardManager.init();
|
||||
// push some data onto the history stack so that we can use the browser's back button to exit out of the menu.
|
||||
history.pushState({ menu: true }, null, null);
|
||||
});
|
||||
});
|
||||
}
|
||||
close() {
|
||||
this.container.remove();
|
||||
this.soundManager.handleSound('close');
|
||||
this.keyboardManager.release();
|
||||
this.DOMNodes.forEach((item) => {
|
||||
this.container.removeChild(item);
|
||||
});
|
||||
this.emit('close');
|
||||
}
|
||||
appendToContainer(node) {
|
||||
this.container.appendChild(node);
|
||||
this.DOMNodes.push(node);
|
||||
}
|
||||
handleItemUpdate(value) {
|
||||
this.soundManager.handleSound(value.type, value.value);
|
||||
this.emit('update', this.compile());
|
||||
}
|
||||
onItemFocus(id) {
|
||||
this.soundManager.handleSound('focus');
|
||||
this.currentIndex = this.menuItems.indexOf(this.menuItems.find((item) => item.getID() == id));
|
||||
this.emit('focus', this.menuItems[this.currentIndex]);
|
||||
}
|
||||
focusNext() {
|
||||
if (this.currentIndex < this.menuItems.length - 1) {
|
||||
this.currentIndex++;
|
||||
}
|
||||
this.focusCurrentIndex();
|
||||
}
|
||||
focusPrevious() {
|
||||
if (this.currentIndex > 0) {
|
||||
this.currentIndex--;
|
||||
}
|
||||
this.focusCurrentIndex();
|
||||
}
|
||||
focusCurrentIndex() {
|
||||
this.menuItems[this.currentIndex].focus();
|
||||
}
|
||||
getCurrentFocus() {
|
||||
return this.menuItems[this.currentIndex];
|
||||
}
|
||||
getContainer() {
|
||||
return this.container;
|
||||
}
|
||||
clickDefaultAction() {
|
||||
if (!this.defaultAction)
|
||||
return;
|
||||
const item = this.menuItems.find((item) => item.getID() === this.defaultAction);
|
||||
item.click();
|
||||
}
|
||||
clickCancelAction() {
|
||||
if (!this.cancelAction)
|
||||
return;
|
||||
const node = this.menuItems.find((item) => item.getID() === this.cancelAction);
|
||||
node.click();
|
||||
}
|
||||
compile() {
|
||||
const menuMap = new Map();
|
||||
this.menuItems.forEach((item) => menuMap.set(item.getID(), item.getContents()));
|
||||
menuMap.set('selected', this.menuItems[this.currentIndex].getID());
|
||||
return menuMap;
|
||||
}
|
||||
}
|
||||
export * from './items';
|
3
framework/ui/menu/interfaces/playable-sound.d.ts
vendored
Normal file
3
framework/ui/menu/interfaces/playable-sound.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface IPlayableSound {
|
||||
play(): any;
|
||||
}
|
1
framework/ui/menu/interfaces/playable-sound.js
Normal file
1
framework/ui/menu/interfaces/playable-sound.js
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
17
framework/ui/menu/interfaces/sound-set.d.ts
vendored
Normal file
17
framework/ui/menu/interfaces/sound-set.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import { IPlayableSound } from './playable-sound';
|
||||
export interface SoundSet {
|
||||
open?: IPlayableSound;
|
||||
close?: IPlayableSound;
|
||||
boundary?: IPlayableSound;
|
||||
choose?: IPlayableSound;
|
||||
move?: IPlayableSound;
|
||||
scroller?: IPlayableSound;
|
||||
sliderLeft?: IPlayableSound;
|
||||
sliderRight?: IPlayableSound;
|
||||
wrap?: IPlayableSound;
|
||||
char?: IPlayableSound;
|
||||
delete?: IPlayableSound;
|
||||
enter?: IPlayableSound;
|
||||
checked?: IPlayableSound;
|
||||
unchecked?: IPlayableSound;
|
||||
}
|
1
framework/ui/menu/interfaces/sound-set.js
Normal file
1
framework/ui/menu/interfaces/sound-set.js
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
13
framework/ui/menu/items/base-item.d.ts
vendored
Normal file
13
framework/ui/menu/items/base-item.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
export declare class BaseItem extends EventEmitter {
|
||||
protected id: string;
|
||||
protected title: string;
|
||||
protected container: HTMLElement;
|
||||
constructor(id: string, title: string);
|
||||
getDOMNode(): HTMLElement;
|
||||
getContents(): void;
|
||||
protected onFocus(event: Event): void;
|
||||
focus(): void;
|
||||
click(): void;
|
||||
getID(): string;
|
||||
}
|
29
framework/ui/menu/items/base-item.js
Normal file
29
framework/ui/menu/items/base-item.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
export class BaseItem extends EventEmitter {
|
||||
constructor(id, title) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
}
|
||||
getDOMNode() {
|
||||
let node = document.createTextNode(this.title);
|
||||
let element = document.createElement('div');
|
||||
element.appendChild(node);
|
||||
return element;
|
||||
}
|
||||
getContents() {
|
||||
return;
|
||||
}
|
||||
onFocus(event) {
|
||||
this.emit('focus', this.id);
|
||||
}
|
||||
focus() {
|
||||
this.container && this.container.focus();
|
||||
}
|
||||
click() {
|
||||
return;
|
||||
}
|
||||
getID() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
10
framework/ui/menu/items/checkbox-item.d.ts
vendored
Normal file
10
framework/ui/menu/items/checkbox-item.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { BaseItem } from './base-item';
|
||||
export declare class CheckboxItem extends BaseItem {
|
||||
private checkboxElement;
|
||||
private label;
|
||||
constructor(id: string, title: string);
|
||||
getDOMNode(): HTMLElement;
|
||||
getContents(): boolean;
|
||||
private onChange;
|
||||
focus(): void;
|
||||
}
|
32
framework/ui/menu/items/checkbox-item.js
Normal file
32
framework/ui/menu/items/checkbox-item.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { BaseItem } from './base-item';
|
||||
export class CheckboxItem extends BaseItem {
|
||||
constructor(id, title) {
|
||||
super(id, title);
|
||||
}
|
||||
getDOMNode() {
|
||||
this.container = document.createElement('div');
|
||||
this.label = document.createElement('label');
|
||||
this.label.setAttribute('for', `chkbx_${this.id}`);
|
||||
this.label.textContent = this.title;
|
||||
this.checkboxElement = document.createElement('input');
|
||||
this.checkboxElement.setAttribute('type', 'checkbox');
|
||||
this.checkboxElement.setAttribute('id', `chkbx_${this.id}`);
|
||||
this.checkboxElement.addEventListener('focus', this.onFocus.bind(this));
|
||||
this.checkboxElement.addEventListener('change', this.onChange.bind(this));
|
||||
this.container.appendChild(this.label);
|
||||
this.container.appendChild(this.checkboxElement);
|
||||
return this.container;
|
||||
}
|
||||
getContents() {
|
||||
return this.checkboxElement.checked;
|
||||
}
|
||||
onChange(event) {
|
||||
this.emit('update', {
|
||||
type: 'checkbox',
|
||||
value: this.checkboxElement.checked
|
||||
});
|
||||
}
|
||||
focus() {
|
||||
this.checkboxElement.focus();
|
||||
}
|
||||
}
|
13
framework/ui/menu/items/edit-item.d.ts
vendored
Normal file
13
framework/ui/menu/items/edit-item.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { BaseItem } from './base-item';
|
||||
export declare class EditItem extends BaseItem {
|
||||
private initialText;
|
||||
private isPassword;
|
||||
private contents;
|
||||
private label;
|
||||
private editField;
|
||||
constructor(id: string, title: string, initialText: string, isPassword?: boolean);
|
||||
getDOMNode(): HTMLElement;
|
||||
getContents(): string;
|
||||
private onChange;
|
||||
focus(): void;
|
||||
}
|
42
framework/ui/menu/items/edit-item.js
Normal file
42
framework/ui/menu/items/edit-item.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import { BaseItem } from './base-item';
|
||||
export class EditItem extends BaseItem {
|
||||
constructor(id, title, initialText, isPassword = false) {
|
||||
super(id, title);
|
||||
this.initialText = initialText;
|
||||
this.isPassword = isPassword;
|
||||
this.contents = initialText;
|
||||
}
|
||||
getDOMNode() {
|
||||
const node = document.createElement('div');
|
||||
const label = document.createElement('label');
|
||||
label.setAttribute('for', `edit_${this.id}`);
|
||||
label.textContent = this.title;
|
||||
const editField = document.createElement('input');
|
||||
editField.id = `edit_${this.id}`;
|
||||
editField.value = this.contents;
|
||||
editField.addEventListener('keydown', this.onChange.bind(this));
|
||||
editField.addEventListener('focus', this.onFocus.bind(this));
|
||||
if (this.isPassword) {
|
||||
editField.type = 'password';
|
||||
}
|
||||
node.appendChild(label);
|
||||
node.appendChild(editField);
|
||||
node.addEventListener('focus', this.onFocus.bind(this));
|
||||
this.editField = editField;
|
||||
this.label = label;
|
||||
this.container = node;
|
||||
return node;
|
||||
}
|
||||
getContents() {
|
||||
return this.editField.value;
|
||||
}
|
||||
onChange(event) {
|
||||
this.emit('update', {
|
||||
type: 'edit',
|
||||
value: this.editField.value
|
||||
});
|
||||
}
|
||||
focus() {
|
||||
this.editField && this.editField.focus();
|
||||
}
|
||||
}
|
6
framework/ui/menu/items/index.d.ts
vendored
Normal file
6
framework/ui/menu/items/index.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
export { BaseItem } from './base-item';
|
||||
export { EditItem } from './edit-item';
|
||||
export { MenuItem } from './menu-item';
|
||||
export { SelectorItem } from './selector-item';
|
||||
export { SliderItem } from './slider-item';
|
||||
export { CheckboxItem } from './checkbox-item';
|
6
framework/ui/menu/items/index.js
Normal file
6
framework/ui/menu/items/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export { BaseItem } from './base-item';
|
||||
export { EditItem } from './edit-item';
|
||||
export { MenuItem } from './menu-item';
|
||||
export { SelectorItem } from './selector-item';
|
||||
export { SliderItem } from './slider-item';
|
||||
export { CheckboxItem } from './checkbox-item';
|
10
framework/ui/menu/items/menu-item.d.ts
vendored
Normal file
10
framework/ui/menu/items/menu-item.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import { BaseItem } from './base-item';
|
||||
export declare class MenuItem extends BaseItem {
|
||||
private button;
|
||||
constructor(id: string, title: string);
|
||||
getDOMNode(): HTMLElement;
|
||||
getContents(): string;
|
||||
private handleClick;
|
||||
focus(): void;
|
||||
click(): void;
|
||||
}
|
29
framework/ui/menu/items/menu-item.js
Normal file
29
framework/ui/menu/items/menu-item.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { BaseItem } from './base-item';
|
||||
export class MenuItem extends BaseItem {
|
||||
constructor(id, title) {
|
||||
super(id, title);
|
||||
}
|
||||
getDOMNode() {
|
||||
const container = document.createElement('div');
|
||||
const button = document.createElement('button');
|
||||
button.textContent = this.title;
|
||||
button.addEventListener('click', this.handleClick.bind(this));
|
||||
button.addEventListener('focus', this.onFocus.bind(this));
|
||||
container.appendChild(button);
|
||||
this.container = container;
|
||||
this.button = button;
|
||||
return container;
|
||||
}
|
||||
getContents() {
|
||||
return this.id;
|
||||
}
|
||||
handleClick(event) {
|
||||
this.emit('choose', this.id);
|
||||
}
|
||||
focus() {
|
||||
this.button && this.button.focus();
|
||||
}
|
||||
click() {
|
||||
this.button.click();
|
||||
}
|
||||
}
|
21
framework/ui/menu/items/selector-item.d.ts
vendored
Normal file
21
framework/ui/menu/items/selector-item.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import { BaseItem } from './base-item';
|
||||
export declare class SelectorItem extends BaseItem {
|
||||
private items;
|
||||
private listContainer;
|
||||
private fieldSet;
|
||||
private label;
|
||||
private entries;
|
||||
private currentValue;
|
||||
constructor(id: string, title: string, items: SelectorEntry[]);
|
||||
getDOMNode(): HTMLElement;
|
||||
private buildEntries;
|
||||
private onItemFocus;
|
||||
getContents(): any;
|
||||
private onSelectItem;
|
||||
private onChangeItem;
|
||||
focus(): void;
|
||||
}
|
||||
export interface SelectorEntry {
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
62
framework/ui/menu/items/selector-item.js
Normal file
62
framework/ui/menu/items/selector-item.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import { BaseItem } from './base-item';
|
||||
export class SelectorItem extends BaseItem {
|
||||
constructor(id, title, items) {
|
||||
super(id, title);
|
||||
this.items = items;
|
||||
this.entries = [];
|
||||
}
|
||||
getDOMNode() {
|
||||
this.container = document.createElement('div');
|
||||
this.listContainer = document.createElement('ul');
|
||||
this.label = document.createElement('legend');
|
||||
this.fieldSet = document.createElement('fieldset');
|
||||
this.fieldSet.setAttribute('class', 'radiogroup');
|
||||
this.fieldSet.id = `fs_selector_${this.id}`;
|
||||
const name = document.createTextNode(this.title);
|
||||
this.label.appendChild(name);
|
||||
this.fieldSet.appendChild(this.label);
|
||||
this.buildEntries();
|
||||
this.container.appendChild(this.fieldSet);
|
||||
this.container.addEventListener('focus', this.onFocus.bind(this));
|
||||
return this.container;
|
||||
}
|
||||
buildEntries() {
|
||||
this.items.forEach((item, index) => {
|
||||
const node = document.createElement('input');
|
||||
node.type = 'radio';
|
||||
node.id = `${this.id}_${item.id}`;
|
||||
node.name = this.id;
|
||||
node.value = item.id || `${index}`;
|
||||
node.addEventListener('focus', this.onItemFocus.bind(this));
|
||||
node.addEventListener('select', this.onSelectItem.bind(this));
|
||||
node.addEventListener('change', this.onChangeItem.bind(this));
|
||||
this.entries.push(node);
|
||||
const label = document.createElement('label');
|
||||
label.setAttribute('for', `${this.id}_${item.id}`);
|
||||
label.textContent = item.title;
|
||||
this.fieldSet.append(node);
|
||||
this.fieldSet.append(label);
|
||||
});
|
||||
}
|
||||
onItemFocus(event) {
|
||||
console.log(`Item focused: `, event);
|
||||
this.emit('focus', this.id);
|
||||
}
|
||||
getContents() {
|
||||
return this.currentValue;
|
||||
}
|
||||
onSelectItem(event) { }
|
||||
onChangeItem(event) {
|
||||
const node = document.querySelector(`input[name = "${this.id}"]:checked`);
|
||||
this.currentValue = this.items.find((item) => `${this.id}_${item.id}` === node.id);
|
||||
this.emit('update', {
|
||||
type: 'selector',
|
||||
value: this.currentValue
|
||||
});
|
||||
}
|
||||
focus() {
|
||||
const node = document.querySelector(`input[name = "${this.id}"]:checked`) ||
|
||||
this.entries[0];
|
||||
node.focus();
|
||||
}
|
||||
}
|
15
framework/ui/menu/items/slider-item.d.ts
vendored
Normal file
15
framework/ui/menu/items/slider-item.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { BaseItem } from './base-item';
|
||||
export declare class SliderItem extends BaseItem {
|
||||
private min;
|
||||
private max;
|
||||
private step;
|
||||
private defaultValue;
|
||||
private slider;
|
||||
private label;
|
||||
private currentValue;
|
||||
constructor(id: string, title: string, min: number, max: number, step: number, defaultValue?: number);
|
||||
getDOMNode(): HTMLElement;
|
||||
getContents(): string;
|
||||
private onChange;
|
||||
focus(): void;
|
||||
}
|
42
framework/ui/menu/items/slider-item.js
Normal file
42
framework/ui/menu/items/slider-item.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import { BaseItem } from './base-item';
|
||||
export class SliderItem extends BaseItem {
|
||||
constructor(id, title, min, max, step, defaultValue = null) {
|
||||
super(id, title);
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.step = step;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
getDOMNode() {
|
||||
this.container = document.createElement('div');
|
||||
this.label = document.createElement('label');
|
||||
this.label.textContent = this.title;
|
||||
this.label.setAttribute('for', `slider_${this.id}`);
|
||||
this.slider = document.createElement('input');
|
||||
this.slider.id = `slider_${this.id}`;
|
||||
this.slider.type = 'range';
|
||||
this.slider.setAttribute('min', this.min.toString());
|
||||
this.slider.setAttribute('max', this.max.toString());
|
||||
this.slider.setAttribute('step', this.step.toString());
|
||||
if (this.defaultValue)
|
||||
this.slider.value = this.defaultValue.toString();
|
||||
this.slider.addEventListener('change', this.onChange.bind(this));
|
||||
this.slider.addEventListener('focus', this.onFocus.bind(this));
|
||||
this.container.appendChild(this.label);
|
||||
this.container.appendChild(this.slider);
|
||||
this.container.addEventListener('focus', this.onFocus.bind(this));
|
||||
return this.container;
|
||||
}
|
||||
getContents() {
|
||||
return this.slider.value;
|
||||
}
|
||||
onChange(event) {
|
||||
this.emit('update', {
|
||||
type: 'slider',
|
||||
value: this.slider.value
|
||||
});
|
||||
}
|
||||
focus() {
|
||||
this.slider && this.slider.focus();
|
||||
}
|
||||
}
|
8
framework/ui/menu/keyboard-manager.d.ts
vendored
Normal file
8
framework/ui/menu/keyboard-manager.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Menu } from '.';
|
||||
export declare class KeyboardManager {
|
||||
private menu;
|
||||
constructor(menu: Menu);
|
||||
init(): void;
|
||||
private handler;
|
||||
release(): void;
|
||||
}
|
40
framework/ui/menu/keyboard-manager.js
Normal file
40
framework/ui/menu/keyboard-manager.js
Normal file
@@ -0,0 +1,40 @@
|
||||
export class KeyboardManager {
|
||||
constructor(menu) {
|
||||
this.menu = menu;
|
||||
}
|
||||
init() {
|
||||
this.menu
|
||||
.getContainer()
|
||||
.addEventListener('keydown', this.handler.bind(this));
|
||||
// This trick let's us detect the press of the back or forward buttons to exit out of the menu.
|
||||
window.onpopstate = () => this.menu.clickCancelAction();
|
||||
}
|
||||
handler(event) {
|
||||
switch (event.key) {
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
this.menu.focusNext();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
this.menu.focusPrevious();
|
||||
break;
|
||||
case 'Enter':
|
||||
event.preventDefault();
|
||||
this.menu.clickDefaultAction();
|
||||
break;
|
||||
case 'Escape':
|
||||
event.preventDefault();
|
||||
this.menu.clickCancelAction();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
release() {
|
||||
this.menu
|
||||
.getContainer()
|
||||
.removeEventListener('keydown', this.handler.bind(this));
|
||||
window.onpopstate = null;
|
||||
}
|
||||
}
|
0
framework/ui/menu/menu.d.ts
vendored
Normal file
0
framework/ui/menu/menu.d.ts
vendored
Normal file
0
framework/ui/menu/menu.js
Normal file
0
framework/ui/menu/menu.js
Normal file
16
framework/ui/menu/sound-manager.d.ts
vendored
Normal file
16
framework/ui/menu/sound-manager.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import { SoundSet } from './interfaces/sound-set';
|
||||
export declare class SoundManager {
|
||||
private soundSet;
|
||||
private data;
|
||||
constructor(soundSet?: SoundSet);
|
||||
setSoundSet(soundSet: SoundSet): void;
|
||||
handleSound(type: string, data?: any): void;
|
||||
private handleEditSound;
|
||||
private handleSelectorSound;
|
||||
private handleSliderSound;
|
||||
private handleFocusSound;
|
||||
private handleOpenSound;
|
||||
private handleCloseSound;
|
||||
private handleChooseSound;
|
||||
private handleCheckboxSound;
|
||||
}
|
84
framework/ui/menu/sound-manager.js
Normal file
84
framework/ui/menu/sound-manager.js
Normal file
@@ -0,0 +1,84 @@
|
||||
export class SoundManager {
|
||||
constructor(soundSet = null) {
|
||||
this.soundSet = null;
|
||||
this.data = new Map();
|
||||
this.soundSet = soundSet;
|
||||
}
|
||||
setSoundSet(soundSet) {
|
||||
this.soundSet = soundSet;
|
||||
}
|
||||
handleSound(type, data = null) {
|
||||
switch (type) {
|
||||
case 'edit':
|
||||
this.handleEditSound(data);
|
||||
break;
|
||||
case 'slider':
|
||||
this.handleSliderSound(data);
|
||||
break;
|
||||
case 'selector':
|
||||
this.handleSelectorSound(data);
|
||||
break;
|
||||
case 'checkbox':
|
||||
this.handleCheckboxSound(data);
|
||||
break;
|
||||
case 'focus':
|
||||
this.handleFocusSound();
|
||||
break;
|
||||
case 'choose':
|
||||
this.handleChooseSound();
|
||||
break;
|
||||
case 'open':
|
||||
this.handleOpenSound();
|
||||
break;
|
||||
case 'close':
|
||||
this.handleCloseSound();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
handleEditSound(data) {
|
||||
const prevData = this.data.get('edit') || '';
|
||||
if (data.length <= prevData.length) {
|
||||
this.soundSet.delete && this.soundSet.delete.play();
|
||||
}
|
||||
else {
|
||||
this.soundSet.char && this.soundSet.char.play();
|
||||
}
|
||||
this.data.set('edit', data);
|
||||
}
|
||||
handleSelectorSound(data) {
|
||||
this.soundSet.scroller && this.soundSet.scroller.play();
|
||||
}
|
||||
handleSliderSound(data) {
|
||||
const prevData = this.data.get('slider');
|
||||
if (data < prevData) {
|
||||
this.soundSet.sliderLeft && this.soundSet.sliderLeft.play();
|
||||
}
|
||||
else {
|
||||
this.soundSet.sliderRight && this.soundSet.sliderRight.play();
|
||||
}
|
||||
this.data.set('slider', data);
|
||||
}
|
||||
handleFocusSound() {
|
||||
this.soundSet.move && this.soundSet.move.play();
|
||||
}
|
||||
handleOpenSound() {
|
||||
this.soundSet.open && this.soundSet.open.play();
|
||||
}
|
||||
handleCloseSound() {
|
||||
this.soundSet.close && this.soundSet.close.play();
|
||||
}
|
||||
handleChooseSound() {
|
||||
this.soundSet.choose && this.soundSet.choose.play();
|
||||
}
|
||||
handleCheckboxSound(data) {
|
||||
if (data === true) {
|
||||
this.soundSet.checked && this.soundSet.checked.play();
|
||||
}
|
||||
else {
|
||||
this.soundSet.unchecked && this.soundSet.unchecked.play();
|
||||
}
|
||||
}
|
||||
}
|
28
framework/ui/text/index.d.ts
vendored
Normal file
28
framework/ui/text/index.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
import { SoundSet } from '../menu/interfaces/sound-set';
|
||||
import { Line } from './line';
|
||||
export declare class ScrollingText extends EventEmitter {
|
||||
private text;
|
||||
private delimiter;
|
||||
private soundSet;
|
||||
private appearingCharacters;
|
||||
private characterAppearSpeed;
|
||||
private currentLineIndex;
|
||||
private currentLine;
|
||||
private lines;
|
||||
private wrapper;
|
||||
private container;
|
||||
private soundManager;
|
||||
private keyboardManager;
|
||||
constructor(text?: string, delimiter?: string, soundSet?: SoundSet, appearingCharacters?: boolean, characterAppearSpeed?: number);
|
||||
setText(text: string): this;
|
||||
setSoundSet(soundSet: SoundSet): this;
|
||||
setDelimiter(delimiter: string): this;
|
||||
setAppearingCharacters(appearing: boolean): this;
|
||||
setAppearingCharacterSpeed(speed: number): this;
|
||||
init(): void;
|
||||
run(element: HTMLElement): Promise<void>;
|
||||
displayLine(index: number): Promise<void>;
|
||||
getContainer(): HTMLElement;
|
||||
getCurrentLine(): Line;
|
||||
}
|
102
framework/ui/text/index.js
Normal file
102
framework/ui/text/index.js
Normal file
@@ -0,0 +1,102 @@
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
import { Line } from './line';
|
||||
import { SoundManager } from './sound-manager';
|
||||
import { KeyboardManager } from './keyboard-manager';
|
||||
export class ScrollingText extends EventEmitter {
|
||||
constructor(text = null, delimiter = '\n', soundSet = null, appearingCharacters = false, characterAppearSpeed = 0) {
|
||||
super();
|
||||
this.text = text;
|
||||
this.delimiter = delimiter;
|
||||
this.soundSet = soundSet;
|
||||
this.appearingCharacters = appearingCharacters;
|
||||
this.characterAppearSpeed = characterAppearSpeed;
|
||||
this.lines = [];
|
||||
this.soundManager = new SoundManager(this, this.soundSet);
|
||||
this.keyboardManager = new KeyboardManager(this);
|
||||
this.init();
|
||||
}
|
||||
setText(text) {
|
||||
this.text = text;
|
||||
this.init();
|
||||
return this;
|
||||
}
|
||||
setSoundSet(soundSet) {
|
||||
this.soundSet = soundSet;
|
||||
this.init();
|
||||
this.soundManager.setSoundSet(this.soundSet);
|
||||
return this;
|
||||
}
|
||||
setDelimiter(delimiter) {
|
||||
this.delimiter = delimiter;
|
||||
this.init();
|
||||
return this;
|
||||
}
|
||||
setAppearingCharacters(appearing) {
|
||||
this.appearingCharacters = appearing;
|
||||
this.init();
|
||||
return this;
|
||||
}
|
||||
setAppearingCharacterSpeed(speed) {
|
||||
this.characterAppearSpeed = speed;
|
||||
this.init();
|
||||
return this;
|
||||
}
|
||||
init() {
|
||||
const split = this.text.split(this.delimiter);
|
||||
this.lines = split.map((line) => new Line(line));
|
||||
}
|
||||
run(element) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
||||
this.wrapper = document.createElement('div');
|
||||
this.wrapper.setAttribute('aria-role', 'polite');
|
||||
this.container = document.createElement('div');
|
||||
this.wrapper.appendChild(this.container);
|
||||
element.appendChild(this.wrapper);
|
||||
this.soundManager.init();
|
||||
this.keyboardManager.init();
|
||||
this.emit('open');
|
||||
let index = 0;
|
||||
this.currentLineIndex = 0;
|
||||
while (index < this.lines.length) {
|
||||
this.currentLineIndex = index;
|
||||
yield this.displayLine(index);
|
||||
index++;
|
||||
}
|
||||
this.emit('close');
|
||||
this.keyboardManager.release();
|
||||
this.container.remove();
|
||||
resolve();
|
||||
}));
|
||||
});
|
||||
}
|
||||
displayLine(index) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.container.innerHTML = '';
|
||||
this.container.appendChild(this.lines[index].getDOMNode());
|
||||
this.lines[index].display(this.container, this.appearingCharacters, this.characterAppearSpeed);
|
||||
this.lines[index].on('character.appear', (event) => this.emit('character.appear', event));
|
||||
this.lines[index].on('advance', () => {
|
||||
this.emit('advance');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
getContainer() {
|
||||
return this.wrapper;
|
||||
}
|
||||
getCurrentLine() {
|
||||
return this.lines[this.currentLineIndex];
|
||||
}
|
||||
}
|
3
framework/ui/text/interfaces/playable-sound.d.ts
vendored
Normal file
3
framework/ui/text/interfaces/playable-sound.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface IPlayableSound {
|
||||
play(): any;
|
||||
}
|
1
framework/ui/text/interfaces/playable-sound.js
Normal file
1
framework/ui/text/interfaces/playable-sound.js
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
7
framework/ui/text/interfaces/sound-set.d.ts
vendored
Normal file
7
framework/ui/text/interfaces/sound-set.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import { IPlayableSound } from './playable-sound';
|
||||
export interface SoundSet {
|
||||
open?: IPlayableSound;
|
||||
close?: IPlayableSound;
|
||||
scroll?: IPlayableSound;
|
||||
characterAppear?: IPlayableSound;
|
||||
}
|
1
framework/ui/text/interfaces/sound-set.js
Normal file
1
framework/ui/text/interfaces/sound-set.js
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
8
framework/ui/text/keyboard-manager.d.ts
vendored
Normal file
8
framework/ui/text/keyboard-manager.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { ScrollingText } from '.';
|
||||
export declare class KeyboardManager {
|
||||
private scrollingText;
|
||||
constructor(scrollingText: ScrollingText);
|
||||
init(): void;
|
||||
release(): void;
|
||||
private handler;
|
||||
}
|
25
framework/ui/text/keyboard-manager.js
Normal file
25
framework/ui/text/keyboard-manager.js
Normal file
@@ -0,0 +1,25 @@
|
||||
export class KeyboardManager {
|
||||
constructor(scrollingText) {
|
||||
this.scrollingText = scrollingText;
|
||||
}
|
||||
init() {
|
||||
this.scrollingText
|
||||
.getContainer()
|
||||
.addEventListener('keydown', (event) => this.handler(event));
|
||||
}
|
||||
release() {
|
||||
this.scrollingText
|
||||
.getContainer()
|
||||
.removeEventListener('keydown', (event) => this.handler(event));
|
||||
}
|
||||
handler(event) {
|
||||
switch (event.key) {
|
||||
case 'Enter':
|
||||
event.preventDefault();
|
||||
this.scrollingText.getCurrentLine().getAdvanceButton().click();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
13
framework/ui/text/line.d.ts
vendored
Normal file
13
framework/ui/text/line.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
export declare class Line extends EventEmitter {
|
||||
private text;
|
||||
private container;
|
||||
private textField;
|
||||
private advanceButton;
|
||||
private active;
|
||||
constructor(text: string);
|
||||
getDOMNode(): HTMLElement;
|
||||
display(element: HTMLElement, appearingCharacters?: boolean, appearingCharacterSpeed?: number): void;
|
||||
private fillText;
|
||||
getAdvanceButton(): HTMLElement;
|
||||
}
|
45
framework/ui/text/line.js
Normal file
45
framework/ui/text/line.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import * as EventEmitter from 'eventemitter3';
|
||||
export class Line extends EventEmitter {
|
||||
constructor(text) {
|
||||
super();
|
||||
this.text = text;
|
||||
this.active = false;
|
||||
}
|
||||
getDOMNode() {
|
||||
this.container = document.createElement('div');
|
||||
this.container.setAttribute('aria-role', 'polite');
|
||||
this.textField = document.createElement('div');
|
||||
this.container.appendChild(this.textField);
|
||||
this.advanceButton = document.createElement('button');
|
||||
this.advanceButton.textContent = 'Advance';
|
||||
this.advanceButton.addEventListener('click', (event) => {
|
||||
this.emit('advance');
|
||||
this.active = false;
|
||||
});
|
||||
this.container.appendChild(this.advanceButton);
|
||||
return this.container;
|
||||
}
|
||||
display(element, appearingCharacters = false, appearingCharacterSpeed = 0) {
|
||||
this.active = true;
|
||||
this.textField.focus();
|
||||
if (!appearingCharacters) {
|
||||
this.textField.textContent = this.text;
|
||||
}
|
||||
else {
|
||||
this.fillText(0, appearingCharacterSpeed);
|
||||
}
|
||||
}
|
||||
fillText(index, speed) {
|
||||
if (!this.active)
|
||||
return;
|
||||
if (index > this.text.length) {
|
||||
return;
|
||||
}
|
||||
this.textField.textContent += this.text.charAt(index);
|
||||
this.emit('character.appear', this.textField.textContent);
|
||||
setTimeout(() => this.fillText((index += 1), speed), speed);
|
||||
}
|
||||
getAdvanceButton() {
|
||||
return this.advanceButton;
|
||||
}
|
||||
}
|
13
framework/ui/text/sound-manager.d.ts
vendored
Normal file
13
framework/ui/text/sound-manager.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { SoundSet } from './interfaces/sound-set';
|
||||
import { ScrollingText } from '.';
|
||||
export declare class SoundManager {
|
||||
private instance;
|
||||
private soundSet;
|
||||
constructor(instance: ScrollingText, soundSet: SoundSet);
|
||||
setSoundSet(soundSet: SoundSet): void;
|
||||
init(): void;
|
||||
private handleOpen;
|
||||
private handleCharacterAppear;
|
||||
private handleAdvance;
|
||||
private handleClose;
|
||||
}
|
27
framework/ui/text/sound-manager.js
Normal file
27
framework/ui/text/sound-manager.js
Normal file
@@ -0,0 +1,27 @@
|
||||
export class SoundManager {
|
||||
constructor(instance, soundSet) {
|
||||
this.instance = instance;
|
||||
this.soundSet = soundSet;
|
||||
}
|
||||
setSoundSet(soundSet) {
|
||||
this.soundSet = soundSet;
|
||||
}
|
||||
init() {
|
||||
this.instance.on('character.appear', this.handleCharacterAppear.bind(this));
|
||||
this.instance.on('open', this.handleOpen.bind(this));
|
||||
this.instance.on('close', this.handleClose.bind(this));
|
||||
this.instance.on('advance', this.handleAdvance.bind(this));
|
||||
}
|
||||
handleOpen() {
|
||||
this.soundSet.open && this.soundSet.open.play();
|
||||
}
|
||||
handleCharacterAppear() {
|
||||
this.soundSet.characterAppear && this.soundSet.characterAppear.play();
|
||||
}
|
||||
handleAdvance() {
|
||||
this.soundSet.scroll && this.soundSet.scroll.play();
|
||||
}
|
||||
handleClose() {
|
||||
this.soundSet.close && this.soundSet.close.play();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user