refactore: create feature modules (#387)
* add @angular/cli to the project * increase nodejs version -> 8.2.1 * add lerna * merge web and shared module * move electron module into packages as uhk-agent Electron agent functionality is not working * delete symlinker * convert private properties to public of component if used in html * revert uhk-message.component * fix component path * fix the correct name of the uhk-message.component.scss * building web and electron module * delete uhk-renderer package * handle device connect disconnect state * add privilege detection * fix set privilege functionality * turn back download keymap functionality * add bootstrap, select2 js and fix null pointer exception * turn back upload data to keyboard * fix send keymap * fix test-serializer * add missing package.json * merging * fix appveyor build * fix linting * turn back electron storage service * commit the missing electron-datastorage-repository * update node to 8.3.0 in .nvmrc and log node version in appveyor build * set exact version number in appveyor build * vertical align privilege and missing device components * set back node version to 8 in appveyor * move node-usb dependency from usb dir to root maybe it is fix the appveyor build * revert usb to root * fix electron builder script * fix electron builder script * turn off electron devtools * remove CTRL+U functionality * fix CTRL+o * fix lint error * turnoff store freeze * start process when got `Error: EPERM: operation not permitted` error * move files from root usb dir -> packages/usb
This commit is contained in:
committed by
László Monda
parent
97770f67c0
commit
0f558e4132
@@ -0,0 +1 @@
|
||||
export { MacroActionEditorComponent } from './macro-action-editor.component';
|
||||
@@ -0,0 +1,46 @@
|
||||
<div class="action--editor">
|
||||
<div class="row">
|
||||
<div class="col-lg-3 editor__tab-links">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li #macroText [class.active]="activeTab === TabName.Text" (click)="selectTab(TabName.Text)">
|
||||
<a>
|
||||
<i class="fa fa-font"></i>
|
||||
<span>Type text</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #macroKeypress [class.active]="activeTab === TabName.Keypress" (click)="selectTab(TabName.Keypress)">
|
||||
<a>
|
||||
<i class="fa fa-keyboard-o"></i>
|
||||
<span>Key action</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #macroMouse [class.active]="activeTab === TabName.Mouse" (click)="selectTab(TabName.Mouse)">
|
||||
<a>
|
||||
<i class="fa fa-mouse-pointer"></i>
|
||||
<span>Mouse action</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #macroDelay [class.active]="activeTab === TabName.Delay" (click)="selectTab(TabName.Delay)">
|
||||
<a>
|
||||
<i class="fa fa-clock-o"></i>
|
||||
<span>Delay</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-12 col-lg-9 editor__tabs" [ngSwitch]="activeTab">
|
||||
<macro-text-tab #tab *ngSwitchCase="TabName.Text" [macroAction]="editableMacroAction"></macro-text-tab>
|
||||
<macro-key-tab #tab *ngSwitchCase="TabName.Keypress" [macroAction]="editableMacroAction"></macro-key-tab>
|
||||
<macro-mouse-tab #tab *ngSwitchCase="TabName.Mouse" [macroAction]="editableMacroAction"></macro-mouse-tab>
|
||||
<macro-delay-tab #tab *ngSwitchCase="TabName.Delay" [macroAction]="editableMacroAction"></macro-delay-tab>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 flex-button-wrapper editor__actions-container">
|
||||
<div class="editor__actions">
|
||||
<button class="btn btn-sm btn-default flex-button" type="button" (click)="onCancelClick()"> Cancel </button>
|
||||
<button class="btn btn-sm btn-primary flex-button" type="button" (click)="onSaveClick()"> Save </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,88 @@
|
||||
@import '../../../../styles/variables';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.action--editor {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.nav {
|
||||
padding-bottom: 1rem;
|
||||
|
||||
li {
|
||||
a {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
|
||||
&.selected {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
z-index: 2;
|
||||
|
||||
a {
|
||||
&.selected {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
top: 0;
|
||||
right: -4rem;
|
||||
border-color: transparent transparent transparent $icon-hover;
|
||||
border-style: solid;
|
||||
border-width: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.editor {
|
||||
&__tabs,
|
||||
&__tab-links {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
border-left: 1px solid #ddd;
|
||||
margin-left: -1.6rem;
|
||||
padding-left: 3rem;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
float: right;
|
||||
|
||||
&-container {
|
||||
background: #f5f5f5;
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flex-button-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.flex-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
|
||||
import {
|
||||
MacroAction,
|
||||
DelayMacroAction,
|
||||
KeyMacroAction,
|
||||
ScrollMouseMacroAction,
|
||||
MoveMouseMacroAction,
|
||||
MouseButtonMacroAction,
|
||||
TextMacroAction,
|
||||
Helper as MacroActionHelper
|
||||
} from '../../../config-serializer/config-items/macro-action';
|
||||
import { MacroDelayTabComponent, MacroMouseTabComponent, MacroKeyTabComponent, MacroTextTabComponent } from './tab';
|
||||
|
||||
enum TabName {
|
||||
Keypress,
|
||||
Text,
|
||||
Mouse,
|
||||
Delay
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'macro-action-editor',
|
||||
templateUrl: './macro-action-editor.component.html',
|
||||
styleUrls: ['./macro-action-editor.component.scss'],
|
||||
host: { 'class': 'macro-action-editor' }
|
||||
})
|
||||
export class MacroActionEditorComponent implements OnInit {
|
||||
@Input() macroAction: MacroAction;
|
||||
|
||||
@Output() save = new EventEmitter<MacroAction>();
|
||||
@Output() cancel = new EventEmitter<void>();
|
||||
|
||||
@ViewChild('tab') selectedTab: MacroTextTabComponent | MacroKeyTabComponent | MacroMouseTabComponent | MacroDelayTabComponent;
|
||||
|
||||
editableMacroAction: MacroAction;
|
||||
activeTab: TabName;
|
||||
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
|
||||
TabName = TabName;
|
||||
/* tslint:enable:variable-name */
|
||||
|
||||
ngOnInit() {
|
||||
this.updateEditableMacroAction();
|
||||
const tab: TabName = this.getTabName(this.editableMacroAction);
|
||||
this.activeTab = tab;
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.ngOnInit();
|
||||
}
|
||||
|
||||
onCancelClick(): void {
|
||||
this.cancel.emit();
|
||||
}
|
||||
|
||||
onSaveClick(): void {
|
||||
try {
|
||||
// TODO: Refactor after getKeyMacroAction has been added to all tabs
|
||||
const action = this.selectedTab instanceof MacroKeyTabComponent ?
|
||||
this.selectedTab.getKeyMacroAction() :
|
||||
this.selectedTab.macroAction;
|
||||
this.save.emit(action);
|
||||
} catch (e) {
|
||||
// TODO: show error dialog
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
selectTab(tab: TabName): void {
|
||||
this.activeTab = tab;
|
||||
if (tab === this.getTabName(this.macroAction)) {
|
||||
this.updateEditableMacroAction();
|
||||
} else {
|
||||
this.editableMacroAction = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
getTabName(action: MacroAction): TabName {
|
||||
if (action instanceof DelayMacroAction) {
|
||||
return TabName.Delay;
|
||||
} else if (action instanceof TextMacroAction) {
|
||||
return TabName.Text;
|
||||
} else if (action instanceof KeyMacroAction) {
|
||||
return TabName.Keypress;
|
||||
} else if (action instanceof MouseButtonMacroAction ||
|
||||
action instanceof MoveMouseMacroAction ||
|
||||
action instanceof ScrollMouseMacroAction) {
|
||||
return TabName.Mouse;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private updateEditableMacroAction() {
|
||||
const macroAction: MacroAction = this.macroAction ? this.macroAction : new TextMacroAction();
|
||||
this.editableMacroAction = MacroActionHelper.createMacroAction(macroAction);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { MacroDelayTabComponent } from './macro-delay.component';
|
||||
@@ -0,0 +1,26 @@
|
||||
<div class="macro-delay">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<h4>Enter delay in seconds</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-4">
|
||||
<input #macroDelayInput
|
||||
type="number"
|
||||
min="0"
|
||||
max="1000"
|
||||
step="0.1"
|
||||
placeholder="Delay amount"
|
||||
class="form-control"
|
||||
[attr.value]="delay"
|
||||
(change)="setDelay($event)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row macro-delay__presets">
|
||||
<div class="col-xs-12">
|
||||
<h6>Choose a preset</h6>
|
||||
<button *ngFor="let delay of presets" class="btn btn-sm btn-default" (click)="setDelay(delay)">{{delay}}s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,16 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.macro-delay {
|
||||
&__presets {
|
||||
margin-top: 1rem;
|
||||
|
||||
button {
|
||||
margin-right: 0.25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
OnInit,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
|
||||
import { DelayMacroAction } from '../../../../../config-serializer/config-items/macro-action';
|
||||
|
||||
const INITIAL_DELAY = 0.5; // In seconds
|
||||
|
||||
@Component({
|
||||
selector: 'macro-delay-tab',
|
||||
templateUrl: './macro-delay.component.html',
|
||||
styleUrls: ['./macro-delay.component.scss'],
|
||||
host: { 'class': 'macro__delay' },
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MacroDelayTabComponent implements OnInit {
|
||||
@Input() macroAction: DelayMacroAction;
|
||||
@ViewChild('macroDelayInput') input: ElementRef;
|
||||
|
||||
delay: number;
|
||||
presets: number[] = [0.3, 0.5, 0.8, 1, 2, 3, 4, 5];
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.macroAction) {
|
||||
this.macroAction = new DelayMacroAction();
|
||||
}
|
||||
this.delay = this.macroAction.delay > 0 ? this.macroAction.delay / 1000 : INITIAL_DELAY;
|
||||
}
|
||||
|
||||
setDelay(value: number): void {
|
||||
this.delay = value;
|
||||
this.macroAction.delay = this.delay * 1000;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export { MacroDelayTabComponent } from './delay';
|
||||
export { MacroKeyTabComponent } from './key';
|
||||
export { MacroMouseTabComponent } from './mouse';
|
||||
export { MacroTextTabComponent } from './text';
|
||||
@@ -0,0 +1 @@
|
||||
export { MacroKeyTabComponent } from './macro-key.component';
|
||||
@@ -0,0 +1,32 @@
|
||||
<div class="col-xs-12 macro-key__container">
|
||||
<div class="col-xs-3 macro-key__types">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li #keyMove [class.active]="activeTab === TabName.Keypress" (click)="selectTab(TabName.Keypress)">
|
||||
<a>
|
||||
<i class="fa fa-hand-pointer-o"></i>
|
||||
<span>Press key</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #keyHold [class.active]="activeTab === TabName.Hold" (click)="selectTab(TabName.Hold)">
|
||||
<a>
|
||||
<i class="fa fa-hand-rock-o"></i>
|
||||
<span>Hold key</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #keyRelease [class.active]="activeTab === TabName.Release" (click)="selectTab(TabName.Release)">
|
||||
<a>
|
||||
<i class="fa fa-hand-paper-o"></i>
|
||||
<span>Release key</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-9 macro-key__action-container">
|
||||
<div class="macro-key__action">
|
||||
<h4 *ngIf="activeTab === TabName.Keypress">Press key</h4>
|
||||
<h4 *ngIf="activeTab === TabName.Hold">Hold key</h4>
|
||||
<h4 *ngIf="activeTab === TabName.Release">Release key</h4>
|
||||
<keypress-tab #keypressTab [defaultKeyAction]="defaultKeyAction" [longPressEnabled]="false"></keypress-tab>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
.macro-key {
|
||||
&__container {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&__types {
|
||||
margin-left: 0;
|
||||
padding: 0 0 1rem;
|
||||
}
|
||||
|
||||
&__action {
|
||||
&-container {
|
||||
margin-top: -1rem;
|
||||
padding-top: 1rem;
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
padding-left: 3rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
min-width: 14px;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||
|
||||
import { KeystrokeAction } from '../../../../../config-serializer/config-items/key-action';
|
||||
import { KeyMacroAction, MacroSubAction } from '../../../../../config-serializer/config-items/macro-action';
|
||||
import { KeypressTabComponent, Tab } from '../../../../popover/tab';
|
||||
|
||||
enum TabName {
|
||||
Keypress,
|
||||
Hold,
|
||||
Release
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'macro-key-tab',
|
||||
templateUrl: './macro-key.component.html',
|
||||
styleUrls: [
|
||||
'../../macro-action-editor.component.scss',
|
||||
'./macro-key.component.scss'
|
||||
],
|
||||
host: { 'class': 'macro__mouse' }
|
||||
})
|
||||
export class MacroKeyTabComponent implements OnInit {
|
||||
@Input() macroAction: KeyMacroAction;
|
||||
@ViewChild('tab') selectedTab: Tab;
|
||||
@ViewChild('keypressTab') keypressTab: KeypressTabComponent;
|
||||
|
||||
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
|
||||
TabName = TabName;
|
||||
/* tslint:enable:variable-name */
|
||||
activeTab: TabName;
|
||||
defaultKeyAction: KeystrokeAction;
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.macroAction) {
|
||||
this.macroAction = new KeyMacroAction();
|
||||
}
|
||||
this.defaultKeyAction = new KeystrokeAction(<any>this.macroAction);
|
||||
this.selectTab(this.getTabName(this.macroAction));
|
||||
}
|
||||
|
||||
selectTab(tab: TabName): void {
|
||||
this.activeTab = tab;
|
||||
}
|
||||
|
||||
getTabName(macroAction: KeyMacroAction): TabName {
|
||||
if (!macroAction.action) {
|
||||
return TabName.Keypress;
|
||||
} else if (macroAction.action === MacroSubAction.hold) {
|
||||
return TabName.Hold;
|
||||
} else if (macroAction.action === MacroSubAction.release) {
|
||||
return TabName.Release;
|
||||
}
|
||||
}
|
||||
|
||||
getActionType(tab: TabName): MacroSubAction {
|
||||
switch (tab) {
|
||||
case TabName.Keypress:
|
||||
return MacroSubAction.press;
|
||||
case TabName.Hold:
|
||||
return MacroSubAction.hold;
|
||||
case TabName.Release:
|
||||
return MacroSubAction.release;
|
||||
default:
|
||||
throw new Error('Invalid tab type');
|
||||
}
|
||||
}
|
||||
|
||||
getKeyMacroAction(): KeyMacroAction {
|
||||
const keyMacroAction = Object.assign(new KeyMacroAction(), this.keypressTab.toKeyAction());
|
||||
keyMacroAction.action = this.getActionType(this.activeTab);
|
||||
return keyMacroAction;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { MacroMouseTabComponent } from './macro-mouse.component';
|
||||
@@ -0,0 +1,77 @@
|
||||
<div class="col-xs-12 macro-mouse__container">
|
||||
<div class="col-xs-3 macro-mouse__types">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li #mouseMove [class.active]="activeTab === TabName.Move" (click)="selectTab(TabName.Move)">
|
||||
<a>
|
||||
<i class="fa fa-arrows"></i>
|
||||
<span>Move pointer</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #mouseScroll [class.active]="activeTab === TabName.Scroll" (click)="selectTab(TabName.Scroll)">
|
||||
<a>
|
||||
<i class="fa fa-arrows-v"></i>
|
||||
<span>Scroll</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #mouseClick [class.active]="activeTab === TabName.Click" (click)="selectTab(TabName.Click)">
|
||||
<a>
|
||||
<i class="fa fa-mouse-pointer"></i>
|
||||
<span>Click button</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #mouseHold [class.active]="activeTab === TabName.Hold" (click)="selectTab(TabName.Hold)">
|
||||
<a>
|
||||
<i class="fa fa-hand-rock-o"></i>
|
||||
<span>Hold button</span>
|
||||
</a>
|
||||
</li>
|
||||
<li #mouseRelease [class.active]="activeTab === TabName.Release" (click)="selectTab(TabName.Release)">
|
||||
<a>
|
||||
<i class="fa fa-hand-paper-o"></i>
|
||||
<span>Release button</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-9 macro-mouse__actions" [ngSwitch]="activeTab">
|
||||
<div #tab *ngSwitchCase="TabName.Move">
|
||||
<h4>Move pointer</h4>
|
||||
<p>Use negative values to move down or left from current position.</p>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label for="move-mouse-x">X</label>
|
||||
<input id="move-mouse-x" type="number" class="form-control" [(ngModel)]="macroAction['x']"> pixels
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="move-mouse-y">Y</label>
|
||||
<input id="move-mouse-y" type="number" class="form-control" [(ngModel)]="macroAction['y']"> pixels
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div #tab *ngSwitchCase="TabName.Scroll">
|
||||
<h4>Scroll</h4>
|
||||
<p>Use negative values to move down or left from current position.</p>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label for="scroll-mouse-x">X</label>
|
||||
<input id="scroll-mouse-x" type="number" class="form-control" [(ngModel)]="macroAction['x']"> pixels
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="scroll-mouse-y">Y</label>
|
||||
<input id="scroll-mouse-y" type="number" class="form-control" [(ngModel)]="macroAction['y']"> pixels
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div #tab *ngIf="activeTab === TabName.Click || activeTab === TabName.Hold || activeTab === TabName.Release">
|
||||
<h4 *ngIf="activeTab === TabName.Click">Click mouse button</h4>
|
||||
<h4 *ngIf="activeTab === TabName.Hold">Hold mouse button</h4>
|
||||
<h4 *ngIf="activeTab === TabName.Release">Release mouse button</h4>
|
||||
<div class="btn-group macro-mouse__buttons">
|
||||
<button *ngFor="let buttonLabel of buttonLabels; let buttonIndex = index"
|
||||
class="btn btn-default"
|
||||
[class.btn-primary]="hasButton(buttonIndex)"
|
||||
(click)="setMouseClick(buttonIndex)">{{buttonLabel}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,43 @@
|
||||
.macro-mouse {
|
||||
&__container {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&__types {
|
||||
border-right: 1px solid #ddd;
|
||||
border-left: 0;
|
||||
margin-top: -1rem;
|
||||
margin-left: 0;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
padding-left: 3rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
min-width: 14px;
|
||||
}
|
||||
|
||||
.form-horizontal {
|
||||
.form-group {
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
display: inline-block;
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import { Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||
|
||||
import {
|
||||
MouseButtonMacroAction,
|
||||
MoveMouseMacroAction,
|
||||
ScrollMouseMacroAction,
|
||||
MacroSubAction
|
||||
} from '../../../../../config-serializer/config-items/macro-action';
|
||||
import { Tab } from '../../../../popover/tab';
|
||||
|
||||
type MouseMacroAction = MouseButtonMacroAction | MoveMouseMacroAction | ScrollMouseMacroAction;
|
||||
|
||||
enum TabName {
|
||||
Move,
|
||||
Scroll,
|
||||
Click,
|
||||
Hold,
|
||||
Release
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'macro-mouse-tab',
|
||||
templateUrl: './macro-mouse.component.html',
|
||||
styleUrls: [
|
||||
'../../macro-action-editor.component.scss',
|
||||
'./macro-mouse.component.scss'
|
||||
],
|
||||
host: { 'class': 'macro__mouse' }
|
||||
})
|
||||
export class MacroMouseTabComponent implements OnInit {
|
||||
@Input() macroAction: MouseMacroAction;
|
||||
@ViewChild('tab') selectedTab: Tab;
|
||||
|
||||
/* tslint:disable:variable-name: It is an enum type. So it can start with uppercase. */
|
||||
TabName = TabName;
|
||||
/* tslint:enable:variable-name */
|
||||
activeTab: TabName;
|
||||
buttonLabels: string[];
|
||||
private selectedButtons: boolean[];
|
||||
|
||||
constructor() {
|
||||
this.buttonLabels = ['Left', 'Middle', 'Right'];
|
||||
this.selectedButtons = Array(this.buttonLabels.length).fill(false);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.macroAction) {
|
||||
this.macroAction = new MouseButtonMacroAction();
|
||||
this.macroAction.action = MacroSubAction.press;
|
||||
}
|
||||
const tabName = this.getTabName(this.macroAction);
|
||||
this.selectTab(tabName);
|
||||
const buttonActions = [TabName.Click, TabName.Hold, TabName.Release];
|
||||
if (buttonActions.includes(this.activeTab)) {
|
||||
this.selectedButtons = (<MouseButtonMacroAction>this.macroAction).getMouseButtons();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.ngOnInit();
|
||||
}
|
||||
|
||||
selectTab(tab: TabName): void {
|
||||
this.activeTab = tab;
|
||||
|
||||
if (tab === this.getTabName(this.macroAction)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (tab) {
|
||||
case TabName.Scroll:
|
||||
this.macroAction = new ScrollMouseMacroAction();
|
||||
break;
|
||||
case TabName.Move:
|
||||
this.macroAction = new MoveMouseMacroAction();
|
||||
break;
|
||||
default:
|
||||
this.macroAction = new MouseButtonMacroAction();
|
||||
this.macroAction.action = this.getAction(tab);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setMouseClick(index: number): void {
|
||||
this.selectedButtons[index] = !this.selectedButtons[index];
|
||||
(<MouseButtonMacroAction>this.macroAction).setMouseButtons(this.selectedButtons);
|
||||
}
|
||||
|
||||
hasButton(index: number): boolean {
|
||||
return this.selectedButtons[index];
|
||||
}
|
||||
|
||||
getAction(tab: TabName): MacroSubAction {
|
||||
switch (tab) {
|
||||
case TabName.Click:
|
||||
return MacroSubAction.press;
|
||||
case TabName.Hold:
|
||||
return MacroSubAction.hold;
|
||||
case TabName.Release:
|
||||
return MacroSubAction.release;
|
||||
default:
|
||||
throw new Error(`Invalid tab name: ${TabName[tab]}`);
|
||||
}
|
||||
}
|
||||
|
||||
getTabName(action: MouseMacroAction): TabName {
|
||||
if (action instanceof MouseButtonMacroAction) {
|
||||
if (!action.action || action.isOnlyPressAction()) {
|
||||
return TabName.Click;
|
||||
} else if (action.isOnlyHoldAction()) {
|
||||
return TabName.Hold;
|
||||
} else if (action.isOnlyReleaseAction()) {
|
||||
return TabName.Release;
|
||||
}
|
||||
} else if (action instanceof MoveMouseMacroAction) {
|
||||
return TabName.Move;
|
||||
} else if (action instanceof ScrollMouseMacroAction) {
|
||||
return TabName.Scroll;
|
||||
}
|
||||
return TabName.Move;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { MacroTextTabComponent } from './macro-text.component';
|
||||
@@ -0,0 +1,5 @@
|
||||
<div>
|
||||
<h4>Type text</h4>
|
||||
<p>Input the text you want to type with this macro action.</p>
|
||||
<textarea #macroTextInput name="macro-text" (change)="onTextChange()" class="macro__text-input">{{ macroAction.text }}</textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.macro__text-input {
|
||||
width: 100%;
|
||||
min-height: 10rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
OnInit,
|
||||
AfterViewInit,
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
Renderer,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
|
||||
import { TextMacroAction } from '../../../../../config-serializer/config-items/macro-action';
|
||||
|
||||
@Component({
|
||||
selector: 'macro-text-tab',
|
||||
templateUrl: './macro-text.component.html',
|
||||
styleUrls: ['./macro-text.component.scss'],
|
||||
host: { 'class': 'macro__text' }
|
||||
})
|
||||
export class MacroTextTabComponent implements OnInit, AfterViewInit {
|
||||
@Input() macroAction: TextMacroAction;
|
||||
@ViewChild('macroTextInput') input: ElementRef;
|
||||
|
||||
constructor(private renderer: Renderer) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.macroAction) {
|
||||
this.macroAction = new TextMacroAction();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.renderer.invokeElementMethod(this.input.nativeElement, 'focus');
|
||||
}
|
||||
|
||||
onTextChange() {
|
||||
this.macroAction.text = this.input.nativeElement.value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<ng-template [ngIf]="macro">
|
||||
<macro-header
|
||||
[macro]="macro"
|
||||
[isNew]="isNew"
|
||||
></macro-header>
|
||||
<macro-list
|
||||
[macro]="macro"
|
||||
(add)="addAction($event.macroId, $event.action)"
|
||||
(edit)="editAction($event.macroId, $event.index, $event.action)"
|
||||
(delete)="deleteAction($event.macroId, $event.index, $event.action)"
|
||||
(reorder)="reorderAction($event.macroId, $event.oldIndex, $event.newIndex)"
|
||||
></macro-list>
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="!macro" class="not-found">
|
||||
There is no macro with id {{ route.params.select('id') | async }}.
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
:host {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.not-found {
|
||||
margin-top: 30px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import 'rxjs/add/operator/pluck';
|
||||
|
||||
import { Macro } from '../../../config-serializer/config-items/macro';
|
||||
import { MacroAction } from '../../../config-serializer/config-items/macro-action/macro-action';
|
||||
|
||||
import { MacroActions } from '../../../store/actions';
|
||||
import { AppState } from '../../../store/index';
|
||||
import { getMacro } from '../../../store/reducers/user-configuration';
|
||||
|
||||
@Component({
|
||||
selector: 'macro-edit',
|
||||
templateUrl: './macro-edit.component.html',
|
||||
styleUrls: ['./macro-edit.component.scss'],
|
||||
host: {
|
||||
'class': 'container-fluid'
|
||||
}
|
||||
})
|
||||
export class MacroEditComponent implements OnDestroy {
|
||||
macro: Macro;
|
||||
isNew: boolean;
|
||||
|
||||
private subscription: Subscription;
|
||||
constructor(private store: Store<AppState>, public route: ActivatedRoute) {
|
||||
this.subscription = route
|
||||
.params
|
||||
.pluck<{}, string>('id')
|
||||
.switchMap((id: string) => store.let(getMacro(+id)))
|
||||
.subscribe((macro: Macro) => {
|
||||
this.macro = macro;
|
||||
});
|
||||
|
||||
this.isNew = this.route.snapshot.params['empty'] === 'new';
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
addAction(macroId: number, action: MacroAction) {
|
||||
this.store.dispatch(MacroActions.addMacroAction(macroId, action));
|
||||
}
|
||||
|
||||
editAction(macroId: number, index: number, action: MacroAction) {
|
||||
this.store.dispatch(MacroActions.saveMacroAction(macroId, index, action));
|
||||
}
|
||||
|
||||
deleteAction(macroId: number, index: number, action: MacroAction) {
|
||||
this.store.dispatch(MacroActions.deleteMacroAction(macroId, index, action));
|
||||
}
|
||||
|
||||
reorderAction(macroId: number, oldIndex: number, newIndex: number) {
|
||||
this.store.dispatch(MacroActions.reorderMacroAction(macroId, oldIndex, newIndex));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<uhk-header>
|
||||
<div class="row">
|
||||
<h1 class="col-xs-12 pane-title">
|
||||
<i class="fa fa-play"></i>
|
||||
<input #macroName cancelable
|
||||
class="pane-title__name"
|
||||
type="text"
|
||||
(change)="editMacroName($event.target.value)"
|
||||
(keyup.enter)="macroName.blur()"
|
||||
/>
|
||||
<i class="glyphicon glyphicon-trash macro__remove pull-right" title=""
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
html="true"
|
||||
data-original-title="<span class='text-nowrap'>Delete macro</span>"
|
||||
(click)="removeMacro()"
|
||||
></i>
|
||||
<i class="fa fa-files-o macro__duplicate pull-right" title=""
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
data-original-title="Duplicate macro"
|
||||
(click)="duplicateMacro()"
|
||||
></i>
|
||||
</h1>
|
||||
</div>
|
||||
</uhk-header>
|
||||
@@ -0,0 +1,43 @@
|
||||
@import '../../../../styles/variables';
|
||||
|
||||
.macro {
|
||||
&__remove {
|
||||
font-size: 0.75em;
|
||||
top: 8px;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $icon-hover-delete;
|
||||
}
|
||||
}
|
||||
|
||||
&__duplicate {
|
||||
font-size: 0.75em;
|
||||
top: 7px;
|
||||
margin-right: 15px;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $icon-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pane-title {
|
||||
margin-bottom: 1em;
|
||||
|
||||
&__name {
|
||||
border: none;
|
||||
border-bottom: 2px dotted #999;
|
||||
padding: 0;
|
||||
margin: 0 0.25rem;
|
||||
width: 330px;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1px #ccc, 0 0 5px 0 #ccc;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
ElementRef,
|
||||
Input,
|
||||
OnChanges,
|
||||
Renderer2,
|
||||
SimpleChanges,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { Macro } from '../../../config-serializer/config-items/macro';
|
||||
|
||||
import { MacroActions } from '../../../store/actions';
|
||||
import { AppState } from '../../../store/index';
|
||||
|
||||
@Component({
|
||||
selector: 'macro-header',
|
||||
templateUrl: './macro-header.component.html',
|
||||
styleUrls: ['./macro-header.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class MacroHeaderComponent implements AfterViewInit, OnChanges {
|
||||
@Input() macro: Macro;
|
||||
@Input() isNew: boolean;
|
||||
@ViewChild('macroName') macroName: ElementRef;
|
||||
|
||||
constructor(private store: Store<AppState>, private renderer: Renderer2) { }
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (this.isNew) {
|
||||
this.setFocusOnName();
|
||||
}
|
||||
if (changes['macro']) {
|
||||
this.setName();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
if (this.isNew) {
|
||||
this.setFocusOnName();
|
||||
}
|
||||
}
|
||||
|
||||
removeMacro() {
|
||||
this.store.dispatch(MacroActions.removeMacro(this.macro.id));
|
||||
}
|
||||
|
||||
duplicateMacro() {
|
||||
this.store.dispatch(MacroActions.duplicateMacro(this.macro));
|
||||
}
|
||||
|
||||
editMacroName(name: string) {
|
||||
if (name.length === 0) {
|
||||
this.setName();
|
||||
return;
|
||||
}
|
||||
|
||||
this.store.dispatch(MacroActions.editMacroName(this.macro.id, name));
|
||||
}
|
||||
|
||||
private setFocusOnName() {
|
||||
this.macroName.nativeElement.select();
|
||||
}
|
||||
|
||||
private setName(): void {
|
||||
this.renderer.setProperty(this.macroName.nativeElement, 'value', this.macro.name);
|
||||
}
|
||||
|
||||
}
|
||||
8
packages/uhk-web/src/app/components/macro/index.ts
Normal file
8
packages/uhk-web/src/app/components/macro/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export * from './edit/macro-edit.component';
|
||||
export * from './list/macro-list.component';
|
||||
export * from './header/macro-header.component';
|
||||
export * from './macro.routes';
|
||||
export * from './not-found';
|
||||
export * from './item';
|
||||
export * from './action-editor';
|
||||
export * from './action-editor/tab';
|
||||
1
packages/uhk-web/src/app/components/macro/item/index.ts
Normal file
1
packages/uhk-web/src/app/components/macro/item/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { MacroItemComponent } from './macro-item.component';
|
||||
@@ -0,0 +1,17 @@
|
||||
<div class="list-group-item action--item" [class.is-editing]="editing">
|
||||
<span *ngIf="movable" class="glyphicon glyphicon-option-vertical action--movable" aria-hidden="true"></span>
|
||||
<div class="action--item--wrap" [class.pointer]="editable" (click)="editAction()">
|
||||
<icon [name]="iconName"></icon>
|
||||
<div class="action--title">{{ title }}</div>
|
||||
<icon *ngIf="editable && macroAction && !editing" name="pencil"></icon>
|
||||
</div>
|
||||
<icon *ngIf="deletable" name="trash" (click)="deleteAction()"></icon>
|
||||
</div>
|
||||
<div class="list-group-item macro-action-editor__container"
|
||||
[@toggler]="((editable && editing) || newItem) ? 'active' : 'inactive'">
|
||||
<macro-action-editor
|
||||
[macroAction]="macroAction"
|
||||
(cancel)="cancelEdit()"
|
||||
(save)="saveEditedAction($event)">
|
||||
</macro-action-editor>
|
||||
</div>
|
||||
@@ -0,0 +1,86 @@
|
||||
@import '../../../../styles/variables';
|
||||
|
||||
:host {
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
|
||||
&.macro-item:first-of-type {
|
||||
.list-group-item {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.macro-item:last-of-type {
|
||||
.list-group-item {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.gu-transit {
|
||||
opacity: 0.2;
|
||||
|
||||
.list-group-item {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
&--item {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
|
||||
icon {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&.is-editing {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
&--wrap {
|
||||
justify-content: space-between;
|
||||
|
||||
&.pointer {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
color: $icon-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--title {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&--movable {
|
||||
&:hover {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.macro-action-editor__container {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
import { Component, Input, Output, EventEmitter, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
|
||||
import { KeyModifiers } from '../../../config-serializer/config-items/key-modifiers';
|
||||
import {
|
||||
DelayMacroAction,
|
||||
KeyMacroAction,
|
||||
MacroAction,
|
||||
MouseButtonMacroAction,
|
||||
MoveMouseMacroAction,
|
||||
ScrollMouseMacroAction,
|
||||
TextMacroAction
|
||||
} from '../../../config-serializer/config-items/macro-action';
|
||||
|
||||
import { MapperService } from '../../../services/mapper.service';
|
||||
|
||||
@Component({
|
||||
animations: [
|
||||
trigger('toggler', [
|
||||
state('inactive', style({
|
||||
height: '0px'
|
||||
})),
|
||||
state('active', style({
|
||||
height: '*'
|
||||
})),
|
||||
transition('inactive <=> active', animate('500ms ease-out'))
|
||||
])
|
||||
],
|
||||
selector: 'macro-item',
|
||||
templateUrl: './macro-item.component.html',
|
||||
styleUrls: ['./macro-item.component.scss'],
|
||||
host: { 'class': 'macro-item' }
|
||||
})
|
||||
export class MacroItemComponent implements OnInit, OnChanges {
|
||||
@Input() macroAction: MacroAction;
|
||||
@Input() editable: boolean;
|
||||
@Input() deletable: boolean;
|
||||
@Input() movable: boolean;
|
||||
|
||||
@Output() save = new EventEmitter<MacroAction>();
|
||||
@Output() cancel = new EventEmitter<void>();
|
||||
@Output() edit = new EventEmitter<void>();
|
||||
@Output() delete = new EventEmitter<void>();
|
||||
|
||||
title: string;
|
||||
iconName: string;
|
||||
editing: boolean;
|
||||
newItem: boolean = false;
|
||||
|
||||
constructor(private mapper: MapperService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.updateView();
|
||||
if (!this.macroAction) {
|
||||
this.editing = true;
|
||||
this.newItem = true;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['macroAction']) {
|
||||
this.updateView();
|
||||
}
|
||||
}
|
||||
|
||||
saveEditedAction(editedAction: MacroAction): void {
|
||||
this.macroAction = editedAction;
|
||||
this.editing = false;
|
||||
this.updateView();
|
||||
this.save.emit(editedAction);
|
||||
}
|
||||
|
||||
editAction(): void {
|
||||
if (!this.editable || this.editing) {
|
||||
this.cancelEdit();
|
||||
return;
|
||||
}
|
||||
|
||||
this.editing = true;
|
||||
this.edit.emit();
|
||||
}
|
||||
|
||||
cancelEdit(): void {
|
||||
this.editing = false;
|
||||
this.cancel.emit();
|
||||
}
|
||||
|
||||
deleteAction(): void {
|
||||
this.delete.emit();
|
||||
}
|
||||
|
||||
private updateView(): void {
|
||||
if (!this.macroAction) {
|
||||
this.title = 'New macro action';
|
||||
} else if (this.macroAction instanceof DelayMacroAction) {
|
||||
// Delay
|
||||
this.iconName = 'clock';
|
||||
const action: DelayMacroAction = this.macroAction as DelayMacroAction;
|
||||
const delay = action.delay > 0 ? action.delay / 1000 : 0;
|
||||
this.title = `Delay of ${delay}s`;
|
||||
} else if (this.macroAction instanceof TextMacroAction) {
|
||||
// Write text
|
||||
const action: TextMacroAction = this.macroAction as TextMacroAction;
|
||||
this.iconName = 'font';
|
||||
this.title = `Write text: ${action.text}`;
|
||||
} else if (this.macroAction instanceof KeyMacroAction) {
|
||||
// Key pressed/held/released
|
||||
const action: KeyMacroAction = this.macroAction as KeyMacroAction;
|
||||
this.setKeyActionContent(action);
|
||||
} else if (this.macroAction instanceof MouseButtonMacroAction) {
|
||||
// Mouse button clicked/held/released
|
||||
const action: MouseButtonMacroAction = this.macroAction as MouseButtonMacroAction;
|
||||
this.setMouseButtonActionContent(action);
|
||||
} else if (this.macroAction instanceof MoveMouseMacroAction || this.macroAction instanceof ScrollMouseMacroAction) {
|
||||
// Mouse moved or scrolled
|
||||
this.setMouseMoveScrollActionContent(this.macroAction);
|
||||
} else {
|
||||
this.title = this.macroAction.constructor.name;
|
||||
}
|
||||
}
|
||||
|
||||
private setKeyActionContent(action: KeyMacroAction): void {
|
||||
if (!action.hasScancode() && !action.hasModifiers()) {
|
||||
this.title = 'Invalid keypress';
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.isPressAction()) {
|
||||
// Press key
|
||||
this.iconName = 'hand-pointer';
|
||||
this.title = 'Press key: ';
|
||||
} else if (action.isHoldAction()) {
|
||||
// Hold key
|
||||
this.iconName = 'hand-rock';
|
||||
this.title = 'Hold key: ';
|
||||
} else if (action.isReleaseAction()) {
|
||||
// Release key
|
||||
this.iconName = 'hand-paper';
|
||||
this.title = 'Release key: ';
|
||||
}
|
||||
|
||||
if (action.hasScancode()) {
|
||||
const scancode: string = (this.mapper.scanCodeToText(action.scancode, action.type) || ['Unknown']).join(' ');
|
||||
if (scancode) {
|
||||
this.title += scancode;
|
||||
}
|
||||
}
|
||||
|
||||
if (action.hasModifiers()) {
|
||||
// Press/hold/release modifiers
|
||||
for (let i = KeyModifiers.leftCtrl; i <= KeyModifiers.rightGui; i <<= 1) {
|
||||
if (action.isModifierActive(i)) {
|
||||
this.title += ' ' + KeyModifiers[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setMouseMoveScrollActionContent(action: MacroAction): void {
|
||||
let typedAction: any;
|
||||
if (action instanceof MoveMouseMacroAction) {
|
||||
// Move mouse pointer
|
||||
this.iconName = 'mouse-pointer';
|
||||
this.title = 'Move pointer';
|
||||
typedAction = this.macroAction as MoveMouseMacroAction;
|
||||
} else {
|
||||
// Scroll mouse
|
||||
this.iconName = 'mouse-pointer';
|
||||
this.title = 'Scroll';
|
||||
typedAction = this.macroAction as ScrollMouseMacroAction;
|
||||
}
|
||||
|
||||
let needAnd: boolean;
|
||||
if (Math.abs(typedAction.x) !== 0) {
|
||||
this.title += ` by ${Math.abs(typedAction.x)}px ${typedAction.x > 0 ? 'left' : 'right'}`;
|
||||
needAnd = true;
|
||||
}
|
||||
if (Math.abs(typedAction.y) !== 0) {
|
||||
this.title += ` ${needAnd ? 'and' : 'by'} ${Math.abs(typedAction.y)}px ${typedAction.y > 0 ? 'down' : 'up'}`;
|
||||
}
|
||||
}
|
||||
|
||||
private setMouseButtonActionContent(action: MouseButtonMacroAction): void {
|
||||
// Press/hold/release mouse buttons
|
||||
if (action.isOnlyPressAction()) {
|
||||
this.iconName = 'mouse-pointer';
|
||||
this.title = 'Click mouse button: ';
|
||||
} else if (action.isOnlyHoldAction()) {
|
||||
this.iconName = 'hand-rock';
|
||||
this.title = 'Hold mouse button: ';
|
||||
} else if (action.isOnlyReleaseAction()) {
|
||||
this.iconName = 'hand-paper';
|
||||
this.title = 'Release mouse button: ';
|
||||
}
|
||||
|
||||
const buttonLabels: string[] = ['Left', 'Middle', 'Right'];
|
||||
const selectedButtons: boolean[] = action.getMouseButtons();
|
||||
const selectedButtonLabels: string[] = [];
|
||||
selectedButtons.forEach((isSelected, idx) => {
|
||||
if (isSelected && buttonLabels[idx]) {
|
||||
selectedButtonLabels.push(buttonLabels[idx]);
|
||||
}
|
||||
});
|
||||
this.title += selectedButtonLabels.join(', ');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<div class="row list-container">
|
||||
<div class="col-xs-10 col-xs-offset-1 list-group">
|
||||
<div class="macro-actions-container" [dragula]="'macroActions'" [dragulaModel]="macro.macroActions">
|
||||
<macro-item *ngFor="let macroAction of macro.macroActions; let macroActionIndex = index"
|
||||
[macroAction]="macroAction"
|
||||
[editable]="true"
|
||||
[deletable]="true"
|
||||
[movable]="true"
|
||||
(save)="saveAction($event, macroActionIndex)"
|
||||
(edit)="editAction(macroActionIndex)"
|
||||
(cancel)="cancelAction()"
|
||||
(delete)="deleteAction(macroAction, macroActionIndex)"
|
||||
[attr.data-index]="macroActionIndex"
|
||||
></macro-item>
|
||||
|
||||
<macro-item *ngIf="showNew"
|
||||
[@togglerNew]="showNew ? 'active' : 'inactive'"
|
||||
[macroAction]="newMacro"
|
||||
[editable]="true"
|
||||
[deletable]="false"
|
||||
[movable]="false"
|
||||
(save)="addNewAction($event)"
|
||||
(cancel)="hideNewAction()"
|
||||
></macro-item>
|
||||
</div>
|
||||
<div class="list-group add-new__action-container" [@toggler]="(!showNew) ? 'active' : 'inactive'">
|
||||
<div class="list-group-item action--item add-new__action-item no-reorder clearfix">
|
||||
<a class="add-new__action-item--link" (click)="showNewAction()">
|
||||
<i class="fa fa-plus"></i> Add macro action
|
||||
</a>
|
||||
<a class="add-new__action-item--link">
|
||||
<i class="fa fa fa-circle"></i> Add captured keystroke
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,153 @@
|
||||
@import '../../../../styles/variables';
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.list-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.main-wrapper {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.action {
|
||||
&--edit__form {
|
||||
background-color: #fff;
|
||||
margin-left: -0.5rem;
|
||||
margin-right: -15px;
|
||||
margin-top: 15px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
&--item {
|
||||
padding-left: 8px;
|
||||
|
||||
&.active,
|
||||
&.active:hover {
|
||||
background-color: white;
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
border-color: black;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-group {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.macro__name {
|
||||
border-bottom: 2px dotted #999;
|
||||
padding: 0 0.5rem;
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
|
||||
.macro-settings {
|
||||
border: 1px solid black;
|
||||
border-top-color: #999;
|
||||
z-index: 100;
|
||||
|
||||
.helper {
|
||||
position: absolute;
|
||||
display: block;
|
||||
height: 13px;
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: -14px;
|
||||
}
|
||||
}
|
||||
|
||||
.action--item.active.callout,
|
||||
.macro-settings.callout {
|
||||
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.macro-actions-container {
|
||||
margin-bottom: 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.list-group-item .move-handle:hover {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.flex-button-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.flex-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.add-new__action-container {
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.add-new__action-item {
|
||||
border-radius: 0 0 4px 4px;
|
||||
border-top: 0;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&--link {
|
||||
width: 50%;
|
||||
float: left;
|
||||
padding: 10px 5px;
|
||||
text-align: center;
|
||||
color: $icon-hover;
|
||||
|
||||
&:first-of-type {
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background: #e6e6e6;
|
||||
}
|
||||
}
|
||||
|
||||
.fa-circle {
|
||||
color: #c00;
|
||||
}
|
||||
}
|
||||
|
||||
// Dragula styles
|
||||
.gu {
|
||||
&-mirror {
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
z-index: 9999;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-unselectable {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { Component, EventEmitter, Input, Output, QueryList, ViewChildren, forwardRef } from '@angular/core';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
|
||||
import { DragulaService } from 'ng2-dragula/ng2-dragula';
|
||||
|
||||
import { Macro } from '../../../config-serializer/config-items/macro';
|
||||
import { MacroAction } from '../../../config-serializer/config-items/macro-action';
|
||||
import { MacroItemComponent } from '../item';
|
||||
|
||||
@Component({
|
||||
animations: [
|
||||
trigger('toggler', [
|
||||
state('inactive', style({
|
||||
height: '0px'
|
||||
})),
|
||||
state('active', style({
|
||||
height: '*'
|
||||
})),
|
||||
transition('inactive <=> active', animate('500ms ease-out'))
|
||||
]),
|
||||
trigger('togglerNew', [
|
||||
state('void', style({
|
||||
height: '0px'
|
||||
})),
|
||||
state('active', style({
|
||||
height: '*'
|
||||
})),
|
||||
transition(':enter', animate('500ms ease-out')),
|
||||
transition(':leave', animate('500ms ease-out'))
|
||||
])
|
||||
],
|
||||
selector: 'macro-list',
|
||||
templateUrl: './macro-list.component.html',
|
||||
styleUrls: ['./macro-list.component.scss'],
|
||||
viewProviders: [DragulaService]
|
||||
})
|
||||
export class MacroListComponent {
|
||||
@Input() macro: Macro;
|
||||
@ViewChildren(forwardRef(() => MacroItemComponent)) macroItems: QueryList<MacroItemComponent>;
|
||||
|
||||
@Output() add = new EventEmitter();
|
||||
@Output() edit = new EventEmitter();
|
||||
@Output() delete = new EventEmitter();
|
||||
@Output() reorder = new EventEmitter();
|
||||
|
||||
newMacro: Macro = undefined;
|
||||
showNew: boolean = false;
|
||||
private activeEdit: number = undefined;
|
||||
private dragIndex: number;
|
||||
|
||||
constructor(dragulaService: DragulaService) {
|
||||
/* tslint:disable:no-unused-variable: Used by Dragula. */
|
||||
dragulaService.setOptions('macroActions', {
|
||||
moves: function (el: any, container: any, handle: any) {
|
||||
return handle.className.includes('action--movable');
|
||||
}
|
||||
});
|
||||
|
||||
dragulaService.drag.subscribe((value: any) => {
|
||||
this.dragIndex = +value[1].getAttribute('data-index');
|
||||
});
|
||||
|
||||
dragulaService.drop.subscribe((value: any) => {
|
||||
if (value[4]) {
|
||||
this.reorder.emit({
|
||||
macroId: this.macro.id,
|
||||
oldIndex: this.dragIndex,
|
||||
newIndex: +value[4].getAttribute('data-index')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
showNewAction() {
|
||||
this.hideActiveEditor();
|
||||
|
||||
this.newMacro = undefined;
|
||||
this.showNew = true;
|
||||
}
|
||||
|
||||
hideNewAction() {
|
||||
this.showNew = false;
|
||||
}
|
||||
|
||||
addNewAction(macroAction: MacroAction) {
|
||||
this.add.emit({
|
||||
macroId: this.macro.id,
|
||||
action: macroAction
|
||||
});
|
||||
|
||||
this.newMacro = undefined;
|
||||
this.showNew = false;
|
||||
}
|
||||
|
||||
editAction(index: number) {
|
||||
// Hide other editors when clicking edit button of a macro action
|
||||
this.hideActiveEditor();
|
||||
this.showNew = false;
|
||||
this.activeEdit = index;
|
||||
}
|
||||
|
||||
cancelAction() {
|
||||
this.activeEdit = undefined;
|
||||
}
|
||||
|
||||
saveAction(macroAction: MacroAction, index: number) {
|
||||
this.edit.emit({
|
||||
macroId: this.macro.id,
|
||||
index: index,
|
||||
action: macroAction
|
||||
});
|
||||
|
||||
this.hideActiveEditor();
|
||||
}
|
||||
|
||||
deleteAction(macroAction: MacroAction, index: number) {
|
||||
this.delete.emit({
|
||||
macroId: this.macro.id,
|
||||
index: index,
|
||||
action: macroAction
|
||||
});
|
||||
|
||||
this.hideActiveEditor();
|
||||
}
|
||||
|
||||
private hideActiveEditor() {
|
||||
if (this.activeEdit !== undefined) {
|
||||
this.macroItems.toArray()[this.activeEdit].cancelEdit();
|
||||
}
|
||||
}
|
||||
}
|
||||
20
packages/uhk-web/src/app/components/macro/macro.routes.ts
Normal file
20
packages/uhk-web/src/app/components/macro/macro.routes.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
import { MacroEditComponent } from './edit/macro-edit.component';
|
||||
import { MacroNotFoundComponent, MacroNotFoundGuard } from './not-found';
|
||||
|
||||
export const macroRoutes: Routes = [
|
||||
{
|
||||
path: 'macro',
|
||||
component: MacroNotFoundComponent,
|
||||
canActivate: [MacroNotFoundGuard]
|
||||
},
|
||||
{
|
||||
path: 'macro/:id',
|
||||
component: MacroEditComponent
|
||||
},
|
||||
{
|
||||
path: 'macro/:id/:empty',
|
||||
component: MacroEditComponent
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,2 @@
|
||||
export { MacroNotFoundComponent } from './macro-not-found.component';
|
||||
export { MacroNotFoundGuard } from './macro-not-found-guard.service';
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CanActivate, Router } from '@angular/router';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import 'rxjs/add/operator/let';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
import { AppState } from '../../../store/index';
|
||||
import { getMacros } from '../../../store/reducers/user-configuration';
|
||||
import { Macro } from '../../../config-serializer/config-items/macro';
|
||||
|
||||
@Injectable()
|
||||
export class MacroNotFoundGuard implements CanActivate {
|
||||
|
||||
constructor(private store: Store<AppState>, private router: Router) { }
|
||||
|
||||
canActivate(): Observable<boolean> {
|
||||
return this.store
|
||||
.let(getMacros())
|
||||
.map((macros: Macro[]) => {
|
||||
const hasMacros = macros.length > 0;
|
||||
if (hasMacros) {
|
||||
this.router.navigate(['/macro', macros[0].id]);
|
||||
}
|
||||
return !hasMacros;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="container-fluid">
|
||||
<uhk-header>
|
||||
<h1> </h1>
|
||||
</uhk-header>
|
||||
<div class="not-found">
|
||||
You don't have any macros. Try to add one!
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
.not-found {
|
||||
margin-top: 30px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'macro-not-found',
|
||||
templateUrl: './macro-not-found.component.html',
|
||||
styleUrls: ['./macro-not-found.component.scss']
|
||||
})
|
||||
export class MacroNotFoundComponent { }
|
||||
Reference in New Issue
Block a user