From a66f45e2106b917b0a4417970650c396d1ca800d Mon Sep 17 00:00:00 2001
From: assureclaims <84139537+assureclaims@users.noreply.github.com>
Date: Tue, 12 Jul 2022 12:44:20 +0530
Subject: [PATCH] File Input control
---
angular.json | 2 +-
.../directives/file-format.directive.ts | 50 ++
.../dxc-file-error.component.html | 3 +
.../dxc-file-error.component.scss | 9 +
.../dxc-file-error.component.ts | 19 +
.../dxc-file-input.component.html | 53 ++
.../dxc-file-input.component.spec.ts | 308 +++++++++++
.../dxc-file-input.component.ts | 490 ++++++++++++++++++
.../dxc-file-input/dxc-file-input.module.ts | 13 +
.../dxc-file-input/dxc-file-input.types.ts | 34 ++
.../dxc-file/dxc-file.component.html | 60 +++
.../dxc-file/dxc-file.component.ts | 274 ++++++++++
.../interfaces/file.interface.ts | 5 +
.../interfaces/files.interface.ts | 6 +
.../dxc-file-input/services/files.services.ts | 58 +++
.../src/lib/typings/ng-onchange.ts | 18 +
projects/dxc-ngx-cdk/src/public-api.ts | 5 +
17 files changed, 1406 insertions(+), 1 deletion(-)
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/directives/file-format.directive.ts
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.html
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.scss
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.ts
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.html
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.spec.ts
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.ts
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.module.ts
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.types.ts
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file/dxc-file.component.html
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file/dxc-file.component.ts
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/interfaces/file.interface.ts
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/interfaces/files.interface.ts
create mode 100644 projects/dxc-ngx-cdk/src/lib/dxc-file-input/services/files.services.ts
create mode 100644 projects/dxc-ngx-cdk/src/lib/typings/ng-onchange.ts
diff --git a/angular.json b/angular.json
index 5c581b06a..1f447a0d9 100644
--- a/angular.json
+++ b/angular.json
@@ -330,7 +330,7 @@
}
}
},
- "defaultProject": "angular-dxc-site",
+ "defaultProject": "dxc-ngx-cdk",
"cli": {
"analytics": false
}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/directives/file-format.directive.ts b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/directives/file-format.directive.ts
new file mode 100644
index 000000000..4aba16d02
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/directives/file-format.directive.ts
@@ -0,0 +1,50 @@
+import { Directive, ElementRef, Input, Inject } from '@angular/core';
+import { DOCUMENT } from '@angular/common';
+
+@Directive({
+ selector: '[dxcFileFormat]'
+})
+export class FileFormatDirective {
+
+ @Input() format;
+
+ constructor(private elementRef: ElementRef, @Inject(DOCUMENT) private document: any) {
+ }
+
+ ngOnInit(): void {
+ let xmlns = "http://www.w3.org/2000/svg";
+ const commonPathChild = this.document.createElementNS(xmlns, "path");
+ commonPathChild.setAttributeNS(null, 'd', 'M0 0h24v24H0V0z');
+ commonPathChild.setAttributeNS(null, 'fill', 'none');
+ const child = this.document.createElementNS(xmlns, "path");
+ switch (this.categorizeFileFormat(this.format)) {
+ case 'image':
+ child.setAttributeNS(null, 'd', 'M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6z');
+ break;
+ case 'video':
+ child.setAttributeNS(null, 'd', 'M4 6.47L5.76 10H20v8H4V6.47M22 4h-4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4z');
+ break;
+ case 'audio':
+ child.setAttributeNS(null, 'd', 'M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14zM8 15c0-1.66 1.34-3 3-3 .35 0 .69.07 1 .18V6h5v2h-3v7.03c-.02 1.64-1.35 2.97-3 2.97-1.66 0-3-1.34-3-3z');
+ break;
+ default:
+ child.setAttributeNS(null, 'd', 'M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6z');
+ break;
+ }
+ this.elementRef.nativeElement.append(commonPathChild);
+ this.elementRef.nativeElement.append(child);
+ }
+
+ private categorizeFileFormat(fileFormat:string){
+ if (fileFormat.includes("image")) {
+ return 'image';
+ } else if (fileFormat.includes("video")) {
+ return 'video';
+
+ } else if (fileFormat.includes("audio")) {
+ return 'audio';
+
+ }
+ return 'default';
+ }
+}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.html b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.html
new file mode 100644
index 000000000..3683e6bc2
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.html
@@ -0,0 +1,3 @@
+{{error}}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.scss b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.scss
new file mode 100644
index 000000000..3bef7f30f
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.scss
@@ -0,0 +1,9 @@
+.errorMessage {
+ text-align: left;
+ letter-spacing: 0.37px;
+ color: var(--fileInput-errorMessageFontColor);
+ font-family: var(--fileInput-errorMessageFontFamily);
+ font-size: var(--fileInput-errorMessageFontSize);
+ font-weight: var(--fileInput-errorMessageFontWeight);
+ line-height: var(--fileInput-errorMessageLineHeight);
+}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.ts b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.ts
new file mode 100644
index 000000000..93c5ad6fc
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-error/dxc-file-error.component.ts
@@ -0,0 +1,19 @@
+import { ChangeDetectionStrategy, Component, HostBinding, Input, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'dxc-file-error',
+ templateUrl: './dxc-file-error.component.html',
+ styleUrls: ['./dxc-file-error.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class DxcFileErrorComponent implements OnInit {
+
+ @Input()
+ error: string;
+
+ constructor() { }
+
+ ngOnInit(): void {
+
+ }
+}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.html b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.html
new file mode 100644
index 000000000..54cdb2420
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.html
@@ -0,0 +1,53 @@
+
+{{ helperText }}
+
+
+
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.spec.ts b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.spec.ts
new file mode 100644
index 000000000..7fcb7ed87
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.spec.ts
@@ -0,0 +1,308 @@
+import { fireEvent, render } from "@testing-library/angular";
+import { DxcFileInputComponent } from "./dxc-file-input.component";
+import { DxcFileInputModule } from "./dxc-file-input.module";
+import { screen, waitFor } from "@testing-library/dom";
+import { FileData } from "./interfaces/file.interface";
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting,
+} from "@angular/platform-browser-dynamic/testing";
+import { TestBed } from "@angular/core/testing";
+
+TestBed.initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting()
+);
+
+describe("DxcFileInputComponent", () => {
+ test("should render dxc-file-input in file mode", async () => {
+ const fileInput = await render(DxcFileInputComponent, {
+ componentProperties: {
+ label: "Label",
+ helperText: "Helper Text",
+ },
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ });
+
+ expect(fileInput.getByText("Select files"));
+ expect(fileInput.getByText("Label"));
+ expect(fileInput.getByText("Helper Text"));
+ });
+
+ test("should render dxc-file-input with custom buttonLabel", async () => {
+ const fileInput = await render(DxcFileInputComponent, {
+ componentProperties: {
+ label: "Label",
+ helperText: "Helper Text",
+ buttonLabel: "Custom Button Label",
+ },
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ });
+
+ expect(fileInput.getByText("Custom Button Label"));
+ expect(fileInput.getByText("Label"));
+ expect(fileInput.getByText("Helper Text"));
+ });
+
+
+ test("should render dxc-file-input in file drop mode", async () => {
+ const fileInput = await render(DxcFileInputComponent, {
+ componentProperties: {
+ mode: "filedrop",
+ },
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ });
+
+ expect(fileInput.getByText("or drop files"));
+ });
+
+ test("should render dxc-file-input in drop zone mode", async () => {
+ const fileInput = await render(DxcFileInputComponent, {
+ componentProperties: {
+ mode: "dropzone",
+ },
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ });
+
+ expect(fileInput.getByText("or drop files"));
+ });
+
+ test("should render disabled dxc-file-input", async () => {
+ const fileInput = await render(DxcFileInputComponent, {
+ componentProperties: {
+ disabled: true,
+ },
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ });
+
+ expect(fileInput.getByText("Select files"));
+ const btn = fileInput.getAllByRole("button");
+ expect(btn[0].hasAttribute("disabled")).toBe(true);
+ });
+
+ test("should not have files even if they are selected", async () => {
+ const fileInput = await render(
+ ``,
+ {
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ declarations: [DxcFileInputComponent],
+ }
+ );
+ const inputEl = fileInput.getByTestId("input");
+ const file = new File(["foo"], "foo.txt", {
+ type: "text/plain",
+ });
+ fireEvent.change(inputEl, { target: { files: [file] } });
+ const fileInScreen = screen.queryByText("foo.txt");
+ expect(fileInScreen).toBeFalsy();
+ });
+
+ test("should render error when file does not meet minSize", async () => {
+ const file = new File(["foo"], "foo.txt", {
+ type: "text/plain",
+ });
+ const value: Array = [
+ {
+ data: file,
+ image: "",
+ error: "",
+ },
+ ];
+ const callback = jest.fn();
+ const fileInput = await render(
+ ``,
+ {
+ componentProperties: { callback, value },
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ declarations: [DxcFileInputComponent],
+ }
+ );
+ await waitFor(() => {
+ fileInput.detectChanges();
+ expect(screen.getByText("foo.txt"));
+ expect(screen.getByText("File size must be greater than min size."));
+ });
+ });
+
+ test("should render error when file does not meet maxSize", async () => {
+ const file = new File(["foo"], "foo.txt", {
+ type: "text/plain",
+ });
+ const value: Array = [
+ {
+ data: file,
+ image: "",
+ error: "",
+ },
+ ];
+ const maxSize = 1;
+ const callback = jest.fn();
+ const fileInput = await render(
+ ``,
+ {
+ componentProperties: { callback, value, maxSize },
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ declarations: [DxcFileInputComponent],
+ }
+ );
+ fileInput.detectChanges();
+ await waitFor(() => {
+ fileInput.detectChanges();
+ expect(screen.getByText("foo.txt"));
+ expect(screen.getByText("File size must be less than max size."));
+ });
+ });
+
+ test("render given values when multiple is false", async () => {
+ const callback = jest.fn();
+ const file = new File(["foo"], "foo.txt", {
+ type: "text/plain",
+ });
+ const file2 = new File(["chucknorris"], "chucknorris.txt", {
+ type: "text/plain",
+ });
+ let value: Array = [
+ {
+ data: file,
+ image: "",
+ error: "Error for file",
+ },
+ {
+ data: file2,
+ image: "",
+ error: "Error for file2",
+ },
+ ];
+ const fileInput = await render(DxcFileInputComponent, {
+ componentProperties: {
+ multiple: false,
+ value: value,
+ callbackFile: {
+ emit: callback,
+ } as any,
+ },
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ });
+
+ await waitFor(() => {
+ expect(screen.getByText("foo.txt"));
+ expect(screen.getByText("chucknorris.txt"));
+ expect(screen.getByText("Error for file"));
+ expect(screen.getByText("Error for file2"));
+ });
+ });
+
+ test("render dxc-file-input with multiple files", async () => {
+ const callback = jest.fn();
+ const file = new File(["foo"], "foo.txt", {
+ type: "text/plain",
+ });
+ const file2 = new File(["test"], "test.txt", {
+ type: "text/plain",
+ });
+ const value = [
+ {
+ data: file,
+ image: "",
+ },
+ {
+ data: file2,
+ image: "",
+ },
+ ];
+ const fileInput = await render(
+ ``,
+ {
+ componentProperties: { callback, value },
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ declarations: [DxcFileInputComponent],
+ }
+ );
+ const inputEl = fileInput.getByTestId("input");
+ await waitFor(() => {
+ expect(screen.getByText("foo.txt"));
+ expect(screen.getByText("test.txt"));
+ });
+ });
+
+ test("should remove file from dxc-file-input", async () => {
+ const callback = jest.fn();
+ const file = new File(["foo"], "foo.txt", {
+ type: "text/plain",
+ });
+ const file2 = new File(["test"], "test.txt", {
+ type: "text/plain",
+ });
+ const value = [
+ {
+ data: file,
+ image: null,
+ },
+ {
+ data: file2,
+ image: null,
+ },
+ ];
+ const fileInput = await render(
+ ``,
+ {
+ componentProperties: { callback, value },
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ declarations: [DxcFileInputComponent],
+ }
+ );
+ const inputEl = fileInput.getByTestId("input");
+
+ fireEvent.change(inputEl, { target: { files: [file, file2] } });
+ fileInput.detectChanges();
+ await waitFor(() => {
+ expect(screen.getByText("foo.txt"));
+ });
+ const removeIcons = fileInput.getAllByTestId("removeIcon");
+ fireEvent.click(removeIcons[0]);
+ await waitFor(() => {
+ expect(callback).toHaveBeenCalledWith([
+ { data: file2, error: null, image: null },
+ ]);
+ });
+ });
+
+ test("should return callback files", async () => {
+ const callback = jest.fn();
+ const value = [];
+ const fileInput = await render(
+ ``,
+ {
+ componentProperties: { callback, value },
+ excludeComponentDeclaration: true,
+ imports: [DxcFileInputModule],
+ declarations: [DxcFileInputComponent],
+ }
+ );
+ const inputEl = fileInput.getByTestId("input");
+ const file = new File(["foo"], "foo.txt", {
+ type: "text/plain",
+ });
+ const file2 = new File(["(⌐□_□)"], "chucknorris.txt", {
+ type: "text/plain",
+ });
+ fireEvent.change(inputEl, { target: { files: [file, file2] } });
+ await waitFor(() => {
+ expect(callback).toHaveBeenCalledWith([
+ { data: file, error: null, image: null },
+ { data: file2, error: null, image: null },
+ ]);
+ });
+ });
+});
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.ts b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.ts
new file mode 100644
index 000000000..a9691d22b
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.component.ts
@@ -0,0 +1,490 @@
+import {
+ coerceBooleanProperty,
+ coerceNumberProperty,
+} from "@angular/cdk/coercion";
+import {
+ ChangeDetectorRef,
+ Component,
+ ElementRef,
+ EventEmitter,
+ HostBinding,
+ Input,
+ OnChanges,
+ OnInit,
+ Output,
+ ViewChild,
+} from "@angular/core";
+import { css } from "emotion";
+import { BehaviorSubject } from "rxjs";
+import { CssUtils } from "../utils";
+import { v4 as uuidv4 } from "uuid";
+import { FileData } from "./interfaces/file.interface";
+import { FilesService } from "./services/files.services";
+import { NgChanges } from "../typings/ng-onchange";
+import { FileInputProperties, Space, Spacing } from "./dxc-file-input.types";
+
+@Component({
+ selector: "dxc-file-input",
+ templateUrl: "./dxc-file-input.component.html",
+ providers: [CssUtils, FilesService],
+})
+export class DxcFileInputComponent implements OnChanges, OnInit {
+ @ViewChild("fileInput", { static: false }) fileInputNative: ElementRef;
+ @HostBinding("class") className;
+ /**
+ * Name attribute.
+ */
+ @Input() public name: string = "";
+ /**
+ * Available modes of the component.
+ */
+ @Input() public mode: "file" | "filedrop" | "dropzone" = "file";
+ /**
+ * Text to be placed above the component.
+ */
+ @Input() public label: string = "";
+ /**
+ * Text to be placed inside the button.
+ */
+ @Input() public buttonLabel: string;
+ /**
+ * Helper text to be placed above the component.
+ */
+ @Input() public helperText: string = "";
+ /**
+ * An array of files representing the selected files.
+ */
+ @Input() public value: FileData[];
+ /**
+ * The file types that the component accepts. Its value must be one of all the possible values of the HTML file input's accept attribute.
+ */
+ @Input() public accept: string;
+ /**
+ * If true, the component allows multiple file items and will show all of them. If false, only one file will be shown,
+ * and if there is already one file selected and a new one is chosen, it will be replaced by the new selected one.
+ */
+ @Input()
+ get multiple(): boolean {
+ return this._multiple;
+ }
+ set multiple(value: boolean) {
+ this._multiple = coerceBooleanProperty(value);
+ }
+ private _multiple = true;
+ /**
+ * If true, if the file is an image, a preview of it will be shown. If not, an icon refering to the file type will be shown.
+ */
+ @Input()
+ get showPreview(): boolean {
+ return this._showPreview;
+ }
+ set showPreview(value: boolean) {
+ this._showPreview = coerceBooleanProperty(value);
+ }
+ private _showPreview = false;
+ /**
+ * If true, the component will be disabled.
+ */
+ @Input()
+ get disabled(): boolean {
+ return this._disabled;
+ }
+ set disabled(value: boolean) {
+ this._disabled = coerceBooleanProperty(value);
+ }
+ private _disabled = false;
+ /**
+ * The minimum file size (in bytes) allowed. If the size of the file does not comply the minSize, the file will have an error.
+ */
+ @Input()
+ get minSize(): number {
+ return this._minSize;
+ }
+ set minSize(value: number) {
+ this._minSize = coerceNumberProperty(value);
+ }
+ private _minSize;
+ /**
+ * The maximum file size (in bytes) allowed. If the size of the file does not comply the maxSize, the file will have an error.
+ */
+ @Input()
+ get maxSize(): number {
+ return this._maxSize;
+ }
+ set maxSize(value: number) {
+ this._maxSize = coerceNumberProperty(value);
+ }
+ private _maxSize;
+ /**
+ * Size of the margin to be applied to the component ('xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge').
+ * You can pass an object with 'top', 'bottom', 'left' and 'right' properties in order to specify different margin sizes.
+ */
+ @Input() public margin: Space | Spacing;
+ /**
+ * Value of the tabindex attribute.
+ */
+ @Input()
+ get tabIndexValue(): number {
+ return this._tabIndexValue;
+ }
+ set tabIndexValue(value: number) {
+ this._tabIndexValue = coerceNumberProperty(value);
+ }
+ private _tabIndexValue = 0;
+ hasShowError: boolean = false;
+ /**
+ * This event will emit when the user selects or drops a file. The file or list of files will be sent as a parameter.
+ */
+ @Output() callbackFile = new EventEmitter();
+
+ defaultInputs = new BehaviorSubject({
+ name: null,
+ mode: "file",
+ label: null,
+ buttonLabel: null,
+ helperText: null,
+ accept: null,
+ multiple: true,
+ showPreview: false,
+ disabled: false,
+ margin: null,
+ tabIndexValue: 0,
+ value: null,
+ maxSize: null,
+ minSize: null,
+ });
+
+ id: string;
+ files: Array = [];
+ hoveringWithFile: boolean = false;
+ filesLoaded: boolean = false;
+ numberFiles: number = 0;
+ hasMultipleFiles: boolean = false;
+ hasSingleFile: boolean = false;
+ hasErrorSingleFile: boolean = false;
+ hasValue: boolean = false;
+
+ constructor(private utils: CssUtils, private service: FilesService) {
+ this.service.files.subscribe(({ files, event }) => {
+ if (event !== "reset" && (files.length || this.hasValue)) {
+ this.hasShowError = this.isErrorShow();
+ this.hasMultipleFiles = this.isMultipleFilesPrintables();
+ this.hasSingleFile = this.isSingleFilesPrintables();
+ this.callbackFile.emit(files);
+ }
+ });
+ }
+
+ ngOnInit() {
+ this.id = this.id || uuidv4();
+ this.value ? (this.hasValue = true) : (this.hasValue = false);
+ this.hasShowError = this.isErrorShow();
+ this.hasMultipleFiles = this.isMultipleFilesPrintables();
+ this.hasSingleFile = this.isSingleFilesPrintables();
+ this.className = `${this.getDynamicStyle(this.defaultInputs.getValue())}`;
+ if (!this.buttonLabel) {
+ this.buttonLabel =
+ this.mode === "file"
+ ? this.multiple
+ ? "Select files"
+ : "Select file"
+ : "Select";
+ }
+ }
+
+ ngOnChanges(changes: NgChanges): void {
+ if (this.fileInputNative) {
+ this.multiple
+ ? this.fileInputNative.nativeElement.setAttribute("multiple", true)
+ : this.fileInputNative.nativeElement.removeAttribute("multiple");
+ }
+ if (this.value?.length > 0) {
+ const arr: FileData[] = [];
+ this.service.files.next({ files: arr, event: "reset" });
+ this.value.forEach((file) => {
+ if (!file.error) {
+ file.error = this.checkFileSize(file.data);
+ }
+ this.service.addFile(file);
+ });
+ }
+ const inputs = Object.keys(changes).reduce((result, item) => {
+ result[item] = changes[item].currentValue;
+ return result;
+ }, {});
+ this.defaultInputs.next({ ...this.defaultInputs.getValue(), ...inputs });
+ this.className = `${this.getDynamicStyle(this.defaultInputs.getValue())}`;
+ }
+
+ ngAfterViewInit(): void {
+ if (this.fileInputNative) {
+ this.multiple
+ ? this.fileInputNative.nativeElement.setAttribute("multiple", true)
+ : this.fileInputNative.nativeElement.removeAttribute("multiple");
+ }
+ }
+
+ checkFileSize(file: File) {
+ if (file.size < this.minSize) {
+ return "File size must be greater than min size.";
+ }
+ if (file.size > this.maxSize) {
+ return "File size must be less than max size.";
+ }
+ return null;
+ }
+
+ /**
+ * File drop y drop zone
+ * @param event
+ */
+ dragOver(event) {
+ event.preventDefault();
+ this.hoveringWithFile = true;
+ }
+
+ /**
+ * File drop y drop zone
+ * @param event
+ */
+ dragLeave(event) {
+ event.preventDefault();
+ this.hoveringWithFile = false;
+ }
+
+ /**
+ * File drop y drop zone
+ * @param event
+ */
+ drop(event) {
+ event.preventDefault();
+ this.hoveringWithFile = false;
+ if (this.callbackFile.observers?.length > 0 && this.hasValue) {
+ if (!this.multiple) {
+ this.service.emptyArrayFiles();
+ }
+ this.getPreviewsFiles(event.dataTransfer.files);
+ }
+ }
+
+ /**
+ * Common function for both file modes.
+ * @param event
+ */
+ onFileInput(event) {
+ if (this.callbackFile.observers?.length > 0 && this.hasValue) {
+ if (!this.multiple) {
+ this.service.emptyArrayFiles();
+ }
+ this.getPreviewsFiles(event.target.files);
+ event.target.value = "";
+ }
+ }
+
+ /**
+ * Common function for both file modes.
+ * @param eventFiles
+ */
+ getPreviewsFiles(eventFiles) {
+ this.numberFiles = eventFiles?.length;
+ Array.from(eventFiles).map((file) => {
+ this.getPreview(file);
+ });
+ }
+
+ /**
+ * Common function for both file modes.
+ * @param file
+ */
+ getPreview(file) {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onload = (event) => {
+ if (!file.type.includes("image") || file.type.includes("image/svg")) {
+ let fileToAdd: FileData = {
+ data: file,
+ image: null,
+ error: this.checkFileSize(file),
+ };
+ this.service.addFile(fileToAdd);
+ } else {
+ let fileToAdd: FileData = {
+ data: file,
+ image: event.target["result"],
+ error: this.checkFileSize(file),
+ };
+ this.service.addFile(fileToAdd);
+ }
+ };
+ }
+
+ private isMultipleFilesPrintables(isSingle = false) {
+ return isSingle
+ ? this.value?.length > 0 && !this.disabled && !this.multiple
+ : this.value?.length > 0 && !this.disabled && this.multiple;
+ }
+
+ private isSingleFilesPrintables() {
+ return this.mode === "file" && this.value?.length === 1 && !this.multiple;
+ }
+
+ private isErrorShow = (): boolean =>
+ this.value?.length === 1 &&
+ this.mode === "file" &&
+ this.value[0]?.error &&
+ !this.multiple &&
+ !this.disabled;
+
+ /**
+ * Define the type of file component. Just for styling
+ * @param inputs
+ * @returns
+ */
+ getModeStyle(inputs: FileInputProperties) {
+ if (inputs.mode === "filedrop") {
+ return this.getFileDropStyle();
+ } else if (inputs.mode === "dropzone") {
+ return this.getDropZoneStyle();
+ } else {
+ return this.getFileStyle();
+ }
+ }
+
+ /**
+ * Just for file mode.
+ * @param inputs
+ * @returns
+ */
+ getFileStyle() {
+ return css`
+ .fileInputContainer {
+ flex-direction: ${this.value?.length > 1 || this.multiple
+ ? "column"
+ : "row"};
+ }
+ `;
+ }
+
+ /**
+ * Just for drop zone.
+ * @param inputs
+ * @returns
+ */
+ getDropZoneStyle() {
+ return css`
+ .fileInputContainer {
+ flex-direction: column;
+ .dragDropArea {
+ height: 160px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ .dropLabel {
+ margin-top: 8px;
+ }
+ }
+ }
+ `;
+ }
+ /**
+ * Just for drop zone.
+ * @param inputs
+ * @returns
+ */
+ getFileDropStyle() {
+ return css`
+ .fileInputContainer {
+ flex-direction: column;
+ .dragDropArea {
+ padding: 3px;
+ height: 48px;
+ box-sizing: border-box;
+ .dropLabel {
+ margin-left: 12px;
+ }
+ }
+ }
+ `;
+ }
+
+ /**
+ * Common functionality for styling
+ * @param inputs
+ * @returns
+ */
+ getDynamicStyle(inputs) {
+ return css`
+ ${this.utils.getMargins(inputs.margin)}
+ ${this.getModeStyle(inputs)}
+ width: fit-content;
+ display: flex;
+ flex-direction: column;
+ ${inputs.disabled ? "cursor: not-allowed;" : ""}
+ .fileInputContainer {
+ display: flex;
+ dxc-button {
+ width: fit-content;
+ }
+ input {
+ visibility: hidden;
+ width: 0px;
+ height: 0px;
+ }
+ .dragDropArea {
+ width: 320px;
+ box-sizing: border-box;
+ background: #ffffff 0% 0% no-repeat padding-box;
+ border: var(--fileInput-dropBorderThickness)
+ var(--fileInput-dropBorderStyle)
+ ${!inputs.disabled
+ ? "var(--fileInput-dropBorderColor)"
+ : "var(--fileInput-disabledDropBorderColor)"};
+ border-radius: var(--fileInput-dropBorderRadius);
+ .dropLabel {
+ text-align: left;
+ letter-spacing: 0.49px;
+ color: ${!inputs.disabled
+ ? "var(--fileInput-dropLabelFontColor)"
+ : "var(--fileInput-disabledDropLabelFontColor)"};
+ font-family: var(--fileInput-dropLabelFontFamily);
+ font-size: var(--fileInput-dropLabelFontSize);
+ font-weight: var(--fileInput-dropLabelFontWeight);
+ }
+ &.hovering {
+ ${!inputs.disabled
+ ? "border: 2px solid var(--fileInput-focusDropBorderColor); background: var(--fileInput-dragoverDropBackgroundColor) 0% 0% no-repeat padding-box;"
+ : ""}
+ }
+ }
+ .fileContainer {
+ display: flex;
+ flex-direction: column;
+ }
+ }
+ .label {
+ text-align: left;
+ letter-spacing: 0px;
+ color: ${!inputs.disabled
+ ? "var(--fileInput-labelFontColor)"
+ : "var(--fileInput-disabledLabelFontColor)"};
+ font-family: var(--fileInput-labelFontFamily);
+ font-size: var(--fileInput-labelFontSize);
+ font-weight: var(--fileInput-labelFontWeight);
+ line-height: var(--fileInput-labelLineHeight);
+ }
+ .helperText {
+ margin-bottom: 4px;
+ text-align: left;
+ letter-spacing: 0px;
+ color: ${!inputs.disabled
+ ? "var(--fileInput-helperTextFontColor)"
+ : "var(--fileInput-disabledHelperTextFontColor)"};
+ font-family: var(--fileInput-helperTextFontFamily);
+ font-size: var(--fileInput-helperTextFontSize);
+ font-weight: var(--fileInput-helperTextFontWeight);
+ line-height: var(--fileInput-helperTextLineHeight);
+ }
+ `;
+ }
+}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.module.ts b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.module.ts
new file mode 100644
index 000000000..ed7abfc6d
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { DxcFileInputComponent } from './dxc-file-input.component';
+import { DxcButtonModule } from "../dxc-button/dxc-button.module";
+import { DxcFileComponent } from './dxc-file/dxc-file.component';
+import { DxcFileErrorComponent } from './dxc-file-error/dxc-file-error.component';
+import { FileFormatDirective } from './directives/file-format.directive';
+@NgModule({
+ declarations: [DxcFileInputComponent, DxcFileComponent, DxcFileErrorComponent, FileFormatDirective],
+ imports: [CommonModule, DxcButtonModule],
+ exports: [DxcFileInputComponent],
+})
+export class DxcFileInputModule { }
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.types.ts b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.types.ts
new file mode 100644
index 000000000..3b4b38020
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file-input.types.ts
@@ -0,0 +1,34 @@
+import { FileData } from "./interfaces/file.interface";
+
+export interface FileInputProperties {
+ margin?: Space | Spacing;
+ tabIndexValue?: number;
+ name?: string;
+ mode?: string;
+ label?: string;
+ buttonLabel?: string;
+ helperText?: string;
+ value?: FileData[];
+ accept?: string;
+ multiple?: boolean;
+ showPreview?: boolean;
+ disabled?: boolean;
+ minSize?: number;
+ maxSize?: number;
+}
+
+export type Space =
+ | "xxsmall"
+ | "xsmall"
+ | "small"
+ | "medium"
+ | "large"
+ | "xlarge"
+ | "xxlarge";
+
+export type Spacing = {
+ top?: Space;
+ bottom?: Space;
+ left?: Space;
+ right?: Space;
+};
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file/dxc-file.component.html b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file/dxc-file.component.html
new file mode 100644
index 000000000..6dd2e0067
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file/dxc-file.component.html
@@ -0,0 +1,60 @@
+
+
+
![]()
+
+
+
+
+
{{ file.data.name }}
+
+
+
+ {{ file.error }}
+
+
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file/dxc-file.component.ts b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file/dxc-file.component.ts
new file mode 100644
index 000000000..6de9bfcdd
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/dxc-file/dxc-file.component.ts
@@ -0,0 +1,274 @@
+import {
+ coerceBooleanProperty,
+ coerceNumberProperty,
+} from "@angular/cdk/coercion";
+import { C } from "@angular/cdk/keycodes";
+import {
+ Component,
+ HostBinding,
+ Input,
+ OnInit,
+ SimpleChanges,
+} from "@angular/core";
+import { css } from "emotion";
+import { BehaviorSubject } from "rxjs";
+import { FileData } from "../interfaces/file.interface";
+import { FilesService } from "../services/files.services";
+
+@Component({
+ selector: "dxc-file",
+ templateUrl: "./dxc-file.component.html",
+})
+export class DxcFileComponent implements OnInit {
+ @HostBinding("class") className;
+
+ @Input() file: FileData;
+ @Input() multiple: boolean;
+ @Input() mode: string;
+ @Input() updatable: boolean;
+ @Input()
+ get showPreview(): boolean {
+ return this._showPreview;
+ }
+ set showPreview(value: boolean) {
+ this._showPreview = coerceBooleanProperty(value);
+ }
+ private _showPreview = false;
+ @Input()
+ get tabIndexValue(): number {
+ return this._tabIndexValue;
+ }
+ set tabIndexValue(value: number) {
+ this._tabIndexValue = coerceNumberProperty(value);
+ }
+ private _tabIndexValue = 0;
+
+ hasError: boolean = false;
+ hasShowError: boolean = false;
+ hasShowPreviewImage: boolean = false;
+ hasShowPreviewIcon: boolean = false;
+ hasShowPreview: boolean = false;
+
+ defaultInputs = new BehaviorSubject({
+ showPreview: false,
+ multiple: false,
+ mode: null,
+ });
+
+ constructor(private service: FilesService) {}
+
+ public ngOnChanges(changes: SimpleChanges): void {
+ this.file.error !== null &&
+ this.file.error !== undefined &&
+ this.file.error.length !== 0
+ ? (this.hasError = true)
+ : (this.hasError = false);
+ this.hasShowError = this.isErrorPrintable();
+ this.hasShowPreviewImage = this.isShowPreviewPrintable();
+ this.hasShowPreviewIcon = this.isShowPreviewPrintable(false);
+ this.hasShowPreview = this.isShowPreview();
+
+ const inputs = Object.keys(changes).reduce((result, item) => {
+ result[item] = changes[item].currentValue;
+ return result;
+ }, {});
+ this.defaultInputs.next({ ...this.defaultInputs.getValue(), ...inputs });
+ this.className = `${this.getDynamicStyle(this.defaultInputs.getValue())}`;
+ }
+
+ ngOnInit(): void {
+ this.className = `${this.getDynamicStyle(this.defaultInputs.getValue())}`;
+ }
+
+ onRemoveHandler(event: any): void {
+ if (this.updatable) {
+ this.service.removeFile(this.file);
+ }
+ }
+
+ private isShowPreview() {
+ return (
+ (this.showPreview && this.mode !== "file") ||
+ (this.showPreview && this.mode === "file" && this.multiple)
+ );
+ }
+
+ private isShowPreviewPrintable(containsImage = true) {
+ return containsImage
+ ? (this.showPreview && this.file.image && this.mode !== "file") ||
+ (this.showPreview &&
+ this.file.image &&
+ this.mode === "file" &&
+ this.multiple)
+ : (this.showPreview && !this.file.image && this.mode !== "file") ||
+ (this.showPreview &&
+ !this.file.image &&
+ this.mode === "file" &&
+ this.multiple);
+ }
+
+ private isErrorPrintable() {
+ return (
+ this.hasError &&
+ ((this.multiple && this.mode === "file") || this.mode !== "file")
+ );
+ }
+
+ getIconAriaLabel() {
+ if (this.file.data.type.includes("video")) {
+ return "video";
+ }
+ if (this.file.data.type.includes("audio")) {
+ return "audio";
+ }
+ if (this.file.data.type.includes("image")) {
+ return "image";
+ }
+ return "file";
+ }
+
+ getRemoveAriaLabel() {
+ return "Remove " + this.file.data.name;
+ }
+
+ getDynamicStyle(inputs) {
+ return css`
+ height: ${this.hasError
+ ? inputs.mode !== "file"
+ ? "fit-content"
+ : !inputs.multiple
+ ? "40px"
+ : "fit-content"
+ : this.hasShowPreviewImage || this.hasShowPreviewIcon
+ ? "64px"
+ : "40px"};
+ background: ${this.hasError
+ ? "var(--fileInput-errorFileItemBackgroundColor)"
+ : "#ffffff"}
+ 0% 0% no-repeat padding-box;
+ border: var(--fileInput-fileItemBorderThickness)
+ var(--fileInput-fileItemBorderStyle);
+ border-color: ${this.hasError
+ ? "var(--fileInput-errorFileItemBorderColor)"
+ : "var(--fileInput-fileItemBorderColor)"};
+ width: ${inputs.multiple || inputs.mode !== "file" ? "320px" : "230px"};
+ border-radius: var(--fileInput-fileItemBorderRadius);
+ padding: 8px;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: row;
+ margin-top: ${inputs.multiple || inputs.mode !== "file" ? "4px" : ""};
+ margin-left: ${!inputs.multiple && inputs.mode === "file" ? "4px" : ""};
+ .previewContainer {
+ background-color: ${this.hasError
+ ? "var(--fileInput-errorFilePreviewBackgroundColor)"
+ : "var(--fileInput-filePreviewBackgroundColor)"};
+ display: flex;
+ align-items: center;
+ place-content: center;
+ padding: 12px;
+ box-sizing: border-box;
+ height: 48px;
+ width: 48px;
+ svg,
+ img {
+ fill: ${this.hasError
+ ? "var(--fileInput-errorFilePreviewIconColor)"
+ : "var(--fileInput-filePreviewIconColor)"};
+ height: 24px;
+ width: 24px;
+ }
+ }
+ .infoContainer {
+ display: flex;
+ flex-direction: column;
+ width: ${(inputs.showPreview &&
+ inputs.mode === "file" &&
+ !inputs.multiple) ||
+ !inputs.showPreview
+ ? "100% "
+ : "calc(100% - 48px)"};
+ .fileContainer {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ .fileName {
+ color: var(--fileInput-fileNameFontColor);
+ padding-left: 8px;
+ height: 24px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ width: 100%;
+ display: block;
+ overflow: hidden;
+ box-sizing: border-box;
+ font-family: var(--fileInput-fileItemFontFamily);
+ font-size: var(--fileInput-fileItemFontSize);
+ font-weight: var(--fileInput-fileItemFontWeight);
+ line-height: var(--fileInput-fileItemLineHeight);
+ }
+ .fileIcons {
+ display: flex;
+ flex-direction: row;
+ .removeIcon,
+ .errorIcon {
+ display: flex;
+ align-items: center;
+ height: 24px;
+ width: 24px;
+ justify-content: center;
+ }
+ .removeIcon {
+ svg {
+ height: 16px;
+ width: 16px;
+ fill: var(--fileInput-deleteFileItemIconColor);
+ }
+ &:hover,
+ &:active {
+ border-radius: 4px;
+ cursor: pointer;
+ }
+ &:hover {
+ background-color: var(
+ --fileInput-hoverDeleteFileItemBackgroundColor
+ );
+ }
+ &:active {
+ background-color: var(
+ --fileInput-activeDeleteFileItemBackgroundColor
+ );
+ }
+ &:focus {
+ outline: var(--fileInput-focusDeleteFileItemBackgroundColor)
+ auto 1px;
+ }
+ }
+ .errorIcon {
+ margin-right: 4px;
+ svg {
+ height: 18px;
+ width: 18px;
+ fill: #d0011b;
+ }
+ }
+ }
+ }
+ }
+ .errorContainer {
+ width: 100%;
+ padding-left: 8px;
+ box-sizing: border-box;
+ .errorMessage {
+ text-align: left;
+ letter-spacing: 0.37px;
+ color: var(--fileInput-errorMessageFontColor);
+ font-family: var(--fileInput-errorMessageFontFamily);
+ font-size: var(--fileInput-errorMessageFontSize);
+ font-weight: var(--fileInput-errorMessageFontWeight);
+ line-height: var(--fileInput-errorMessageLineHeight);
+ }
+ }
+ `;
+ }
+}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/interfaces/file.interface.ts b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/interfaces/file.interface.ts
new file mode 100644
index 000000000..fbf05333a
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/interfaces/file.interface.ts
@@ -0,0 +1,5 @@
+export interface FileData{
+ data: File;
+ error: string;
+ image: string | ArrayBuffer;
+}
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/interfaces/files.interface.ts b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/interfaces/files.interface.ts
new file mode 100644
index 000000000..383e70714
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/interfaces/files.interface.ts
@@ -0,0 +1,6 @@
+import { FileData } from "./file.interface";
+
+export interface FilesData{
+ files: Array;
+ event: string
+}
\ No newline at end of file
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-file-input/services/files.services.ts b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/services/files.services.ts
new file mode 100644
index 000000000..74106aadb
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/dxc-file-input/services/files.services.ts
@@ -0,0 +1,58 @@
+import { Injectable } from "@angular/core";
+import { BehaviorSubject } from "rxjs";
+import { FileData } from "../interfaces/file.interface";
+import { FilesData } from "../interfaces/files.interface";
+
+@Injectable({
+ providedIn: "root",
+})
+export class FilesService {
+ constructor() {}
+
+ public files: BehaviorSubject = new BehaviorSubject({
+ files: new Array(),
+ event: null,
+ });
+
+
+ addFile(file: FileData) {
+ // Check if exist
+ const existingFile = this.files.value.files.filter(item=> item.data.name === file.data.name);
+ let updatedValue: FileData[];
+
+ if (existingFile.length > 0){
+ updatedValue = this.files.value.files.map(item=> {
+ if (item.data.name === file.data.name){
+ item.data = file.data;
+ item.error = file.error;
+ }
+ return item;
+ } );
+ }else{
+ updatedValue = [...this.files.value.files , file];
+ }
+
+ this.files.next({
+ files: updatedValue,
+ event: "add",
+ });
+ }
+
+ removeFile(file: FileData) {
+ const array: Array = this.files.value.files.filter((item) => {
+ return item.data.name !== file.data.name;
+ });
+
+ this.files.next({
+ files: array,
+ event: "remove",
+ });
+ }
+
+ emptyArrayFiles(){
+ this.files.next({
+ files: [],
+ event: "",
+ });
+ }
+}
diff --git a/projects/dxc-ngx-cdk/src/lib/typings/ng-onchange.ts b/projects/dxc-ngx-cdk/src/lib/typings/ng-onchange.ts
new file mode 100644
index 000000000..f46c2c448
--- /dev/null
+++ b/projects/dxc-ngx-cdk/src/lib/typings/ng-onchange.ts
@@ -0,0 +1,18 @@
+import { Component } from '@angular/core';
+
+type MarkFunctionProperties = {
+ [Key in keyof Component]: Component[Key] extends Function ? never : Key;
+}
+
+type ExcludeFunctionPropertyNames = MarkFunctionProperties[keyof T];
+
+type ExcludeFunctions = Pick>;
+
+export type NgChanges> = {
+ [Key in keyof Props]: {
+ previousValue: Props[Key];
+ currentValue: Props[Key];
+ firstChange: boolean;
+ isFirstChange(): boolean;
+ }
+}
diff --git a/projects/dxc-ngx-cdk/src/public-api.ts b/projects/dxc-ngx-cdk/src/public-api.ts
index 47d8979b0..7e613833d 100644
--- a/projects/dxc-ngx-cdk/src/public-api.ts
+++ b/projects/dxc-ngx-cdk/src/public-api.ts
@@ -144,6 +144,11 @@ export * from './lib/dxc-application-layout/dxc-application-layout-main/dxc-appl
export * from './lib/dxc-application-layout/dxc-application-layout-sidenav/dxc-application-layout-sidenav.component';
export * from './lib/dxc-application-layout/dxc-application-layout-header/dxc-application-layout-header.component';
+
+export * from "./lib/dxc-file-input/dxc-file-input.component";
+export * from "./lib/dxc-file-input/dxc-file-input.module";
+export * from "./lib/dxc-file-input/interfaces/file.interface";
+
// export * from './lib/services/startup/configurationsetup.service';
// export * from './lib/services/httpconfiguration/resourcerequest.service';
// export * from './lib/services/httpconfiguration/httpcall.service';