npm.io
21.0.0 • Published 4 months ago

@eqproject/eqp-attachments

Licence
Version
21.0.0
Deps
4
Size
443 kB
Vulns
0
Weekly
0

Table of Contents

=================

Overview

@eqproject/eqp-attachments provides a complete, client-side solution for attachment management, now fully modernized with the latest Angular features.

Key Features:

  • Single or Multiple Attachments: Easily configure the component to handle one or many attachments.
  • Multiple Sources: Supports local file uploads, direct links from the web, and Dropbox integration.
  • Modern UI: A clean, animated drag-and-drop interface with two layout variants (full and compact).
  • Dual View Mode: Allows users to switch between a responsive Card Grid and a detailed Table View .
  • Image Cropper: A built-in tool for cropping and rotating uploaded images.
  • Client-Side Validation: Set limits on file size, file types, and more.
  • Video Support: Automatic thumbnail generation and optional server-side video compression.
  • Large File Handling: Files exceeding a configurable byte threshold are stored as a native File object instead of Base64, preventing memory issues with large uploads.
  • Extensible Table: Add custom columns and custom "more options" menu actions to the table view.
  • Granular Action Control: Show, hide, or disable any built-in or custom action on a per-row basis via hook functions.
  • Highly Configurable: Almost every label, button, and feature can be customized via inputs.
  • Purely Client-Side: The component manages attachments locally without requiring a backend connection, emitting the final data for you to handle.

Requirements

  • Angular 17+
  • Angular Material installed and configured in your project.
  • Font Awesome for a complete icon set (optional, but recommended for the best visual experience).

Getting Started

Step 1: Install the Package

Shell

npm install --save @eqproject/eqp-attachments
Step 2: Import the Component

The component is standalone , so you can import it directly into your component or module.

TypeScript

// in your-component.ts or app/shared module
import { EqpAttachmentsModule } from '@eqproject/eqp-attachments';

@Component({
  selector: 'my-feature-component',
  standalone: true,
  imports: [ EqpAttachmentsModule ],
  template: `
    <eqp-attachments
      [attachmentsList]="myAttachments"
      (localEditedAttachments)="onAttachmentsChange($event)">
    </eqp-attachments>
  `
})
export class MyFeatureComponent {
  myAttachments: IAttachmentDTO[] = [];

  onAttachmentsChange(updatedList: IAttachmentDTO[]) {
    this.myAttachments = updatedList;
    console.log('Attachments list updated!', this.myAttachments);
  }
}
Step 3: Add Styles (Optional)

For the best icon display, include Font Awesome in your project's styles, for example, in angular.json:

JSON

"styles": [
  "src/styles.scss",
  "node_modules/@fortawesome/fontawesome-free/css/all.min.css"
],

API Reference

Inputs
Core Configuration
Input Type Default Description
attachmentsList IAttachmentDTO[] null The array of attachments to display and manage.
singleAttachment IAttachmentDTO null An alternative to attachmentsList for passing a single attachment object in non-multiple mode.
multipleAttachment boolean true If true, manages a list of attachments. If false, handles only a single attachment.
allowedTypes AttachmentType[] [FILE, LINK] An array of allowed attachment sources (e.g., [1, 2, 3] for File, Link, and Dropbox).

UI & View Configuration
Input Type Default Description
layout 'full' | 'compact' 'compact' 'full' renders a large centered dropbox area; 'compact' renders a slim inline uploader bar.
viewMode 'card' | 'table' 'table' The default display mode for the attachment list.
chooseView boolean true If true, displays the Card/Table view switcher in the header.
showHeader boolean true If true, displays the title in the header when multipleAttachment is true.
showSummary boolean false If true, displays the summary block with file count and total size.
showUploadTitle boolean true If true, shows the title above the drop area.
showDropArea boolean true If false, hides the entire upload/drop area (useful for read-only display).
cardSize 'small' | 'medium' | 'large' | 'custom' 'small' Controls the size of cards in card view. Use 'custom' with customCardWidthPx and customCardHeightPx.
customCardWidthPx number 200 Sets the custom card width in pixels when cardSize is 'custom'.
customCardHeightPx number 180 Sets the custom card height in pixels when cardSize is 'custom'.
showMatCard boolean true If true (and in table mode), the component is rendered inside a mat-card.
cropDialogClass string undefined Assigns a custom CSS class to the image cropper dialog panel.

