]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add DimlessBinaryDirective 20972/head
authorRicardo Marques <rimarques@suse.com>
Tue, 27 Feb 2018 09:51:54 +0000 (09:51 +0000)
committerRicardo Marques <rimarques@suse.com>
Fri, 23 Mar 2018 09:41:40 +0000 (09:41 +0000)
Signed-off-by: Ricardo Marques <rimarques@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/shared/directives/dimless-binary.directive.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/directives/dimless-binary.directive.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/shared.module.ts

diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/dimless-binary.directive.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/dimless-binary.directive.spec.ts
new file mode 100644 (file)
index 0000000..5822e7d
--- /dev/null
@@ -0,0 +1,12 @@
+import { DimlessBinaryDirective } from './dimless-binary.directive';
+
+export class MockElementRef {
+  nativeElement: {};
+}
+
+describe('DimlessBinaryDirective', () => {
+  it('should create an instance', () => {
+    const directive = new DimlessBinaryDirective(new MockElementRef(), null, null, null);
+    expect(directive).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/dimless-binary.directive.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/directives/dimless-binary.directive.ts
new file mode 100644 (file)
index 0000000..f30c80f
--- /dev/null
@@ -0,0 +1,117 @@
+import {
+  Directive,
+  ElementRef,
+  EventEmitter,
+  HostListener,
+  Input,
+  OnInit,
+  Output
+} from '@angular/core';
+import { NgControl } from '@angular/forms';
+
+import * as _ from 'lodash';
+
+import { DimlessBinaryPipe } from '../pipes/dimless-binary.pipe';
+import { FormatterService } from '../services/formatter.service';
+
+@Directive({
+  selector: '[cdDimlessBinary]'
+})
+export class DimlessBinaryDirective implements OnInit {
+
+  @Output() ngModelChange: EventEmitter<any> = new EventEmitter();
+
+  /**
+   * Minimum size in bytes.
+   * If user enter a value lower than <minBytes>,
+   * the model will automatically be update to <minBytes>.
+   *
+   * If <roundPower> is used, this value should be a power of <roundPower>.
+   *
+   * Example:
+   *   Given minBytes=4096 (4KiB), if user type 1KiB, then model will be updated to 4KiB
+   */
+  @Input() minBytes: number;
+
+  /**
+   * Maximum size in bytes.
+   * If user enter a value greater than <maxBytes>,
+   * the model will automatically be update to <maxBytes>.
+   *
+   * If <roundPower> is used, this value should be a power of <roundPower>.
+   *
+   * Example:
+   *   Given maxBytes=3145728 (3MiB), if user type 4MiB, then model will be updated to 3MiB
+   */
+  @Input() maxBytes: number;
+
+  /**
+   * Value will be rounded up the nearest power of <roundPower>
+   *
+   * Example:
+   *   Given roundPower=2, if user type 7KiB, then model will be updated to 8KiB
+   *   Given roundPower=2, if user type 5KiB, then model will be updated to 4KiB
+   */
+  @Input() roundPower: number;
+
+  /**
+   * Default unit that should be used when user do not type a unit.
+   * By default, "MiB" will be used.
+   *
+   * Example:
+   *   Given defaultUnit=null, if user type 7, then model will be updated to 7MiB
+   *   Given defaultUnit=k, if user type 7, then model will be updated to 7KiB
+   */
+  @Input() defaultUnit: string;
+
+  private el: HTMLInputElement;
+
+  constructor(private elementRef: ElementRef,
+              private control: NgControl,
+              private dimlessBinaryPipe: DimlessBinaryPipe,
+              private formatter: FormatterService) {
+    this.el = this.elementRef.nativeElement;
+  }
+
+  ngOnInit() {
+    this.setValue(this.el.value);
+  }
+
+  setValue(value) {
+    if (/^[\d.]+$/.test(value)) {
+      value += this.defaultUnit || 'm';
+    }
+    const size = this.formatter.toBytes(value);
+    const roundedSize = this.round(size);
+    this.el.value = this.dimlessBinaryPipe.transform(roundedSize);
+    if (size !== null) {
+      this.ngModelChange.emit(this.el.value);
+      this.control.control.setValue(this.el.value);
+    } else {
+      this.ngModelChange.emit(null);
+      this.control.control.setValue(null);
+    }
+  }
+
+  round(size) {
+    if (size !== null && size !== 0) {
+      if (!_.isUndefined(this.minBytes) && size < this.minBytes) {
+        return this.minBytes;
+      }
+      if (!_.isUndefined(this.maxBytes) && size > this.maxBytes) {
+        return this.maxBytes;
+      }
+      if (!_.isUndefined(this.roundPower)) {
+        const power = Math.round(Math.log(size) / Math.log(this.roundPower));
+        return Math.pow(this.roundPower, power);
+      }
+    }
+    return size;
+  }
+
+  @HostListener('blur', ['$event.target.value'])
+  onBlur(value) {
+    this.setValue(value);
+  }
+
+}
index 7651338d9c0bc7eea46d8945f92730ca1ad10f99..96c6facd2d9defc093d3f7f82922e60d7e5f5189 100644 (file)
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
 
 import { ComponentsModule } from './components/components.module';
 import { DataTableModule } from './datatable/datatable.module';
+import { DimlessBinaryDirective } from './directives/dimless-binary.directive';
 import { PasswordButtonDirective } from './directives/password-button.directive';
 import { PipesModule } from './pipes/pipes.module';
 import { AuthGuardService } from './services/auth-guard.service';
@@ -22,13 +23,15 @@ import { ServicesModule } from './services/services.module';
     DataTableModule
   ],
   declarations: [
-    PasswordButtonDirective
+    PasswordButtonDirective,
+    DimlessBinaryDirective
   ],
   exports: [
     ComponentsModule,
     PipesModule,
     ServicesModule,
     PasswordButtonDirective,
+    DimlessBinaryDirective,
     DataTableModule
   ],
   providers: [