diff --git a/app/javascript/controllers/file_input_reset_controller.ts b/app/javascript/controllers/file_input_reset_controller.ts index cbce6ab6b..0320c54b1 100644 --- a/app/javascript/controllers/file_input_reset_controller.ts +++ b/app/javascript/controllers/file_input_reset_controller.ts @@ -1,37 +1,81 @@ import { ApplicationController } from './application_controller'; -import { hide, show } from '@utils'; - export class FileInputResetController extends ApplicationController { - static targets = ['reset']; - - declare readonly resetTarget: HTMLElement; + static targets = ['fileList']; + declare fileListTarget: HTMLElement; connect() { - this.on('change', (event) => { - if (event.target == this.fileInput) { - this.showResetButton(); + super.connect(); + this.updateFileList(); + this.element.addEventListener('change', (event) => { + if ( + event.target instanceof HTMLInputElement && + event.target.type === 'file' + ) { + this.updateFileList(); } }); } - reset(event: Event) { - event.preventDefault(); - this.fileInput.value = ''; - hide(this.resetTarget); + updateFileList() { + const files = this.fileInput?.files ?? []; + this.fileListTarget.innerHTML = ''; + + const deleteLabel = + this.element.getAttribute('data-delete-label') || 'Delete'; + + Array.from(files).forEach((file, index) => { + const container = document.createElement('li'); + container.style.display = 'flex'; + container.style.alignItems = 'center'; + + const deleteButton = this.createDeleteButton(deleteLabel, index); + container.appendChild(deleteButton); + + const listItem = document.createElement('div'); + listItem.textContent = file.name; + listItem.style.marginLeft = '8px'; + + container.appendChild(listItem); + this.fileListTarget.appendChild(container); + }); } - showResetButton() { - show(this.resetTarget); + createDeleteButton(deleteLabel: string, index: number) { + const button = document.createElement('button'); + button.textContent = deleteLabel; + button.classList.add( + 'fr-btn', + 'fr-btn--tertiary', + 'fr-btn--sm', + 'fr-icon-delete-line' + ); + + button.addEventListener('click', (event) => { + event.preventDefault(); + this.removeFile(index); + }); + + return button; } - private get fileInput() { - const inputs = - this.element.querySelectorAll('input[type="file"]'); - if (inputs.length == 0) { - throw new Error('No file input found'); - } else if (inputs.length > 1) { - throw new Error('Multiple file inputs found'); - } - return inputs[0]; + removeFile(index: number) { + const files = this.fileInput?.files; + if (!files) return; + + const dataTransfer = new DataTransfer(); + Array.from(files).forEach((file, i) => { + if (index !== i) { + dataTransfer.items.add(file); + } + }); + + if (this.fileInput) this.fileInput.files = dataTransfer.files; + this.updateFileList(); + } + + private get fileInput(): HTMLInputElement | null { + return this.element.querySelector( + 'input[type="file"]' + ) as HTMLInputElement | null; } }