feat(keymap): add description to keymap (#559)
* feat(keymap): add description to keymaps * add new feature request * preserve new lines
This commit is contained in:
committed by
László Monda
parent
38f6688930
commit
02d57fdabf
@@ -0,0 +1,31 @@
|
||||
<div class="text-center">
|
||||
<span *ngIf="showPlaceholder"
|
||||
class="placeholder">
|
||||
<span (click)="editText()">{{ placeholder }}</span>
|
||||
</span>
|
||||
|
||||
<span *ngIf="showText"
|
||||
class="editable">
|
||||
<span (click)="editText()"
|
||||
[innerHtml]="displayText"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="editing">
|
||||
<textarea class="text-editor"
|
||||
[(ngModel)]="text"
|
||||
autofocus
|
||||
(keydown.control.enter)="keydownEnter()"
|
||||
(keydown.alt.enter)="keydownEnter()"></textarea>
|
||||
<div class="pull-right buttons">
|
||||
<button class="btn btn-danger"
|
||||
(click)="cancelEditText()">
|
||||
Cancel
|
||||
</button>
|
||||
<button class="btn btn-primary"
|
||||
(click)="saveText()"
|
||||
[disabled]="isSaveDisabled">
|
||||
Update description
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,26 @@
|
||||
:host {
|
||||
margin-top: 0.5em;
|
||||
|
||||
span.placeholder {
|
||||
color: gray;
|
||||
display: inline-block;
|
||||
|
||||
.glyphicon {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
span.editable,
|
||||
span.placeholder {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
textarea.text-editor {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, Input } from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'editable-text',
|
||||
templateUrl: './editable-text.component.html',
|
||||
styleUrls: ['./editable-text.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
{provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EditableTextComponent), multi: true}
|
||||
]
|
||||
})
|
||||
export class EditableTextComponent implements ControlValueAccessor {
|
||||
|
||||
@Input() placeholder = 'No editable content';
|
||||
text: string;
|
||||
originalText: string;
|
||||
editing = false;
|
||||
|
||||
get isSaveDisabled(): boolean {
|
||||
return !this.text || this.text.trim().length === 0;
|
||||
}
|
||||
|
||||
get displayText(): string {
|
||||
return this.text && this.text.replace(/\n/g, '<br>');
|
||||
}
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {
|
||||
|
||||
}
|
||||
|
||||
writeValue(obj: any): void {
|
||||
if (this.text === obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.text = obj;
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.textChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
}
|
||||
|
||||
saveText(): void {
|
||||
this.originalText = null;
|
||||
this.editing = false;
|
||||
this.textChange(this.text);
|
||||
}
|
||||
|
||||
editText(): void {
|
||||
this.originalText = this.text;
|
||||
this.editing = true;
|
||||
}
|
||||
|
||||
cancelEditText(): void {
|
||||
this.text = this.originalText;
|
||||
this.editing = false;
|
||||
}
|
||||
|
||||
keydownEnter(): void {
|
||||
if (this.isSaveDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.saveText();
|
||||
}
|
||||
|
||||
get showPlaceholder(): boolean {
|
||||
return !this.editing && !this.text;
|
||||
}
|
||||
|
||||
get showText(): boolean {
|
||||
return !this.editing && !!this.text;
|
||||
}
|
||||
|
||||
private textChange: any = () => {
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,10 @@
|
||||
[selectedKey]="selectedKey"
|
||||
[selected]="selectedKey?.layerId === index"
|
||||
[keyboardLayout]="keyboardLayout"
|
||||
[description]="description"
|
||||
(keyClick)="keyClick.emit($event)"
|
||||
(keyHover)="keyHover.emit($event)"
|
||||
(capture)="capture.emit($event)"
|
||||
(descriptionChanged)="descriptionChanged.emit($event)"
|
||||
>
|
||||
</svg-keyboard>
|
||||
|
||||
|
Before Width: | Height: | Size: 659 B After Width: | Height: | Size: 770 B |
@@ -1,4 +1,4 @@
|
||||
import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||
import { animate, keyframes, state, style, transition, trigger } from '@angular/animations';
|
||||
import { Layer } from 'uhk-common';
|
||||
|
||||
@@ -81,11 +81,14 @@ export class KeyboardSliderComponent implements OnChanges {
|
||||
@Input() halvesSplit: boolean;
|
||||
@Input() selectedKey: { layerId: number, moduleId: number, keyId: number };
|
||||
@Input() keyboardLayout = KeyboardLayout.ANSI;
|
||||
@Input() description: string;
|
||||
@Output() keyClick = new EventEmitter();
|
||||
@Output() keyHover = new EventEmitter();
|
||||
@Output() capture = new EventEmitter();
|
||||
@Output() descriptionChanged = new EventEmitter<string>();
|
||||
|
||||
layerAnimationState: AnimationKeyboard[];
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['layers']) {
|
||||
this.layerAnimationState = this.layers.map<AnimationKeyboard>(() => 'initOut');
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
(downloadClick)="downloadKeymap()"></keymap-header>
|
||||
<svg-keyboard-wrap [keymap]="keymap$ | async"
|
||||
[halvesSplit]="keyboardSplit"
|
||||
[keyboardLayout]="keyboardLayout$ | async"></svg-keyboard-wrap>
|
||||
[keyboardLayout]="keyboardLayout$ | async"
|
||||
(descriptionChanged)="descriptionChanged($event)"></svg-keyboard-wrap>
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="!(keymap$ | async)" class="not-found">
|
||||
|
||||
@@ -18,6 +18,8 @@ import { AppState, getKeyboardLayout } from '../../../store';
|
||||
import { getKeymap, getKeymaps, getUserConfiguration } from '../../../store/reducers/user-configuration';
|
||||
import { SvgKeyboardWrapComponent } from '../../svg/wrap';
|
||||
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
|
||||
import { KeymapActions } from '../../../store/actions';
|
||||
import { ChangeKeymapDescription } from '../../../models/ChangeKeymapDescription';
|
||||
|
||||
@Component({
|
||||
selector: 'keymap-edit',
|
||||
@@ -64,7 +66,7 @@ export class KeymapEditComponent {
|
||||
const keymap = latest[0];
|
||||
const exportableJSON = latest[1];
|
||||
const fileName = keymap.name + '_keymap.json';
|
||||
saveAs(new Blob([exportableJSON], { type: 'application/json' }), fileName);
|
||||
saveAs(new Blob([exportableJSON], {type: 'application/json'}), fileName);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -73,6 +75,10 @@ export class KeymapEditComponent {
|
||||
this.keyboardSplit = !this.keyboardSplit;
|
||||
}
|
||||
|
||||
descriptionChanged(event: ChangeKeymapDescription): void {
|
||||
this.store.dispatch(new KeymapActions.EditDescriptionAction(event));
|
||||
}
|
||||
|
||||
private toExportableJSON(keymap: Keymap): Observable<any> {
|
||||
return this.store
|
||||
.let(getUserConfiguration())
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" [attr.viewBox]="viewBox" height="100%" width="100%">
|
||||
<svg:g svg-module *ngFor="let module of modules; let i = index"
|
||||
[coverages]="module.coverages"
|
||||
[keyboardKeys]="module.keyboardKeys"
|
||||
[keybindAnimationEnabled]="keybindAnimationEnabled"
|
||||
[capturingEnabled]="capturingEnabled"
|
||||
[attr.transform]="module.attributes.transform"
|
||||
[keyActions]="moduleConfig[i].keyActions"
|
||||
[selectedKey]="selectedKey"
|
||||
[@split]="moduleAnimationStates[i]"
|
||||
[selected]="selectedKey?.moduleId === i"
|
||||
(keyClick)="onKeyClick(i, $event.index, $event.keyTarget)"
|
||||
(keyHover)="onKeyHover($event.index, $event.event, $event.over, i)"
|
||||
(capture)="onCapture(i, $event.index, $event.captured)"
|
||||
[coverages]="module.coverages"
|
||||
[keyboardKeys]="module.keyboardKeys"
|
||||
[keybindAnimationEnabled]="keybindAnimationEnabled"
|
||||
[capturingEnabled]="capturingEnabled"
|
||||
[attr.transform]="module.attributes.transform"
|
||||
[keyActions]="moduleConfig[i].keyActions"
|
||||
[selectedKey]="selectedKey"
|
||||
[@split]="moduleAnimationStates[i]"
|
||||
[selected]="selectedKey?.moduleId === i"
|
||||
(keyClick)="onKeyClick(i, $event.index, $event.keyTarget)"
|
||||
(keyHover)="onKeyHover($event.index, $event.event, $event.over, i)"
|
||||
(capture)="onCapture(i, $event.index, $event.captured)"
|
||||
/>
|
||||
</svg>
|
||||
<editable-text [ngModel]="description"
|
||||
(ngModelChange)="descriptionChanged.emit($event)"
|
||||
placeholder="No description provided for this keymap."></editable-text>
|
||||
|
||||
|
Before Width: | Height: | Size: 854 B After Width: | Height: | Size: 1.0 KiB |
@@ -1,5 +1,10 @@
|
||||
:host {
|
||||
display: flex;
|
||||
display: block;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
editable-text {
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,11 @@ export class SvgKeyboardComponent implements OnInit {
|
||||
@Input() selected: boolean;
|
||||
@Input() halvesSplit: boolean;
|
||||
@Input() keyboardLayout = KeyboardLayout.ANSI;
|
||||
@Input() description: string;
|
||||
@Output() keyClick = new EventEmitter();
|
||||
@Output() keyHover = new EventEmitter();
|
||||
@Output() capture = new EventEmitter();
|
||||
@Output() descriptionChanged = new EventEmitter<string>();
|
||||
|
||||
modules: SvgModule[];
|
||||
viewBox: string;
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
[selectedKey]="selectedKey"
|
||||
[halvesSplit]="halvesSplit"
|
||||
[keyboardLayout]="keyboardLayout"
|
||||
[description]="keymap.description"
|
||||
(keyClick)="onKeyClick($event.moduleId, $event.keyId, $event.keyTarget)"
|
||||
(keyHover)="onKeyHover($event.moduleId, $event.event, $event.over, $event.keyId)"
|
||||
(capture)="onCapture($event.moduleId, $event.keyId, $event.captured)"
|
||||
(descriptionChanged)="onDescriptionChanged($event)"
|
||||
></keyboard-slider>
|
||||
<popover tabindex="0" [visible]="popoverShown" [keyPosition]="keyPosition" [wrapPosition]="wrapPosition" [defaultKeyAction]="popoverInitKeyAction"
|
||||
[currentKeymap]="keymap" [currentLayer]="currentLayer" (cancel)="hidePopover()" (remap)="onRemap($event)"></popover>
|
||||
|
||||
@@ -2,14 +2,16 @@ import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
ElementRef,
|
||||
Renderer,
|
||||
EventEmitter,
|
||||
HostBinding,
|
||||
HostListener,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
SimpleChanges
|
||||
Output,
|
||||
Renderer,
|
||||
SimpleChanges,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
@@ -25,10 +27,10 @@ import {
|
||||
KeystrokeAction,
|
||||
Layer,
|
||||
LayerName,
|
||||
SecondaryRoleAction,
|
||||
MouseAction,
|
||||
MouseActionParam,
|
||||
PlayMacroAction,
|
||||
SecondaryRoleAction,
|
||||
SwitchKeymapAction,
|
||||
SwitchLayerAction
|
||||
} from 'uhk-common';
|
||||
@@ -38,6 +40,7 @@ import { AppState } from '../../../store';
|
||||
import { KeymapActions } from '../../../store/actions';
|
||||
import { PopoverComponent } from '../../popover';
|
||||
import { KeyboardLayout } from '../../../keyboard/keyboard-layout.enum';
|
||||
import { ChangeKeymapDescription } from '../../../models/ChangeKeymapDescription';
|
||||
|
||||
interface NameValuePair {
|
||||
name: string;
|
||||
@@ -56,6 +59,7 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
|
||||
@Input() tooltipEnabled: boolean = false;
|
||||
@Input() halvesSplit: boolean;
|
||||
@Input() keyboardLayout: KeyboardLayout.ANSI;
|
||||
@Output() descriptionChanged = new EventEmitter<ChangeKeymapDescription>();
|
||||
|
||||
@ViewChild(PopoverComponent, { read: ElementRef }) popover: ElementRef;
|
||||
|
||||
@@ -237,6 +241,13 @@ export class SvgKeyboardWrapComponent implements OnInit, OnChanges {
|
||||
return this.currentLayer;
|
||||
}
|
||||
|
||||
onDescriptionChanged(description: string): void {
|
||||
this.descriptionChanged.emit({
|
||||
description,
|
||||
abbr: this.keymap.abbreviation
|
||||
});
|
||||
}
|
||||
|
||||
private getKeyActionContent(keyAction: KeyAction): Observable<NameValuePair[]> {
|
||||
if (keyAction instanceof KeystrokeAction) {
|
||||
const keystrokeAction: KeystrokeAction = keyAction;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { AfterViewInit, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[autofocus]'
|
||||
})
|
||||
export class Autofocus implements AfterViewInit {
|
||||
constructor(private el: ElementRef) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.el.nativeElement.focus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ChangeKeymapDescription {
|
||||
abbr: string;
|
||||
description: string;
|
||||
}
|
||||
@@ -101,6 +101,8 @@ import { UhkDeviceLoadingGuard } from './services/uhk-device-loading.guard';
|
||||
import { UhkDeviceLoadedGuard } from './services/uhk-device-loaded.guard';
|
||||
import { XtermComponent } from './components/xterm/xterm.component';
|
||||
import { SliderWrapperComponent } from './components/slider-wrapper/slider-wrapper.component';
|
||||
import { EditableTextComponent } from './components/editable-text/editable-text.component';
|
||||
import { Autofocus } from './directives/autofocus/autofocus.directive';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -169,7 +171,9 @@ import { SliderWrapperComponent } from './components/slider-wrapper/slider-wrapp
|
||||
ProgressButtonComponent,
|
||||
LoadingDevicePageComponent,
|
||||
XtermComponent,
|
||||
SliderWrapperComponent
|
||||
SliderWrapperComponent,
|
||||
EditableTextComponent,
|
||||
Autofocus
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Action } from '@ngrx/store';
|
||||
import { KeyAction, Keymap, Macro } from 'uhk-common';
|
||||
import { UndoUserConfigData } from '../../models/undo-user-config-data';
|
||||
import { ChangeKeymapDescription } from '../../models/ChangeKeymapDescription';
|
||||
|
||||
export type KeymapAction =
|
||||
KeymapActions.AddKeymapAction |
|
||||
@@ -11,7 +12,8 @@ export type KeymapAction =
|
||||
KeymapActions.SetDefaultAction |
|
||||
KeymapActions.RemoveKeymapAction |
|
||||
KeymapActions.SaveKeyAction |
|
||||
KeymapActions.CheckMacroAction;
|
||||
KeymapActions.CheckMacroAction |
|
||||
KeymapActions.EditDescriptionAction;
|
||||
|
||||
export namespace KeymapActions {
|
||||
export const ADD = '[Keymap] Add keymap';
|
||||
@@ -98,6 +100,16 @@ export namespace KeymapActions {
|
||||
payload: UndoUserConfigData
|
||||
};
|
||||
|
||||
export const EDIT_DESCRIPTION = '[Keymap] Edit description';
|
||||
|
||||
export class EditDescriptionAction {
|
||||
type = EDIT_DESCRIPTION;
|
||||
|
||||
constructor(public payload: ChangeKeymapDescription) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export function loadKeymaps(): Action {
|
||||
return {
|
||||
type: KeymapActions.LOAD_KEYMAPS
|
||||
|
||||
@@ -81,7 +81,7 @@ export class UserConfigEffects {
|
||||
@Effect() saveUserConfig$: Observable<Action> = (this.actions$
|
||||
.ofType(
|
||||
KeymapActions.ADD, KeymapActions.DUPLICATE, KeymapActions.EDIT_NAME, KeymapActions.EDIT_ABBR,
|
||||
KeymapActions.SET_DEFAULT, KeymapActions.REMOVE, KeymapActions.SAVE_KEY,
|
||||
KeymapActions.SET_DEFAULT, KeymapActions.REMOVE, KeymapActions.SAVE_KEY, KeymapActions.EDIT_DESCRIPTION,
|
||||
MacroActions.ADD, MacroActions.DUPLICATE, MacroActions.EDIT_NAME, MacroActions.REMOVE, MacroActions.ADD_ACTION,
|
||||
MacroActions.SAVE_ACTION, MacroActions.DELETE_ACTION, MacroActions.REORDER_ACTION,
|
||||
ActionTypes.RENAME_USER_CONFIGURATION, ActionTypes.SET_USER_CONFIGURATION_VALUE) as
|
||||
|
||||
@@ -7,7 +7,7 @@ export const initialState: Keymap[] = [];
|
||||
export function reducer(state = initialState, action: KeymapAction): Keymap[] {
|
||||
switch (action.type) {
|
||||
case KeymapActions.LOAD_KEYMAPS_SUCCESS: {
|
||||
return action.payload;
|
||||
return (action as KeymapActions.LoadKeymapSuccessAction).payload ;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -263,6 +263,18 @@ export function reducer(state = initialState, action: Action & { payload?: any }
|
||||
break;
|
||||
}
|
||||
|
||||
case KeymapActions.EDIT_DESCRIPTION: {
|
||||
const data = (action as KeymapActions.EditDescriptionAction).payload;
|
||||
|
||||
changedUserConfiguration.keymaps = state.keymaps.map(keymap => {
|
||||
if (keymap.abbreviation === data.abbr) {
|
||||
keymap.description = data.description;
|
||||
}
|
||||
return keymap;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user