### File

`src/lib/upload-button/upload-button.component.ts`

### Implements

`ControlValueAccessor` `MatFormFieldControl` `OnDestroy`

### Metadata

changeDetection | `ChangeDetectionStrategy.OnPush`
host | `{ }`
providers | `{ provide: MatFormFieldControl, useExisting: UploadButtonComponent, }`
selector | `rxap-upload-button`
standalone | `true`
imports | `MatButtonModule` `CdkOverlayOrigin` `MethodDirective` `MatIconModule` `NgIf` `MatProgressSpinnerModule` `MatTooltipModule` `CdkConnectedOverlay` `AsyncPipe` `ReadAsDataURLPipe` `MatSnackBarModule`
styleUrls | `./upload-button.component.scss`
templateUrl | `./upload-button.component.html`

### Index

###### Properties
Optional autofilled Optional controlType errorState focused Public isOpen Static nextId Public Readonly ngControl Public positions shouldLabelFloat Public Readonly snackBar Public stateChanges touched Optional userAriaDescribedBy Public value
###### Methods
download Public onContainerClick onFileInputChange onFocusIn onFocusOut Public openOverlay Public registerOnChange Public registerOnTouched Public setDescribedByIds Public setDisabledState Public writeValue
###### Inputs
accept allowDownload disabled placeholder required
###### Outputs
uploaded
###### HostListeners
focusin focusout
###### Accessors
isImage placeholder empty id

### Constructor

`constructor()`
Defined in src/lib/upload-button/upload-button.component.ts:113

### Inputs

accept
Type : `string`
Default value : `'**/**'`
Defined in src/lib/upload-button/upload-button.component.ts:73

allowDownload
Type : `boolean`
Default value : `false`
Defined in src/lib/upload-button/upload-button.component.ts:97

disabled
Type : `boolean`
Default value : `false`
Defined in src/lib/upload-button/upload-button.component.ts:78

placeholder
Type : `string`
Defined in src/lib/upload-button/upload-button.component.ts:132

required
Type : `boolean`
Default value : `false`
Defined in src/lib/upload-button/upload-button.component.ts:95

### Outputs

uploaded
Type : `EventEmitter`
Defined in src/lib/upload-button/upload-button.component.ts:76

### HostListeners

focusin
Arguments : `'$event'`
`focusin(event: FocusEvent)`
Defined in src/lib/upload-button/upload-button.component.ts:188

focusout
Arguments : `'$event'`
`focusout(event: FocusEvent)`
Defined in src/lib/upload-button/upload-button.component.ts:196

### Methods

download
`download()`
Defined in src/lib/upload-button/upload-button.component.ts:207
Returns : `void`

Public onContainerClick
`onContainerClick(event: MouseEvent)`
Defined in src/lib/upload-button/upload-button.component.ts:146
Parameters :
Name | Type | Optional
event | `MouseEvent` | No
Returns : `void`

onFileInputChange
`onFileInputChange($event: any)`
Defined in src/lib/upload-button/upload-button.component.ts:218
Parameters :
Name | Type | Optional
$event | `any` | No
Returns : `void`

onFocusIn
`onFocusIn(event: FocusEvent)`
Decorators : `@HostListener('focusin', ['$event'])`
Defined in src/lib/upload-button/upload-button.component.ts:188
Parameters :
Name | Type | Optional
event | `FocusEvent` | No
Returns : `void`

onFocusOut
`onFocusOut(event: FocusEvent)`
Decorators : `@HostListener('focusout', ['$event'])`
Defined in src/lib/upload-button/upload-button.component.ts:196
Parameters :
Name | Type | Optional
event | `FocusEvent` | No
Returns : `void`

Public openOverlay
`openOverlay()`
Defined in src/lib/upload-button/upload-button.component.ts:178
Returns : `void`

Public registerOnChange
`registerOnChange(fn: any)`
Defined in src/lib/upload-button/upload-button.component.ts:161
Parameters :
Name | Type | Optional
fn | `any` | No
Returns : `void`

Public registerOnTouched
`registerOnTouched(fn: any)`
Defined in src/lib/upload-button/upload-button.component.ts:165
Parameters :
Name | Type | Optional
fn | `any` | No
Returns : `void`

Public setDescribedByIds
`setDescribedByIds(ids: string[])`
Defined in src/lib/upload-button/upload-button.component.ts:150
Parameters :
Name | Type | Optional
ids | `string[]` | No
Returns : `void`

Public setDisabledState
`setDisabledState(isDisabled: boolean)`
Defined in src/lib/upload-button/upload-button.component.ts:169
Parameters :
Name | Type | Optional
isDisabled | `boolean` | No
Returns : `void`

Public writeValue
`writeValue(file: File)`
Defined in src/lib/upload-button/upload-button.component.ts:173
Parameters :
Name | Type | Optional
file | `File` | No
Returns : `void`

### Properties

Optional autofilled
Type : `boolean | undefined`
Defined in src/lib/upload-button/upload-button.component.ts:90

Optional controlType
Type : `string | undefined`
Default value : `'rxap-upload-button'`
Defined in src/lib/upload-button/upload-button.component.ts:91

