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:
committed by
László Monda
parent
8e20c85e07
commit
ee53a0df9b
@@ -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)">
|
||||||
@@ -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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './auto-grow-input.component';
|
||||||
@@ -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']">
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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%;
|
||||||
|
|||||||
22
packages/uhk-web/src/styles/_side-menu.scss
Normal file
22
packages/uhk-web/src/styles/_side-menu.scss
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user