Table View Configuration
Input Type Default Description
customColumns AttachmentFieldColumn[] [] Array to add custom columns to the table view.
customMenuActions AttachmentMenuAction[] [] Array to add custom actions to the table's "more options" (...) menu.
hiddenColumns string[] [] An array of column key values to hide from the table view.
hiddenActions string[] [] An array of action key values to hide from the actions menu (e.g., ['delete']).
showInlinePreview boolean false If true, shows a thumbnail preview column directly in the table rows.
isEqpTableMultiLanguage boolean false Enables multilanguage support for the embedded table.
tablePaginatorVisible boolean true If false, hides the paginator below the table.
isTableSearcheable boolean true If false, hides the search field above the table.
tablePaginatorSize number null Sets the default page size for the table paginator.

Functional Configuration
Input Type Default Description
maxFileSizeMB number 500 Sets the maximum file size in megabytes for each uploaded file. Files exceeding this limit are rejected with a toast notification.
base64LimitMB number 100 Files exceeding this size (MB) are stored as a native File object in LargeFile instead of being Base64-encoded. See Large File Handling.
loadMultipleFiles boolean false If true, allows selecting multiple files at once from the file browser.
singleAttachmentDragAndDrop boolean true If true, enables the drag-and-drop UI even in single-attachment mode.
showActionButtons boolean false If true, shows Download/Preview/Delete action buttons below the attachment in single-attachment mode.
isDisabled boolean false Disables all "add attachment" controls.
disableAction boolean false If true, disables the Delete action in both card and table views.
allowOnlyImages boolean false If true, restricts file uploads to image types only.
acceptedFileTypes string undefined A string for the <input> accept attribute (e.g., '.pdf,image/*').
showPreview boolean true Enables the preview functionality for attachments.
enableImageCrop boolean true If false, skips the crop dialog and uploads images directly (compression still applies).
compressionOptions IOptions { maxSizeMB: 0.5, maxWidthOrHeight: 1920, useWebWorker: true } Options for client-side image compression.
cropOptions CropOptionEnum[] [1, 2] Enables specific tools in the image cropper (1: Rotate, 2: Flip). Pass [] to show no tools.
separatedUploadButtons boolean false If true and multiple allowedTypes are available, shows separate buttons instead of a dropdown menu.
getAttachmentEndpoint string null API endpoint (POST) to fetch full attachment data (including FileDataBase64) for image previews.
productionBaseUrl string null Base URL used to build a Google Viewer URL for document previews (e.g., PDFs, Word files).
videoCompression object { enabled: false, ... } Configuration for optional server-side video compression. See Video Support.
actionHiddenFn (actionKey: string, att?: IAttachmentDTO) => boolean undefined A global hook to dynamically hide any action (built-in or custom) for a specific attachment. See Dynamic Action Control.
actionDisabledFn (actionKey: string, att?: IAttachmentDTO) => boolean undefined A global hook to dynamically disable any action for a specific attachment.

