@symfony/ux-dropzone 3.2.0 → 3.5.1

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.
@@ -6,15 +6,35 @@ declare class export_default extends Controller {
6
6
  readonly previewClearButtonTarget: HTMLButtonElement;
7
7
  readonly previewFilenameTarget: HTMLDivElement;
8
8
  readonly previewImageTarget: HTMLDivElement;
9
+ readonly previewListTarget: HTMLUListElement;
10
+ readonly hasPreviewListTarget: boolean;
11
+ readonly multipleValue: boolean;
12
+ readonly removeLabelValue: string;
9
13
  static targets: string[];
14
+ static values: {
15
+ multiple: BooleanConstructor;
16
+ removeLabel: {
17
+ type: StringConstructor;
18
+ default: string;
19
+ };
20
+ };
21
+ private dataTransfer;
10
22
  initialize(): void;
11
23
  connect(): void;
12
24
  disconnect(): void;
13
25
  clear(): void;
14
26
  onInputChange(event: any): void;
15
- _populateImagePreview(file: Blob): void;
27
+ _populateImagePreview(target: HTMLElement, file: Blob): void;
16
28
  onDragEnter(): void;
17
29
  onDragLeave(event: any): void;
30
+ onDrop(): void;
31
+ private connectMultiple;
32
+ onMultipleChange(): void;
33
+ private syncMultiple;
34
+ private removeFileAt;
35
+ private containsFile;
36
+ private renderList;
37
+ private buildListItem;
18
38
  private dispatchEvent;
19
39
  }
20
40
  export { export_default as default };
@@ -1,24 +1,54 @@
1
1
  import { Controller } from "@hotwired/stimulus";
