feat: set disable style of device name in side menu (#656)

* feat: visualise disabled state of the 'Device name' input control

* fix: original value handling
This commit is contained in:
Róbert Kiss
2018-05-27 02:30:41 +02:00
committed by László Monda
parent 8e20c85e07
commit ee53a0df9b
9 changed files with 161 additions and 53 deletions

View File

@@ -0,0 +1,10 @@
<input #inputControl
cancelable
[class]="css"
type="text"
[disabled]="disabled"
[(ngModel)]="model"
(blur)="blur()"
(focus)="focus()"
(keyup.enter)="keyEnter($event)"
(keyup)="calculateTextWidth($event.target.value)">

View File

@@ -0,0 +1,118 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
forwardRef, HostListener,
Input,
Renderer2,
ViewChild
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import * as util from '../../util';
const noop = (_: any) => {
};
@Component({
selector: 'auto-grow-input',
templateUrl: './auto-grow-input.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AutoGrowInputComponent),
multi: true
}
]
})
export class AutoGrowInputComponent implements ControlValueAccessor {
@Input() maxParentWidthPercent = 1;
@Input() css: string;
@ViewChild('inputControl') inputControl: ElementRef;
disabled: boolean;
get model(): string {
return this._model;
}
set model(value: string) {
if (this._model === value) {
return;
}
this._model = value;
}
private _model: string;
private _originalModel: string;
private _onChanged = noop;
private _onTouched = noop;
constructor(private _cdRef: ChangeDetectorRef,
private _renderer: Renderer2) {
}
registerOnChange(fn: any): void {
this._onChanged = fn;
}
registerOnTouched(fn: any): void {
this._onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
if (this.disabled === isDisabled) {
return;
}
this.disabled = isDisabled;
this._cdRef.markForCheck();
}
@HostListener('window:resize')
windowResize(): void {
this.calculateTextWidth(this._model);
}
writeValue(obj: any): void {
console.log('write', new Date());
if (this.model === obj) {
return;
}
this._model = obj;
this._originalModel = obj;
this.calculateTextWidth(this._model);
this._cdRef.markForCheck();
}
focus(): void {
this._onTouched(this);
}
blur(): void {
if (!util.isValidName(this._model) || this._model.trim() === this._originalModel) {
this._model = this._originalModel;
this.calculateTextWidth(this._model);
this._cdRef.markForCheck();
return;
}
this._originalModel = this._model;
this._onChanged(this._model);
}
keyEnter(event): void {
event.target.blur();
}
calculateTextWidth(text: string): void {
const htmlInput = this.inputControl.nativeElement as HTMLInputElement;
const maxWidth = htmlInput.parentElement.parentElement.offsetWidth * this.maxParentWidthPercent;
const textWidth = util.getContentWidth(window.getComputedStyle(htmlInput), text);
this._renderer.setStyle(htmlInput, 'width', Math.min(maxWidth, textWidth) + 'px');
}
}

View File

@@ -0,0 +1 @@
export * from './auto-grow-input.component';

View File

@@ -2,13 +2,11 @@
<li class="sidebar__level-0--item"> <li class="sidebar__level-0--item">
<div class="sidebar__level-0"> <div class="sidebar__level-0">
<i class="uhk-icon uhk-icon-0401-usb-stick rotate-right"></i> <i class="uhk-icon uhk-icon-0401-usb-stick rotate-right"></i>
<input #deviceName cancelable <auto-grow-input [ngModel]="state.deviceName"
class="pane-title__name" [maxParentWidthPercent]="0.65"
type="text" [css]="'side-menu-pane-title__name'"
[readonly]="state.restoreUserConfiguration" [disabled]="state.restoreUserConfiguration || state.updatingFirmware"
(change)="editDeviceName($event.target.value)" (ngModelChange)="editDeviceName($event)"></auto-grow-input>
(keyup.enter)="deviceName.blur()"
(keyup)="calculateHeaderTextWidth($event.target.value)">
<i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'device')"></i> <i class="fa fa-chevron-up pull-right" (click)="toggleHide($event, 'device')"></i>
</div> </div>
<ul [@toggler]="animation['device']"> <ul [@toggler]="animation['device']">

View File

@@ -162,22 +162,3 @@ ul {
} }
} }
} }
.pane-title {
margin-bottom: 1em;
&__name {
border: none;
border-bottom: 2px dotted #999;
padding: 0;
margin: 0 0.25rem;
text-overflow: ellipsis;
background-color: transparent;
&:focus {
box-shadow: 0 0 0 1px #ccc, 0 0 5px 0 #ccc;
border-color: transparent;
background-color: transparent;
}
}
}

View File