errorState
Default value : `false`
Defined in src/lib/upload-button/upload-button.component.ts:92

focused
Default value : `false`
Defined in src/lib/upload-button/upload-button.component.ts:93

Public isOpen
Default value : `false`
Defined in src/lib/upload-button/upload-button.component.ts:79

Static nextId
Type : `number`
Default value : `0`
Defined in src/lib/upload-button/upload-button.component.ts:70

Public Readonly ngControl
Default value : `inject(NgControl, { optional: true, self: true, })`
Defined in src/lib/upload-button/upload-button.component.ts:107

Public positions
Type : `ConnectedPosition[]`
Default value : `[ { originY: 'bottom', originX: 'start', overlayX: 'start', overlayY: 'top', offsetY: 12, }, ]`
Defined in src/lib/upload-button/upload-button.component.ts:80

shouldLabelFloat
Default value : `true`
Defined in src/lib/upload-button/upload-button.component.ts:98

Public Readonly snackBar
Default value : `inject(MatSnackBar)`
Defined in src/lib/upload-button/upload-button.component.ts:105

Public stateChanges
Default value : `new Subject<void>()`
Defined in src/lib/upload-button/upload-button.component.ts:99

touched
Default value : `false`
Defined in src/lib/upload-button/upload-button.component.ts:89

Optional userAriaDescribedBy
Type : `string | undefined`
Defined in src/lib/upload-button/upload-button.component.ts:100

Public value
Type : `File | null`
Default value : `null`
Defined in src/lib/upload-button/upload-button.component.ts:101

### Accessors

isImage
get `isImage()`
Defined in src/lib/upload-button/upload-button.component.ts:121

placeholder
get `placeholder()`
Defined in src/lib/upload-button/upload-button.component.ts:127
set `placeholder(plh)`
Defined in src/lib/upload-button/upload-button.component.ts:132
Parameters :
Name | Optional
plh | No
Returns : `void`

empty
get `empty()`
Defined in src/lib/upload-button/upload-button.component.ts:137

