Skip to content

Commit ce483d7

Browse files
committed
bug #2154 [Dropzone] Enable file replacement via "drag-and-drop" (rrr63)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [Dropzone] Enable file replacement via "drag-and-drop" | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | Issues | Fix #1480 | License | MIT Adding `dragenter` event to allow file replacement Commits ------- a78fdf4 [Dropzone] Enable file replacement via "drag-and-drop"
2 parents 234040c + a78fdf4 commit ce483d7

File tree

5 files changed

+75
-0
lines changed

5 files changed

+75
-0
lines changed

src/Dropzone/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## 2.20
4+
5+
- Enable file replacement via "drag-and-drop"
6+
37
## 2.13.2
48

59
- Revert "Change JavaScript package to `type: module`"

src/Dropzone/assets/dist/controller.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ export default class extends Controller {
1313
clear(): void;
1414
onInputChange(event: any): void;
1515
_populateImagePreview(file: Blob): void;
16+
onDragEnter(): void;
17+
onDragLeave(event: any): void;
1618
private dispatchEvent;
1719
}

src/Dropzone/assets/dist/controller.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,22 @@ class default_1 extends Controller {
44
initialize() {
55
this.clear = this.clear.bind(this);
66
this.onInputChange = this.onInputChange.bind(this);
7+
this.onDragEnter = this.onDragEnter.bind(this);
8+
this.onDragLeave = this.onDragLeave.bind(this);
79
}
810
connect() {
911
this.clear();
1012
this.previewClearButtonTarget.addEventListener('click', this.clear);
1113
this.inputTarget.addEventListener('change', this.onInputChange);
14+
this.element.addEventListener('dragenter', this.onDragEnter);
15+
this.element.addEventListener('dragleave', this.onDragLeave);
1216
this.dispatchEvent('connect');
1317
}
1418
disconnect() {
1519
this.previewClearButtonTarget.removeEventListener('click', this.clear);
1620
this.inputTarget.removeEventListener('change', this.onInputChange);
21+
this.element.removeEventListener('dragenter', this.onDragEnter);
22+
this.element.removeEventListener('dragleave', this.onDragLeave);
1723
}
1824
clear() {
1925
this.inputTarget.value = '';
@@ -51,6 +57,19 @@ class default_1 extends Controller {
5157
});
5258
reader.readAsDataURL(file);
5359
}
60+
onDragEnter() {
61+
this.inputTarget.style.display = 'block';
62+
this.placeholderTarget.style.display = 'block';
63+
this.previewTarget.style.display = 'none';
64+
}
65+
onDragLeave(event) {
66+
event.preventDefault();
67+
if (!this.element.contains(event.relatedTarget)) {
68+
this.inputTarget.style.display = 'none';
69+
this.placeholderTarget.style.display = 'none';
70+
this.previewTarget.style.display = 'block';
71+
}
72+
}
5473
dispatchEvent(name, payload = {}) {
5574
this.dispatch(name, { detail: payload, prefix: 'dropzone' });
5675
}

src/Dropzone/assets/src/controller.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export default class extends Controller {
2222
initialize() {
2323
this.clear = this.clear.bind(this);
2424
this.onInputChange = this.onInputChange.bind(this);
25+
this.onDragEnter = this.onDragEnter.bind(this);
26+
this.onDragLeave = this.onDragLeave.bind(this);
2527
}
2628

2729
connect() {
@@ -34,12 +36,20 @@ export default class extends Controller {
3436
// Listen on input change and display preview
3537
this.inputTarget.addEventListener('change', this.onInputChange);
3638

39+
// Add dragenter event listener
40+
this.element.addEventListener('dragenter', this.onDragEnter);
41+
42+
// Add dragleave event listener
43+
this.element.addEventListener('dragleave', this.onDragLeave);
44+
3745
this.dispatchEvent('connect');
3846
}
3947

4048
disconnect() {
4149
this.previewClearButtonTarget.removeEventListener('click', this.clear);
4250
this.inputTarget.removeEventListener('change', this.onInputChange);
51+
this.element.removeEventListener('dragenter', this.onDragEnter);
52+
this.element.removeEventListener('dragleave', this.onDragLeave);
4353
}
4454

4555
clear() {
@@ -93,6 +103,23 @@ export default class extends Controller {
93103
reader.readAsDataURL(file);
94104
}
95105

106+
onDragEnter() {
107+
this.inputTarget.style.display = 'block';
108+
this.placeholderTarget.style.display = 'block';
109+
this.previewTarget.style.display = 'none';
110+
}
111+
112+
onDragLeave(event: any) {
113+
event.preventDefault();
114+
115+
// Check if we really leave the main drag area
116+
if (!this.element.contains(event.relatedTarget as Node)) {
117+
this.inputTarget.style.display = 'none';
118+
this.placeholderTarget.style.display = 'none';
119+
this.previewTarget.style.display = 'block';
120+
}
121+
}
122+
96123
private dispatchEvent(name: string, payload: any = {}) {
97124
this.dispatch(name, { detail: payload, prefix: 'dropzone' });
98125
}

src/Dropzone/assets/test/controller.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,27 @@ describe('DropzoneController', () => {
130130
expect(dispatched).not.toBeNull();
131131
expect(dispatched.detail).toStrictEqual(file);
132132
});
133+
134+
it('on drag', async () => {
135+
startStimulus();
136+
137+
// Simulate dragenter event
138+
const dragEnterEvent = new Event('dragenter');
139+
getByTestId(container, 'container').dispatchEvent(dragEnterEvent);
140+
141+
// Check that the input and placeholder are visible, and preview hidden
142+
await waitFor(() => expect(getByTestId(container, 'input')).toHaveStyle({ display: 'block' }));
143+
await waitFor(() => expect(getByTestId(container, 'placeholder')).toHaveStyle({ display: 'block' }));
144+
await waitFor(() => expect(getByTestId(container, 'preview')).toHaveStyle({ display: 'none' }));
145+
146+
// Simulate dragleave event with relatedTarget set to outside the dropzone
147+
const dragLeaveEvent = new Event('dragleave', { bubbles: true });
148+
Object.defineProperty(dragLeaveEvent, 'relatedTarget', { value: document.body });
149+
getByTestId(container, 'container').dispatchEvent(dragLeaveEvent);
150+
151+
// Check that the input and placeholder are hidden, and preview shown
152+
await waitFor(() => expect(getByTestId(container, 'input')).toHaveStyle({ display: 'none' }));
153+
await waitFor(() => expect(getByTestId(container, 'placeholder')).toHaveStyle({ display: 'none' }));
154+
await waitFor(() => expect(getByTestId(container, 'preview')).toHaveStyle({ display: 'block' }));
155+
});
133156
});

0 commit comments

Comments
 (0)