Skip to content

Commit a2d1ce3

Browse files
committed
Merge branch 'release/0.1.1' into main
2 parents 01f7828 + aa70759 commit a2d1ce3

File tree

8 files changed

+245
-7
lines changed

8 files changed

+245
-7
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "django-image-uploader-widget",
3-
"version": "0.1.0",
3+
"version": "0.1.1",
44
"main": "index.js",
55
"repository": "https://github.com/EduardoJM/django-image-uploader-widget.git",
66
"author": "Eduardo Oliveira <[email protected]>",

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
setup(
1010
name='django-image-uploader-widget',
11-
version='0.1.0',
11+
version='0.1.1',
1212
description='Simple Image Uploader Widget for Django-Admin',
1313
long_description=readme,
1414
long_description_content_type='text/markdown',

src/ImageUploaderInline.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class ImageUploaderInline {
1414
tempFileInput: HTMLInputElement | null = null;
1515
next: number = 0;
1616
dragging: boolean = false;
17+
canPreview: boolean = true;
1718

1819
constructor(element: HTMLElement) {
1920
this.element = element;
@@ -171,6 +172,19 @@ class ImageUploaderInline {
171172
this.updateEmpty();
172173
return;
173174
}
175+
if (target.closest('.iuw-preview-icon')) {
176+
let image = item.querySelector('img');
177+
if (image) {
178+
image = image.cloneNode(true) as HTMLImageElement;
179+
const modal = this.createPreviewModal(image);
180+
setTimeout(() => {
181+
modal.classList.add('visible');
182+
modal.classList.remove('hide');
183+
document.body.style.overflow = 'hidden';
184+
}, 50);
185+
return;
186+
}
187+
}
174188
var fileInput = item.querySelector('input[type=file]') as HTMLInputElement;
175189
if (e.target === fileInput) {
176190
return;
@@ -194,6 +208,47 @@ class ImageUploaderInline {
194208
}
195209
}
196210

211+
closePreviewModal = () => {
212+
document.body.style.overflow = 'auto';
213+
const modal = document.getElementById('iuw-modal-element');
214+
if (modal) {
215+
modal.classList.remove('visible');
216+
modal.classList.add('hide');
217+
setTimeout(() => {
218+
modal.parentElement.removeChild(modal);
219+
}, 300);
220+
}
221+
}
222+
223+
onModalClick = (e: Event) => {
224+
if (e && e.target) {
225+
const element = e.target as HTMLElement;
226+
if (element.closest('img.iuw-modal-image-preview-item')) {
227+
return;
228+
}
229+
}
230+
this.closePreviewModal();
231+
}
232+
233+
createPreviewModal = (image: HTMLImageElement) : HTMLElement => {
234+
image.className = '';
235+
image.classList.add('iuw-modal-image-preview-item');
236+
237+
const modal = document.createElement('div');
238+
modal.id = 'iuw-modal-element';
239+
modal.classList.add('iuw-modal', 'hide');
240+
modal.addEventListener('click', this.onModalClick);
241+
242+
const preview = document.createElement('div');
243+
preview.classList.add('iuw-modal-image-preview');
244+
preview.innerHTML = '<span class="iuw-modal-close"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="100%" height="100%"><path xmlns="http://www.w3.org/2000/svg" d="m289.94 256 95-95A24 24 0 0 0 351 127l-95 95-95-95a24 24 0 0 0-34 34l95 95-95 95a24 24 0 1 0 34 34l95-95 95 95a24 24 0 0 0 34-34z"></path></svg></span>';
245+
preview.appendChild(image);
246+
modal.appendChild(preview);
247+
248+
document.body.appendChild(modal);
249+
return modal;
250+
}
251+
197252
appendItem(element: Element, url: string) {
198253
let delete_icon: Element | null = null;
199254
const related = element.closest('.inline-related');
@@ -202,6 +257,15 @@ class ImageUploaderInline {
202257
delete_icon.classList.add('iuw-delete-icon');
203258
delete_icon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="100%" height="100%"><path xmlns="http://www.w3.org/2000/svg" d="m289.94 256 95-95A24 24 0 0 0 351 127l-95 95-95-95a24 24 0 0 0-34 34l95 95-95 95a24 24 0 1 0 34 34l95-95 95 95a24 24 0 0 0 34-34z"></path></svg>';
204259
}
260+
if (this.canPreview) {
261+
const span = document.createElement('span');
262+
span.classList.add('iuw-preview-icon');
263+
if (related.getAttribute('data-candelete') !== 'true') {
264+
span.classList.add('iuw-only-preview');
265+
}
266+
span.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-zoom-in" viewBox="0 0 16 16" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="100%" height="100%"><path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" d="M6.5 12a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM13 6.5a6.5 6.5 0 1 1-13 0 6.5 6.5 0 0 1 13 0z"></path><path xmlns="http://www.w3.org/2000/svg" d="M10.344 11.742c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1 6.538 6.538 0 0 1-1.398 1.4z"></path><path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" d="M6.5 3a.5.5 0 0 1 .5.5V6h2.5a.5.5 0 0 1 0 1H7v2.5a.5.5 0 0 1-1 0V7H3.5a.5.5 0 0 1 0-1H6V3.5a.5.5 0 0 1 .5-.5z"></path></svg>';
267+
element.appendChild(span);
268+
}
205269
const img = document.createElement('img');
206270
img.src = url;
207271
element.appendChild(img);

src/ImageUploaderWidget.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
@import "./styles/mixins";
33
@import "./styles/widget";
44
@import "./styles/inline";
5+
@import "./styles/modal"

src/ImageUploaderWidget.ts

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class ImageUploaderWidget {
66
dropLabel: HTMLElement;
77
canDelete: boolean = false;
88
dragging: boolean = false;
9+
canPreview: boolean = true;
910

1011
id: string;
1112

@@ -77,10 +78,51 @@ class ImageUploaderWidget {
7778
this.element.classList.remove('drop-zone');
7879
}
7980

81+
closePreviewModal = () => {
82+
document.body.style.overflow = 'auto';
83+
const modal = document.getElementById('iuw-modal-element');
84+
if (modal) {
85+
modal.classList.remove('visible');
86+
modal.classList.add('hide');
87+
setTimeout(() => {
88+
modal.parentElement.removeChild(modal);
89+
}, 300);
90+
}
91+
}
92+
93+
onModalClick = (e: Event) => {
94+
if (e && e.target) {
95+
const element = e.target as HTMLElement;
96+
if (element.closest('img.iuw-modal-image-preview-item')) {
97+
return;
98+
}
99+
}
100+
this.closePreviewModal();
101+
}
102+
103+
createPreviewModal = (image: HTMLImageElement) : HTMLElement => {
104+
image.className = '';
105+
image.classList.add('iuw-modal-image-preview-item');
106+
107+
const modal = document.createElement('div');
108+
modal.id = 'iuw-modal-element';
109+
modal.classList.add('iuw-modal', 'hide');
110+
modal.addEventListener('click', this.onModalClick);
111+
112+
const preview = document.createElement('div');
113+
preview.classList.add('iuw-modal-image-preview');
114+
preview.innerHTML = '<span class="iuw-modal-close"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="100%" height="100%"><path xmlns="http://www.w3.org/2000/svg" d="m289.94 256 95-95A24 24 0 0 0 351 127l-95 95-95-95a24 24 0 0 0-34 34l95 95-95 95a24 24 0 1 0 34 34l95-95 95 95a24 24 0 0 0 34-34z"></path></svg></span>';
115+
preview.appendChild(image);
116+
modal.appendChild(preview);
117+
118+
document.body.appendChild(modal);
119+
return modal;
120+
}
121+
80122
onImagePreviewClick = (e: Event) => {
81123
if (e && e.target) {
82124
const targetElement = e.target as HTMLElement;
83-
if (e && e.target && targetElement.closest('.iuw-delete-icon')) {
125+
if (targetElement.closest('.iuw-delete-icon')) {
84126
const element = targetElement.closest('.iuw-image-preview');
85127
element.parentElement.removeChild(element);
86128
this.checkboxInput.checked = true;
@@ -90,6 +132,20 @@ class ImageUploaderWidget {
90132
this.renderWidget();
91133
return;
92134
}
135+
if (targetElement.closest('.iuw-preview-icon')) {
136+
const element = targetElement.closest('.iuw-image-preview');
137+
let image = element.querySelector('img');
138+
if (image) {
139+
image = image.cloneNode(true) as HTMLImageElement;
140+
const modal = this.createPreviewModal(image);
141+
setTimeout(() => {
142+
modal.classList.add('visible');
143+
modal.classList.remove('hide');
144+
document.body.style.overflow = 'hidden';
145+
}, 50);
146+
return;
147+
}
148+
}
93149
}
94150
this.fileInput.click();
95151
}
@@ -113,6 +169,17 @@ class ImageUploaderWidget {
113169
span.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="100%" height="100%"><path xmlns="http://www.w3.org/2000/svg" d="m289.94 256 95-95A24 24 0 0 0 351 127l-95 95-95-95a24 24 0 0 0-34 34l95 95-95 95a24 24 0 1 0 34 34l95-95 95 95a24 24 0 0 0 34-34z"></path></svg>';
114170
preview.appendChild(span);
115171
}
172+
173+
if (this.canPreview) {
174+
const span = document.createElement('span');
175+
span.classList.add('iuw-preview-icon');
176+
if (!this.canDelete) {
177+
span.classList.add('iuw-only-preview');
178+
}
179+
span.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-zoom-in" viewBox="0 0 16 16" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="100%" height="100%"><path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" d="M6.5 12a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM13 6.5a6.5 6.5 0 1 1-13 0 6.5 6.5 0 0 1 13 0z"></path><path xmlns="http://www.w3.org/2000/svg" d="M10.344 11.742c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1 6.538 6.538 0 0 1-1.398 1.4z"></path><path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" d="M6.5 3a.5.5 0 0 1 .5.5V6h2.5a.5.5 0 0 1 0 1H7v2.5a.5.5 0 0 1-1 0V7H3.5a.5.5 0 0 1 0-1H6V3.5a.5.5 0 0 1 .5-.5z"></path></svg>';
180+
preview.appendChild(span);
181+
}
182+
116183
return preview;
117184
}
118185

src/styles/_mixins.scss

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@
205205
object-position: center;
206206
}
207207

208-
.iuw-delete-icon {
208+
.iuw-delete-icon,
209+
.iuw-preview-icon {
209210
/* shape */
210211
width: 32px;
211212
height: 32px;
@@ -237,15 +238,18 @@
237238
transform: none;
238239
transition: transform 0.3s ease;
239240
}
240-
}
241241

242-
&:hover {
243-
.iuw-delete-icon {
242+
&:hover {
244243
opacity: 1;
245244

246245
svg {
247246
transform: scale(1.3);
248247
}
249248
}
250249
}
250+
251+
.iuw-preview-icon:not(.iuw-only-preview) {
252+
top: 32px;
253+
border-radius: 0;
254+
}
251255
}

src/styles/_modal.scss

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#iuw-modal-element {
2+
/* position */
3+
position: fixed;
4+
z-index: 150;
5+
left: 0;
6+
top: 0;
7+
8+
/* size */
9+
width: 100%;
10+
height: 100vh;
11+
12+
/* styles */
13+
background: var(--iuw-modal-overlay);
14+
15+
/* behaviour */
16+
user-select: none;
17+
cursor: pointer;
18+
19+
/* display */
20+
display: flex;
21+
align-items: center;
22+
justify-content: center;
23+
24+
/* animations */
25+
&.visible {
26+
transition: opacity 0.3s;
27+
}
28+
&.hide {
29+
transition: opacity 0.3s;
30+
opacity: 0;
31+
}
32+
33+
.iuw-modal-image-preview {
34+
width: 90%;
35+
height: 80%;
36+
37+
position: relative;
38+
39+
img {
40+
background: var(--iuw-modal-image-background);
41+
42+
width: 100%;
43+
height: 100%;
44+
45+
object-fit: contain;
46+
object-position: center;
47+
48+
cursor: default;
49+
}
50+
51+
.iuw-modal-close {
52+
position: absolute;
53+
right: 0;
54+
top: 0;
55+
transform: translate(50%, -50%);
56+
57+
width: 40px;
58+
height: 40px;
59+
border-radius: 50%;
60+
61+
display: flex;
62+
align-items: center;
63+
justify-content: center;
64+
65+
background: var(--iuw-modal-closebutton-background);
66+
filter: drop-shadow(0 0 5px var(--iuw-modal-closebutton-shadow));
67+
68+
svg {
69+
width: 18px;
70+
height: auto;
71+
fill: var(--iuw-modal-closebutton-color);
72+
}
73+
74+
@media (max-width: 768px) {
75+
transform: none;
76+
box-shadow: none;
77+
border-radius: unset;
78+
}
79+
}
80+
}
81+
}

src/styles/_variables.scss

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,29 @@ body {
99
--iuw-image-preview-shadow: rgba(0, 0, 0, 0.3);
1010
--iuw-add-image-background: #EFEFEF;
1111
--iuw-add-image-color: #AAA;
12+
13+
--iuw-modal-overlay: rgba(0, 0, 0, 0.6);
14+
--iuw-modal-image-background: #FFF;
15+
--iuw-modal-closebutton-background: #FFF;
16+
--iuw-modal-closebutton-color: #333;
17+
--iuw-modal-closebutton-shadow: rgba(0, 0, 0, 0.2);
1218

1319
@media (prefers-color-scheme: dark) {
20+
--iuw-background: #121212;
21+
--iuw-border-color: #CCC;
22+
--iuw-color: #333;
23+
--iuw-placeholder-text-color: #AAA;
24+
--iuw-placeholder-destak-color: #417690;
25+
--iuw-dropzone-background: rgba(0, 0, 0, 0.8);
26+
--iuw-image-preview-border: #333;
27+
--iuw-image-preview-shadow: rgba(0, 0, 0, 0.3);
28+
--iuw-add-image-background: #333;
29+
--iuw-add-image-color: #CCC;
1430

31+
--iuw-modal-overlay: rgba(0, 0, 0, 0.6);
32+
--iuw-modal-image-background: #FFF;
33+
--iuw-modal-closebutton-background: #FFF;
34+
--iuw-modal-closebutton-color: #333;
35+
--iuw-modal-closebutton-shadow: rgba(0, 0, 0, 0.2);
1536
}
1637
}

0 commit comments

Comments
 (0)