Labels & Text
Input Default
uploadTitle 'Upload file'
uploadSubtitle 'Drag & drop files or click'
dropHereLabel 'Drop files here'
supportedFormatsLabel 'Supported formats: JPEG, PNG, PDF...'
browseFilesLabel 'Browse files'
uploadSummaryLabel 'Attachments List'
filesLabel 'Files'
totalSizeLabel 'Total Size'
emptyTableMessage 'No data found'
emptyStateLabel 'No files have been uploaded'
openLinkLabel "Open link"
addButtonLabel "Add"
downloadLabel "Download"
deleteLabel "Delete"
previewLabel "Preview"
confirmLabel "Confirm"
abortLabel "Cancel"
saveLabel "Save"
exitLabel "Exit"
fileNameLabel "File name"
uploadFileLabel "Upload file"
uploadWithDropboxLabel "Upload with Dropbox"
cropLabel "Choose the image dimensions"
removedLabel 'File removed'
addedSuccessfullyLabel 'file(s) uploaded successfully.'
deleteDialogTitle null
deleteDialogMessage 'Are you sure you want to delete...'
noImageSelectedErrorMessage 'You cannot select a file that is not...'
wrongTypeSelectedErrorMessage 'The selected file cannot be uploaded.'
videoPreviewErrorMessage 'Cannot open a preview of a video file.'
audioPreviewErrorMessage 'Cannot open a preview of an audio file.'
flipHorinzontalLabel 'Flip horizontally'
flipVerticalLabel 'Flip vertically'
rotateRightLabel 'Rotate right'
rotateLeftLabel 'Rotate left'
eqpTableSearchText 'Search'
downloadTooltipPosition 'below'
Outputs
Output Event Arguments Description
(localEditedAttachments) IAttachmentDTO[] Emits the complete, updated list of attachments whenever a file/link is added or removed. This is the primary output to listen to.
(downloadAttachment) IAttachmentDTO Triggered on a download attempt for an attachment missing its FileDataBase64, allowing the parent component to fetch the data.
(onDeleteAttachment) IAttachmentDTO Emits the attachment object just before it is removed from the list.
(abortAddAttachment) void Fired when the user cancels an action from a modal dialog (e.g., the crop dialog).
Data Models
IAttachmentDTO Interface
Property Type Description
ID number | string Unique ID of the attachment.
FileName string Name of the file or link.
FilePath string Path of the link or the file on the server.
FileExtension string The file extension (e.g., 'pdf', 'png').
AttachmentType AttachmentType The type of attachment (1: FILE, 2: LINK, 3: DROPBOX).
FileDataBase64 string The Base64 content of the file. null for large files (see LargeFile).
FileContentType string The MIME type of the file (e.g., image/png).
IsImage boolean true if the attachment is an image.
FileThumbnailBase64 string The Base64 content of a low-resolution thumbnail (auto-generated for videos).
LargeFile File The native browser File object for files exceeding base64LimitMB. Used instead of FileDataBase64.
IsLargeFile boolean true when the attachment is stored in LargeFile rather than FileDataBase64.
TrustedUrl any (Internal State) A SafeResourceUrl used for previewing links, PDFs, and videos in the dialog.
isUploading boolean (Internal State) true during the upload process.

AttachmentFieldColumn Interface
Property Type Description
key string Unique identifier for the column. Used by hiddenColumns.
display string The column header label to display.
type TypeAttachmentColumn The rendering type (TEXT, DATE, TEMPLATE). Defaults to TEXT.
externalTemplate TemplateRef<any> The ng-template to use (required if type is TEMPLATE).
styles { flex: string } Defines the column width using CSS flex shorthand (e.g., '1 1 0%' or '0 0 150px').
position number The column's display order. The built-in "File" column is 10, "Actions" is 100.
class string A custom CSS class added to every cell in this column.
hidden boolean | (() => boolean) If true (or if the function returns true), the column is excluded from rendering.

AttachmentMenuAction Interface
Property Type Description
key string Optional unique identifier for the action. Used by hiddenActions and actionHiddenFn/actionDisabledFn.
icon string mat-icon name to display (e.g., 'share').
name string Text to show in the menu item.
fn (att: IAttachmentDTO) => void Function to execute when the item is clicked.
disabled (att: IAttachmentDTO) => boolean Function to dynamically disable the action for a specific row.
hidden (att: IAttachmentDTO) => boolean Function to dynamically hide the action for a specific row.
position number Display order. Built-in "Preview" is 10, "Delete" is 100. Use a number in between to insert a custom action.

AttachmentCardSize Type

export type AttachmentCardSize = 'small' | 'medium' | 'large' | 'custom';



Layout Configuration

export type Layout = 'full' | 'compact';


Advanced Features

Large File Handling

By default, files are read as Base64 strings and stored in IAttachmentDTO.FileDataBase64. However, for files exceeding the base64LimitMB threshold (default: 100 MB), the component switches strategy to avoid memory exhaustion: the native browser File object is stored in IAttachmentDTO.LargeFile, and FileDataBase64 is set to null.

When localEditedAttachments emits a list containing large files, your backend integration must handle them differently (e.g., via FormData multipart upload).