id
get `id()`
Defined in src/lib/upload-button/upload-button.component.ts:141

    import {
      CdkConnectedOverlay,
      CdkOverlayOrigin,
      ConnectedPosition,
    } from '@angular/cdk/overlay';
    import {
      AsyncPipe,
      DOCUMENT,
      NgIf,
    } from '@angular/common';
    import {
      ChangeDetectionStrategy,
      ChangeDetectorRef,
      Component,
      ElementRef,
      EventEmitter,
      HostListener,
      inject,
      Input,
      OnDestroy,
      Output,
    } from '@angular/core';
    import {
      ControlValueAccessor,
      NgControl,
    } from '@angular/forms';
    import { MatButtonModule } from '@angular/material/button';
    import { MatFormFieldControl } from '@angular/material/form-field';
    import { MatIconModule } from '@angular/material/icon';
    import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
    import {
      MatSnackBar,
      MatSnackBarModule,
    } from '@angular/material/snack-bar';
    import { MatTooltipModule } from '@angular/material/tooltip';
    import { MethodDirective } from '@rxap/directives';
    import { Subject } from 'rxjs';
    import { ReadAsDataURLPipe } from './read-as-data-url.pipe';

    @Component({
      selector: 'rxap-upload-button',
      templateUrl: './upload-button.component.html',
      styleUrls: [ './upload-button.component.scss' ],
      changeDetection: ChangeDetectionStrategy.OnPush,
      // eslint-disable-next-line @angular-eslint/no-host-metadata-property
      host: { class: 'rxap-upload-button' },
      providers: [
        {
          provide: MatFormFieldControl,
          useExisting: UploadButtonComponent,
        },
      ],
      standalone: true,
      imports: [
        MatButtonModule,
        CdkOverlayOrigin,
        MethodDirective,
        MatIconModule,
        NgIf,
        MatProgressSpinnerModule,
        MatTooltipModule,
        CdkConnectedOverlay,
        AsyncPipe,
        ReadAsDataURLPipe,
        MatSnackBarModule,
      ],
    })
    export class UploadButtonComponent implements ControlValueAccessor, MatFormFieldControl<File>, OnDestroy {

      static nextId = 0;

      @Input()
      public accept = '**/**';

      @Output()
      public uploaded = new EventEmitter<File>();
      @Input()
      public disabled = false;
      public isOpen = false;
      public positions: ConnectedPosition[] = [
        {
          originY: 'bottom',
          originX: 'start',
          overlayX: 'start',
          overlayY: 'top',
          offsetY: 12,
        },
      ];
      touched = false;
      autofilled?: boolean | undefined;
      controlType?: string | undefined = 'rxap-upload-button';
      errorState = false;
      focused = false;
      @Input()
      public required = false;
      @Input()
      public allowDownload = false;
      shouldLabelFloat = true;
      public stateChanges = new Subject<void>();
      userAriaDescribedBy?: string | undefined;
      public value: File | null = null;
      private onChange?: (file: File) => any;
      private onTouched?: () => any;

      public readonly snackBar = inject(MatSnackBar);

      public readonly ngControl = inject(NgControl, {
        optional: true,
        self: true,
      });
      private readonly cdr = inject(ChangeDetectorRef);
      private readonly _elementRef = inject(ElementRef);
      private readonly document = inject(DOCUMENT);

      constructor() {
        if (this.ngControl != null) {
          this.ngControl.valueAccessor = this;
        }
      }

      get isImage(): boolean {
        return !!this.value?.type.match(/^image\//);
      }

      private _placeholder!: string;

      get placeholder() {
        return this._placeholder;
      }

      @Input()
      set placeholder(plh) {
        this._placeholder = plh;
        this.stateChanges.next();
      }

      public get empty(): boolean {
        return this.value === null;
      }

      public get id(): string {
        return `rxap-upload-button-${ UploadButtonComponent.nextId++ }`;
      }

      // eslint-disable-next-line @typescript-eslint/no-empty-function
      public onContainerClick(event: MouseEvent): void {

      }

      public setDescribedByIds(ids: string[]): void {
        this._elementRef
            .nativeElement
            .querySelector('button')
            ?.setAttribute('aria-describedby', ids.join(' '));
      }

      public ngOnDestroy() {
        this.stateChanges.complete();
      }

      public registerOnChange(fn: any): void {
        this.onChange = fn;
      }

      public registerOnTouched(fn: any): void {
        this.onTouched = fn;
      }

      public setDisabledState(isDisabled: boolean) {
        this.disabled = isDisabled;
      }

      public writeValue(file: File): void {
        this.value = file;
        this.cdr.detectChanges();
      }

      public openOverlay() {
        if (this.value) {
          if (this.value.type.match(/^image\//)) {
            this.isOpen = true;
            this.cdr.detectChanges();
          }
        }
      }

      @HostListener('focusin', [ '$event' ])
      onFocusIn(event: FocusEvent) {
        if (!this.focused) {
          this.focused = true;
          this.stateChanges.next();
        }
      }

      @HostListener('focusout', [ '$event' ])
      onFocusOut(event: FocusEvent) {
        if (!this._elementRef.nativeElement.contains(event.relatedTarget as Element)) {
          this.touched = true;
          this.focused = false;
          if (this.onTouched) {
            this.onTouched();
          }
          this.stateChanges.next();
        }
      }

      download() {
        if (this.value) {
          const elm = this.document.createElement('a');
          this.document.body.appendChild(elm);
          elm.href = URL.createObjectURL(this.value);
          elm.setAttribute('download', this.value.name);
          elm.click();
          this.document.body.removeChild(elm);
        }
      }

      onFileInputChange($event: any) {
        const files: { [key: string]: File } = $event?.target?.files as any ?? {};
        const file = Object.values(files)[0];
        if (!file) {
          this.snackBar.open('No file selected', 'close', { duration: 3000 });
        } else {
          this.uploaded.emit(file);
          this.value = file;
          if (this.onChange) {
            this.onChange(file);
          }
          this.stateChanges.next();
        }
      }
    }

    <div class="flex flex-row gap-2 items-center">
      <button
        #trigger="cdkOverlayOrigin"
        [disabled]="disabled"
        (click)="fileInput.click()"
        cdkOverlayOrigin
        class="grow-0"
        mat-stroked-button
        type="button">
        <span class="flex flex-row justify-center items-center gap-3">
          <mat-icon class="grow-0" svgIcon="upload"></mat-icon>&nbsp;
          <span *ngIf="value; else infoText" class="truncate grow-0">{{value.name}}</span>
          <ng-template #infoText>
            <span *ngIf="placeholder">{{ placeholder }}</span>
            <ng-content></ng-content>
          </ng-template>
        </span>
      </button>
      <button (click)="openOverlay()"
              *ngIf="isImage"
              class="grow-0"
              i18n-matTooltip
              mat-icon-button
              matTooltip="Open image preview"
              type="button">
        <mat-icon>preview</mat-icon>
      </button>
      <button (click)="download()" *ngIf="allowDownload && value" class="grow-0" i18n-matTooltip mat-icon-button
              matTooltip="Download file" type="button">
        <mat-icon svgIcon="download"></mat-icon>
      </button>
    </div>

    <ng-template
      (backdropClick)="isOpen = false"
      [cdkConnectedOverlayHasBackdrop]="true"
      [cdkConnectedOverlayOpen]="isOpen"
      [cdkConnectedOverlayOrigin]="trigger"
      [cdkConnectedOverlayPositions]="positions"
      cdkConnectedOverlay
    >
      <div class="bg-white dark:bg-black drop-shadow-xl overlay-container p-2 rounded overflow-auto">
        <img [src]="value | readAsDataURL | async" class="w-full">
      </div>
    </ng-template>

    <input #fileInput (change)="onFileInputChange($event)" [accept]="accept" [id]="id" class="hidden" type="file">

`./upload-button.component.scss`

    .overlay-container {
      min-width: 256px;
      max-width: 50vw;
      max-height: 50vh;
    }

Legend
Html element
Component
Html element with directive

# results matching ""

# No results matching ""