@@ -1,5 +1,4 @@
import { import {
AfterContentInit,
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
@@ -19,7 +18,6 @@ import 'rxjs/add/operator/let';
import { AppState, getSideMenuPageState } from '../../store'; import { AppState, getSideMenuPageState } from '../../store';
import { MacroActions } from '../../store/actions'; import { MacroActions } from '../../store/actions';
import * as util from '../../util';
import { RenameUserConfigurationAction } from '../../store/actions/user-config'; import { RenameUserConfigurationAction } from '../../store/actions/user-config';
import { SideMenuPageState } from '../../models/side-menu-page-state'; import { SideMenuPageState } from '../../models/side-menu-page-state';
@@ -40,7 +38,7 @@ import { SideMenuPageState } from '../../models/side-menu-page-state';
styleUrls: ['./side-menu.component.scss'], styleUrls: ['./side-menu.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SideMenuComponent implements AfterContentInit, OnInit, OnDestroy { export class SideMenuComponent implements OnInit, OnDestroy {
state: SideMenuPageState; state: SideMenuPageState;
animation: { [key: string]: 'active' | 'inactive' }; animation: { [key: string]: 'active' | 'inactive' };
@ViewChild('deviceName') deviceName: ElementRef; @ViewChild('deviceName') deviceName: ElementRef;
@@ -62,15 +60,10 @@ export class SideMenuComponent implements AfterContentInit, OnInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
this.stateSubscription = this.store.select(getSideMenuPageState).subscribe(data => { this.stateSubscription = this.store.select(getSideMenuPageState).subscribe(data => {
this.state = data; this.state = data;
this.setDeviceName();
this.cdRef.markForCheck(); this.cdRef.markForCheck();
}); });
} }
ngAfterContentInit(): void {
this.setDeviceName();
}
ngOnDestroy(): void { ngOnDestroy(): void {
if (this.stateSubscription) { if (this.stateSubscription) {
this.stateSubscription.unsubscribe(); this.stateSubscription.unsubscribe();
@@ -106,24 +99,6 @@ export class SideMenuComponent implements AfterContentInit, OnInit, OnDestroy {
} }
editDeviceName(name: string): void { editDeviceName(name: string): void {
if (!util.isValidName(name) || name.trim() === this.state.deviceName) {
this.setDeviceName();
return;
}
this.store.dispatch(new RenameUserConfigurationAction(name)); this.store.dispatch(new RenameUserConfigurationAction(name));
} }
calculateHeaderTextWidth(text): void {
const htmlInput = this.deviceName.nativeElement as HTMLInputElement;
const maxWidth = htmlInput.parentElement.offsetWidth * 0.66;
const textWidth = util.getContentWidth(window.getComputedStyle(htmlInput), text);
this.renderer.setStyle(htmlInput, 'width', Math.min(maxWidth, textWidth) + 'px');
}
private setDeviceName(): void {
if (this.deviceName) {
this.renderer.setProperty(this.deviceName.nativeElement, 'value', this.state.deviceName);
this.calculateHeaderTextWidth(this.deviceName.nativeElement.value);
}
}
} }

View File

@@ -108,6 +108,7 @@ import { EditableTextComponent } from './components/editable-text/editable-text.
import { Autofocus } from './directives/autofocus/autofocus.directive'; import { Autofocus } from './directives/autofocus/autofocus.directive';
import { UhkDeviceBootloaderNotActiveGuard } from './services/uhk-device-bootloader-not-active.guard'; import { UhkDeviceBootloaderNotActiveGuard } from './services/uhk-device-bootloader-not-active.guard';
import { FileUploadComponent } from './components/file-upload'; import { FileUploadComponent } from './components/file-upload';
import { AutoGrowInputComponent } from './components/auto-grow-input';
@NgModule({ @NgModule({
declarations: [ declarations: [
@@ -181,7 +182,8 @@ import { FileUploadComponent } from './components/file-upload';
Autofocus, Autofocus,
RestoreConfigurationComponent, RestoreConfigurationComponent,
RecoveryModeComponent, RecoveryModeComponent,
FileUploadComponent FileUploadComponent,
AutoGrowInputComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,

View File

@@ -4,6 +4,7 @@
@import '~font-awesome/scss/font-awesome'; @import '~font-awesome/scss/font-awesome';
@import './styles/tooltip'; @import './styles/tooltip';
@import './styles/uhk-icons/uhk-icon'; @import './styles/uhk-icons/uhk-icon';
@import './styles/side-menu';
html, body { html, body {
width: 100%; width: 100%;

View File

@@ -0,0 +1,22 @@
.side-menu-pane-title {
margin-bottom: 1em;
&__name {
border: none;
border-bottom: 2px dotted #999;
padding: 0;
margin: 0 0.25rem;
text-overflow: ellipsis;
background-color: transparent;
&:focus {
box-shadow: 0 0 0 1px #ccc, 0 0 5px 0 #ccc;
border-color: transparent;
background-color: transparent;
}
&:disabled {
border-bottom: none;
}
}
}