2
- var _Class = class extends Controller {
2
+ var controller_default = class extends Controller {
3
+ static targets = [
4
+ "input",
5
+ "placeholder",
6
+ "preview",
7
+ "previewClearButton",
8
+ "previewFilename",
9
+ "previewImage",
10
+ "previewList"
11
+ ];
12
+ static values = {
13
+ multiple: Boolean,
14
+ removeLabel: {
15
+ type: String,
16
+ default: "Remove"
17
+ }
18
+ };
19
+ dataTransfer;
3
20
  initialize() {
4
21
  this.clear = this.clear.bind(this);
5
22
  this.onInputChange = this.onInputChange.bind(this);
6
23
  this.onDragEnter = this.onDragEnter.bind(this);
7
24
  this.onDragLeave = this.onDragLeave.bind(this);
25
+ this.onDrop = this.onDrop.bind(this);
26
+ this.onMultipleChange = this.onMultipleChange.bind(this);
8
27
  }
9
28
  connect() {
29
+ if (this.multipleValue) {
30
+ this.connectMultiple();
31
+ this.dispatchEvent("connect");
32
+ return;
33
+ }
10
34
  this.clear();
11
35
  this.previewClearButtonTarget.addEventListener("click", this.clear);
12
36
  this.inputTarget.addEventListener("change", this.onInputChange);
13
37
  this.element.addEventListener("dragenter", this.onDragEnter);
14
38
  this.element.addEventListener("dragleave", this.onDragLeave);
39
+ this.element.addEventListener("drop", this.onDrop);
15
40
  this.dispatchEvent("connect");
16
41
  }
17
42
  disconnect() {
43
+ if (this.multipleValue) {
44
+ this.inputTarget.removeEventListener("change", this.onMultipleChange);
45
+ return;
46
+ }
18
47
  this.previewClearButtonTarget.removeEventListener("click", this.clear);
19
48
  this.inputTarget.removeEventListener("change", this.onInputChange);
20
49
  this.element.removeEventListener("dragenter", this.onDragEnter);
21
50
  this.element.removeEventListener("dragleave", this.onDragLeave);
51
+ this.element.removeEventListener("drop", this.onDrop);
22
52
  }
23
53
  clear() {
24
54
  this.inputTarget.value = "";
@@ -38,15 +68,15 @@ var _Class = class extends Controller {
38
68
  this.previewFilenameTarget.textContent = file.name;
39
69
  this.previewTarget.style.display = "flex";
40
70
  this.previewImageTarget.style.display = "none";
41
- if (file.type && file.type.indexOf("image") !== -1) this._populateImagePreview(file);
71
+ if (file.type && file.type.indexOf("image") !== -1) this._populateImagePreview(this.previewImageTarget, file);
42
72
  this.dispatchEvent("change", file);
43
73
  }
44
- _populateImagePreview(file) {
74
+ _populateImagePreview(target, file) {
45
75
  if (typeof FileReader === "undefined") return;
46
76
  const reader = new FileReader();
47
77
  reader.addEventListener("load", (event) => {
48
- this.previewImageTarget.style.display = "block";
49
- this.previewImageTarget.style.backgroundImage = `url("${event.target.result}")`;
78
+ target.style.display = "block";
79
+ target.style.backgroundImage = `url("${event.target.result}")`;
50
80
  });
51
81
  reader.readAsDataURL(file);
52
82
  }
@@ -63,6 +93,60 @@ var _Class = class extends Controller {
63
93
  this.previewTarget.style.display = "block";
64
94
  }
65
95
  }
96
+ onDrop() {
97
+ setTimeout(() => {
98
+ if (!this.inputTarget.files?.length) return;
99
+ this.inputTarget.style.display = "none";
100
+ this.placeholderTarget.style.display = "none";
101
+ this.previewTarget.style.display = "flex";
102
+ });
103
+ }
104
+ connectMultiple() {
105
+ this.dataTransfer = new DataTransfer();
106
+ for (const file of Array.from(this.inputTarget.files ?? [])) this.dataTransfer.items.add(file);
107
+ this.inputTarget.addEventListener("change", this.onMultipleChange);
108
+ this.renderList();
109
+ }
110
+ onMultipleChange() {
111
+ for (const file of Array.from(this.inputTarget.files ?? [])) if (!this.containsFile(file)) this.dataTransfer.items.add(file);
112
+ this.syncMultiple();
113
+ }
114
+ syncMultiple() {
115
+ this.inputTarget.files = this.dataTransfer.files;
116
+ this.renderList();
117
+ this.dispatchEvent("change", this.inputTarget.files);
118
+ }
119
+ removeFileAt(index, file) {
120
+ this.dataTransfer.items.remove(index);
121
+ this.syncMultiple();
122
+ this.dispatchEvent("remove", file);
123
+ }
124
+ containsFile(file) {
125
+ return Array.from(this.dataTransfer.files).some((existing) => existing.name === file.name && existing.size === file.size && existing.lastModified === file.lastModified);
126
+ }
127
+ renderList() {
128
+ if (!this.hasPreviewListTarget) return;
129
+ const items = Array.from(this.dataTransfer.files).map((file, index) => this.buildListItem(file, index));
130
+ this.previewListTarget.replaceChildren(...items);
131
+ }
132
+ buildListItem(file, index) {
133
+ const item = document.createElement("li");
134
+ item.className = "dropzone-preview-list-item";
135
+ const image = document.createElement("div");
136
+ image.className = "dropzone-preview-image";
137
+ image.style.display = "none";
138
+ if (file.type && file.type.indexOf("image") !== -1) this._populateImagePreview(image, file);
139
+ const filename = document.createElement("span");
140
+ filename.className = "dropzone-preview-filename";
141
+ filename.textContent = file.name;
142
+ const remove = document.createElement("button");
143
+ remove.type = "button";
144
+ remove.className = "dropzone-preview-list-remove";
145
+ remove.setAttribute("aria-label", `${this.removeLabelValue} ${file.name}`);
146
+ remove.addEventListener("click", () => this.removeFileAt(index, file));
147
+ item.append(image, filename, remove);
148
+ return item;
149
+ }
66
150
  dispatchEvent(name, payload = {}) {
67
151
  this.dispatch(name, {
68
152
  detail: payload,
@@ -70,12 +154,4 @@ var _Class = class extends Controller {
70
154
  });
71
155
  }
72
156
  };
73
- _Class.targets = [
74
- "input",
75
- "placeholder",
76
- "preview",
77
- "previewClearButton",
78
- "previewFilename",
79
- "previewImage"
80
- ];
81
- export { _Class as default };
157
+ export { controller_default as default };
@@ -1 +1 @@
1
- .dropzone-container{border:2px dashed #bbb;align-items:center;min-height:100px;padding:20px 10px;display:flex;position:relative}.dropzone-input{opacity:0;cursor:pointer;z-index:1;width:100%;height:100%;display:block;position:absolute;top:0;left:0}.dropzone-preview{align-items:center;max-width:100%;display:flex}.dropzone-preview-image{background-position:50%;background-repeat:no-repeat;background-size:contain;flex-basis:0;min-width:50px;max-width:50px;height:50px;margin-right:10px}.dropzone-preview-filename{word-wrap:anywhere}.dropzone-preview-button{z-index:1;width:auto;color:inherit;font:inherit;-webkit-font-smoothing:inherit;-moz-osx-font-smoothing:inherit;-webkit-appearance:none;background:0 0;border:none;margin:0;padding:0;line-height:normal;position:absolute;top:0;right:0;overflow:visible}.dropzone-preview-button:before{content:"×";cursor:pointer;padding:3px 7px}.dropzone-placeholder{text-align:center;color:#999;flex-grow:1}
1
+ .dropzone-container{border:2px dashed #bbb;align-items:center;min-height:100px;padding:20px 10px;display:flex;position:relative}.dropzone-input{opacity:0;cursor:pointer;z-index:1;width:100%;height:100%;display:block;position:absolute;top:0;left:0}.dropzone-preview{align-items:center;max-width:100%;display:flex}.dropzone-preview-image{background-position:50%;background-repeat:no-repeat;background-size:contain;flex-basis:0;min-width:50px;max-width:50px;height:50px;margin-right:10px}.dropzone-preview-filename{word-wrap:anywhere}.dropzone-preview-button{z-index:1;width:auto;color:inherit;font:inherit;-webkit-font-smoothing:inherit;-moz-osx-font-smoothing:inherit;-webkit-appearance:none;background:0 0;border:none;margin:0;padding:0;line-height:normal;position:absolute;top:0;right:0;overflow:visible}.dropzone-preview-button:before{content:"×";cursor:pointer;padding:3px 7px}.dropzone-placeholder{text-align:center;color:#999;flex-grow:1}.dropzone-container-multiple{flex-direction:column;align-items:stretch}.dropzone-preview-list{z-index:2;width:100%;margin:10px 0 0;padding:0;list-style:none;position:relative}.dropzone-preview-list-item{align-items:center;gap:10px;padding:5px 0;display:flex}.dropzone-preview-list-item .dropzone-preview-image{margin-right:0}.dropzone-preview-list .dropzone-preview-filename{flex-grow:1}.dropzone-preview-list-remove{color:inherit;font:inherit;cursor:pointer;-webkit-appearance:none;background:0 0;border:none;margin:0;padding:0;line-height:normal}.dropzone-preview-list-remove:before{content:"×";padding:3px 7px}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@symfony/ux-dropzone",
3
3
  "description": "File input dropzones for Symfony Forms",
4
4
  "license": "MIT",
5
- "version": "3.2.0",
5
+ "version": "3.5.1",
6
6
  "keywords": [
7
7
  "symfony-ux"
8
8
  ],