onAttachmentsChange(list: IAttachmentDTO[]) {
  for (const att of list) {
    if (att.IsLargeFile && att.LargeFile) {
      // Handle with multipart/form-data upload
      const formData = new FormData();
      formData.append('file', att.LargeFile, att.FileName);
      this.http.post('/api/upload', formData).subscribe();
    } else {
      // Standard Base64 handling
    }
  }
}

Note: Video files are always treated as large files regardless of size — they are stored directly in LargeFile and a Base64 thumbnail is generated and stored in FileThumbnailBase64.


Video Support

The component has first-class support for video attachments:

  • Thumbnail generation: When a video is selected, the component automatically captures a frame at the 1-second mark and stores it as a JPEG Base64 string in FileThumbnailBase64. This thumbnail is displayed in both card and table views.
  • In-browser preview: Videos stored in LargeFile can be played directly in the preview dialog using the native HTML5 <video> player.
  • Server-side compression (optional): Pass the videoCompression input to enable compression via an external API endpoint.
<eqp-attachments
  [attachmentsList]="videoList"
  [videoCompression]="compressionConfig"
  (localEditedAttachments)="onAttachmentsChange($event)">
</eqp-attachments>
compressionConfig = {
  enabled: true,
  maxWidth: 1280,  // Max output width in px
  crf: 23,         // Constant Rate Factor (quality, lower = better)
  preset: 'veryfast',
  maxFps: 30,
  audioBitrate: 128000
};

The compression call is a multipart/form-data POST to the URL configured inside the component. The response is expected to be a Blob (the compressed .mp4 file).


Dynamic Action Control

For fine-grained control over which actions are available for each attachment in the table view, use the actionHiddenFn and actionDisabledFn hook inputs. These are called for every action (both built-in and custom) on every row render.

Built-in action keys: 'preview', 'delete'. Custom actions use the key field you define in AttachmentMenuAction.

<eqp-attachments
  [attachmentsList]="documents"
  [actionHiddenFn]="myHiddenFn"
  [actionDisabledFn]="myDisabledFn"
  (localEditedAttachments)="onAttachmentsChange($event)">
</eqp-attachments>
// Hide the 'delete' action for already-persisted attachments (those with a numeric ID)
myHiddenFn = (actionKey: string, att: IAttachmentDTO): boolean => {
  if (actionKey === 'delete' && typeof att.ID === 'number' && att.ID > 0) {
    return true;
  }
  return false;
};

// Disable the 'preview' action for attachments without local data
myDisabledFn = (actionKey: string, att: IAttachmentDTO): boolean => {
  if (actionKey === 'preview') {
    return !att.FileDataBase64 && !att.LargeFile;
  }
  return false;
};

You can also hide actions by key name using the simpler hiddenActions array input, which is evaluated before the hook:

<!-- Always hide the 'delete' button for everyone -->
<eqp-attachments [hiddenActions]="['delete']" ...></eqp-attachments>

Theming with CSS Custom Properties

The component exposes a set of CSS custom properties on :host that you can override from the parent application to match your design system.

// In your component or global styles
eqp-attachments {
  --primary-color: #0d6efd;          // Main accent color (buttons, borders, icons)
  --success-color: #198754;          // Toast success / progress fill
  --error-color: #dc3545;            // Toast error color
  --background-light: #f8f9fa;       // Page background tint
  --background-card: #ffffff;        // Card and panel background
  --text-color: #212529;             // Primary text
  --text-color-light: #6c757d;       // Secondary/muted text
  --border-color: rgba(0,0,0,0.12);  // Dividers and card borders
  --shadow-color: rgba(13,110,253,0.15); // Box shadows
  --border-radius: 12px;             // Card and dialog corner radius
  --transition-speed: 0.25s;         // Animation duration
}

Use Cases

Case 1: Single Image Upload

A configuration for uploading a single image, with drag-and-drop and the image cropper enabled.

HTML

<eqp-attachments
  [multipleAttachment]="false"
  [attachmentsList]="singleImage"
  [singleAttachmentDragAndDrop]="true"
  [allowOnlyImages]="true"
  [cropOptions]="[1, 2]"
  [maxFileSizeMB]="10"
  (localEditedAttachments)="onAttachmentsChange($event)">
</eqp-attachments>

TypeScript

