feature(filuppladdning): Informativ rapport med bilaga. (TV-874)
Merge in TEA/mina-sidor-fa-web from feature/TV-874-fe-lagg-till-mojlighet-att-lagga-upp-fil-och-synka-med-be to develop Squashed commit of the following: commit 2fdc27affebd4310fa432830ae4e2cf19a3669e9 Merge: 2706cd4e609698ebAuthor: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Tue Nov 30 08:29:48 2021 +0100 Merge branch 'develop' into feature/TV-874-fe-lagg-till-mojlighet-att-lagga-upp-fil-och-synka-med-be commit 2706cd4e61b497b50d82518bdfd5e5c310dcc7aa Author: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Mon Nov 29 13:33:24 2021 +0100 Added uploaded file to submitData commit aa477af83df371e7701885aed7418a283fcf0a2d Author: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Mon Nov 29 10:12:34 2021 +0100 Amends commit 8c36a32e3d5f9c7d9530959d341d1ebf3e41be9d Merge: b49a06f385f398caAuthor: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Fri Nov 26 10:28:19 2021 +0100 Merge branch 'develop' into feature/TV-874-fe-lagg-till-mojlighet-att-lagga-upp-fil-och-synka-med-be commit b49a06f36616f2b49b62f66dfc53fb5ee7572273 Author: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Wed Nov 24 09:39:42 2021 +0100 Laddningssnurra för informativ rapport commit 1fb6b51af5325a199b86630a12769ab3ac00f70c Merge: c516a91bf0354d0aAuthor: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Mon Nov 22 14:17:14 2021 +0100 - Merge branch 'develop' into feature/TV-874-fe-lagg-till-mojlighet-att-lagga-upp-fil-och-synka-med-be - Added label text to ui-select to prevent digi-core warning in console and added display:none; to hide it. commit c516a91b8f2aabca7e2393f2ccb997447be5bedf Author: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Mon Nov 22 13:57:55 2021 +0100 Prettier, changed in-parameter type. commit d78e8e867ca3b632e86669af1780b5e5249eacd2 Merge: 1aa96b52a0d10765Author: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Mon Nov 15 09:57:13 2021 +0100 Merge branch 'develop' into feature/TV-874-fe-lagg-till-mojlighet-att-lagga-upp-fil-och-synka-med-be commit 1aa96b5275115f0d60d0c0637b34bfc3174a4b34 Author: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Fri Nov 12 11:20:51 2021 +0100 - Reset package-lock.json - Changed type from unknown to file commit abae56f4e3b9803552d2028f3c55184662181b22 Author: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Fri Nov 12 11:13:35 2021 +0100 File-upload commit b1bc6f931a6fd043573a57cf489cf964796e2943 Merge: 5fe8cace873b6a0cAuthor: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Wed Nov 10 12:49:57 2021 +0100 Merge branch 'develop' into feature/TV-874-fe-lagg-till-mojlighet-att-lagga-upp-fil-och-synka-med-be commit 5fe8cace262a75b75e4bebde6fa4c3effb11e39f Author: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Tue Nov 9 14:21:24 2021 +0100 - Fortsatt utveckling av filuppladdaren - Byt alla af till ui, digi-ng referenser till digi-core/projektets - Ersätt digiNgFormValidatioMmesage till digi-cores - Validering (Måste ha fil) (funkar inte riktigt ännu då valideringens sker på alla kategorier eftersom den finns i modellen. commit bda789b4451983ad69ab6c05a3bad1723b438c2d Author: WP\holno <nikola.holst-nikolic@arbetsformedlingen.se> Date: Mon Nov 1 16:40:16 2021 +0100 File-upload
This commit is contained in:
@@ -57,7 +57,30 @@
|
|||||||
[uiInvalid]="formControlIsInvalid('category')"
|
[uiInvalid]="formControlIsInvalid('category')"
|
||||||
[uiOptions]="categorySelectItems"
|
[uiOptions]="categorySelectItems"
|
||||||
[uiValidationMessage]="categoryFormControl.errors?.required"
|
[uiValidationMessage]="categoryFormControl.errors?.required"
|
||||||
|
(uiOnChange)="categorySelectChange(categoryFormControl.value)"
|
||||||
></ui-select>
|
></ui-select>
|
||||||
|
<ng-container *ngIf="formData$ | async as formData">
|
||||||
|
<ui-file-upload
|
||||||
|
*ngIf="formData.category === 'dokument'"
|
||||||
|
ngDefaultControl
|
||||||
|
[formControl]="fileFormControl"
|
||||||
|
uiId="informativRapportFileUpload"
|
||||||
|
uiUploadBtnText="Välj fil"
|
||||||
|
uiLabel="Bifoga dokument"
|
||||||
|
uiLabelDescription="Exempel på dokument är individuell utvecklingsplan."
|
||||||
|
[uiRequired]="true"
|
||||||
|
[uiInvalid]="fileFormControl.invalid && informativRapportFormGroup.touched"
|
||||||
|
[uiAnnounceIfOptional]="true"
|
||||||
|
uploadBtnAriaLabel="Välj en fil att ladda upp"
|
||||||
|
[uiMultiple]="false"
|
||||||
|
[uiMaxFileSize]="8192"
|
||||||
|
uiAccept="application/pdf"
|
||||||
|
uiName="informativRapportFileUpload"
|
||||||
|
(uiOnUploadFiles)="setUploadedFile($event)"
|
||||||
|
(uiOnRemoveFile)="removeUploadedFile()"
|
||||||
|
[uiInvalidMessage]="fileFormControl.errors?.required"
|
||||||
|
></ui-file-upload>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div class="informativ-rapport-form__form-item">
|
<div class="informativ-rapport-form__form-item">
|
||||||
<ui-textarea
|
<ui-textarea
|
||||||
@@ -71,7 +94,6 @@
|
|||||||
[uiMaxLength]="2000"
|
[uiMaxLength]="2000"
|
||||||
></ui-textarea>
|
></ui-textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="informativ-rapport-form__footer">
|
<footer class="informativ-rapport-form__footer">
|
||||||
<div class="informativ-rapport-form__cta-wrapper">
|
<div class="informativ-rapport-form__cta-wrapper">
|
||||||
<digi-button af-type="submit" af-size="m">Förhandsgranska</digi-button>
|
<digi-button af-type="submit" af-size="m">Förhandsgranska</digi-button>
|
||||||
@@ -81,7 +103,6 @@
|
|||||||
</ui-link-button>
|
</ui-link-button>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<digi-ng-dialog
|
<digi-ng-dialog
|
||||||
[afActive]="confirmDialogOpen$ | async"
|
[afActive]="confirmDialogOpen$ | async"
|
||||||
(afOnPrimaryClick)="submitAndCloseConfirmDialog()"
|
(afOnPrimaryClick)="submitAndCloseConfirmDialog()"
|
||||||
@@ -101,6 +122,14 @@
|
|||||||
<dd>{{getInformativRapportCategory(submitData.category)}}</dd>
|
<dd>{{getInformativRapportCategory(submitData.category)}}</dd>
|
||||||
<dt>Kompletterande information</dt>
|
<dt>Kompletterande information</dt>
|
||||||
<dd>{{submitData.comment}}</dd>
|
<dd>{{submitData.comment}}</dd>
|
||||||
|
<ng-container *ngIf="submitData.file?.size">
|
||||||
|
<dt>Dokument</dt>
|
||||||
|
<dd>
|
||||||
|
<a [href]="getInformativRapportFileUrl(submitData.file)" [download]="submitData.file?.name">
|
||||||
|
{{submitData.file?.name}}
|
||||||
|
</a>
|
||||||
|
</dd>
|
||||||
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</msfa-report-description-list>
|
</msfa-report-description-list>
|
||||||
<digi-notification-alert
|
<digi-notification-alert
|
||||||
@@ -123,8 +152,6 @@
|
|||||||
afAriaLabel="Här står det mer detaljerad information för vad kategorierna innebär"
|
afAriaLabel="Här står det mer detaljerad information för vad kategorierna innebär"
|
||||||
id="category-information"
|
id="category-information"
|
||||||
>
|
>
|
||||||
<!-- <h3>Skicka dokumentation till Arbetsförmedlingen</h3>
|
|
||||||
<p>Skicka in dokumentation som Arbetsförmedlingen begärt.</p> -->
|
|
||||||
<h3>Behov av annan insats</h3>
|
<h3>Behov av annan insats</h3>
|
||||||
<p>
|
<p>
|
||||||
Informera om att deltagaren kan behöva en arbetsmarknadspolitisk insats, exempelvis utbildning,
|
Informera om att deltagaren kan behöva en arbetsmarknadspolitisk insats, exempelvis utbildning,
|
||||||
@@ -142,6 +169,8 @@
|
|||||||
Informera om att det behövs dialog gällande deltagarens ärende, exempelvis inför eventuellt
|
Informera om att det behövs dialog gällande deltagarens ärende, exempelvis inför eventuellt
|
||||||
stöd/anpassning eller hälsa.
|
stöd/anpassning eller hälsa.
|
||||||
</p>
|
</p>
|
||||||
|
<h3>Skicka dokumentation till Arbetsförmedlingen</h3>
|
||||||
|
<p>Skicka in dokumentation som Arbetsförmedlingen begärt.</p>
|
||||||
</digi-ng-dialog>
|
</digi-ng-dialog>
|
||||||
</form>
|
</form>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|||||||
import { FormControl, FormGroup } from '@angular/forms';
|
import { FormControl, FormGroup } from '@angular/forms';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { InformativRapportCategory, InformativRapportCategoryKey } from '@msfa-enums/informativ-rapport-category.enum';
|
import { InformativRapportCategory, InformativRapportCategoryKey } from '@msfa-enums/informativ-rapport-category.enum';
|
||||||
import { InformativRapportRequest } from '@msfa-models/api/informativ-rapport.request.model';
|
import {
|
||||||
|
InformativRapportRequest,
|
||||||
|
InformativRapportWithFileRequest,
|
||||||
|
} from '@msfa-models/api/informativ-rapport.request.model';
|
||||||
import { DeltagareAvrop } from '@msfa-models/avrop.model';
|
import { DeltagareAvrop } from '@msfa-models/avrop.model';
|
||||||
import { CustomError } from '@msfa-models/error/custom-error';
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
import { RequiredValidator } from '@msfa-utils/validators/required.validator';
|
import { RequiredValidator } from '@msfa-utils/validators/required.validator';
|
||||||
@@ -11,6 +14,7 @@ import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
|||||||
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
|
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
|
||||||
import { InformativRapportFormData } from './informativ-rapport-form.model';
|
import { InformativRapportFormData } from './informativ-rapport-form.model';
|
||||||
import { InformativRapportFormService } from './informativ-rapport-form.service';
|
import { InformativRapportFormService } from './informativ-rapport-form.service';
|
||||||
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-informativ-rapport-form',
|
selector: 'msfa-informativ-rapport-form',
|
||||||
@@ -19,6 +23,7 @@ import { InformativRapportFormService } from './informativ-rapport-form.service'
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class InformativRapportFormComponent {
|
export class InformativRapportFormComponent {
|
||||||
|
categoryDocumentSelected = false;
|
||||||
shouldValidate$ = new BehaviorSubject<boolean>(false);
|
shouldValidate$ = new BehaviorSubject<boolean>(false);
|
||||||
confirmDialogOpen$ = new BehaviorSubject<boolean>(false);
|
confirmDialogOpen$ = new BehaviorSubject<boolean>(false);
|
||||||
categoryInformationDialogOpen$ = new BehaviorSubject<boolean>(false);
|
categoryInformationDialogOpen$ = new BehaviorSubject<boolean>(false);
|
||||||
@@ -36,6 +41,7 @@ export class InformativRapportFormComponent {
|
|||||||
informativRapportFormGroup = new FormGroup({
|
informativRapportFormGroup = new FormGroup({
|
||||||
category: new FormControl(null, [RequiredValidator('Ett ärende måste väljas')]),
|
category: new FormControl(null, [RequiredValidator('Ett ärende måste väljas')]),
|
||||||
comment: new FormControl('', [RequiredValidator('Kompletterande information är obligatoriskt')]),
|
comment: new FormControl('', [RequiredValidator('Kompletterande information är obligatoriskt')]),
|
||||||
|
file: new FormControl(null),
|
||||||
});
|
});
|
||||||
categorySelectItems: SelectOption[] = Object.entries(InformativRapportCategory).map(([value, name]) => ({
|
categorySelectItems: SelectOption[] = Object.entries(InformativRapportCategory).map(([value, name]) => ({
|
||||||
name,
|
name,
|
||||||
@@ -51,7 +57,8 @@ export class InformativRapportFormComponent {
|
|||||||
);
|
);
|
||||||
constructor(
|
constructor(
|
||||||
private informativRapportFormService: InformativRapportFormService,
|
private informativRapportFormService: InformativRapportFormService,
|
||||||
private activatedRoute: ActivatedRoute
|
private activatedRoute: ActivatedRoute,
|
||||||
|
private domSanitizer: DomSanitizer
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get categoryFormControl(): FormControl {
|
get categoryFormControl(): FormControl {
|
||||||
@@ -60,23 +67,41 @@ export class InformativRapportFormComponent {
|
|||||||
get commentFormControl(): FormControl {
|
get commentFormControl(): FormControl {
|
||||||
return this.informativRapportFormGroup.get('comment') as FormControl;
|
return this.informativRapportFormGroup.get('comment') as FormControl;
|
||||||
}
|
}
|
||||||
|
get fileFormControl(): FormControl {
|
||||||
|
return this.informativRapportFormGroup.get('file') as FormControl;
|
||||||
|
}
|
||||||
|
|
||||||
private _formDataToSubmitData(
|
private _formDataToSubmitData(
|
||||||
genomforandeReferens: number,
|
genomforandeReferens: number,
|
||||||
formData: InformativRapportFormData
|
formData: InformativRapportFormData
|
||||||
): InformativRapportRequest {
|
): InformativRapportRequest | InformativRapportWithFileRequest {
|
||||||
const { category, comment } = formData;
|
const { category, comment } = formData;
|
||||||
return {
|
|
||||||
genomforandeReferens,
|
const file: File = (this.fileFormControl?.value as File) ?? null;
|
||||||
category,
|
|
||||||
comment,
|
return this.categoryDocumentSelected
|
||||||
};
|
? { genomforandeReferens, category, comment, file }
|
||||||
|
: { genomforandeReferens, category, comment };
|
||||||
|
}
|
||||||
|
|
||||||
|
getInformativRapportFileUrl(doc: File): string {
|
||||||
|
const url = URL.createObjectURL(new Blob([doc]));
|
||||||
|
return this.domSanitizer.bypassSecurityTrustUrl(url) as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _markFormAsTouchedAndDirty(): void {
|
private _markFormAsTouchedAndDirty(): void {
|
||||||
this.informativRapportFormGroup.markAllAsTouched();
|
this.informativRapportFormGroup.markAllAsTouched();
|
||||||
this.commentFormControl.markAsDirty();
|
this.commentFormControl.markAsDirty();
|
||||||
this.categoryFormControl.markAsDirty();
|
this.categoryFormControl.markAsDirty();
|
||||||
|
this.fileFormControl?.markAsDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
setUploadedFile(file: File): void {
|
||||||
|
this.fileFormControl.setValue(file[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeUploadedFile(): void {
|
||||||
|
this.fileFormControl.setValue(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
getInformativRapportCategory(category: InformativRapportCategoryKey): InformativRapportCategory {
|
getInformativRapportCategory(category: InformativRapportCategoryKey): InformativRapportCategory {
|
||||||
@@ -95,6 +120,18 @@ export class InformativRapportFormComponent {
|
|||||||
this.categoryInformationDialogOpen$.next(false);
|
this.categoryInformationDialogOpen$.next(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
categorySelectChange(category: string): void {
|
||||||
|
this.categoryDocumentSelected = category === 'dokument';
|
||||||
|
|
||||||
|
this.informativRapportFormGroup
|
||||||
|
.get('file')
|
||||||
|
.setValidators(this.categoryDocumentSelected ? [RequiredValidator('En fil måste väljas')] : null);
|
||||||
|
|
||||||
|
if (!this.categoryDocumentSelected) {
|
||||||
|
this.removeUploadedFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
openConfirmDialog(): void {
|
openConfirmDialog(): void {
|
||||||
this.shouldValidate$.next(true);
|
this.shouldValidate$.next(true);
|
||||||
this._markFormAsTouchedAndDirty();
|
this._markFormAsTouchedAndDirty();
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import { ReportDescriptionListModule } from '../../../components/report-descript
|
|||||||
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
|
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
|
||||||
import { InformativRapportFormComponent } from './informativ-rapport-form.component';
|
import { InformativRapportFormComponent } from './informativ-rapport-form.component';
|
||||||
import { InformativRapportFormService } from './informativ-rapport-form.service';
|
import { InformativRapportFormService } from './informativ-rapport-form.service';
|
||||||
|
import { UiFileUploadModule } from '@ui/file-upload/file-upload.module';
|
||||||
import { UiSelectModule } from '@ui/select/select.module';
|
import { UiSelectModule } from '@ui/select/select.module';
|
||||||
|
import { UiLoaderModule } from '@ui/loader/loader.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
@@ -31,7 +33,9 @@ import { UiSelectModule } from '@ui/select/select.module';
|
|||||||
UiSelectModule,
|
UiSelectModule,
|
||||||
DigiNgDialogModule,
|
DigiNgDialogModule,
|
||||||
UiTextareaModule,
|
UiTextareaModule,
|
||||||
|
UiFileUploadModule,
|
||||||
UiLinkButtonModule,
|
UiLinkButtonModule,
|
||||||
|
UiLoaderModule,
|
||||||
],
|
],
|
||||||
providers: [InformativRapportFormService],
|
providers: [InformativRapportFormService],
|
||||||
exports: [InformativRapportFormComponent],
|
exports: [InformativRapportFormComponent],
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export enum InformativRapportCategory {
|
|||||||
hjalpmedel = 'Behov av hjälpmedel',
|
hjalpmedel = 'Behov av hjälpmedel',
|
||||||
teckensprakstolk = 'Behov av teckenspråkstolk',
|
teckensprakstolk = 'Behov av teckenspråkstolk',
|
||||||
dialog = 'Behov av dialog med Arbetsförmedlingen',
|
dialog = 'Behov av dialog med Arbetsförmedlingen',
|
||||||
|
dokument = 'Skicka dokumentation till Arbetsförmedlingen',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InformativRapportCategoryKey = keyof InformativRapportCategory;
|
export type InformativRapportCategoryKey = keyof InformativRapportCategory;
|
||||||
|
|||||||
@@ -4,3 +4,10 @@ export interface InformativRapportRequest {
|
|||||||
category: InformativRapportCategoryKey;
|
category: InformativRapportCategoryKey;
|
||||||
comment: string;
|
comment: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface InformativRapportWithFileRequest {
|
||||||
|
genomforandeReferens: number;
|
||||||
|
category: InformativRapportCategoryKey;
|
||||||
|
comment: string;
|
||||||
|
file: File;
|
||||||
|
}
|
||||||
|
|||||||
52
libs/ui/src/file-upload/file-upload.component.html
Normal file
52
libs/ui/src/file-upload/file-upload.component.html
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<div class="ui-file-upload">
|
||||||
|
<digi-form-label
|
||||||
|
class="ui-file-upload__label"
|
||||||
|
[afFor]="uiId"
|
||||||
|
[afLabel]="uiLabel"
|
||||||
|
[afRequired]="uiRequired"
|
||||||
|
[afDescription]="uiLabelDescription"
|
||||||
|
[afAnnounceIfOptional]="uiAnnounceIfOptional"
|
||||||
|
></digi-form-label>
|
||||||
|
<digi-button
|
||||||
|
*ngIf="uiMultiple || files.length < 1"
|
||||||
|
af-variation="secondary"
|
||||||
|
[afAriaLabel]="uploadBtnAriaLabel"
|
||||||
|
(afOnClick)="fileInput.click()"
|
||||||
|
>
|
||||||
|
{{ uiUploadBtnText }}
|
||||||
|
<ui-icon [uiType]="iconType.PAPERCLIP" slot="icon"></ui-icon>
|
||||||
|
<input
|
||||||
|
#fileInput
|
||||||
|
(change)="onUploadFileHandler($event)"
|
||||||
|
[id]="uiId"
|
||||||
|
class="ui-file-upload__input"
|
||||||
|
[attr.multiple]="uiMultiple"
|
||||||
|
type="file"
|
||||||
|
[attr.accept]="uiAccept"
|
||||||
|
[attr.name]="uiName"
|
||||||
|
[attr.required]="uiRequired"
|
||||||
|
/>
|
||||||
|
</digi-button>
|
||||||
|
<digi-form-validation-message class="ui-file-upload__validation-message" *ngIf="uiInvalid" af-variation="error">
|
||||||
|
{{ this.uiInvalidMessage }}
|
||||||
|
</digi-form-validation-message>
|
||||||
|
<p class="ui-file-upload__status-message" #statusMessage aria-live="polite"></p>
|
||||||
|
<dl *ngIf="files.length" class="ui-file-upload__file-row-container">
|
||||||
|
<dt>
|
||||||
|
<h2 class="msfa__a11y-sr-only">Uppladdade filer</h2>
|
||||||
|
</dt>
|
||||||
|
<dd *ngFor="let file of files" class="ui-file-upload__file-row">
|
||||||
|
<p class="ui-file-upload__file-name">{{ file.name }}</p>
|
||||||
|
<digi-button
|
||||||
|
(afOnClick)="onRemoveFileHandler(file['id'])"
|
||||||
|
class="ui-file-upload__file-row-btn"
|
||||||
|
[afAriaLabel]="'Ta bort ' + file.name"
|
||||||
|
af-size="s"
|
||||||
|
af-variation="secondary"
|
||||||
|
>
|
||||||
|
<ui-icon size="m" class="ui-file-upload__icon-remove" [icon]="iconType.X" slot="icon"></ui-icon>
|
||||||
|
Ta bort
|
||||||
|
</digi-button>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
61
libs/ui/src/file-upload/file-upload.component.scss
Normal file
61
libs/ui/src/file-upload/file-upload.component.scss
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
@import 'variables/gutters';
|
||||||
|
|
||||||
|
$ui__color--gray-20: #efefef;
|
||||||
|
|
||||||
|
.ui-file-upload {
|
||||||
|
&__label {
|
||||||
|
::ng-deep .digi-form-label {
|
||||||
|
margin-bottom: $digi--layout--gutter--s;
|
||||||
|
margin-top: $digi--layout--gutter--m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__file-row {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: $digi--layout--gutter;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__file-row-container {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-top: $digi--layout--gutter--l;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__file-name {
|
||||||
|
font-family: var(--digi--typography--font-family);
|
||||||
|
flex: 1;
|
||||||
|
background-color: $ui__color--gray-20;
|
||||||
|
color: var(--digi--typography--color--text);
|
||||||
|
max-width: u(60);
|
||||||
|
padding: u(1);
|
||||||
|
font-weight: var(--digi--typography--font-weight--semibold);
|
||||||
|
margin-right: u(1.6);
|
||||||
|
margin-bottom: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__status-message {
|
||||||
|
clip: rect(0 0 0 0);
|
||||||
|
clip-path: inset(50%);
|
||||||
|
height: 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon-remove {
|
||||||
|
--digi--ui--width--icon: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__validation-message {
|
||||||
|
display: block;
|
||||||
|
margin-top: var(--digi--layout--gutter--s);
|
||||||
|
}
|
||||||
|
}
|
||||||
190
libs/ui/src/file-upload/file-upload.component.ts
Normal file
190
libs/ui/src/file-upload/file-upload.component.ts
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
EventEmitter,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
ViewChild,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { randomIdGenerator } from '@digi/core/dist/collection/global/utils/randomIdGenerator';
|
||||||
|
import { UiIconType } from '@ui/icon/icon-type.enum';
|
||||||
|
/**
|
||||||
|
* This component is used for uploading files. It restyles an input type file and emits events when a file is added or
|
||||||
|
* removed. It is up to the implementor to actually save the file to a backend.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ui-file-upload',
|
||||||
|
templateUrl: './file-upload.component.html',
|
||||||
|
styleUrls: ['./file-upload.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class FileUploadComponent {
|
||||||
|
iconType = UiIconType;
|
||||||
|
/**
|
||||||
|
* Label for button
|
||||||
|
*/
|
||||||
|
@Input() uiUploadBtnText = 'Välj filer';
|
||||||
|
/**
|
||||||
|
* If multiple file upload
|
||||||
|
*/
|
||||||
|
@Input() uiMultiple = false;
|
||||||
|
/**
|
||||||
|
* UiHeader is used to set the header to the component
|
||||||
|
*/
|
||||||
|
@Input() uiLabel?: string;
|
||||||
|
/**
|
||||||
|
* If invalid
|
||||||
|
*/
|
||||||
|
@Input() uiInvalid = false;
|
||||||
|
/**
|
||||||
|
* UiText is used to set the information text
|
||||||
|
*/
|
||||||
|
@Input() uiLabelDescription?: string;
|
||||||
|
/**
|
||||||
|
* ID for the input
|
||||||
|
*/
|
||||||
|
@Input() uiId: string = randomIdGenerator('ui-fileupload');
|
||||||
|
/**
|
||||||
|
* uiSecondary is to set outlined or contained variant on the button.
|
||||||
|
* Defaults to true.
|
||||||
|
*/
|
||||||
|
@Input() uiSecondary = false;
|
||||||
|
/**
|
||||||
|
* Maps directly to the input field's accept attribute. Use this to limit accepted file types.
|
||||||
|
*/
|
||||||
|
@Input() uiAccept?: string;
|
||||||
|
/**
|
||||||
|
* Maps directly to the input field's name attribute.
|
||||||
|
*/
|
||||||
|
@Input() uiName?: string;
|
||||||
|
/**
|
||||||
|
* Is this a required form field?
|
||||||
|
*/
|
||||||
|
@Input() uiRequired = false;
|
||||||
|
/**
|
||||||
|
* Max file size allowed in kb
|
||||||
|
*/
|
||||||
|
@Input() uiMaxFileSize: number;
|
||||||
|
/**
|
||||||
|
* Normally a required field is visually and semantically emphasized.
|
||||||
|
* This flips that and emphasizes the field if it is not required.
|
||||||
|
* Set this to true if your form contains more required fields than not.
|
||||||
|
*/
|
||||||
|
@Input() uiAnnounceIfOptional = false;
|
||||||
|
/**
|
||||||
|
* Invalid message
|
||||||
|
*/
|
||||||
|
@Input() uiInvalidMessage?: string;
|
||||||
|
/**
|
||||||
|
* UiOnUploadFiles is a function which emits all selected files.
|
||||||
|
*/
|
||||||
|
@Output() uiOnUploadFiles: EventEmitter<File[]> = new EventEmitter<File[]>();
|
||||||
|
/**
|
||||||
|
* UiOnRemoveFile emits when the selected file is successfully removed.
|
||||||
|
* Emits the updated array with files.
|
||||||
|
*/
|
||||||
|
@Output() uiOnRemoveFile: EventEmitter<File> = new EventEmitter<File>();
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
@ViewChild('fileInput') fileInput: HTMLInputElement;
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
@ViewChild('statusMessage') statusMessage: ElementRef;
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
files: File[] = [];
|
||||||
|
|
||||||
|
constructor(private changeDetector: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
onUploadFileHandler(event: any): void {
|
||||||
|
if (!event.target.files) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.uiMaxFileSize) {
|
||||||
|
if (this.convertBytesToKb(2, event.target.files[0].size) > this.uiMaxFileSize) {
|
||||||
|
this.uiInvalid = true;
|
||||||
|
this.uiInvalidMessage = `
|
||||||
|
Filen överskrider max tillåten storlek på
|
||||||
|
${this.convertKbToMb(this.uiMaxFileSize)} MB
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.uiInvalid = false;
|
||||||
|
this.uiInvalidMessage = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.files = [...this.files, ...event.target.files];
|
||||||
|
this.files.forEach(file => (file['id'] = randomIdGenerator('file')));
|
||||||
|
this.uiOnUploadFiles.emit(this.files);
|
||||||
|
|
||||||
|
const fileName = event.target.files[0] ? event.target.files[0].name : '';
|
||||||
|
this.updateStatusMessage('add', fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
convertBytesToKb(decimals: number, fileSize: number): number {
|
||||||
|
const k = 1024;
|
||||||
|
const dm = decimals < 0 ? 0 : decimals;
|
||||||
|
const i = Math.floor(Math.log(fileSize) / Math.log(k));
|
||||||
|
return parseFloat((fileSize / Math.pow(k, i)).toFixed(dm));
|
||||||
|
}
|
||||||
|
|
||||||
|
convertKbToMb(size: number): string {
|
||||||
|
return (size / 1024).toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
onRemoveFileHandler(id: string) {
|
||||||
|
const index = this.files.findIndex(file => file['id'] === id);
|
||||||
|
if (index === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fileToRemove = this.files[index];
|
||||||
|
this.files = [...this.files.slice(0, index), ...this.files.slice(index + 1)];
|
||||||
|
this.uiOnRemoveFile.emit(fileToRemove);
|
||||||
|
this.changeDetector.detectChanges();
|
||||||
|
|
||||||
|
this.updateStatusMessage('remove');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
updateStatusMessage(type: string, fileName: string = '') {
|
||||||
|
const statusMessage = this.statusMessage.nativeElement;
|
||||||
|
statusMessage.textContent = '';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
switch (type) {
|
||||||
|
case 'remove':
|
||||||
|
statusMessage.textContent =
|
||||||
|
this.files.length > 0 ? 'Fil borttagen' : 'Fil borttagen. Du har inga filer uppladdade';
|
||||||
|
break;
|
||||||
|
case 'add':
|
||||||
|
statusMessage.textContent = `Fil uppladdad ${fileName}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
statusMessage.textContent = '';
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*/
|
||||||
|
get uploadBtnAriaLabel() {
|
||||||
|
return this.files.length > 0 && this.uiMultiple ? 'Välj ny fil' : 'Välj fil';
|
||||||
|
}
|
||||||
|
}
|
||||||
15
libs/ui/src/file-upload/file-upload.module.ts
Normal file
15
libs/ui/src/file-upload/file-upload.module.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
import { FileUploadComponent } from './file-upload.component';
|
||||||
|
import { UiIconModule } from '@ui/icon/icon.module';
|
||||||
|
|
||||||
|
const COMPONENTS = [FileUploadComponent];
|
||||||
|
const MODULES = [CommonModule];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
imports: [...MODULES, UiIconModule],
|
||||||
|
declarations: [...COMPONENTS],
|
||||||
|
exports: [...COMPONENTS],
|
||||||
|
})
|
||||||
|
export class UiFileUploadModule {}
|
||||||
@@ -22,4 +22,5 @@ export enum UiIconType {
|
|||||||
EYE = 'eye',
|
EYE = 'eye',
|
||||||
EYESLASH = 'eyeslash',
|
EYESLASH = 'eyeslash',
|
||||||
ARCHIVE = 'archive',
|
ARCHIVE = 'archive',
|
||||||
|
PAPERCLIP = 'paperclip'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,5 +99,6 @@
|
|||||||
<digi-icon-eye *ngSwitchCase="iconType.EYE" [ngClass]="iconClass"></digi-icon-eye>
|
<digi-icon-eye *ngSwitchCase="iconType.EYE" [ngClass]="iconClass"></digi-icon-eye>
|
||||||
<digi-icon-eye-slash *ngSwitchCase="iconType.EYESLASH" [ngClass]="iconClass"></digi-icon-eye-slash>
|
<digi-icon-eye-slash *ngSwitchCase="iconType.EYESLASH" [ngClass]="iconClass"></digi-icon-eye-slash>
|
||||||
<digi-icon-archive *ngSwitchCase="iconType.ARCHIVE" [ngClass]="iconClass"></digi-icon-archive>
|
<digi-icon-archive *ngSwitchCase="iconType.ARCHIVE" [ngClass]="iconClass"></digi-icon-archive>
|
||||||
|
<digi-icon-paperclip *ngSwitchCase="iconType.PAPERCLIP" [ngClass]="iconClass"></digi-icon-paperclip>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
757
package-lock.json
generated
757
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user