Keymap data layer (#95)

This commit is contained in:
Nejc Zdovc
2016-09-18 12:38:42 +02:00
committed by József Farkas
parent fb8cd163ec
commit b78bc5850f
40 changed files with 748 additions and 195 deletions

View File

@@ -10,23 +10,26 @@
<input type="text" class="form-control" placeholder="Search ..." (input)="filterKeyboards($event.target.value)">
</div>
<div class="keymap__search_amount">
{{ keymaps.length }} / {{ keymapsAll.length }} keymaps shown
{{ (presets$ | async).length }} / {{ (presetsAll$ | async).length }} keymaps shown
</div>
</div>
<div class="keymap__list">
<div #keyboard class="keymap__list_item" *ngFor="let keymap of keymaps">
<div #keyboard class="keymap__list_item" *ngFor="let keymap of presets$ | async">
<h2>{{ keymap.name }}</h2>
<p class="keymap__description">
{{ keymap.description }}
</p>
<svg-keyboard-wrap
[layers]="keymap.layers.elements"
[keymap]="keymap"
[popoverEnabled]="false"
[tooltipEnabled]="true"
>
</svg-keyboard-wrap>
<div class="btn-group btn-group-lg">
<button class="btn btn-default">Add keymap</button>
<button class="btn btn-default" (click)="addKeymap(keymap)">Add keymap</button>
</div>
</div>
</div>
<div *ngIf="(presets$ | async).length === 0">
Sorry, no keyboard found under this search query.
</div>

View File

@@ -1,8 +1,13 @@
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import 'rxjs/add/operator/publishReplay';
import { Observable } from 'rxjs/Observable';
import { ConnectableObservable } from 'rxjs/observable/ConnectableObservable';
import { Keymap } from '../../../config-serializer/config-items/Keymap';
import { Keymaps } from '../../../config-serializer/config-items/Keymaps';
import {DataProviderService} from '../../../services/data-provider.service';
import { AppState } from '../../../store';
import { KeymapActions } from '../../../store/actions';
@Component({
selector: 'keymap-add',
@@ -10,19 +15,32 @@ import {DataProviderService} from '../../../services/data-provider.service';
styles: [require('./keymap-add.component.scss')]
})
export class KeymapAddComponent {
private keymaps: Keymap[];
private keymapsAll: Keymap[];
private currentKeyboards: number;
private presets$: Observable<Keymap[]>;
private presetsAll$: Observable<Keymap[]>;
constructor(data: DataProviderService) {
let json: any = data.getDefaultKeymaps();
let all: Keymaps = new Keymaps().fromJsObject(json.keymaps);
this.keymaps = all.elements;
this.keymapsAll = this.keymaps.slice(0);
this.currentKeyboards = this.keymaps.length;
constructor(private store: Store<AppState>) {
let presetConnectable: ConnectableObservable<Keymap[]> = store
.select((appState: AppState) => appState.presetKeymaps)
.publishReplay();
this.presets$ = presetConnectable;
presetConnectable.connect();
this.presetsAll$ = store.select((appState: AppState) => appState.presetKeymaps);
}
filterKeyboards(value: string) {
this.keymaps = this.keymapsAll.filter((item: Keymap) => item.name.toLocaleLowerCase().indexOf(value) !== -1);
let presetConnectable: ConnectableObservable<Keymap[]> = this.presetsAll$
.map((keymaps: Keymap[]) => keymaps.filter(
(keymap: Keymap) => keymap.name.toLocaleLowerCase().includes(value))
)
.publishReplay();
this.presets$ = presetConnectable;
presetConnectable.connect();
}
addKeymap(keymap: Keymap) {
this.store.dispatch(KeymapActions.addKeymap(keymap));
}
}

View File

@@ -0,0 +1,30 @@
<div class="row">
<h1 class="col-xs-12 pane-title">
<i class="fa fa-keyboard-o"></i>
<input class="keymap__name pane-title__name"
value="{{ keymap.name }}"
(change)="editKeymapName($event.target.value)"
/> keymap
(<input class="keymap__abbrev pane-title__abbrev"
value="{{ keymap.abbreviation }}"
(change)="editKeymapAbbr($event.target.value)"
[attr.maxLength]="3"
/>)
<i class="fa keymap__is-default"
[ngClass]="{'fa-star-o': !keymap.isDefault, 'fa-star': keymap.isDefault}"
(click)="setDefault()"
></i>
<i class="glyphicon glyphicon-trash keymap__remove pull-right" title=""
data-toggle="tooltip"
data-placement="left"
data-original-title="Remove keymap"
(click)="removeKeymap()"
></i>
<i class="fa fa-files-o keymap__duplicate pull-right" title=""
data-toggle="tooltip"
data-placement="left"
data-original-title="Duplicate keymap"
(click)="duplicateKeymap()"
></i>
</h1>
</div>

View File

@@ -0,0 +1,61 @@
.keymap {
&__is-default {
&.fa-star-o {
cursor: pointer;
&:hover {
color: #b9b6b6;
}
}
}
&__remove {
font-size: 0.75em;
top: 8px;
&:hover {
cursor: pointer;
color: #900;
}
}
&__duplicate {
font-size: 0.75em;
top: 7px;
margin-right: 15px;
position: relative;
&:hover {
cursor: pointer;
color: #900;
}
}
}
.pane-title {
margin-bottom: 1em;
&__name,
&__abbrev {
border: none;
border-bottom: 2px dotted #999;
padding: 0;
margin: 0 0.25rem;
&:focus {
box-shadow: 0 0 0 1px #ccc, 0 0 5px 0 #ccc;
border-color: transparent;
}
}
&__name {
width: 290px;
text-overflow: ellipsis;
}
&__abbrev {
width: 90px;
text-align: center;
}
}

View File

@@ -0,0 +1,44 @@
import { Component, Input } from '@angular/core';
import { Store } from '@ngrx/store';
import { Keymap } from '../../../config-serializer/config-items/Keymap';
import { AppState } from '../../../store';
import { KeymapActions } from '../../../store/actions';
@Component({
selector: 'keymap-header',
template: require('./keymap-header.component.html'),
styles: [require('./keymap-header.component.scss')]
})
export class KeymapHeaderComponent {
@Input() keymap: Keymap;
constructor(
private store: Store<AppState>
) { }
setDefault() {
if (!this.keymap.isDefault) {
this.store.dispatch(KeymapActions.setDefault(this.keymap.abbreviation));
}
}
removeKeymap() {
this.store.dispatch(KeymapActions.removeKeymap(this.keymap.abbreviation));
}
duplicateKeymap() {
this.store.dispatch(KeymapActions.duplicateKeymap(this.keymap));
}
editKeymapName(name: string) {
this.store.dispatch(KeymapActions.editKeymapName(this.keymap.abbreviation, name));
}
editKeymapAbbr(newAbbr: string) {
newAbbr = newAbbr.toUpperCase();
this.store.dispatch(KeymapActions.editKeymapAbbr(this.keymap.abbreviation, newAbbr));
}
}

View File

@@ -1,3 +1,4 @@
export * from './keymap.component';
export * from './keymap.routes';
export * from './add/keymap-add.component';
export * from './header/keymap-header.component';

View File

@@ -1,13 +1,8 @@
<template [ngIf]="keymap">
<div class="row">
<h1 class="col-xs-12 pane-title">
<i class="fa fa-keyboard-o"></i>
<span class="keymap__name pane-title__name" contenteditable="true">{{keymap.name}}</span> keymap
(<span class="keymap__abbrev pane-title__abbrev" contenteditable="true">{{keymap.abbreviation}}</span>)
<i class="fa fa-star-o fa-star keymap__is-default"></i>
<i class="glyphicon glyphicon-trash keymap__remove pull-right" title=""
data-toggle="tooltip" data-placement="left" data-original-title="Remove keymap"></i>
</h1>
</div>
<svg-keyboard-wrap [layers]="layers.elements"></svg-keyboard-wrap>
</template>
<template [ngIf]="keymap$ | async">
<keymap-header [keymap]="keymap$ | async"></keymap-header>
<svg-keyboard-wrap [keymap]="keymap$ | async"></svg-keyboard-wrap>
</template>
<div *ngIf="!(keymap$ | async)" class="not-found">
Sorry, there is no keymap with this abbreviation.
</div>

View File

@@ -4,48 +4,9 @@
display: block;
}
.keymap {
&__is-default {
&.fa-star {
color: #333;
}
&.fa-star-o {
color: #333;
}
&:hover {
color: #555;
cursor: pointer;
}
}
&__remove {
font-size: 0.75em;
top: 0.3em;
&:hover {
cursor: pointer;
color: #900;
}
}
.not-found {
margin-top: 30px;
font-size: 16px;
text-align: center;
}
.pane-title {
margin-bottom: 1em;
&__name,
&__abbrev {
&[contenteditable=true] {
border: none;
border-bottom: 2px dotted #999;
padding: 0 0.5rem;
margin: 0 0.25rem;
&.active {
box-shadow: 0 0 0 1px #ccc, 0 0 5px 0 #ccc;
border-color: transparent;
}
}
}
}

View File

@@ -1,42 +1,38 @@
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import '@ngrx/core/add/operator/select';
import { Store } from '@ngrx/store';
import 'rxjs/add/operator/let';
import 'rxjs/add/operator/publishReplay';
import 'rxjs/add/operator/switchMap';
import { Observable } from 'rxjs/Observable';
import { ConnectableObservable } from 'rxjs/observable/ConnectableObservable';
import { Keymap } from '../../config-serializer/config-items/Keymap';
import { Layers } from '../../config-serializer/config-items/Layers';
import { UhkConfigurationService } from '../../services/uhk-configuration.service';
import { AppState } from '../../store';
import { getKeymap } from '../../store/reducers/keymap';
@Component({
selector: 'keymap',
template: require('./keymap.component.html'),
styles: [require('./keymap.component.scss')]
})
export class KeymapComponent implements OnInit {
private keymapId: number = 0;
private layers: Layers;
private keymap: Keymap;
private subParams: Subscription;
export class KeymapComponent {
private keymap$: Observable<Keymap>;
constructor(
private uhkConfigurationService: UhkConfigurationService,
private store: Store<AppState>,
private route: ActivatedRoute
) {
}
let keymapConnectable: ConnectableObservable<Keymap> = route
.params
.select<string>('abbr')
.switchMap((abbr: string) => store.let(getKeymap(abbr)))
.publishReplay();
ngOnInit() {
this.subParams = this.route.params.subscribe((params: { id: string }) => {
let id: number = +params.id;
if (!isNaN(id)) {
this.keymapId = id;
}
this.keymap = this.uhkConfigurationService.getUhkConfiguration().keymaps.elements[this.keymapId];
this.layers = this.keymap.layers;
});
}
ngOnDestroy() {
this.subParams.unsubscribe();
this.keymap$ = keymapConnectable;
keymapConnectable.connect();
}
}

View File

@@ -18,7 +18,7 @@ export const keymapRoutes: Routes = [
component: KeymapAddComponent
},
{
path: 'keymap/:id',
path: 'keymap/:abbr',
component: KeymapComponent
}
];

View File

@@ -46,7 +46,7 @@
<layer-tab #tab *ngSwitchCase="TabName.Layer" class="popover-content" [defaultKeyAction]="defaultKeyAction"></layer-tab>
<mouse-tab #tab *ngSwitchCase="TabName.Mouse" class="popover-content" [defaultKeyAction]="defaultKeyAction"></mouse-tab>
<macro-tab #tab *ngSwitchCase="TabName.Macro" class="popover-content" [defaultKeyAction]="defaultKeyAction"></macro-tab>
<keymap-tab #tab *ngSwitchCase="TabName.Keymap" class="popover-content" [defaultKeyAction]="defaultKeyAction"></keymap-tab>
<keymap-tab #tab *ngSwitchCase="TabName.Keymap" class="popover-content" [defaultKeyAction]="defaultKeyAction" [keymaps]="keymaps$ | async"></keymap-tab>
<none-tab #tab *ngSwitchCase="TabName.None" class="popover-content"></none-tab>
</div>
<div class="row">

View File

@@ -1,4 +1,6 @@
import {Component, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { Store } from '@ngrx/store';
import {
KeyAction,
@@ -8,7 +10,12 @@ import {
SwitchKeymapAction,
SwitchLayerAction
} from '../../config-serializer/config-items/key-action';
import {Tab} from './tab/tab';
import { Keymap } from '../../config-serializer/config-items/Keymap';
import { Tab } from './tab/tab';
import { AppState } from '../../store';
import { Observable } from 'rxjs/Observable';
enum TabName {
Keypress,
@@ -38,11 +45,15 @@ export class PopoverComponent implements OnInit {
private TabName = TabName;
/* tslint:enable:no-unused-variable tslint:enable:variable-name */
private activeTab: TabName;
private keymaps$: Observable<Keymap[]>;
constructor() { }
constructor(private store: Store<AppState>) {
this.keymaps$ = store.select((appState: AppState) => appState.keymaps);
}
ngOnInit() {
let tab: TabName;
if (this.defaultKeyAction instanceof KeystrokeAction) {
tab = TabName.Keypress;
} else if (this.defaultKeyAction instanceof SwitchLayerAction) {
@@ -56,6 +67,7 @@ export class PopoverComponent implements OnInit {
} else {
tab = TabName.None;
}
this.selectTab(tab);
}
@@ -76,5 +88,4 @@ export class PopoverComponent implements OnInit {
selectTab(tab: TabName): void {
this.activeTab = tab;
}
}

View File

@@ -1,12 +1,17 @@
<div>
<b>Switch to keymap:</b>
<select2 [data]="keymapOptions" [value]="keymapOptions[selectedKeymapIndex + 1].id" (valueChanged)="onChange($event)" [width]="'100%'"></select2>
<select2
[data]="keymapOptions"
[value]="selectedKeymap?.abbreviation"
(valueChanged)="onChange($event)"
[width]="'100%'"
></select2>
</div>
<div>
<div *ngIf="selectedKeymapIndex === -1">
<div *ngIf="!selectedKeymap?.abbreviation">
<img src="./images/base-layer--blank.svg">
</div>
<svg-keyboard *ngIf="selectedKeymapIndex >= 0"
[moduleConfig]="keymaps[selectedKeymapIndex].layers.elements[0].modules.elements">
<svg-keyboard *ngIf="selectedKeymap?.abbreviation"
[moduleConfig]="selectedKeymap.layers.elements[0].modules.elements">
</svg-keyboard>
</div>

View File

@@ -1,56 +1,55 @@
import {Component, Input, OnInit } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import {Select2OptionData} from 'ng2-select2/ng2-select2';
import { Select2OptionData } from 'ng2-select2/ng2-select2';
import {KeyAction, SwitchKeymapAction} from '../../../../config-serializer/config-items/key-action';
import {Keymap} from '../../../../config-serializer/config-items/Keymap';
import {Tab} from '../tab';
import {UhkConfigurationService} from '../../../../services/uhk-configuration.service';
import { KeyAction, SwitchKeymapAction } from '../../../../config-serializer/config-items/key-action';
import { Keymap } from '../../../../config-serializer/config-items/Keymap';
import { Tab } from '../tab';
@Component({
selector: 'keymap-tab',
template: require('./keymap-tab.component.html'),
styles: [require('./keymap-tab.component.scss')]
styles: [require('./keymap-tab.component.scss')],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class KeymapTabComponent implements OnInit, Tab {
@Input() defaultKeyAction: KeyAction;
@Input() keymaps: Keymap[];
private keymaps: Keymap[];
private keymapOptions: Array<Select2OptionData>;
private selectedKeymapIndex: number;
private selectedKeymap: Keymap;
constructor(private uhkConfigurationService: UhkConfigurationService) {
this.keymaps = [];
constructor() {
this.keymapOptions = [];
this.selectedKeymapIndex = -1;
}
ngOnInit() {
this.keymaps = this.uhkConfigurationService.getUhkConfiguration().keymaps.elements;
this.keymapOptions.push({
id: '-1',
text: 'Switch to keymap'
});
this.keymapOptions = this.keymapOptions.concat(this.keymaps.map(function (keymap: Keymap): Select2OptionData {
this.keymapOptions = this.keymaps.map((keymap: Keymap): Select2OptionData => {
return {
id: keymap.id.toString(),
id: keymap.abbreviation,
text: keymap.name
};
}));
});
this.fromKeyAction(this.defaultKeyAction);
}
// TODO: change to the correct type when the wrapper has added it.
onChange(event: any) {
this.selectedKeymapIndex = +event.value;
if (event.value === '-1') {
this.selectedKeymap = undefined;
} else {
this.selectedKeymap = this.keymaps.find((keymap: Keymap) => keymap.abbreviation === event.value);
}
}
keyActionValid(): boolean {
return this.selectedKeymapIndex >= 0;
return !!this.selectedKeymap;
}
fromKeyAction(keyAction: KeyAction): boolean {
@@ -58,16 +57,17 @@ export class KeymapTabComponent implements OnInit, Tab {
return false;
}
let switchKeymapAction: SwitchKeymapAction = <SwitchKeymapAction>keyAction;
this.selectedKeymapIndex = this.keymaps.findIndex(keymap => switchKeymapAction.keymapId === keymap.id);
return true;
this.selectedKeymap = this.keymaps
.find((keymap: Keymap) => keymap.abbreviation === switchKeymapAction.keymapAbbreviation);
}
toKeyAction(): SwitchKeymapAction {
if (!this.keyActionValid()) {
throw new Error('KeyAction is not valid. No selected keymap!');
}
let keymapAction = new SwitchKeymapAction();
keymapAction.keymapId = this.keymaps[this.selectedKeymapIndex].id;
keymapAction.keymapAbbreviation = this.selectedKeymap.abbreviation;
return keymapAction;
}
}

View File

@@ -8,9 +8,9 @@
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'keymap')"></i>
</div>
<ul [@toggler]="animation.keymap">
<li *ngFor="let keymap of keymaps" class="sidebar__level-2--item">
<li *ngFor="let keymap of keymaps$ | async" class="sidebar__level-2--item">
<div class="sidebar__level-2">
<a [routerLink]="['/keymap', keymap.id]">{{keymap.name}}</a>
<a [routerLink]="['/keymap', keymap.abbreviation]">{{keymap.name}}</a>
<i *ngIf="keymap.isDefault" class="fa fa-star sidebar__fav"></i>
</div>
</li>
@@ -25,7 +25,7 @@
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'macro')"></i>
</div>
<ul [@toggler]="animation.macro">
<li *ngFor="let macro of macros" class="sidebar__level-2--item">
<li *ngFor="let macro of macros$ | async" class="sidebar__level-2--item">
<div class="sidebar__level-2">
<a [routerLink]="['/macro', macro.id]">{{macro.name}}</a>
</div>

View File

@@ -1,9 +1,11 @@
import { Component, OnInit, Renderer, animate, state, style, transition, trigger } from '@angular/core';
import { Component, Renderer, animate, state, style, transition, trigger } from '@angular/core';
import { Keymap } from '../../config-serializer/config-items/Keymap';
import { Macro } from '../../config-serializer/config-items/Macro';
import { UhkConfigurationService } from '../../services/uhk-configuration.service';
import { AppState } from '../../store';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
@Component({
animations: [
@@ -21,22 +23,20 @@ import { UhkConfigurationService } from '../../services/uhk-configuration.servic
template: require('./side-menu.component.html'),
styles: [require('./side-menu.component.scss')]
})
export class SideMenuComponent implements OnInit {
private keymaps: Keymap[];
private macros: Macro[];
export class SideMenuComponent {
private keymaps$: Observable<Keymap[]>;
private macros$: Observable<Macro[]>;
private animation: {[key: string]: 'active' | 'inactive'};
constructor(private uhkConfigurationService: UhkConfigurationService, private renderer: Renderer) {
constructor(private store: Store<AppState>, private renderer: Renderer) {
this.animation = {
keymap: 'active',
macro: 'active',
addon: 'active'
};
}
ngOnInit() {
this.keymaps = this.uhkConfigurationService.getUhkConfiguration().keymaps.elements;
this.macros = this.uhkConfigurationService.getUhkConfiguration().macros.elements;
this.keymaps$ = store.select(appState => appState.keymaps);
this.macros$ = store.select(appState => appState.macros);
}
toggleHide(event: Event, type: string) {

View File

@@ -145,8 +145,7 @@ export class SvgKeyboardKeyComponent implements OnInit, OnChanges {
} else if (this.keyAction instanceof SwitchKeymapAction) {
let keyAction: SwitchKeymapAction = this.keyAction as SwitchKeymapAction;
this.labelType = LabelTypes.SwitchKeymap;
let uhkConfiguration: UhkConfiguration = this.uhkConfigurationService.getUhkConfiguration();
this.labelSource = uhkConfiguration.getKeymap(keyAction.keymapId).abbreviation;
this.labelSource = keyAction.keymapAbbreviation;
} else if (this.keyAction instanceof PlayMacroAction) {
let keyAction: PlayMacroAction = this.keyAction as PlayMacroAction;
this.labelType = LabelTypes.IconText;

View File

@@ -1,11 +1,17 @@
import {
Component, Input, OnChanges, OnInit, animate,
Component, Input, OnChanges, SimpleChanges, animate,
state, style, transition, trigger
} from '@angular/core';
import { Store } from '@ngrx/store';
import { KeyAction, NoneAction } from '../../../config-serializer/config-items/key-action';
import { Keymap } from '../../../config-serializer/config-items/Keymap';
import { Layer } from '../../../config-serializer/config-items/Layer';
import { AppState } from '../../../store';
import { KeymapActions } from '../../../store/actions';
@Component({
selector: 'svg-keyboard-wrap',
template: require('./svg-keyboard-wrap.component.html'),
@@ -74,8 +80,8 @@ import { Layer } from '../../../config-serializer/config-items/Layer';
])
]
})
export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
@Input() layers: Layer[];
export class SvgKeyboardWrapComponent implements OnChanges {
@Input() keymap: Keymap;
@Input() popoverEnabled: boolean = true;
@Input() tooltipEnabled: boolean = false;
@@ -84,8 +90,11 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
private popoverInitKeyAction: KeyAction;
private currentLayer: number = 0;
private tooltipData: { posTop: number, posLeft: number, content: {name: string, value: string}[], shown: boolean };
private layers: Layer[];
constructor() {
constructor(
private store: Store<AppState>
) {
this.keyEditConfig = {
keyActions: undefined,
keyId: undefined
@@ -99,15 +108,15 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
};
}
ngOnInit() {
this.layers[0].animation = 'leftIn';
}
ngOnChanges(changes: SimpleChanges) {
if (changes['keymap'].previousValue.abbreviation !== changes['keymap'].currentValue.abbreviation) {
this.layers = this.keymap.layers.elements;
this.currentLayer = 0;
ngOnChanges() {
this.currentLayer = 0;
if (this.layers.length > 0) {
this.layers.forEach(element => element.animation = 'none');
this.layers[0].animation = 'leftIn';
if (this.layers.length > 0) {
this.layers.forEach(element => element.animation = 'none');
this.layers[0].animation = 'leftIn';
}
}
}
@@ -137,6 +146,7 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
onRemap(keyAction: KeyAction): void {
this.changeKeyAction(keyAction);
this.store.dispatch(KeymapActions.saveKey(this.keymap));
this.hidePopover();
}