@eqproject/eqp-attachments 3.1.13 → 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,433 +1,591 @@
1
- ## Table of Contents
2
-
3
- =================
4
-
5
- * [Overview](#overview)
6
- * [Requirements](#requirements)
7
- * [Getting Started](#getting-started)
8
- * [API Reference](#api-reference)
9
- * [Inputs](#inputs)
10
- * [Outputs](#outputs)
11
- * [Data Models](#data-models)
12
- * [Use Cases](#use-cases)
13
- * [Credits](#credits)
14
-
15
- ## Overview
16
-
17
- `@eqproject/eqp-attachments` provides a complete, client-side solution for attachment management, now fully modernized with the latest Angular features.
18
-
19
- ✨ **Key Features:**
20
-
21
- * **Single or Multiple Attachments:** Easily configure the component to handle one or many attachments.
22
- * **Multiple Sources:** Supports local file uploads, direct links from the web, and Dropbox integration.
23
- * **Modern UI:** A clean, animated drag-and-drop interface.
24
- * **Dual View Mode:** Allows users to switch between a responsive **Card Grid** and a detailed **Table View** .
25
- * **Image Cropper:** A built-in tool for cropping and rotating uploaded images.
26
- * **Client-Side Validation:** Set limits on file size, file types, and more.
27
- * **Highly Configurable:** Almost every label, button, and feature can be customized via inputs.
28
- * **Purely Client-Side:** The component manages attachments locally without requiring a backend connection, emitting the final data for you to handle.
29
-
30
- ## Requirements
31
-
32
- * **Angular 17+**
33
- * **Angular Material** installed and configured in your project.
34
- * **Font Awesome** for a complete icon set (optional, but recommended for the best visual experience).
35
-
36
- ## Getting Started
37
-
38
- ### Step 1: Install the Package
39
-
40
- **Shell**
41
-
42
- ```
43
- npm install --save @eqproject/eqp-attachments
44
- ```
45
-
46
- ### Step 2: Import the Component
47
-
48
- The component is **standalone** , so you can import it directly into your component or module.
49
-
50
- **TypeScript**
51
-
52
- ```
53
- // in your-component.ts or app/shared module
54
- import { EqpAttachmentsModule } from '@eqproject/eqp-attachments';
55
-
56
- @Component({
57
- selector: 'my-feature-component',
58
- standalone: true,
59
- imports: [ EqpAttachmentsModule ],
60
- template: `
61
- <eqp-attachments
62
- [attachmentsList]="myAttachments"
63
- (localEditedAttachments)="onAttachmentsChange($event)">
64
- </eqp-attachments>
65
- `
66
- })
67
- export class MyFeatureComponent {
68
- myAttachments: IAttachmentDTO[] = [];
69
-
70
- onAttachmentsChange(updatedList: IAttachmentDTO[]) {
71
- this.myAttachments = updatedList;
72
- console.log('Attachments list updated!', this.myAttachments);
73
- }
74
- }
75
- ```
76
-
77
- ### Step 3: Add Styles (Optional)
78
-
79
- For the best icon display, include Font Awesome in your project's styles, for example, in `angular.json`:
80
-
81
- **JSON**
82
-
83
- ```
84
- "styles": [
85
- "src/styles.scss",
86
- "node_modules/@fortawesome/fontawesome-free/css/all.min.css"
87
- ],
88
- ```
89
-
90
- ## API Reference
91
-
92
- ### Inputs
93
-
94
- #### **Core Configuration**
95
-
96
- | Input | Type | Default | Description |
97
- | ---------------------- | -------------------- | ---------------- | ------------------------------------------------------------------------------------------------- |
98
- | `attachmentsList` | `IAttachmentDTO[]` | `null` | The array of attachments to display and manage. |
99
- | `singleAttachment` | `IAttachmentDTO` | `null` | An alternative to `attachmentsList`for passing a single attachment object in non-multiple mode. |
100
- | `multipleAttachment` | `boolean` | `true` | If `true`, manages a list of attachments. If `false`, handles only a single attachment. |
101
- | `allowedTypes` | `AttachmentType[]` | `[FILE, LINK]` | An array of allowed attachment sources (e.g.,`[1, 2, 3]`for File, Link, and Dropbox). |
102
-
103
- ---
104
-
105
- #### **UI & View Configuration**
106
-
107
- | Input | Type | Default | Description |
108
- | ---------------------- | ----------- | ------------- | -------------------------------------------------------------------------------- |
109
- | `layout` | `'full'` | `'compact'` | `'compact'` |
110
- | `viewMode` | `'card'` | `'table'` | `'card'` |
111
- | `chooseView` | `boolean` | `true` | If `true`, displays the Card/Table view switcher in the header. |
112
- | `showSummary` | `boolean` | `true` | If `true`, displays the summary block with file count and total size. |
113
- | `cardSize` | `'small'` | `'medium'` | `'large'` |
114
- | `customCardWidthPx` | `number` | `200` | Sets the custom card width in pixels when `cardSize`is `'custom'`. |
115
- | `customCardHeightPx` | `number` | `180` | Sets the custom card height in pixels when `cardSize`is `'custom'`. |
116
- | `showMatCard` | `boolean` | `true` | If `true`(and in table mode), the component is rendered inside a `mat-card`. |
117
- | `cropDialogClass` | `string` | `undefined` | Assigns a custom CSS class to the image cropper dialog panel. |
118
- | `showUploadTitle` | `boolean` | `true` | If `true`, will show title over dropbox |
119
- ---
120
-
121
- #### **Table View Configuration**
122
-
123
- | Input | Type | Default | Description |
124
- | ---------------------- | ----------- | ------------- | -------------------------------------------------------------------------------- |
125
- | `customColumns` | `'AttachmentFieldColumn[]'` | `[]` | Array to add custom columns to the table view. |
126
- | `customMenuActions` | `'AttachmentMenuAction[]'` | `[]` | Array to add custom actions to the table's "more options" (...) menu.
127
- ---
128
-
129
- #### **Functional Configuration**
130
-
131
- | Input | Type | Default | Description |
132
- | ------------------------------- | -------------------- | ------------- | --------------------------------------------------------------------------------------------------------- |
133
- | `maxFileSizeMB` | `number` | `100` | Sets the maximum file size in megabytes for each uploaded file. |
134
- | `loadMultipleFiles` | `boolean` | `false` | If `true`, allows selecting multiple files at once from the file browser. |
135
- | `singleAttachmentDragAndDrop` | `boolean` | `false` | If `true`, enables the drag-and-drop UI even in single-attachment mode. |
136
- | `isDisabled` | `boolean` | `false` | Disables all "add attachment" controls. |
137
- | `disableAction` | `boolean` | `false` | If `true`, hides the actions column in the table view. |
138
- | `allowOnlyImages` | `boolean` | `false` | If `true`, restricts file uploads to image types only. |
139
- | `acceptedFileTypes` | `string` | `undefined` | A string for the `<input>` `accept`attribute (e.g.,`'.pdf,image/*'`). |
140
- | `showPreview` | `boolean` | `true` | Enables the preview functionality for attachments. |
141
- | `compressionOptions` | `IOptions` | `{...}` | Options for client-side image compression (`maxSizeMB`,`maxWidthOrHeight`, etc.). |
142
- | `cropOptions` | `CropOptionEnum[]` | `[]` | Enables specific tools in the image cropper (1: Rotate, 2: Flip). |
143
- | `separatedUploadButtons` | `boolean` | `false` | If `true`and multiple `allowedTypes`are available, shows separate buttons instead of a dropdown menu. |
144
- | `getAttachmentEndpoint` | `string` | `null` | API endpoint to fetch full attachment data for previews. |
145
- | `productionBaseUrl` | `string` | `null` | Base URL for the document viewer (e.g., Google Viewer). |
146
-
147
- ---
148
-
149
- #### **Labels & Text**
150
-
151
- | Input | Default |
152
- | --------------------------------- | --------------------------------------------- |
153
- | `uploadTitle` | `'Upload file'` |
154
- | `uploadSubtitle` | `'Drag & drop files or click'` |
155
- | `dropHereLabel` | `'Drop files here'` |
156
- | `supportedFormatsLabel` | `'Supported formats: JPEG, PNG, PDF...'` |
157
- | `browseFilesLabel` | `'Browse files'` |
158
- | `uploadSummaryLabel` | `'Attachments List'` |
159
- | `filesLabel` | `'Files'` |
160
- | `totalSizeLabel` | `'Total Size'` |
161
- | `emptyTableMessage` | `'No data found'` |
162
- | `emptyStateLabel` | `'No files have been uploaded'` |
163
- | `openLinkLabel` | `"Open link"` |
164
- | `addButtonLabel` | `"Add"` |
165
- | `downloadLabel` | `"Download"` |
166
- | `deleteLabel` | `"Delete"` |
167
- | `previewLabel` | `"Preview"` |
168
- | `confirmLabel` | `"Confirm"` |
169
- | `abortLabel` | `"Cancel"` |
170
- | `saveLabel` | `"Save"` |
171
- | `exitLabel` | `"Exit"` |
172
- | `fileNameLabel` | `"File name"` |
173
- | `uploadFileLabel` | `"Upload file"` |
174
- | `uploadWithDropboxLabel` | `"Upload with Dropbox"` |
175
- | `cropLabel` | `"Choose the image dimensions"` |
176
- | `removedLabel` | `'File removed'` |
177
- | `addedSuccessfullyLabel` | `'file(s) uploaded successfully.'` |
178
- | `deleteDialogTitle` | `null` |
179
- | `deleteDialogMessage` | `'Are you sure you want to delete...'` |
180
- | `noImageSelectedErrorMessage` | `'You cannot select a file that is not...'` |
181
- | `wrongTypeSelectedErrorMessage` | `'The selected file cannot be uploaded.'` |
182
- | `videoPreviewErrorMessage` | `'Cannot open a preview of a video file.'` |
183
- | `audioPreviewErrorMessage` | `'Cannot open a preview of an audio file.'` |
184
- | `flipHorinzontalLabel` | `'Flip horizontally'` |
185
- | `flipVerticalLabel` | `'Flip vertically'` |
186
- | `rotateRightLabel` | `'Rotate right'` |
187
- | `rotateLeftLabel` | `'Rotate left'` |
188
- | `eqpTableSearchText` | `'Search'` |
189
- | `downloadTooltipPosition` | `'below'` |
190
-
191
- ### Outputs
192
-
193
- | Output | Event Arguments | Description |
194
- | -------------------------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
195
- | **`(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. |
196
- | `(downloadAttachment)` | `IAttachmentDTO` | Triggered on a download attempt for an attachment missing its `FileDataBase64`, allowing the parent component to fetch the data. |
197
- | `(onDeleteAttachment)` | `IAttachmentDTO` | Emits the attachment object just before it is removed from the list. |
198
- | `(abortAddAttachment)` | `any` | Fired when the user cancels an action from a modal dialog (e.g., the crop dialog). |
199
-
200
- ### Data Models
201
-
202
- #### `IAttachmentDTO` Interface
203
-
204
- | Property | Type | Description |
205
- | ----------------------- | ------------------- | ------------------------------------------------------ |
206
- | `ID` | `number \| string` | Unique ID of the attachment. |
207
- | `FileName` | `string` | Name of the file or link. |
208
- | `FilePath` | `string` | Path of the link or the file on the server. |
209
- | `AttachmentType` | `AttachmentType` | The type of attachment (1: FILE, 2: LINK, 3: DROPBOX). |
210
- | `FileDataBase64` | `string` | The base64 content of the file. |
211
- | `FileSize` | `number` | The size of the file in bytes. |
212
- | `FileContentType` | `string` | The mime type of the file (e.g.,`image/png`). |
213
- | `IsImage` | `boolean` | `true`if the attachment is an image. |
214
- | `FileThumbnailBase64` | `string` | The base64 content of a low-resolution thumbnail. |
215
- | `isUploading` | `boolean` | (Internal State)`true`during the upload process. |
216
- | `uploadError` | `boolean` | (Internal State)`true`if the upload failed. |
217
-
218
- ---
219
-
220
-
221
- #### `AttachmentFieldColumn` Interface
222
-
223
- | Property | Type | Description |
224
- | ----------------------- | ------------------- | ------------------------------------------------------ |
225
- | `key` | `string` | . |
226
- | `display` | `string` | The property key from the IAttachmentDTO object |
227
- | `type` | `TypeAttachmentColumn` | The rendering type (TEXT, DATE, TEMPLATE). Defaults to TEXT. |
228
- | `externalTemplate` | `TemplateRef<any>` | The ng-template to use (required if type is TEMPLATE). |
229
- | `styles` | `{ flex: string }` | Defines the column width (e.g., '1 1 0%' or '0 0 150px'). |
230
- | `position` | `number` | The column's display order (Default: 99). File is 10, Actions is 100. |
231
- | `class` | `string` | A custom CSS class to add to the column's cells. |
232
- ---
233
-
234
- #### `AttachmentMenuAction` Interface
235
-
236
- | Property | Type | Description |
237
- | ----------------------- | ------------------- | ------------------------------------------------------ |
238
- | `icon` | `string` | mat-icon name to display (e.g., 'share'). |
239
- | `name` | `string` | Text to show in the menu item.object |
240
- | `fn` | `(att: IAttachmentDTO) => void` | Function to execute when the item is clicked. |
241
- | `disabled` | `(att: IAttachmentDTO) => boolean` | Function to dynamically disable the action for a specific row. |
242
- | `position` | `number` | Display order. Preview is 10, Delete is 100. Use a number in between (e.g., 20) to add an action. |
243
- ---
244
-
245
-
246
- #### **AttachmentCardSize Type**
247
-
248
- export type AttachmentCardSize = 'small' | 'medium' | 'large' | 'custom';
249
-
250
- ---
251
-
252
- ---
253
-
254
- #### **Layout Configuration**
255
-
256
- export type Layout = 'full' | 'compact';
257
-
258
- ---
259
-
260
-
261
- ## Use Cases
262
-
263
- ### Case 1: Single Image Upload
264
-
265
- A configuration for uploading a single image, with drag-and-drop and the image cropper enabled.
266
-
267
- **HTML**
268
-
269
- ```
270
- <eqp-attachments
271
- [multipleAttachment]="false"
272
- [attachmentsList]="singleImage"
273
- [singleAttachmentDragAndDrop]="true"
274
- [allowOnlyImages]="true"
275
- [cropOptions]="[1, 2]"
276
- [maxFileSizeMB]="10"
277
- (localEditedAttachments)="onAttachmentsChange($event)">
278
- </eqp-attachments>
279
- ```
280
-
281
- **TypeScript**
282
-
283
- ```
284
- import { IAttachmentDTO } from '@eqproject/eqp-attachments';
285
-
286
- export class MyComponent {
287
- singleImage: IAttachmentDTO[] = [];
288
-
289
- onAttachmentsChange(updatedList: IAttachmentDTO[]) {
290
- // In single mode, the list will contain 0 or 1 item
291
- this.singleImage = updatedList;
292
- }
293
- }
294
- ```
295
-
296
- ### Case 2: Multiple Attachments Management
297
-
298
- A full-featured configuration for managing multiple types of attachments, starting in `card` view and allowing the user to switch to `table` view.
299
-
300
- **HTML**
301
-
302
- ```
303
- <eqp-attachments
304
- [multipleAttachment]="true"
305
- [attachmentsList]="documentList"
306
- [allowedTypes]="[1, 2]"
307
- [viewMode]="'card'"
308
- [headerTitle]="'Project Documents'"
309
- [showSummary]="true"
310
- (localEditedAttachments)="onAttachmentsChange($event)">
311
- </eqp-attachments>
312
- ```
313
-
314
- **TypeScript**
315
-
316
- ```
317
- import { IAttachmentDTO, AttachmentType } from '@eqproject/eqp-attachments';
318
-
319
- export class MyComponent {
320
- documentList: IAttachmentDTO[] = [
321
- {
322
- ID: 101,
323
- IsImage: false,
324
- AttachmentType: AttachmentType.FILE,
325
- FileName: "Final Report.pdf",
326
- FileExtension: "pdf",
327
- FileSize: 1200000 // 1.2 MB
328
- },
329
- {
330
- ID: 102,
331
- IsImage: false,
332
- AttachmentType: AttachmentType.LINK,
333
- FileName: "GitHub Repository",
334
- FilePath: "https://github.com/...",
335
- },
336
- ];
337
-
338
- onAttachmentsChange(updatedList: IAttachmentDTO[]) {
339
- this.documentList = updatedList;
340
- }
341
- }
342
- ```
343
-
344
- ### Case 3: Custom Columns
345
- **HTML**
346
-
347
- ```
348
- <eqp-attachments
349
- [multipleAttachment]="true"
350
- [attachmentsList]="documentList"
351
- [allowedTypes]="[1, 2]"
352
- [viewMode]="'card'"
353
- [headerTitle]="'Project Documents'"
354
- [showSummary]="true"
355
- (localEditedAttachments)="onAttachmentsChange($event)"
356
- [customColumns]="myCustomColumns"
357
- [customMenuActions]="myCustomActions">
358
- </eqp-attachments>
359
-
360
- <ng-template #statusTemplate let-att>
361
- <mat-chip-listbox>
362
- <mat-chip [style.background-color]="att.Status === 'Approved' ? '#c8e6c9' : '#ffcdd2'"
363
- [style.color]="att.Status === 'Approved' ? '#2e7d32' : '#c62828'">
364
- {{ att.Status }}
365
- </mat-chip>
366
- </mat-chip-listbox>
367
- </ng-template>
368
- ```
369
-
370
- **TypeScript**
371
-
372
- ```
373
- @ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<any>;
374
-
375
- documentList: IAttachmentDTO[] = [
376
- {
377
- ID: 1,
378
- FileName: 'report.pdf',
379
- FileExtension: 'pdf',
380
- UploadUserName: 'Mario Rossi',
381
- Status: 'Approved'
382
- },
383
- {
384
- ID: 2,
385
- FileName: 'schema.zip',
386
- FileExtension: 'zip',
387
- UploadUserName: 'Laura Bianchi',
388
- Status: 'Pending'
389
- }
390
- ];
391
- myCustomColumns: AttachmentFieldColumn[] = [];
392
- myCustomActions: AttachmentMenuAction[] = [];
393
-
394
- ngOnInit() {
395
- this.myCustomColumns = [
396
- {
397
- key: 'UploadUserName',
398
- header: 'Uploaded By',
399
- type: TypeAttachmentColumn.TEXT, // Renders as plain text
400
- styles: { flex: '2 1 0%' }, // Takes 2 parts of the available space
401
- position: 20 // Shows after "File" (10)
402
- },
403
- {
404
- key: 'Status', // Unique key for this column
405
- header: 'Status',
406
- type: TypeAttachmentColumn.TEMPLATE,
407
- externalTemplate: this.statusTemplate, // Pass the ng-template
408
- styles: { flex: '1 1 0%' }, // Takes 1 part of the available space
409
- position: 30 // Shows after "Uploaded By" (20)
410
- }
411
- ];
412
-
413
- this.myCustomActions = [
414
- {
415
- icon: 'share',
416
- label: 'Share',
417
- onClick: (item) => this.shareAttachment(item),
418
- position: 25 // Show after "Preview" (10) and before "Delete" (100)
419
- },
420
- {
421
- icon: 'edit',
422
- label: 'Edit Name',
423
- onClick: (item) => this.renameAttachment(item),
424
- position: 26
425
- }
426
- ];
427
- }
428
- ```
429
-
430
-
431
- ## Credits
432
-
433
- This library has been developed by EqProject SRL. For more info, contact: info@eqproject.it
1
+ ## Table of Contents
2
+
3
+ =================
4
+
5
+ * [Overview](#overview)
6
+ * [Requirements](#requirements)
7
+ * [Getting Started](#getting-started)
8
+ * [API Reference](#api-reference)
9
+ * [Inputs](#inputs)
10
+ * [Outputs](#outputs)
11
+ * [Data Models](#data-models)
12
+ * [Advanced Features](#advanced-features)
13
+ * [Large File Handling](#large-file-handling)
14
+ * [Video Support](#video-support)
15
+ * [Dynamic Action Control](#dynamic-action-control)
16
+ * [Theming with CSS Custom Properties](#theming-with-css-custom-properties)
17
+ * [Use Cases](#use-cases)
18
+ * [Credits](#credits)
19
+
20
+ ## Overview
21
+
22
+ `@eqproject/eqp-attachments` provides a complete, client-side solution for attachment management, now fully modernized with the latest Angular features.
23
+
24
+ **Key Features:**
25
+
26
+ * **Single or Multiple Attachments:** Easily configure the component to handle one or many attachments.
27
+ * **Multiple Sources:** Supports local file uploads, direct links from the web, and Dropbox integration.
28
+ * **Modern UI:** A clean, animated drag-and-drop interface with two layout variants (`full` and `compact`).
29
+ * **Dual View Mode:** Allows users to switch between a responsive **Card Grid** and a detailed **Table View** .
30
+ * **Image Cropper:** A built-in tool for cropping and rotating uploaded images.
31
+ * **Client-Side Validation:** Set limits on file size, file types, and more.
32
+ * **Video Support:** Automatic thumbnail generation and optional server-side video compression.
33
+ * **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.
34
+ * **Extensible Table:** Add custom columns and custom "more options" menu actions to the table view.
35
+ * **Granular Action Control:** Show, hide, or disable any built-in or custom action on a per-row basis via hook functions.
36
+ * **Highly Configurable:** Almost every label, button, and feature can be customized via inputs.
37
+ * **Purely Client-Side:** The component manages attachments locally without requiring a backend connection, emitting the final data for you to handle.
38
+
39
+ ## Requirements
40
+
41
+ * **Angular 17+**
42
+ * **Angular Material** installed and configured in your project.
43
+ * **Font Awesome** for a complete icon set (optional, but recommended for the best visual experience).
44
+
45
+ ## Getting Started
46
+
47
+ ### Step 1: Install the Package
48
+
49
+ **Shell**
50
+
51
+ ```
52
+ npm install --save @eqproject/eqp-attachments
53
+ ```
54
+
55
+ ### Step 2: Import the Component
56
+
57
+ The component is **standalone** , so you can import it directly into your component or module.
58
+
59
+ **TypeScript**
60
+
61
+ ```
62
+ // in your-component.ts or app/shared module
63
+ import { EqpAttachmentsModule } from '@eqproject/eqp-attachments';
64
+
65
+ @Component({
66
+ selector: 'my-feature-component',
67
+ standalone: true,
68
+ imports: [ EqpAttachmentsModule ],
69
+ template: `
70
+ <eqp-attachments
71
+ [attachmentsList]="myAttachments"
72
+ (localEditedAttachments)="onAttachmentsChange($event)">
73
+ </eqp-attachments>
74
+ `
75
+ })
76
+ export class MyFeatureComponent {
77
+ myAttachments: IAttachmentDTO[] = [];
78
+
79
+ onAttachmentsChange(updatedList: IAttachmentDTO[]) {
80
+ this.myAttachments = updatedList;
81
+ console.log('Attachments list updated!', this.myAttachments);
82
+ }
83
+ }
84
+ ```
85
+
86
+ ### Step 3: Add Styles (Optional)
87
+
88
+ For the best icon display, include Font Awesome in your project's styles, for example, in `angular.json`:
89
+
90
+ **JSON**
91
+
92
+ ```
93
+ "styles": [
94
+ "src/styles.scss",
95
+ "node_modules/@fortawesome/fontawesome-free/css/all.min.css"
96
+ ],
97
+ ```
98
+
99
+ ## API Reference
100
+
101
+ ### Inputs
102
+
103
+ #### **Core Configuration**
104
+
105
+ | Input | Type | Default | Description |
106
+ | ---------------------- | -------------------- | ---------------- | ------------------------------------------------------------------------------------------------- |
107
+ | `attachmentsList` | `IAttachmentDTO[]` | `null` | The array of attachments to display and manage. |
108
+ | `singleAttachment` | `IAttachmentDTO` | `null` | An alternative to `attachmentsList` for passing a single attachment object in non-multiple mode. |
109
+ | `multipleAttachment` | `boolean` | `true` | If `true`, manages a list of attachments. If `false`, handles only a single attachment. |
110
+ | `allowedTypes` | `AttachmentType[]` | `[FILE, LINK]` | An array of allowed attachment sources (e.g., `[1, 2, 3]` for File, Link, and Dropbox). |
111
+
112
+ ---
113
+
114
+ #### **UI & View Configuration**
115
+
116
+ | Input | Type | Default | Description |
117
+ | ---------------------- | ----------- | ------------- | -------------------------------------------------------------------------------- |
118
+ | `layout` | `'full' \| 'compact'` | `'compact'` | `'full'` renders a large centered dropbox area; `'compact'` renders a slim inline uploader bar. |
119
+ | `viewMode` | `'card' \| 'table'` | `'table'` | The default display mode for the attachment list. |
120
+ | `chooseView` | `boolean` | `true` | If `true`, displays the Card/Table view switcher in the header. |
121
+ | `showHeader` | `boolean` | `true` | If `true`, displays the title in the header when `multipleAttachment` is `true`. |
122
+ | `showSummary` | `boolean` | `false` | If `true`, displays the summary block with file count and total size. |
123
+ | `showUploadTitle` | `boolean` | `true` | If `true`, shows the title above the drop area. |
124
+ | `showDropArea` | `boolean` | `true` | If `false`, hides the entire upload/drop area (useful for read-only display). |
125
+ | `cardSize` | `'small' \| 'medium' \| 'large' \| 'custom'` | `'small'` | Controls the size of cards in card view. Use `'custom'` with `customCardWidthPx` and `customCardHeightPx`. |
126
+ | `customCardWidthPx` | `number` | `200` | Sets the custom card width in pixels when `cardSize` is `'custom'`. |
127
+ | `customCardHeightPx` | `number` | `180` | Sets the custom card height in pixels when `cardSize` is `'custom'`. |
128
+ | `showMatCard` | `boolean` | `true` | If `true` (and in table mode), the component is rendered inside a `mat-card`. |
129
+ | `cropDialogClass` | `string` | `undefined` | Assigns a custom CSS class to the image cropper dialog panel. |
130
+
131
+ ---
132
+
133
+ #### **Table View Configuration**
134
+
135
+ | Input | Type | Default | Description |
136
+ | ---------------------- | ----------- | ------------- | -------------------------------------------------------------------------------- |
137
+ | `customColumns` | `AttachmentFieldColumn[]` | `[]` | Array to add custom columns to the table view. |
138
+ | `customMenuActions` | `AttachmentMenuAction[]` | `[]` | Array to add custom actions to the table's "more options" (...) menu. |
139
+ | `hiddenColumns` | `string[]` | `[]` | An array of column `key` values to hide from the table view. |
140
+ | `hiddenActions` | `string[]` | `[]` | An array of action `key` values to hide from the actions menu (e.g., `['delete']`). |
141
+ | `showInlinePreview` | `boolean` | `false` | If `true`, shows a thumbnail preview column directly in the table rows. |
142
+ | `isEqpTableMultiLanguage` | `boolean` | `false` | Enables multilanguage support for the embedded table. |
143
+ | `tablePaginatorVisible` | `boolean` | `true` | If `false`, hides the paginator below the table. |
144
+ | `isTableSearcheable` | `boolean` | `true` | If `false`, hides the search field above the table. |
145
+ | `tablePaginatorSize` | `number` | `null` | Sets the default page size for the table paginator. |
146
+
147
+ ---
148
+
149
+ #### **Functional Configuration**
150
+
151
+ | Input | Type | Default | Description |
152
+ | ------------------------------- | -------------------- | ------------- | --------------------------------------------------------------------------------------------------------- |
153
+ | `maxFileSizeMB` | `number` | `500` | Sets the maximum file size in megabytes for each uploaded file. Files exceeding this limit are rejected with a toast notification. |
154
+ | `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](#large-file-handling). |
155
+ | `loadMultipleFiles` | `boolean` | `false` | If `true`, allows selecting multiple files at once from the file browser. |
156
+ | `singleAttachmentDragAndDrop` | `boolean` | `true` | If `true`, enables the drag-and-drop UI even in single-attachment mode. |
157
+ | `showActionButtons` | `boolean` | `false` | If `true`, shows Download/Preview/Delete action buttons below the attachment in single-attachment mode. |
158
+ | `isDisabled` | `boolean` | `false` | Disables all "add attachment" controls. |
159
+ | `disableAction` | `boolean` | `false` | If `true`, disables the Delete action in both card and table views. |
160
+ | `allowOnlyImages` | `boolean` | `false` | If `true`, restricts file uploads to image types only. |
161
+ | `acceptedFileTypes` | `string` | `undefined` | A string for the `<input>` `accept` attribute (e.g., `'.pdf,image/*'`). |
162
+ | `showPreview` | `boolean` | `true` | Enables the preview functionality for attachments. |
163
+ | `enableImageCrop` | `boolean` | `true` | If `false`, skips the crop dialog and uploads images directly (compression still applies). |
164
+ | `compressionOptions` | `IOptions` | `{ maxSizeMB: 0.5, maxWidthOrHeight: 1920, useWebWorker: true }` | Options for client-side image compression. |
165
+ | `cropOptions` | `CropOptionEnum[]` | `[1, 2]` | Enables specific tools in the image cropper (1: Rotate, 2: Flip). Pass `[]` to show no tools. |
166
+ | `separatedUploadButtons` | `boolean` | `false` | If `true` and multiple `allowedTypes` are available, shows separate buttons instead of a dropdown menu. |
167
+ | `getAttachmentEndpoint` | `string` | `null` | API endpoint (POST) to fetch full attachment data (including `FileDataBase64`) for image previews. |
168
+ | `productionBaseUrl` | `string` | `null` | Base URL used to build a Google Viewer URL for document previews (e.g., PDFs, Word files). |
169
+ | `videoCompression` | `object` | `{ enabled: false, ... }` | Configuration for optional server-side video compression. See [Video Support](#video-support). |
170
+ | `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](#dynamic-action-control). |
171
+ | `actionDisabledFn` | `(actionKey: string, att?: IAttachmentDTO) => boolean` | `undefined` | A global hook to dynamically disable any action for a specific attachment. |
172
+
173
+ ---
174
+
175
+ #### **Labels & Text**
176
+
177
+ | Input | Default |
178
+ | --------------------------------- | --------------------------------------------- |
179
+ | `uploadTitle` | `'Upload file'` |
180
+ | `uploadSubtitle` | `'Drag & drop files or click'` |
181
+ | `dropHereLabel` | `'Drop files here'` |
182
+ | `supportedFormatsLabel` | `'Supported formats: JPEG, PNG, PDF...'` |
183
+ | `browseFilesLabel` | `'Browse files'` |
184
+ | `uploadSummaryLabel` | `'Attachments List'` |
185
+ | `filesLabel` | `'Files'` |
186
+ | `totalSizeLabel` | `'Total Size'` |
187
+ | `emptyTableMessage` | `'No data found'` |
188
+ | `emptyStateLabel` | `'No files have been uploaded'` |
189
+ | `openLinkLabel` | `"Open link"` |
190
+ | `addButtonLabel` | `"Add"` |
191
+ | `downloadLabel` | `"Download"` |
192
+ | `deleteLabel` | `"Delete"` |
193
+ | `previewLabel` | `"Preview"` |
194
+ | `confirmLabel` | `"Confirm"` |
195
+ | `abortLabel` | `"Cancel"` |
196
+ | `saveLabel` | `"Save"` |
197
+ | `exitLabel` | `"Exit"` |
198
+ | `fileNameLabel` | `"File name"` |
199
+ | `uploadFileLabel` | `"Upload file"` |
200
+ | `uploadWithDropboxLabel` | `"Upload with Dropbox"` |
201
+ | `cropLabel` | `"Choose the image dimensions"` |
202
+ | `removedLabel` | `'File removed'` |
203
+ | `addedSuccessfullyLabel` | `'file(s) uploaded successfully.'` |
204
+ | `deleteDialogTitle` | `null` |
205
+ | `deleteDialogMessage` | `'Are you sure you want to delete...'` |
206
+ | `noImageSelectedErrorMessage` | `'You cannot select a file that is not...'` |
207
+ | `wrongTypeSelectedErrorMessage` | `'The selected file cannot be uploaded.'` |
208
+ | `videoPreviewErrorMessage` | `'Cannot open a preview of a video file.'` |
209
+ | `audioPreviewErrorMessage` | `'Cannot open a preview of an audio file.'` |
210
+ | `flipHorinzontalLabel` | `'Flip horizontally'` |
211
+ | `flipVerticalLabel` | `'Flip vertically'` |
212
+ | `rotateRightLabel` | `'Rotate right'` |
213
+ | `rotateLeftLabel` | `'Rotate left'` |
214
+ | `eqpTableSearchText` | `'Search'` |
215
+ | `downloadTooltipPosition` | `'below'` |
216
+
217
+ ### Outputs
218
+
219
+ | Output | Event Arguments | Description |
220
+ | -------------------------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
221
+ | **`(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. |
222
+ | `(downloadAttachment)` | `IAttachmentDTO` | Triggered on a download attempt for an attachment missing its `FileDataBase64`, allowing the parent component to fetch the data. |
223
+ | `(onDeleteAttachment)` | `IAttachmentDTO` | Emits the attachment object just before it is removed from the list. |
224
+ | `(abortAddAttachment)` | `void` | Fired when the user cancels an action from a modal dialog (e.g., the crop dialog). |
225
+
226
+ ### Data Models
227
+
228
+ #### `IAttachmentDTO` Interface
229
+
230
+ | Property | Type | Description |
231
+ | ----------------------- | ------------------- | ------------------------------------------------------ |
232
+ | `ID` | `number \| string` | Unique ID of the attachment. |
233
+ | `FileName` | `string` | Name of the file or link. |
234
+ | `FilePath` | `string` | Path of the link or the file on the server. |
235
+ | `FileExtension` | `string` | The file extension (e.g., `'pdf'`, `'png'`). |
236
+ | `AttachmentType` | `AttachmentType` | The type of attachment (1: FILE, 2: LINK, 3: DROPBOX). |
237
+ | `FileDataBase64` | `string` | The Base64 content of the file. `null` for large files (see `LargeFile`). |
238
+ | `FileContentType` | `string` | The MIME type of the file (e.g., `image/png`). |
239
+ | `IsImage` | `boolean` | `true` if the attachment is an image. |
240
+ | `FileThumbnailBase64` | `string` | The Base64 content of a low-resolution thumbnail (auto-generated for videos). |
241
+ | `LargeFile` | `File` | The native browser `File` object for files exceeding `base64LimitMB`. Used instead of `FileDataBase64`. |
242
+ | `IsLargeFile` | `boolean` | `true` when the attachment is stored in `LargeFile` rather than `FileDataBase64`. |
243
+ | `TrustedUrl` | `any` | (Internal State) A `SafeResourceUrl` used for previewing links, PDFs, and videos in the dialog. |
244
+ | `isUploading` | `boolean` | (Internal State) `true` during the upload process. |
245
+
246
+ ---
247
+
248
+
249
+ #### `AttachmentFieldColumn` Interface
250
+
251
+ | Property | Type | Description |
252
+ | ----------------------- | ------------------- | ------------------------------------------------------ |
253
+ | `key` | `string` | Unique identifier for the column. Used by `hiddenColumns`. |
254
+ | `display` | `string` | The column header label to display. |
255
+ | `type` | `TypeAttachmentColumn` | The rendering type (TEXT, DATE, TEMPLATE). Defaults to TEXT. |
256
+ | `externalTemplate` | `TemplateRef<any>` | The `ng-template` to use (required if type is TEMPLATE). |
257
+ | `styles` | `{ flex: string }` | Defines the column width using CSS flex shorthand (e.g., `'1 1 0%'` or `'0 0 150px'`). |
258
+ | `position` | `number` | The column's display order. The built-in "File" column is 10, "Actions" is 100. |
259
+ | `class` | `string` | A custom CSS class added to every cell in this column. |
260
+ | `hidden` | `boolean \| (() => boolean)` | If `true` (or if the function returns `true`), the column is excluded from rendering. |
261
+
262
+ ---
263
+
264
+ #### `AttachmentMenuAction` Interface
265
+
266
+ | Property | Type | Description |
267
+ | ----------------------- | ------------------- | ------------------------------------------------------ |
268
+ | `key` | `string` | Optional unique identifier for the action. Used by `hiddenActions` and `actionHiddenFn`/`actionDisabledFn`. |
269
+ | `icon` | `string` | `mat-icon` name to display (e.g., `'share'`). |
270
+ | `name` | `string` | Text to show in the menu item. |
271
+ | `fn` | `(att: IAttachmentDTO) => void` | Function to execute when the item is clicked. |
272
+ | `disabled` | `(att: IAttachmentDTO) => boolean` | Function to dynamically disable the action for a specific row. |
273
+ | `hidden` | `(att: IAttachmentDTO) => boolean` | Function to dynamically hide the action for a specific row. |
274
+ | `position` | `number` | Display order. Built-in "Preview" is 10, "Delete" is 100. Use a number in between to insert a custom action. |
275
+
276
+ ---
277
+
278
+
279
+ #### **AttachmentCardSize Type**
280
+
281
+ export type AttachmentCardSize = 'small' | 'medium' | 'large' | 'custom';
282
+
283
+ ---
284
+
285
+ ---
286
+
287
+ #### **Layout Configuration**
288
+
289
+ export type Layout = 'full' | 'compact';
290
+
291
+ ---
292
+
293
+
294
+ ## Advanced Features
295
+
296
+ ### Large File Handling
297
+
298
+ 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`.
299
+
300
+ When `localEditedAttachments` emits a list containing large files, your backend integration must handle them differently (e.g., via `FormData` multipart upload).
301
+
302
+ ```typescript
303
+ onAttachmentsChange(list: IAttachmentDTO[]) {
304
+ for (const att of list) {
305
+ if (att.IsLargeFile && att.LargeFile) {
306
+ // Handle with multipart/form-data upload
307
+ const formData = new FormData();
308
+ formData.append('file', att.LargeFile, att.FileName);
309
+ this.http.post('/api/upload', formData).subscribe();
310
+ } else {
311
+ // Standard Base64 handling
312
+ }
313
+ }
314
+ }
315
+ ```
316
+
317
+ > **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`.
318
+
319
+ ---
320
+
321
+ ### Video Support
322
+
323
+ The component has first-class support for video attachments:
324
+
325
+ * **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.
326
+ * **In-browser preview:** Videos stored in `LargeFile` can be played directly in the preview dialog using the native HTML5 `<video>` player.
327
+ * **Server-side compression (optional):** Pass the `videoCompression` input to enable compression via an external API endpoint.
328
+
329
+ ```html
330
+ <eqp-attachments
331
+ [attachmentsList]="videoList"
332
+ [videoCompression]="compressionConfig"
333
+ (localEditedAttachments)="onAttachmentsChange($event)">
334
+ </eqp-attachments>
335
+ ```
336
+
337
+ ```typescript
338
+ compressionConfig = {
339
+ enabled: true,
340
+ maxWidth: 1280, // Max output width in px
341
+ crf: 23, // Constant Rate Factor (quality, lower = better)
342
+ preset: 'veryfast',
343
+ maxFps: 30,
344
+ audioBitrate: 128000
345
+ };
346
+ ```
347
+
348
+ 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).
349
+
350
+ ---
351
+
352
+ ### Dynamic Action Control
353
+
354
+ 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.
355
+
356
+ Built-in action keys: `'preview'`, `'delete'`. Custom actions use the `key` field you define in `AttachmentMenuAction`.
357
+
358
+ ```html
359
+ <eqp-attachments
360
+ [attachmentsList]="documents"
361
+ [actionHiddenFn]="myHiddenFn"
362
+ [actionDisabledFn]="myDisabledFn"
363
+ (localEditedAttachments)="onAttachmentsChange($event)">
364
+ </eqp-attachments>
365
+ ```
366
+
367
+ ```typescript
368
+ // Hide the 'delete' action for already-persisted attachments (those with a numeric ID)
369
+ myHiddenFn = (actionKey: string, att: IAttachmentDTO): boolean => {
370
+ if (actionKey === 'delete' && typeof att.ID === 'number' && att.ID > 0) {
371
+ return true;
372
+ }
373
+ return false;
374
+ };
375
+
376
+ // Disable the 'preview' action for attachments without local data
377
+ myDisabledFn = (actionKey: string, att: IAttachmentDTO): boolean => {
378
+ if (actionKey === 'preview') {
379
+ return !att.FileDataBase64 && !att.LargeFile;
380
+ }
381
+ return false;
382
+ };
383
+ ```
384
+
385
+ You can also hide actions by key name using the simpler `hiddenActions` array input, which is evaluated before the hook:
386
+
387
+ ```html
388
+ <!-- Always hide the 'delete' button for everyone -->
389
+ <eqp-attachments [hiddenActions]="['delete']" ...></eqp-attachments>
390
+ ```
391
+
392
+ ---
393
+
394
+ ### Theming with CSS Custom Properties
395
+
396
+ The component exposes a set of CSS custom properties on `:host` that you can override from the parent application to match your design system.
397
+
398
+ ```scss
399
+ // In your component or global styles
400
+ eqp-attachments {
401
+ --primary-color: #0d6efd; // Main accent color (buttons, borders, icons)
402
+ --success-color: #198754; // Toast success / progress fill
403
+ --error-color: #dc3545; // Toast error color
404
+ --background-light: #f8f9fa; // Page background tint
405
+ --background-card: #ffffff; // Card and panel background
406
+ --text-color: #212529; // Primary text
407
+ --text-color-light: #6c757d; // Secondary/muted text
408
+ --border-color: rgba(0,0,0,0.12); // Dividers and card borders
409
+ --shadow-color: rgba(13,110,253,0.15); // Box shadows
410
+ --border-radius: 12px; // Card and dialog corner radius
411
+ --transition-speed: 0.25s; // Animation duration
412
+ }
413
+ ```
414
+
415
+ ---
416
+
417
+ ## Use Cases
418
+
419
+ ### Case 1: Single Image Upload
420
+
421
+ A configuration for uploading a single image, with drag-and-drop and the image cropper enabled.
422
+
423
+ **HTML**
424
+
425
+ ```
426
+ <eqp-attachments
427
+ [multipleAttachment]="false"
428
+ [attachmentsList]="singleImage"
429
+ [singleAttachmentDragAndDrop]="true"
430
+ [allowOnlyImages]="true"
431
+ [cropOptions]="[1, 2]"
432
+ [maxFileSizeMB]="10"
433
+ (localEditedAttachments)="onAttachmentsChange($event)">
434
+ </eqp-attachments>
435
+ ```
436
+
437
+ **TypeScript**
438
+
439
+ ```
440
+ import { IAttachmentDTO } from '@eqproject/eqp-attachments';
441
+
442
+ export class MyComponent {
443
+ singleImage: IAttachmentDTO[] = [];
444
+
445
+ onAttachmentsChange(updatedList: IAttachmentDTO[]) {
446
+ // In single mode, the list will contain 0 or 1 item
447
+ this.singleImage = updatedList;
448
+ }
449
+ }
450
+ ```
451
+
452
+ ### Case 2: Multiple Attachments Management
453
+
454
+ A full-featured configuration for managing multiple types of attachments, starting in `card` view and allowing the user to switch to `table` view.
455
+
456
+ **HTML**
457
+
458
+ ```
459
+ <eqp-attachments
460
+ [multipleAttachment]="true"
461
+ [attachmentsList]="documentList"
462
+ [allowedTypes]="[1, 2]"
463
+ [viewMode]="'card'"
464
+ [headerTitle]="'Project Documents'"
465
+ [showSummary]="true"
466
+ (localEditedAttachments)="onAttachmentsChange($event)">
467
+ </eqp-attachments>
468
+ ```
469
+
470
+ **TypeScript**
471
+
472
+ ```
473
+ import { IAttachmentDTO, AttachmentType } from '@eqproject/eqp-attachments';
474
+
475
+ export class MyComponent {
476
+ documentList: IAttachmentDTO[] = [
477
+ {
478
+ ID: 101,
479
+ IsImage: false,
480
+ AttachmentType: AttachmentType.FILE,
481
+ FileName: "Final Report.pdf",
482
+ FileExtension: "pdf",
483
+ },
484
+ {
485
+ ID: 102,
486
+ IsImage: false,
487
+ AttachmentType: AttachmentType.LINK,
488
+ FileName: "GitHub Repository",
489
+ FilePath: "https://github.com/...",
490
+ },
491
+ ];
492
+
493
+ onAttachmentsChange(updatedList: IAttachmentDTO[]) {
494
+ this.documentList = updatedList;
495
+ }
496
+ }
497
+ ```
498
+
499
+ ### Case 3: Custom Columns
500
+
501
+ **HTML**
502
+
503
+ ```
504
+ <eqp-attachments
505
+ [multipleAttachment]="true"
506
+ [attachmentsList]="documentList"
507
+ [allowedTypes]="[1, 2]"
508
+ [viewMode]="'table'"
509
+ [headerTitle]="'Project Documents'"
510
+ [showSummary]="true"
511
+ (localEditedAttachments)="onAttachmentsChange($event)"
512
+ [customColumns]="myCustomColumns"
513
+ [customMenuActions]="myCustomActions">
514
+ </eqp-attachments>
515
+
516
+ <ng-template #statusTemplate let-att>
517
+ <mat-chip-listbox>
518
+ <mat-chip [style.background-color]="att.Status === 'Approved' ? '#c8e6c9' : '#ffcdd2'"
519
+ [style.color]="att.Status === 'Approved' ? '#2e7d32' : '#c62828'">
520
+ {{ att.Status }}
521
+ </mat-chip>
522
+ </mat-chip-listbox>
523
+ </ng-template>
524
+ ```
525
+
526
+ **TypeScript**
527
+
528
+ ```
529
+ @ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<any>;
530
+
531
+ documentList: IAttachmentDTO[] = [
532
+ {
533
+ ID: 1,
534
+ FileName: 'report.pdf',
535
+ FileExtension: 'pdf',
536
+ UploadUserName: 'Mario Rossi',
537
+ Status: 'Approved'
538
+ },
539
+ {
540
+ ID: 2,
541
+ FileName: 'schema.zip',
542
+ FileExtension: 'zip',
543
+ UploadUserName: 'Laura Bianchi',
544
+ Status: 'Pending'
545
+ }
546
+ ];
547
+ myCustomColumns: AttachmentFieldColumn[] = [];
548
+ myCustomActions: AttachmentMenuAction[] = [];
549
+
550
+ ngOnInit() {
551
+ this.myCustomColumns = [
552
+ {
553
+ key: 'UploadUserName',
554
+ display: 'Uploaded By', // 'display' is the header label
555
+ type: TypeAttachmentColumn.TEXT, // Renders as plain text
556
+ styles: { flex: '2 1 0%' }, // Takes 2 parts of the available space
557
+ position: 20 // Shows after "File" (10)
558
+ },
559
+ {
560
+ key: 'Status',
561
+ display: 'Status',
562
+ type: TypeAttachmentColumn.TEMPLATE,
563
+ externalTemplate: this.statusTemplate, // Pass the ng-template
564
+ styles: { flex: '1 1 0%' },
565
+ position: 30 // Shows after "Uploaded By" (20)
566
+ }
567
+ ];
568
+
569
+ this.myCustomActions = [
570
+ {
571
+ key: 'share', // Optional but recommended for hiddenActions/hooks
572
+ icon: 'share',
573
+ name: 'Share', // 'name' is the label shown in the menu
574
+ fn: (item) => this.shareAttachment(item), // 'fn' is the click handler
575
+ position: 25 // Show after "Preview" (10) and before "Delete" (100)
576
+ },
577
+ {
578
+ key: 'rename',
579
+ icon: 'edit',
580
+ name: 'Edit Name',
581
+ fn: (item) => this.renameAttachment(item),
582
+ position: 26
583
+ }
584
+ ];
585
+ }
586
+ ```
587
+
588
+
589
+ ## Credits
590
+
591
+ This library has been developed by EqProject SRL. For more info, contact: info@eqproject.it