diff --git a/packages/stark-ui/src/modules.ts b/packages/stark-ui/src/modules.ts index 758e639e86..ca06eedca9 100644 --- a/packages/stark-ui/src/modules.ts +++ b/packages/stark-ui/src/modules.ts @@ -24,3 +24,4 @@ export * from "./modules/slider"; export * from "./modules/svg-view-box"; export * from "./modules/table"; export * from "./modules/toast-notification"; +export * from "./modules/transform-input-directive"; diff --git a/packages/stark-ui/src/modules/transform-input-directive.ts b/packages/stark-ui/src/modules/transform-input-directive.ts new file mode 100644 index 0000000000..ea820d997a --- /dev/null +++ b/packages/stark-ui/src/modules/transform-input-directive.ts @@ -0,0 +1,2 @@ +export * from "./transform-input-directive/directives"; +export * from "./transform-input-directive/transform-input-directive.module"; diff --git a/packages/stark-ui/src/modules/transform-input-directive/directives.ts b/packages/stark-ui/src/modules/transform-input-directive/directives.ts new file mode 100644 index 0000000000..3d1fbea240 --- /dev/null +++ b/packages/stark-ui/src/modules/transform-input-directive/directives.ts @@ -0,0 +1 @@ +export * from "./directives/transform-input.directive"; diff --git a/packages/stark-ui/src/modules/transform-input-directive/directives/transform-input.directive.spec.ts b/packages/stark-ui/src/modules/transform-input-directive/directives/transform-input.directive.spec.ts new file mode 100644 index 0000000000..ad8605882c --- /dev/null +++ b/packages/stark-ui/src/modules/transform-input-directive/directives/transform-input.directive.spec.ts @@ -0,0 +1,285 @@ +/*tslint:disable:completed-docs no-identical-functions no-duplicate-string no-big-function*/ +import { Component } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { FormControl, FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { StarkInputTransformationType, StarkTransformInputDirective } from "./transform-input.directive"; +import { Observer } from "rxjs"; + +/** + * Mocks an InputEvent on the element with the given value. + * @param value - the value to set the element to. + * @param element - the HTMLInputElement to mock the event on + */ +const mockInputEvent: (value: string, element: HTMLInputElement) => void = (value: string, element: HTMLInputElement): void => { + const inputEvent: Event = document.createEvent("Event"); + inputEvent.initEvent("input", true, true); + + element.value = value; + element.dispatchEvent(inputEvent); +}; + +describe("TransformInputDirective", () => { + describe("with ngModel", () => { + @Component({ + selector: "test-component", + template: "" + }) + class TestComponent { + public starkTransformInputValue: StarkInputTransformationType = () => { + /*noop*/ + }; + public value: string = ""; + } + + let fixture: ComponentFixture; + let component: TestComponent; + let htmlInputElement: HTMLInputElement; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [StarkTransformInputDirective, TestComponent], + imports: [FormsModule] + }); + + fixture = TestBed.createComponent(TestComponent); + component = fixture.componentInstance; + htmlInputElement = fixture.nativeElement.querySelector("input"); + + // trigger initial data binding + fixture.detectChanges(); + + expect(component.value).toBe("", "field 'value' should start as empty string "); + }); + + it("should set value to uppercase", () => { + const input: string = "upper"; + const expected: string = "UPPER"; + + // Set directive to correct implementation + component.starkTransformInputValue = "uppercase"; + fixture.detectChanges(); + + mockInputEvent(input, htmlInputElement); + fixture.detectChanges(); + + expect(component.value).toBe(expected); + }); + + it("should set value to lowercase", () => { + const input: string = "LOWER"; + const expected: string = "lower"; + + // Set directive to correct implementation + component.starkTransformInputValue = "lowercase"; + fixture.detectChanges(); + + mockInputEvent(input, htmlInputElement); + fixture.detectChanges(); + + expect(component.value).toBe(expected); + }); + + it("should replace dirty word", () => { + const input: string = "fudge you!"; + const expected: string = "***** you!"; + + // Set directive to correct implementation + component.starkTransformInputValue = (v: string) => + v.replace("fudge", (match: string) => + match + .split("") + .map(() => "*") + .join("") + ); + fixture.detectChanges(); + + mockInputEvent(input, htmlInputElement); + fixture.detectChanges(); + + expect(component.value).toBe(expected); + }); + }); + + describe("with formControl", () => { + @Component({ + selector: "test-component", + template: "" + }) + class TestComponent { + public starkTransformInputValue: StarkInputTransformationType = () => { + /*noop*/ + }; + public formControl: FormControl = new FormControl(""); + + public constructor() { + this.formControl.valueChanges.subscribe(this.changeObserver); + } + + public changeObserver(): void { + /*Placeholder for spy*/ + } + } + + let fixture: ComponentFixture; + let component: TestComponent; + let mockValueChangeObserver: jasmine.SpyObj>; + let htmlInputElement: HTMLInputElement; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [StarkTransformInputDirective, TestComponent], + imports: [ReactiveFormsModule] + }); + + fixture = TestBed.createComponent(TestComponent); + component = fixture.componentInstance; + htmlInputElement = fixture.nativeElement.querySelector("input"); + + // Register mock subscription + mockValueChangeObserver = jasmine.createSpyObj>("observerSpy", ["next", "error", "complete"]); + component.formControl.valueChanges.subscribe(mockValueChangeObserver); + + // trigger initial data binding + fixture.detectChanges(); + + expect(component.formControl.value).toBe("", "field 'value' should start as empty string "); + }); + + it("should set value to uppercase", () => { + const input: string = "upper"; + const expected: string = "UPPER"; + + // Set directive to correct implementation + component.starkTransformInputValue = "uppercase"; + fixture.detectChanges(); + + mockInputEvent(input, htmlInputElement); + fixture.detectChanges(); + + expect(component.formControl.value).toBe(expected); + expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1); + expect(mockValueChangeObserver.error).not.toHaveBeenCalled(); + expect(mockValueChangeObserver.complete).not.toHaveBeenCalledTimes(1); + }); + + it("should set value to lowercase", () => { + const input: string = "LOWER"; + const expected: string = "lower"; + + // Set directive to correct implementation + component.starkTransformInputValue = "lowercase"; + fixture.detectChanges(); + + mockInputEvent(input, htmlInputElement); + fixture.detectChanges(); + + expect(component.formControl.value).toBe(expected); + expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1); + expect(mockValueChangeObserver.error).not.toHaveBeenCalled(); + expect(mockValueChangeObserver.complete).not.toHaveBeenCalledTimes(1); + }); + + it("should replace dirty word", () => { + const input: string = "fudge you!"; + const expected: string = "***** you!"; + + // Set directive to correct implementation + component.starkTransformInputValue = (v: string) => + v.replace("fudge", (match: string) => + match + .split("") + .map(() => "*") + .join("") + ); + fixture.detectChanges(); + + mockInputEvent(input, htmlInputElement); + fixture.detectChanges(); + + expect(component.formControl.value).toBe(expected); + expect(mockValueChangeObserver.next).toHaveBeenCalledTimes(1); + expect(mockValueChangeObserver.error).not.toHaveBeenCalled(); + expect(mockValueChangeObserver.complete).not.toHaveBeenCalledTimes(1); + }); + }); + + describe("without ngControl", () => { + @Component({ + selector: "test-component", + template: "" + }) + class TestComponent { + public starkTransformInputValue: StarkInputTransformationType = () => { + /*noop*/ + }; + } + + let fixture: ComponentFixture; + let component: TestComponent; + let htmlInputElement: HTMLInputElement; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [StarkTransformInputDirective, TestComponent] + }); + + fixture = TestBed.createComponent(TestComponent); + component = fixture.componentInstance; + htmlInputElement = fixture.nativeElement.querySelector("input"); + htmlInputElement.value = ""; + + // trigger initial data binding + fixture.detectChanges(); + + expect(htmlInputElement.value).toBe("", "field 'value' should start as empty string "); + }); + + it("should set value to uppercase", () => { + const input: string = "upper"; + const expected: string = "UPPER"; + + // Set directive to correct implementation + component.starkTransformInputValue = "uppercase"; + fixture.detectChanges(); + + mockInputEvent(input, htmlInputElement); + fixture.detectChanges(); + + expect(htmlInputElement.value).toBe(expected); + }); + + it("should set value to lowercase", () => { + const input: string = "LOWER"; + const expected: string = "lower"; + + // Set directive to correct implementation + component.starkTransformInputValue = "lowercase"; + fixture.detectChanges(); + + mockInputEvent(input, htmlInputElement); + fixture.detectChanges(); + + expect(htmlInputElement.value).toBe(expected); + }); + + it("should replace dirty word", () => { + const input: string = "fudge you!"; + const expected: string = "***** you!"; + + // Set directive to correct implementation + component.starkTransformInputValue = (v: string) => + v.replace("fudge", (match: string) => + match + .split("") + .map(() => "*") + .join("") + ); + fixture.detectChanges(); + + mockInputEvent(input, htmlInputElement); + fixture.detectChanges(); + + expect(htmlInputElement.value).toBe(expected); + }); + }); +}); diff --git a/packages/stark-ui/src/modules/transform-input-directive/directives/transform-input.directive.ts b/packages/stark-ui/src/modules/transform-input-directive/directives/transform-input.directive.ts new file mode 100644 index 0000000000..7831ba7592 --- /dev/null +++ b/packages/stark-ui/src/modules/transform-input-directive/directives/transform-input.directive.ts @@ -0,0 +1,141 @@ +import { Directive, ElementRef, forwardRef, Input, OnChanges, Renderer2, SimpleChanges } from "@angular/core"; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; + +export const UPPERCASE: "uppercase" = "uppercase"; +export const LOWERCASE: "lowercase" = "lowercase"; +export type StarkInputTransformationType = "uppercase" | "lowercase" | ((value: any) => any); + +/** + * Provider for StarkTransformInputDirective + */ +export const STARK_TRANSFORM_INPUT_PROVIDER: any = { + provide: NG_VALUE_ACCESSOR, + // tslint:disable-next-line:no-forward-ref + useExisting: forwardRef(() => StarkTransformInputDirective), + multi: true +}; + +/** + * Directive to transform the value of an input / textarea before it is passed on to the ngControl (ngModel, formControl). + * It is possible to pass 'upper', 'lower' or a custom function to transform the value. + * + * example: + * ``` + * + * ``` + */ +@Directive({ + // tslint:disable-next-line:directive-selector + selector: "[starkTransformInput]", + providers: [STARK_TRANSFORM_INPUT_PROVIDER], + host: { + "(input)": "_onInput($event)", + "(blur)": "_onTouched()" + } +}) +export class StarkTransformInputDirective implements ControlValueAccessor, OnChanges { + public _transformation: (value: any) => any; + + // tslint:disable-next-line:no-input-rename + @Input("starkTransformInput") + public set transformation(transformation: StarkInputTransformationType) { + switch (transformation) { + case UPPERCASE: + this._transformation = (v: string) => v.toUpperCase(); + break; + case LOWERCASE: + this._transformation = (v: string) => v.toLocaleLowerCase(); + break; + default: + this._transformation = transformation; + break; + } + } + + /** + * @ignore + * The registered callback function called when an input event occurs on the input element. + */ + private _onChange: (_: any) => void = (_: any) => { + /*noop*/ + }; + + /** + * @ignore + * The registered callback function called when a blur event occurs on the input element. + */ + public _onTouched: () => void = () => { + /*noop*/ + }; + + /** + * Class constructor + * @param _renderer - Angular renderer + * @param _elementRef - Reference to the element + */ + public constructor(private _renderer: Renderer2, private _elementRef: ElementRef) {} + + /** + * Sets the "value" property on the input element. + * + * @param value The checked value + */ + public writeValue(value: any): void { + const normalizedValue: any = value === null ? "" : value; + this._renderer.setProperty(this._elementRef.nativeElement, "value", normalizedValue); + } + + /** + * Registers a function called when the control value changes. + * + * @param fn The callback function + */ + public registerOnChange(fn: (_: any) => void): void { + this._onChange = fn; + } + + /** + * Registers a function called when the control is touched. + * + * @param fn The callback function + */ + public registerOnTouched(fn: () => void): void { + this._onTouched = fn; + } + + /** + * Sets the "disabled" property on the input element. + * + * @param isDisabled The disabled value + */ + public setDisabledState(isDisabled: boolean): void { + this._renderer.setProperty(this._elementRef.nativeElement, "disabled", isDisabled); + } + + /** + * @ignore + * Listens to input event + */ + public _onInput(event: Event): void { + const value: any = (event.target).value; + const transformed: any = this._transformation(value); + if (transformed !== value) { + this._elementRef.nativeElement.value = transformed; + this._onChange(transformed); + } else { + this._onChange(value); + } + } + + /** + * Angular life cycle hook + * @param changes - simple changes + */ + public ngOnChanges(changes: SimpleChanges): void { + const currentValue: any = changes.transformation.currentValue; + // Type check + if (typeof currentValue !== "function" && ![UPPERCASE, LOWERCASE].includes(currentValue)) { + throw Error('directive starkTransformInput should be of type "uppercase" | "lowercase" | ((value: any) => any) '); + } + } +} diff --git a/packages/stark-ui/src/modules/transform-input-directive/transform-input-directive.module.ts b/packages/stark-ui/src/modules/transform-input-directive/transform-input-directive.module.ts new file mode 100644 index 0000000000..41b6b7bd21 --- /dev/null +++ b/packages/stark-ui/src/modules/transform-input-directive/transform-input-directive.module.ts @@ -0,0 +1,8 @@ +import { NgModule } from "@angular/core"; +import { StarkTransformInputDirective } from "./directives"; + +@NgModule({ + declarations: [StarkTransformInputDirective], + exports: [StarkTransformInputDirective] +}) +export class StarkTransformInputDirectiveModule {} diff --git a/showcase/src/app/app-menu.config.ts b/showcase/src/app/app-menu.config.ts index 87cca18875..a401fa2b08 100644 --- a/showcase/src/app/app-menu.config.ts +++ b/showcase/src/app/app-menu.config.ts @@ -200,6 +200,13 @@ export const APP_MENU_CONFIG: StarkMenuConfig = { isEnabled: true, targetState: "demo-ui.keyboard-directives" }, + { + id: "menu-stark-ui-transform-input", + label: "Transform input directive", + isVisible: true, + isEnabled: true, + targetState: "demo-ui.transform-input-directive" + }, { id: "menu-stark-ui-directives-progress-indicator", label: "Progress indicator", diff --git a/showcase/src/app/demo-ui/demo-ui.module.ts b/showcase/src/app/demo-ui/demo-ui.module.ts index 87a9d812e2..c8243709cd 100644 --- a/showcase/src/app/demo-ui/demo-ui.module.ts +++ b/showcase/src/app/demo-ui/demo-ui.module.ts @@ -31,6 +31,7 @@ import { StarkDropdownModule, StarkGenericSearchModule, StarkKeyboardDirectivesModule, + StarkTransformInputDirectiveModule, StarkLanguageSelectorModule, StarkMinimapModule, StarkPaginationModule, @@ -65,7 +66,8 @@ import { DemoSidebarPageComponent, DemoSliderPageComponent, DemoTablePageComponent, - DemoToastPageComponent + DemoToastPageComponent, + DemoTransformInputDirectivePageComponent } from "./pages"; import { SharedModule } from "../shared/shared.module"; import { DEMO_STATES } from "./routes"; @@ -116,6 +118,7 @@ import { MatExpansionModule } from "@angular/material/expansion"; StarkDropdownModule, StarkGenericSearchModule, StarkKeyboardDirectivesModule, + StarkTransformInputDirectiveModule, StarkLanguageSelectorModule, StarkMinimapModule, StarkPaginationModule, @@ -157,7 +160,8 @@ import { MatExpansionModule } from "@angular/material/expansion"; TableWithFixedHeaderComponent, TableWithCustomStylingComponent, DemoToastPageComponent, - DemoGenericSearchFormComponent + DemoGenericSearchFormComponent, + DemoTransformInputDirectivePageComponent, ], exports: [ DemoActionBarPageComponent, diff --git a/showcase/src/app/demo-ui/pages/index.ts b/showcase/src/app/demo-ui/pages/index.ts index ea113b74f9..ad72449d61 100644 --- a/showcase/src/app/demo-ui/pages/index.ts +++ b/showcase/src/app/demo-ui/pages/index.ts @@ -21,3 +21,4 @@ export * from "./slider"; export * from "./route-search"; export * from "./table"; export * from "./toast"; +export * from "./transform-input-directive"; diff --git a/showcase/src/app/demo-ui/pages/keyboard-directives/demo-keyboard-directives-page.component.html b/showcase/src/app/demo-ui/pages/keyboard-directives/demo-keyboard-directives-page.component.html index f30a8ac282..e6587a3a14 100644 --- a/showcase/src/app/demo-ui/pages/keyboard-directives/demo-keyboard-directives-page.component.html +++ b/showcase/src/app/demo-ui/pages/keyboard-directives/demo-keyboard-directives-page.component.html @@ -1,42 +1,42 @@ -

SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.TITLE

+

SHOWCASE.DEMO.DIRECTIVES.KEYBOARD.TITLE

SHOWCASE.DEMO.SHARED.EXAMPLE_VIEWER_LIST

-
-

SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.DESCRIPTION

+ +

SHOWCASE.DEMO.DIRECTIVES.KEYBOARD.ON_ENTER_KEY.DESCRIPTION

- SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.INPUT_WITH_CONTEXT + SHOWCASE.DEMO.DIRECTIVES.KEYBOARD.ON_ENTER_KEY.INPUT_WITH_CONTEXT - SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.INPUT_WITHOUT_CONTEXT + SHOWCASE.DEMO.DIRECTIVES.KEYBOARD.ON_ENTER_KEY.INPUT_WITHOUT_CONTEXT - SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.ON_ENTER_KEY.INPUT_WITHOUT_CONTEXT_ALTERNATIVE + SHOWCASE.DEMO.DIRECTIVES.KEYBOARD.ON_ENTER_KEY.INPUT_WITHOUT_CONTEXT_ALTERNATIVE @@ -49,43 +49,43 @@

SHOWCASE.DEMO.SHARED.EXAMPLE_VIEWER_LIST

- -

SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.DESCRIPTION

+ +

SHOWCASE.DEMO.DIRECTIVES.KEYBOARD.RESTRICT_INPUT.DESCRIPTION

- SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.INPUT_ONLY_NUMBERS + SHOWCASE.DEMO.DIRECTIVES.KEYBOARD.RESTRICT_INPUT.INPUT_ONLY_NUMBERS - SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.INPUT_ALPHANUMERICAL_CHARACTERS + SHOWCASE.DEMO.DIRECTIVES.KEYBOARD.RESTRICT_INPUT.INPUT_ALPHANUMERICAL_CHARACTERS - SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.INPUT_NO_SPECIAL_CHARACTERS + SHOWCASE.DEMO.DIRECTIVES.KEYBOARD.RESTRICT_INPUT.INPUT_NO_SPECIAL_CHARACTERS - SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.RESTRICT_INPUT.INPUT_UPPERCASE_CHARACTERS + SHOWCASE.DEMO.DIRECTIVES.KEYBOARD.RESTRICT_INPUT.INPUT_UPPERCASE_CHARACTERS diff --git a/showcase/src/app/demo-ui/pages/keyboard-directives/demo-keyboard-directives-page.component.scss b/showcase/src/app/demo-ui/pages/keyboard-directives/demo-keyboard-directives-page.component.scss index 9bed527bce..4035ed81d1 100644 --- a/showcase/src/app/demo-ui/pages/keyboard-directives/demo-keyboard-directives-page.component.scss +++ b/showcase/src/app/demo-ui/pages/keyboard-directives/demo-keyboard-directives-page.component.scss @@ -1,13 +1,3 @@ -.on-enter-key-directive-form { - display: flex; - flex-direction: column; -} - -.restrict-input-directive-form { - display: flex; - flex-direction: column; -} - pre code { display: block; height: 200px; diff --git a/showcase/src/app/demo-ui/pages/transform-input-directive/demo-transform-input-directive-page.component.html b/showcase/src/app/demo-ui/pages/transform-input-directive/demo-transform-input-directive-page.component.html new file mode 100644 index 0000000000..789848aeb9 --- /dev/null +++ b/showcase/src/app/demo-ui/pages/transform-input-directive/demo-transform-input-directive-page.component.html @@ -0,0 +1,39 @@ +

SHOWCASE.DEMO.DIRECTIVES.TRANSFORM.TITLE

+
+

SHOWCASE.DEMO.SHARED.EXAMPLE_VIEWER_LIST

+ +
+ + SHOWCASE.DEMO.DIRECTIVES.TRANSFORM.ONLY_UPPER_CASE_LABEL + + + + SHOWCASE.DEMO.DIRECTIVES.TRANSFORM.ONLY_LOWER_CASE_LABEL + + + + SHOWCASE.DEMO.DIRECTIVES.TRANSFORM.EMOJI_LABEL + + +
+
+
+ diff --git a/showcase/src/app/demo-ui/pages/transform-input-directive/demo-transform-input-directive-page.component.ts b/showcase/src/app/demo-ui/pages/transform-input-directive/demo-transform-input-directive-page.component.ts new file mode 100644 index 0000000000..905090953a --- /dev/null +++ b/showcase/src/app/demo-ui/pages/transform-input-directive/demo-transform-input-directive-page.component.ts @@ -0,0 +1,40 @@ +import { Component, Inject, OnInit } from "@angular/core"; +import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core"; +import { FormControl } from "@angular/forms"; +import { ReferenceLink } from "../../../shared/components"; + +@Component({ + selector: "showcase-demo-transform-input-directive-page", + templateUrl: "./demo-transform-input-directive-page.component.html" +}) +export class DemoTransformInputDirectivePageComponent implements OnInit { + public value: string = ""; + public formControl: FormControl = new FormControl(""); + private _emojiMap: Map = new Map([ + [":+1:", "👍"], + [":-1:", "👎"], + [":smile:", "😄"], + [":tada:", "🎉"], + [":rocket:", "🚀"], + [":zap:", "⚡️"] + ]); + public simpleEmojiTransform: (value: string) => string = (value: string) => + value.replace(/:[+-]?\w+:/, (match: string): string => this._emojiMap.get(match) || match); + public referenceList: ReferenceLink[]; + + public constructor(@Inject(STARK_LOGGING_SERVICE) private logger: StarkLoggingService) {} + + /** + * Component lifecycle hook + */ + public ngOnInit(): void { + this.referenceList = [ + { + label: "Stark Transform Input directive", + url: "https://stark.nbb.be/api-docs/stark-ui/latest/directives/StarkTransformInputDirective.html" + } + ]; + + this.formControl.valueChanges.subscribe((v: string) => this.logger.debug("formControl value changed: ", v)); + } +} diff --git a/showcase/src/app/demo-ui/pages/transform-input-directive/index.ts b/showcase/src/app/demo-ui/pages/transform-input-directive/index.ts new file mode 100644 index 0000000000..1b31341232 --- /dev/null +++ b/showcase/src/app/demo-ui/pages/transform-input-directive/index.ts @@ -0,0 +1 @@ +export * from "./demo-transform-input-directive-page.component"; diff --git a/showcase/src/app/demo-ui/routes.ts b/showcase/src/app/demo-ui/routes.ts index 97fc7aca80..8b429dc1a2 100644 --- a/showcase/src/app/demo-ui/routes.ts +++ b/showcase/src/app/demo-ui/routes.ts @@ -22,7 +22,8 @@ import { DemoSidebarPageComponent, DemoSliderPageComponent, DemoTablePageComponent, - DemoToastPageComponent + DemoToastPageComponent, + DemoTransformInputDirectivePageComponent } from "./pages"; export const DEMO_STATES: Ng2StateDeclaration[] = [ @@ -100,10 +101,18 @@ export const DEMO_STATES: Ng2StateDeclaration[] = [ name: "demo-ui.keyboard-directives", url: "/keyboard-directives", data: { - translationKey: "SHOWCASE.DEMO.KEYBOARD_DIRECTIVES.TITLE" + translationKey: "SHOWCASE.DEMO.DIRECTIVES.KEYBOARD.TITLE" }, views: { "@": { component: DemoKeyboardDirectivesPageComponent } } }, + { + name: "demo-ui.transform-input-directive", + url: "/transform-input-directive", + data: { + translationKey: "SHOWCASE.DEMO.DIRECTIVES.TRANSFORM.TITLE" + }, + views: { "@": { component: DemoTransformInputDirectivePageComponent } } + }, { name: "demo-ui.language-selector", url: "/language-selector", diff --git a/showcase/src/assets/examples/transform-input-directive/transform-input-directive.html b/showcase/src/assets/examples/transform-input-directive/transform-input-directive.html new file mode 100644 index 0000000000..30bac1647c --- /dev/null +++ b/showcase/src/assets/examples/transform-input-directive/transform-input-directive.html @@ -0,0 +1,14 @@ +
+ + Only upper case + + + + Only lower case + + + + 😄😄😄 + + +
diff --git a/showcase/src/assets/examples/transform-input-directive/transform-input-directive.ts b/showcase/src/assets/examples/transform-input-directive/transform-input-directive.ts new file mode 100644 index 0000000000..4d4d38255a --- /dev/null +++ b/showcase/src/assets/examples/transform-input-directive/transform-input-directive.ts @@ -0,0 +1,27 @@ +import { Component, Inject, OnInit } from "@angular/core"; +import { STARK_LOGGING_SERVICE, StarkLoggingService } from "@nationalbankbelgium/stark-core"; +import { FormControl } from "@angular/forms"; + +@Component({ + selector: "demo-transform-input", + templateUrl: "./demo-transform-input.component.html" +}) +export class DemoTransformInputComponent implements OnInit { + public value: string = ""; + public formControl: FormControl = new FormControl(""); + private _emojiMap: Map = new Map([ + [":+1:", "👍"], + [":-1:", "👎"], + [":smile:", "😄"], + [":tada:", "🎉"], + [":rocket:", "🚀"], + [":zap:", "⚡️"] + ]); + public simpleEmojiTransform: (value: string) => string = (value: string) => + value.replace(/:[+-]?\w+:/, (match: string): string => this._emojiMap.get(match) || match); + + public constructor(@Inject(STARK_LOGGING_SERVICE) private logger: StarkLoggingService) {} + public ngOnInit(): void { + this.formControl.valueChanges.subscribe((v: string) => this.logger.debug("formControl value changed: ", v)); + } +} diff --git a/showcase/src/assets/translations/en.json b/showcase/src/assets/translations/en.json index 9e5db106a0..295dfca114 100644 --- a/showcase/src/assets/translations/en.json +++ b/showcase/src/assets/translations/en.json @@ -94,26 +94,35 @@ "TITLE": "Stark Generic Search", "TOGGLE": "Toggle search" }, - "KEYBOARD_DIRECTIVES": { - "ON_ENTER_KEY": { - "DESCRIPTION": "Type some value in the inputs and then press Enter in any of them to trigger the callback function", - "INPUT_WITH_CONTEXT": "With bound context", - "INPUT_WITHOUT_CONTEXT": "Without bound context (bound values are not accessible)", - "INPUT_WITHOUT_CONTEXT_ALTERNATIVE": "Without bound context but passed as parameter", - "TITLE": "On Enter Key Directive", - "TYPE_AND_PRESS_ENTER": "Type a value and press Enter", - "TYPE_PRESS_ENTER_AND_CHECK_CONSOLE": "Type a value, press Enter and check the console" + "DIRECTIVES": { + "KEYBOARD": { + "TITLE": "Keyboard directives", + "ON_ENTER_KEY": { + "DESCRIPTION": "Type some value in the inputs and then press Enter in any of them to trigger the callback function", + "INPUT_WITH_CONTEXT": "With bound context", + "INPUT_WITHOUT_CONTEXT": "Without bound context (bound values are not accessible)", + "INPUT_WITHOUT_CONTEXT_ALTERNATIVE": "Without bound context but passed as parameter", + "TITLE": "On Enter Key Directive", + "TYPE_AND_PRESS_ENTER": "Type a value and press Enter", + "TYPE_PRESS_ENTER_AND_CHECK_CONSOLE": "Type a value, press Enter and check the console" + }, + "RESTRICT_INPUT": { + "TITLE": "Restrict Input Directive", + "DESCRIPTION": "Type some value in the inputs and some characters will be prevented depending on the restriction set in every field", + "INPUT_ONLY_NUMBERS": "Accept only numbers", + "INPUT_ALPHANUMERICAL_CHARACTERS": "Accept only alphanumeric characters", + "INPUT_NO_SPECIAL_CHARACTERS": "Accept all except special characters", + "INPUT_UPPERCASE_CHARACTERS": "Accept only uppercase characters" + } }, - "RESTRICT_INPUT": { - "TITLE": "Restrict Input Directive", - "DESCRIPTION": "Type some value in the inputs and some characters will be prevented depending on the restriction set in every field", - "INPUT_ONLY_NUMBERS": "Accept only numbers", - "INPUT_ALPHANUMERICAL_CHARACTERS": "Accept only alphanumeric characters", - "INPUT_NO_SPECIAL_CHARACTERS": "Accept all except special characters", - "INPUT_UPPERCASE_CHARACTERS": "Accept only uppercase characters", - "TYPE_A_VALUE": "Type a value" + "TRANSFORM": { + "TITLE": "Transform Input Directive", + "ONLY_UPPER_CASE_LABEL": "Upper case only", + "ONLY_LOWER_CASE_LABEL": "Lower case only", + "EMOJI_LABEL": "\uD83D\uDE04\uD83D\uDE04\uD83D\uDE04", + "EMOJI_EXAMPLE": ":tada:" }, - "TITLE": "Keyboard directive" + "TYPE_A_VALUE": "Type a value" }, "LANGUAGE_SELECTOR": { "DROPDOWN": "Language selector in 'dropdown' mode", diff --git a/showcase/src/assets/translations/fr.json b/showcase/src/assets/translations/fr.json index 4276fa8e52..1073180376 100644 --- a/showcase/src/assets/translations/fr.json +++ b/showcase/src/assets/translations/fr.json @@ -94,26 +94,35 @@ "TITLE": "Stark Generic Search", "TOGGLE": "Toggle search" }, - "KEYBOARD_DIRECTIVES": { - "ON_ENTER_KEY": { - "DESCRIPTION": "Tapez une valeur dans les entrées, puis appuyez sur Entrée dans l'un d'eux pour déclencher la fonction callback", - "INPUT_WITH_CONTEXT": "Avec contexte lié", - "INPUT_WITHOUT_CONTEXT": "Sans contexte lié (les valeurs liées ne sont pas accessibles)", - "INPUT_WITHOUT_CONTEXT_ALTERNATIVE": "Sans contexte lié mais passé en paramètre", - "TITLE": "On Enter Key Directive", - "TYPE_AND_PRESS_ENTER": "Tapez une valeur et appuyez sur Entrée", - "TYPE_PRESS_ENTER_AND_CHECK_CONSOLE": "Tapez une valeur, appuyez sur Entrée et vérifiez la console" + "DIRECTIVES": { + "KEYBOARD": { + "TITLE": "Keyboard directives", + "ON_ENTER_KEY": { + "DESCRIPTION": "Tapez une valeur dans les entrées, puis appuyez sur Entrée dans l'un d'eux pour déclencher la fonction callback", + "INPUT_WITH_CONTEXT": "Avec contexte lié", + "INPUT_WITHOUT_CONTEXT": "Sans contexte lié (les valeurs liées ne sont pas accessibles)", + "INPUT_WITHOUT_CONTEXT_ALTERNATIVE": "Sans contexte lié mais passé en paramètre", + "TITLE": "On Enter Key Directive", + "TYPE_AND_PRESS_ENTER": "Tapez une valeur et appuyez sur Entrée", + "TYPE_PRESS_ENTER_AND_CHECK_CONSOLE": "Tapez une valeur, appuyez sur Entrée et vérifiez la console" + }, + "RESTRICT_INPUT": { + "TITLE": "Restrict Input Directive", + "DESCRIPTION": "Tapez une valeur dans les entrées et certains caractères seront prévenus en fonction de l'ensemble de restriction dans chaque champ", + "INPUT_ONLY_NUMBERS": "Accepter seulement les nombres", + "INPUT_ALPHANUMERICAL_CHARACTERS": "Accepter uniquement les caractères alphanumériques", + "INPUT_NO_SPECIAL_CHARACTERS": "Accepter tous les caractères sauf les caractères spéciaux", + "INPUT_UPPERCASE_CHARACTERS": "Accepter uniquement les majuscules" + } }, - "RESTRICT_INPUT": { - "TITLE": "Restrict Input Directive", - "DESCRIPTION": "Tapez une valeur dans les entrées et certains caractères seront prévenus en fonction de l'ensemble de restriction dans chaque champ", - "INPUT_ONLY_NUMBERS": "Accepter seulement les nombres", - "INPUT_ALPHANUMERICAL_CHARACTERS": "Accepter uniquement les caractères alphanumériques", - "INPUT_NO_SPECIAL_CHARACTERS": "Accepter tous les caractères sauf les caractères spéciaux", - "INPUT_UPPERCASE_CHARACTERS": "Accepter uniquement les majuscules", - "TYPE_A_VALUE": "Taper une valeur" + "TRANSFORM": { + "TITLE": "Transform Input Directive", + "ONLY_UPPER_CASE_LABEL": "Uniquement les majuscules", + "ONLY_LOWER_CASE_LABEL": "Uniquement les minuscules", + "EMOJI_LABEL": "\uD83D\uDE04\uD83D\uDE04\uD83D\uDE04", + "EMOJI_EXAMPLE": ":tada:" }, - "TITLE": "Keyboard directive" + "TYPE_A_VALUE": "Taper une valeur" }, "LANGUAGE_SELECTOR": { "DROPDOWN": "Sélecteur de langue en mode 'dropdown'", diff --git a/showcase/src/assets/translations/nl.json b/showcase/src/assets/translations/nl.json index 66d88d31b1..4b857b4a96 100644 --- a/showcase/src/assets/translations/nl.json +++ b/showcase/src/assets/translations/nl.json @@ -94,26 +94,35 @@ "TITLE": "Stark Generic Search", "TOGGLE": "Toggle search" }, - "KEYBOARD_DIRECTIVES": { - "ON_ENTER_KEY": { - "DESCRIPTION": "Typ een waarde in de invulvelden en druk vervolgens op Enter om de callback functie te activeren", - "INPUT_WITH_CONTEXT": "Met verbonden context", - "INPUT_WITHOUT_CONTEXT": "Zonder verbonden context (verbonden waarden zijn niet toegankelijk)", - "INPUT_WITHOUT_CONTEXT_ALTERNATIVE": "Zonder verbonden context maar doorgegeven als parameter", - "TITLE": "On Enter Key Directive", - "TYPE_AND_PRESS_ENTER": "Typ een waarde en druk Enter", - "TYPE_PRESS_ENTER_AND_CHECK_CONSOLE": "Typ een waarde, druk Enter en controleer de console" + "DIRECTIVES": { + "KEYBOARD": { + "TITLE": "Keyboard directives", + "ON_ENTER_KEY": { + "DESCRIPTION": "Typ een waarde in de velden en druk vervolgens op Enter om de callback functie te activeren", + "INPUT_WITH_CONTEXT": "Met verbonden context", + "INPUT_WITHOUT_CONTEXT": "Zonder verbonden context (verbonden waarden zijn niet toegankelijk)", + "INPUT_WITHOUT_CONTEXT_ALTERNATIVE": "Zonder verbonden context maar doorgegeven als parameter", + "TITLE": "On Enter Key Directive", + "TYPE_AND_PRESS_ENTER": "Typ een waarde en druk Enter", + "TYPE_PRESS_ENTER_AND_CHECK_CONSOLE": "Typ een waarde, druk Enter en controleer de console" + }, + "RESTRICT_INPUT": { + "TITLE": "Restrict Input Directive", + "DESCRIPTION": "Typ een waarde in de input velden en sommige karakters zullen niet toegelaten worden door de restricties die op het veld werden gezet.", + "INPUT_ONLY_NUMBERS": "Aanvaard enkel cijfers", + "INPUT_ALPHANUMERICAL_CHARACTERS": "Aanvaard enkel alfanumerische karakters", + "INPUT_NO_SPECIAL_CHARACTERS": "Aanvaard alle karakters, behalve speciale karakters", + "INPUT_UPPERCASE_CHARACTERS": "Aanvaard enkel hoofdletters" + } }, - "RESTRICT_INPUT": { - "TITLE": "Restrict Input Directive", - "DESCRIPTION": "Typ een waarde in de input velden en sommige karakters zullen niet toegelaten worden door de restricties die op het veld werden gezet.", - "INPUT_ONLY_NUMBERS": "Aanvaard enkel cijfers", - "INPUT_ALPHANUMERICAL_CHARACTERS": "Aanvaard enkel alfanumerische karakters", - "INPUT_NO_SPECIAL_CHARACTERS": "Aanvaard alle karakters, behalve speciale karakters", - "INPUT_UPPERCASE_CHARACTERS": "Aanvaard enkel hoofdletters", - "TYPE_A_VALUE": "Geef een waarde in" + "TRANSFORM": { + "TITLE": "Transform Input Directive", + "ONLY_UPPER_CASE_LABEL": "Alleen hoofdletters", + "ONLY_LOWER_CASE_LABEL": "Alleen kleine letters", + "EMOJI_LABEL": "\uD83D\uDE04\uD83D\uDE04\uD83D\uDE04", + "EMOJI_EXAMPLE": ":tada:" }, - "TITLE": "Keyboard directive" + "TYPE_A_VALUE": "Geef een waarde in" }, "LANGUAGE_SELECTOR": { "DROPDOWN": "Taal selector in 'dropdown' mode", @@ -215,7 +224,7 @@ "WITH_CUSTOM_COLOR_AND_MENU_CONFIG": "Route Search component met 'menuConfig' en aangepaste kleur" }, "SHARED": { - "EXAMPLE_VIEWER_LIST": "Examples and code samples" + "EXAMPLE_VIEWER_LIST": "Voorbeelden met code" }, "SIDEBAR": { "INTRO": "De preview van deze code is de eigenlijke zijbalk van de showcase",