Skip to content

Commit 3c94b1c

Browse files
committed
Feat(web): FileUploader - Support for crop image #DS-954
1 parent eae000d commit 3c94b1c

File tree

4 files changed

+312
-12
lines changed

4 files changed

+312
-12
lines changed

packages/web/src/js/FileUploader.ts

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ const DEFAULT_ERROR_MESSAGES = {
3232
errorFileNotSupported: 'is not a supported file. Please ensure you are uploading a supported file format.',
3333
};
3434

35+
export interface FileMetadata {
36+
[key: string | number]: unknown;
37+
}
38+
39+
export interface FileQueueValueType {
40+
file: File;
41+
meta?: FileMetadata;
42+
}
43+
3544
class FileUploader extends BaseComponent {
3645
wrapper: HTMLElement;
3746
inputElement: HTMLInputElement;
@@ -45,7 +54,7 @@ class FileUploader extends BaseComponent {
4554
isMultiple: boolean;
4655
accept: string;
4756
isDragging: boolean;
48-
fileQueue: Map<string, File>;
57+
fileQueue: Map<string, FileQueueValueType>;
4958
instanceUid: string;
5059
errors: Record<string, string>;
5160
isDisabled: boolean;
@@ -292,7 +301,7 @@ class FileUploader extends BaseComponent {
292301
});
293302
}
294303