import { IAttachmentDTO } from '@eqproject/eqp-attachments';

export class MyComponent {
  singleImage: IAttachmentDTO[] = [];

  onAttachmentsChange(updatedList: IAttachmentDTO[]) {
    // In single mode, the list will contain 0 or 1 item
    this.singleImage = updatedList;
  }
}
Case 2: Multiple Attachments Management

A full-featured configuration for managing multiple types of attachments, starting in card view and allowing the user to switch to table view.

HTML

<eqp-attachments
  [multipleAttachment]="true"
  [attachmentsList]="documentList"
  [allowedTypes]="[1, 2]"
  [viewMode]="'card'"
  [headerTitle]="'Project Documents'"
  [showSummary]="true"
  (localEditedAttachments)="onAttachmentsChange($event)">
</eqp-attachments>

TypeScript

import { IAttachmentDTO, AttachmentType } from '@eqproject/eqp-attachments';

export class MyComponent {
  documentList: IAttachmentDTO[] = [
    {
      ID: 101,
      IsImage: false,
      AttachmentType: AttachmentType.FILE,
      FileName: "Final Report.pdf",
      FileExtension: "pdf",
    },
    {
      ID: 102,
      IsImage: false,
      AttachmentType: AttachmentType.LINK,
      FileName: "GitHub Repository",
      FilePath: "https://github.com/...",
    },
  ];

  onAttachmentsChange(updatedList: IAttachmentDTO[]) {
    this.documentList = updatedList;
  }
}
Case 3: Custom Columns

HTML

<eqp-attachments
  [multipleAttachment]="true"
  [attachmentsList]="documentList"
  [allowedTypes]="[1, 2]"
  [viewMode]="'table'"
  [headerTitle]="'Project Documents'"
  [showSummary]="true"
  (localEditedAttachments)="onAttachmentsChange($event)"
  [customColumns]="myCustomColumns"
  [customMenuActions]="myCustomActions">
</eqp-attachments>

<ng-template #statusTemplate let-att>
  <mat-chip-listbox>
    <mat-chip [style.background-color]="att.Status === 'Approved' ? '#c8e6c9' : '#ffcdd2'"
              [style.color]="att.Status === 'Approved' ? '#2e7d32' : '#c62828'">
      {{ att.Status }}
    </mat-chip>
  </mat-chip-listbox>
</ng-template>

TypeScript

@ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<any>;

  documentList: IAttachmentDTO[] = [
    {
      ID: 1,
      FileName: 'report.pdf',
      FileExtension: 'pdf',
      UploadUserName: 'Mario Rossi',
      Status: 'Approved'
    },
    {
      ID: 2,
      FileName: 'schema.zip',
      FileExtension: 'zip',
      UploadUserName: 'Laura Bianchi',
      Status: 'Pending'
    }
  ];
  myCustomColumns: AttachmentFieldColumn[] = [];
  myCustomActions: AttachmentMenuAction[] = [];

  ngOnInit() {
    this.myCustomColumns = [
      {
        key: 'UploadUserName',
        display: 'Uploaded By',        // 'display' is the header label
        type: TypeAttachmentColumn.TEXT, // Renders as plain text
        styles: { flex: '2 1 0%' }, // Takes 2 parts of the available space
        position: 20 // Shows after "File" (10)
      },
      {
        key: 'Status',
        display: 'Status',
        type: TypeAttachmentColumn.TEMPLATE,
        externalTemplate: this.statusTemplate, // Pass the ng-template
        styles: { flex: '1 1 0%' },
        position: 30 // Shows after "Uploaded By" (20)
      }
    ];

    this.myCustomActions = [
      {
        key: 'share',           // Optional but recommended for hiddenActions/hooks
        icon: 'share',
        name: 'Share',          // 'name' is the label shown in the menu
        fn: (item) => this.shareAttachment(item),  // 'fn' is the click handler
        position: 25 // Show after "Preview" (10) and before "Delete" (100)
      },
      {
        key: 'rename',
        icon: 'edit',
        name: 'Edit Name',
        fn: (item) => this.renameAttachment(item),
        position: 26
      }
    ];
  }

Credits

This library has been developed by EqProject SRL. For more info, contact: info@eqproject.it