Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Dropzone/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## 2.31

- Support for multiple files drag and drop and peviews

## 2.30

- Ensure compatibility with PHP 8.5
Expand Down
25 changes: 18 additions & 7 deletions src/Dropzone/assets/dist/controller.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,30 @@ import { Controller } from '@hotwired/stimulus';
declare class export_default extends Controller {
readonly inputTarget: HTMLInputElement;
readonly placeholderTarget: HTMLDivElement;
readonly previewTarget: HTMLDivElement;
readonly previewClearButtonTarget: HTMLButtonElement;
readonly previewFilenameTarget: HTMLDivElement;
readonly previewImageTarget: HTMLDivElement;
readonly previewTargets: HTMLDivElement[];
readonly previewContainerTarget: HTMLDivElement;
static targets: string[];
files: Map<string, File>;
initialize(): void;
connect(): void;
disconnect(): void;
clear(): void;
onInputChange(event: any): void;
_populateImagePreview(file: Blob): void;
clear(event?: {
target?: HTMLElement;
params?: {
filename?: string;
};
}): void;
onInputChange(): void;
private renderPreview;
private clearPreviewContainer;
private buildPreview;
_populateImagePreview(element: HTMLElement, file: File): void;
onDragEnter(): void;
onDragLeave(event: any): void;
private updateFileInput;
private addFiles;
private isImage;
private get isMultiple();
private dispatchEvent;
}

Expand Down
139 changes: 102 additions & 37 deletions src/Dropzone/assets/dist/controller.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// src/controller.ts
import { Controller } from "@hotwired/stimulus";
var controller_default = class extends Controller {
constructor() {
super(...arguments);
this.files = /* @__PURE__ */ new Map();
}
initialize() {
this.clear = this.clear.bind(this);
this.onInputChange = this.onInputChange.bind(this);
Expand All @@ -9,72 +13,133 @@ var controller_default = class extends Controller {
}
connect() {
this.clear();
this.previewClearButtonTarget.addEventListener("click", this.clear);
this.inputTarget.addEventListener("change", this.onInputChange);
this.element.addEventListener("dragenter", this.onDragEnter);
this.element.addEventListener("dragleave", this.onDragLeave);
this.dispatchEvent("connect");
}
disconnect() {
this.previewClearButtonTarget.removeEventListener("click", this.clear);
this.inputTarget.removeEventListener("change", this.onInputChange);
this.element.removeEventListener("dragenter", this.onDragEnter);
this.element.removeEventListener("dragleave", this.onDragLeave);
}
clear() {
this.inputTarget.value = "";
this.inputTarget.style.display = "block";
this.placeholderTarget.style.display = "block";
this.previewTarget.style.display = "none";
this.previewImageTarget.style.display = "none";
this.previewImageTarget.style.backgroundImage = "none";
this.previewFilenameTarget.textContent = "";
clear(event) {
if (event?.params) {
const filename = event.params.filename;
if (filename && this.files.has(filename)) {
this.files.delete(filename);
this.updateFileInput();
this.renderPreview();
}
}
if (!this.inputTarget || !this.inputTarget.files || this.inputTarget?.files?.length === 0) {
this.placeholderTarget.style.display = "block";
if (!this.isMultiple) {
this.inputTarget.style.display = "block";
}
}
this.dispatchEvent("clear");
}
onInputChange(event) {
const file = event.target.files[0];
if (typeof file === "undefined") {
onInputChange() {
const files = this.inputTarget.files;
if (!files || files.length <= 0) {
return;
}
this.inputTarget.style.display = "none";
this.placeholderTarget.style.display = "none";
this.previewFilenameTarget.textContent = file.name;
this.previewTarget.style.display = "flex";
this.previewImageTarget.style.display = "none";
if (file.type && file.type.indexOf("image") !== -1) {
this._populateImagePreview(file);
if (!this.isMultiple && this.files.size > 0) {
this.inputTarget.style.display = "none";
}
this.dispatchEvent("change", file);
const selectedFiles = this.isMultiple ? Array.from(files) : Array.from(files).slice(0, 1);
this.addFiles(selectedFiles);
this.updateFileInput();
this.renderPreview();
this.dispatchEvent("change", files);
}
_populateImagePreview(file) {
if (typeof FileReader === "undefined") {
return;
renderPreview() {
this.clearPreviewContainer();
for (const file of this.files.values()) {
const preview = this.buildPreview(file);
if (preview) {
this.previewContainerTarget.appendChild(preview);
}
}
if (this.previewTargets.length > 1) {
this.placeholderTarget.style.display = "none";
if (!this.isMultiple) {
this.inputTarget.style.display = "none";
} else {
this.inputTarget.style.display = "block";
}
}
}
clearPreviewContainer() {
const previews = this.previewTargets;
previews.slice(1).forEach((el) => el.remove());
}
buildPreview(file, element) {
if (!element) {
element = this.previewContainerTarget.firstElementChild?.cloneNode(true);
}
element.style.display = "flex";
const fileName = element.querySelector(".dropzone-preview-filename");
if (fileName) {
fileName.textContent = file.name;
}
const button = element.querySelector(".dropzone-preview-button");
if (button) {
button.setAttribute("data-symfony--ux-dropzone--dropzone-filename-param", file.name);
}
this._populateImagePreview(element, file);
return element;
}
_populateImagePreview(element, file) {
const image = element.querySelector(".dropzone-preview-image");
if (image && this.isImage(file) && typeof FileReader !== "undefined") {
const reader = new FileReader();
reader.addEventListener("load", (event) => {
image.querySelector(".dropzone-preview-image")?.remove();
image.style.backgroundImage = `url('${event.target.result}')`;
image.style.display = "block";
});
reader.readAsDataURL(file);
}
const reader = new FileReader();
reader.addEventListener("load", (event) => {
this.previewImageTarget.style.display = "block";
this.previewImageTarget.style.backgroundImage = `url("${event.target.result}")`;
});
reader.readAsDataURL(file);
}
onDragEnter() {
this.inputTarget.style.display = "block";
this.placeholderTarget.style.display = "block";
this.previewTarget.style.display = "none";
}
onDragLeave(event) {
event.preventDefault();
if (!this.element.contains(event.relatedTarget)) {
this.inputTarget.style.display = "none";
this.placeholderTarget.style.display = "none";
this.previewTarget.style.display = "block";
}
updateFileInput() {
const dataTransfer = new DataTransfer();
for (const file of this.files.values()) {
dataTransfer.items.add(file);
}
this.inputTarget.files = dataTransfer.files;
}
addFiles(files) {
for (const file of files) {
this.files.set(file.name, file);
}
}
isImage(file) {
return typeof file.type !== "undefined" && file.type.indexOf("image") !== -1;
}
get isMultiple() {
return this.inputTarget.multiple;
}
dispatchEvent(name, payload = {}) {
this.dispatch(name, { detail: payload, prefix: "dropzone" });
}
};
controller_default.targets = ["input", "placeholder", "preview", "previewClearButton", "previewFilename", "previewImage"];
controller_default.targets = [
"input",
"placeholder",
"preview",
"previewClearButton",
"previewFilename",
"previewImage",
"previewContainer"
];
export {
controller_default as default
};
2 changes: 1 addition & 1 deletion src/Dropzone/assets/dist/style.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/Dropzone/assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@symfony/ux-dropzone",
"description": "File input dropzones for Symfony Forms",
"license": "MIT",
"version": "2.31.0",
"version": "2.30.0",
"keywords": [
"symfony-ux"
],
Expand Down
Loading
Loading