295-
appendToList(file: File) {
304+
appendToList(file: File, meta?: FileMetadata) {
296305
if (!this.listElement) {
297306
if (process.env.NODE_ENV === 'development') {
298307
/* Because part of the sheet is also a hidden title with an identifier */
@@ -317,7 +326,12 @@ class FileUploader extends BaseComponent {
317326
return;
318327
}
319328

320-
this.fileQueue.set(id, file);
329+
const newValue: FileQueueValueType = { file };
330+
if (meta) {
331+
newValue.meta = meta;
332+
}
333+
334+
this.fileQueue.set(id, newValue);
321335

322336
this.listElement.appendChild(attachment);
323337

@@ -326,23 +340,57 @@ class FileUploader extends BaseComponent {
326340
this.dragReset();
327341
}
328342

329-
addToQueue(file: File) {
343+
addToQueue(file: File, meta?: FileMetadata, callback?: (key: string, file: File, meta?: FileMetadata) => void) {
330344
try {
331345
EventHandler.trigger(this.wrapper, EVENT_QUEUE_FILE, { fileQueue: this.fileQueue, currentFile: file });
332346
this.checkAllowedFileType(file);
333347
this.checkAllowedFileSize(file);
334348
this.checkFileQueueDuplicity(file);
335349
this.checkQueueLimit();
336-
this.appendToList(file);
350+
this.appendToList(file, meta);
337351
this.updateDropZoneVisibility();
338352
this.updateNameAttribute();
353+
callback && callback(this.getUpdatedFileName(file.name), file, meta);
339354
EventHandler.trigger(this.wrapper, EVENT_QUEUED_FILE, { fileQueue: this.fileQueue, currentFile: file });
340355
} catch (error) {
341356
EventHandler.trigger(this.wrapper, EVENT_ERROR, { validationText: error.message });
342357
}
343358
}
344359

345-
removeFromQueue(name: string) {
360+
static isCoordsInMeta = (meta: FileMetadata) => {
361+
return ['x', 'y', 'width', 'height'].every((coord) => meta[coord] != null);
362+
};
363+
364+
updateQueue(
365+
name: string,
366+
file: File,
367+
meta?: FileMetadata,
368+
callback?: (key: string, file: File, meta?: FileMetadata) => void,
369+
) {
370+
if (this.fileQueue.has(name)) {
371+
const newValue: FileQueueValueType = { file };
372+
if (meta) {
373+
newValue.meta = meta;
374+
}
375+
376+
this.fileQueue.set(name, newValue);
377+
378+
const itemImgElement = SelectorEngine.findOne(`#${name} .FileUploaderAttachment__image img`);
379+
if (meta && itemImgElement && FileUploader.isCoordsInMeta(meta)) {
380+
const cropStyles = `
381+
--file-uploader-attachment-image-top: -${meta.y}px;
382+
--file-uploader-attachment-image-left: -${meta.x}px;
383+
--file-uploader-attachment-image-width: ${meta.width}px;
384+
--file-uploader-attachment-image-height: ${meta.height}px
385+
`;
386+
itemImgElement?.setAttribute('style', cropStyles);
387+
}
388+
389+
callback && callback(name, file, meta);
390+
}
391+
}
392+
393+
removeFromQueue(name: string, callback?: (key: string) => void) {
346394
if (this.fileQueue.has(name)) {
347395
EventHandler.trigger(this.wrapper, EVENT_UNQUEUE_FILE, { fileQueue: this.fileQueue, currentFile: name });
348396
const itemElement = SelectorEngine.findOne(`#${name}`);
@@ -351,6 +399,7 @@ class FileUploader extends BaseComponent {
351399
this.updateDropZoneVisibility();
352400
this.updateNameAttribute();
353401
this.removeValidationWError();
402+
callback && callback(name);
354403
EventHandler.trigger(this.wrapper, EVENT_UNQUEUED_FILE, { fileQueue: this.fileQueue, currentFile: name });
355404
}
356405
}
@@ -359,7 +408,7 @@ class FileUploader extends BaseComponent {
359408
return this.fileQueue.get(name);
360409
}
361410

362-
onChange(event: Event & { target: HTMLInputElement }) {
411+
onChange(event: Event & { target: HTMLInputElement }, meta?: FileMetadata) {
363412
const { target } = event;
364413
const filesArray = target.files ? [...target.files] : [];
365414

@@ -369,7 +418,7 @@ class FileUploader extends BaseComponent {
369418

370419
filesArray.forEach((file: File) => {
371420
if (counter < this.fileQueueLimit) {
372-
this.addToQueue(file);
421+
this.addToQueue(file, meta);
373422
counter += 1;
374423
} else {
375424
overLimit = true;

packages/web/src/js/__tests__/FileUploader.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,4 +338,29 @@ describe('FileUploader', () => {
338338
expect(instance.getFileFromQueue('test')).toBeUndefined();
339339
});
340340
});
341+
342+
describe('updateQueue', () => {
343+
it('should update the file in the queue without meta and callback', () => {
344+
const name = 'test';
345+
const file = { name: 'test.txt' } as File;
346+
instance.fileQueue = new Map().set(name, { file: null });
347+
348+
instance.updateQueue(name, file);
349+
350+
expect(instance.fileQueue.get(name)).toEqual({ file });
351+
});
352+
353+
it('should update the file in the queue with meta data and trigger callback', () => {
354+
const name = 'test';
355+
const file = { name: 'test.txt' } as File;
356+
const meta = { y: 10, x: 20, width: 100, height: 50 };
357+
const callback = jest.fn();
358+
instance.fileQueue = new Map().set(name, { file: null });
359+
360+
instance.updateQueue(name, file, meta, callback);
361+
362+
expect(instance.fileQueue.get(name)).toEqual({ file, meta });
363+
expect(callback).toHaveBeenCalledWith(name, file, meta);
364+
});
365+
});
341366
});

packages/web/src/scss/components/FileUploader/_FileUploaderAttachment.scss

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
}
1919

2020
.FileUploaderAttachment__image {
21+
position: relative;
2122
width: 56px;
2223
height: 56px;
2324
overflow: hidden;
@@ -26,9 +27,12 @@
2627
}
2728

2829
.FileUploaderAttachment__image > img {
29-
width: 100%;
30-
height: 100%;
31-
object-fit: cover;
30+
position: absolute;
31+
top: var(--file-uploader-attachment-image-top, 0);
32+
left: var(--file-uploader-attachment-image-left, 0);
33+
width: var(--file-uploader-attachment-image-width, 100%);
34+
height: var(--file-uploader-attachment-image-height, 100%);
35+
object-fit: var(--file-uploader-attachment-image-object-fit, cover);
3236
object-position: center;
3337
}
3438

0 commit comments

Comments
 (0)