From 981725056c8a5a5a3b1d4c0e2051090b64390075 Mon Sep 17 00:00:00 2001 From: Eduardo Oliveira Date: Thu, 30 Dec 2021 20:38:34 -0300 Subject: [PATCH 1/9] begin implementation of the modal showing feature --- src/ImageUploaderWidget.ts | 50 +++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/ImageUploaderWidget.ts b/src/ImageUploaderWidget.ts index 17385750..3ebe5807 100644 --- a/src/ImageUploaderWidget.ts +++ b/src/ImageUploaderWidget.ts @@ -6,6 +6,7 @@ class ImageUploaderWidget { dropLabel: HTMLElement; canDelete: boolean = false; dragging: boolean = false; + canPreview: boolean = true; id: string; @@ -77,10 +78,36 @@ class ImageUploaderWidget { this.element.classList.remove('drop-zone'); } + onModalClick = (e: Event) => { + console.log(e); + } + + getOrCreatePreviewModal = (image: HTMLImageElement) : HTMLElement => { + let modal = document.getElementById('iuw-modal-element'); + if (modal) { + const preview = modal.querySelector('.iuw-modal-image-preview'); + preview.innerHTML = ''; + preview.appendChild(image); + return modal; + } + modal = document.createElement('div'); + modal.id = 'iuw-modal-element'; + modal.classList.add('iuw-modal'); + modal.style.display = 'none'; + + const preview = document.createElement('div'); + preview.classList.add('iuw-modal-image-preview'); + preview.appendChild(image); + modal.appendChild(preview); + + document.body.appendChild(modal); + return modal; + } + onImagePreviewClick = (e: Event) => { if (e && e.target) { const targetElement = e.target as HTMLElement; - if (e && e.target && targetElement.closest('.iuw-delete-icon')) { + if (targetElement.closest('.iuw-delete-icon')) { const element = targetElement.closest('.iuw-image-preview'); element.parentElement.removeChild(element); this.checkboxInput.checked = true; @@ -90,6 +117,16 @@ class ImageUploaderWidget { this.renderWidget(); return; } + if (targetElement.closest('.iuw-preview-icon')) { + const element = targetElement.closest('.iuw-image-preview'); + let image = element.querySelector('img'); + if (image) { + image = image.cloneNode(true) as HTMLImageElement; + const modal = this.getOrCreatePreviewModal(image); + modal.style.display = 'block'; + return; + } + } } this.fileInput.click(); } @@ -113,6 +150,17 @@ class ImageUploaderWidget { span.innerHTML = ''; preview.appendChild(span); } + + if (this.canPreview) { + const span = document.createElement('span'); + span.classList.add('iuw-preview-icon'); + if (!this.canDelete) { + span.classList.add('iuw-only-preview'); + } + span.innerHTML = ''; + preview.appendChild(span); + } + return preview; } From 53f15dccec56bbb3a7de6a1a9567706511e44459 Mon Sep 17 00:00:00 2001 From: Eduardo Oliveira Date: Thu, 30 Dec 2021 20:38:44 -0300 Subject: [PATCH 2/9] style the image preview button --- src/styles/_mixins.scss | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss index 5c3f2ac3..8aab7ab2 100644 --- a/src/styles/_mixins.scss +++ b/src/styles/_mixins.scss @@ -205,7 +205,8 @@ object-position: center; } - .iuw-delete-icon { + .iuw-delete-icon, + .iuw-preview-icon { /* shape */ width: 32px; height: 32px; @@ -237,10 +238,8 @@ transform: none; transition: transform 0.3s ease; } - } - &:hover { - .iuw-delete-icon { + &:hover { opacity: 1; svg { @@ -248,4 +247,9 @@ } } } + + .iuw-preview-icon:not(.iuw-only-preview) { + top: 32px; + border-radius: 0; + } } From dd317fac89b22eb21469710bebd51b21c6fb8dbe Mon Sep 17 00:00:00 2001 From: Eduardo Oliveira Date: Thu, 30 Dec 2021 21:04:13 -0300 Subject: [PATCH 3/9] complete adding preview modal for widget --- src/ImageUploaderWidget.scss | 1 + src/ImageUploaderWidget.ts | 45 ++++++++++++++------- src/styles/_modal.scss | 78 ++++++++++++++++++++++++++++++++++++ src/styles/_variables.scss | 6 +++ 4 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 src/styles/_modal.scss diff --git a/src/ImageUploaderWidget.scss b/src/ImageUploaderWidget.scss index 478df29b..33b34c1d 100644 --- a/src/ImageUploaderWidget.scss +++ b/src/ImageUploaderWidget.scss @@ -2,3 +2,4 @@ @import "./styles/mixins"; @import "./styles/widget"; @import "./styles/inline"; +@import "./styles/modal" diff --git a/src/ImageUploaderWidget.ts b/src/ImageUploaderWidget.ts index 3ebe5807..e9298f55 100644 --- a/src/ImageUploaderWidget.ts +++ b/src/ImageUploaderWidget.ts @@ -78,25 +78,39 @@ class ImageUploaderWidget { this.element.classList.remove('drop-zone'); } - onModalClick = (e: Event) => { - console.log(e); + closePreviewModal = () => { + const modal = document.getElementById('iuw-modal-element'); + if (modal) { + modal.classList.remove('visible'); + modal.classList.add('hide'); + setTimeout(() => { + modal.parentElement.removeChild(modal); + }, 300); + } } - getOrCreatePreviewModal = (image: HTMLImageElement) : HTMLElement => { - let modal = document.getElementById('iuw-modal-element'); - if (modal) { - const preview = modal.querySelector('.iuw-modal-image-preview'); - preview.innerHTML = ''; - preview.appendChild(image); - return modal; + onModalClick = (e: Event) => { + if (e && e.target) { + const element = e.target as HTMLElement; + if (element.closest('img.iuw-modal-image-preview-item')) { + return; + } } - modal = document.createElement('div'); + this.closePreviewModal(); + } + + createPreviewModal = (image: HTMLImageElement) : HTMLElement => { + image.className = ''; + image.classList.add('iuw-modal-image-preview-item'); + + const modal = document.createElement('div'); modal.id = 'iuw-modal-element'; - modal.classList.add('iuw-modal'); - modal.style.display = 'none'; + modal.classList.add('iuw-modal', 'hide'); + modal.addEventListener('click', this.onModalClick); const preview = document.createElement('div'); preview.classList.add('iuw-modal-image-preview'); + preview.innerHTML = ''; preview.appendChild(image); modal.appendChild(preview); @@ -122,8 +136,11 @@ class ImageUploaderWidget { let image = element.querySelector('img'); if (image) { image = image.cloneNode(true) as HTMLImageElement; - const modal = this.getOrCreatePreviewModal(image); - modal.style.display = 'block'; + const modal = this.createPreviewModal(image); + setTimeout(() => { + modal.classList.add('visible'); + modal.classList.remove('hide'); + }, 50); return; } } diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss new file mode 100644 index 00000000..6f548536 --- /dev/null +++ b/src/styles/_modal.scss @@ -0,0 +1,78 @@ +#iuw-modal-element { + /* position */ + position: fixed; + z-index: 150; + left: 0; + top: 0; + + /* size */ + width: 100%; + height: 100vh; + + /* styles */ + background: var(--iuw-modal-overlay); + + /* behaviour */ + user-select: none; + + /* display */ + display: flex; + align-items: center; + justify-content: center; + + /* animations */ + &.visible { + transition: opacity 0.3s; + } + &.hide { + transition: opacity 0.3s; + opacity: 0; + } + + .iuw-modal-image-preview { + width: 90%; + height: 80%; + + position: relative; + + img { + background: var(--iuw-modal-image-background); + + width: 100%; + height: 100%; + + object-fit: contain; + object-position: center; + } + + .iuw-modal-close { + position: absolute; + right: 0; + top: 0; + transform: translate(50%, -50%); + + width: 40px; + height: 40px; + border-radius: 50%; + + display: flex; + align-items: center; + justify-content: center; + + background: var(--iuw-modal-closebutton-background); + filter: drop-shadow(0 0 5px var(--iuw-modal-closebutton-shadow)); + + svg { + width: 18px; + height: auto; + fill: var(--iuw-modal-closebutton-color); + } + + @media (max-width: 768px) { + transform: none; + box-shadow: none; + border-radius: unset; + } + } + } +} \ No newline at end of file diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index a59f3dcc..1ea66857 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -9,6 +9,12 @@ body { --iuw-image-preview-shadow: rgba(0, 0, 0, 0.3); --iuw-add-image-background: #EFEFEF; --iuw-add-image-color: #AAA; + + --iuw-modal-overlay: rgba(0, 0, 0, 0.6); + --iuw-modal-image-background: #FFF; + --iuw-modal-closebutton-background: #FFF; + --iuw-modal-closebutton-color: #333; + --iuw-modal-closebutton-shadow: rgba(0, 0, 0, 0.2); @media (prefers-color-scheme: dark) { From 21874157d2b0630f4c423a8c39c9438de3028713 Mon Sep 17 00:00:00 2001 From: Eduardo Oliveira Date: Thu, 30 Dec 2021 21:08:32 -0300 Subject: [PATCH 4/9] add cursor behaviour to modal --- src/styles/_modal.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss index 6f548536..6525be7c 100644 --- a/src/styles/_modal.scss +++ b/src/styles/_modal.scss @@ -14,6 +14,7 @@ /* behaviour */ user-select: none; + cursor: pointer; /* display */ display: flex; @@ -43,6 +44,8 @@ object-fit: contain; object-position: center; + + cursor: default; } .iuw-modal-close { From 171b023909347919fbc684f193c51d5026fc6bc6 Mon Sep 17 00:00:00 2001 From: Eduardo Oliveira Date: Thu, 30 Dec 2021 21:08:56 -0300 Subject: [PATCH 5/9] add preview modal to inline --- src/ImageUploaderInline.ts | 62 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/ImageUploaderInline.ts b/src/ImageUploaderInline.ts index ebb0d6d9..f18fa9c1 100644 --- a/src/ImageUploaderInline.ts +++ b/src/ImageUploaderInline.ts @@ -14,6 +14,7 @@ class ImageUploaderInline { tempFileInput: HTMLInputElement | null = null; next: number = 0; dragging: boolean = false; + canPreview: boolean = true; constructor(element: HTMLElement) { this.element = element; @@ -171,6 +172,18 @@ class ImageUploaderInline { this.updateEmpty(); return; } + if (target.closest('.iuw-preview-icon')) { + let image = item.querySelector('img'); + if (image) { + image = image.cloneNode(true) as HTMLImageElement; + const modal = this.createPreviewModal(image); + setTimeout(() => { + modal.classList.add('visible'); + modal.classList.remove('hide'); + }, 50); + return; + } + } var fileInput = item.querySelector('input[type=file]') as HTMLInputElement; if (e.target === fileInput) { return; @@ -194,6 +207,46 @@ class ImageUploaderInline { } } + closePreviewModal = () => { + const modal = document.getElementById('iuw-modal-element'); + if (modal) { + modal.classList.remove('visible'); + modal.classList.add('hide'); + setTimeout(() => { + modal.parentElement.removeChild(modal); + }, 300); + } + } + + onModalClick = (e: Event) => { + if (e && e.target) { + const element = e.target as HTMLElement; + if (element.closest('img.iuw-modal-image-preview-item')) { + return; + } + } + this.closePreviewModal(); + } + + createPreviewModal = (image: HTMLImageElement) : HTMLElement => { + image.className = ''; + image.classList.add('iuw-modal-image-preview-item'); + + const modal = document.createElement('div'); + modal.id = 'iuw-modal-element'; + modal.classList.add('iuw-modal', 'hide'); + modal.addEventListener('click', this.onModalClick); + + const preview = document.createElement('div'); + preview.classList.add('iuw-modal-image-preview'); + preview.innerHTML = ''; + preview.appendChild(image); + modal.appendChild(preview); + + document.body.appendChild(modal); + return modal; + } + appendItem(element: Element, url: string) { let delete_icon: Element | null = null; const related = element.closest('.inline-related'); @@ -202,6 +255,15 @@ class ImageUploaderInline { delete_icon.classList.add('iuw-delete-icon'); delete_icon.innerHTML = ''; } + if (this.canPreview) { + const span = document.createElement('span'); + span.classList.add('iuw-preview-icon'); + if (related.getAttribute('data-candelete') !== 'true') { + span.classList.add('iuw-only-preview'); + } + span.innerHTML = ''; + element.appendChild(span); + } const img = document.createElement('img'); img.src = url; element.appendChild(img); From 7b4cd8a19972ea316b616d161eaa539611bcd99e Mon Sep 17 00:00:00 2001 From: Eduardo Oliveira Date: Thu, 30 Dec 2021 21:10:43 -0300 Subject: [PATCH 6/9] lock the overflow when modal is opened --- src/ImageUploaderInline.ts | 2 ++ src/ImageUploaderWidget.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/ImageUploaderInline.ts b/src/ImageUploaderInline.ts index f18fa9c1..257524ea 100644 --- a/src/ImageUploaderInline.ts +++ b/src/ImageUploaderInline.ts @@ -180,6 +180,7 @@ class ImageUploaderInline { setTimeout(() => { modal.classList.add('visible'); modal.classList.remove('hide'); + document.body.style.overflow = 'hidden'; }, 50); return; } @@ -208,6 +209,7 @@ class ImageUploaderInline { } closePreviewModal = () => { + document.body.style.overflow = 'auto'; const modal = document.getElementById('iuw-modal-element'); if (modal) { modal.classList.remove('visible'); diff --git a/src/ImageUploaderWidget.ts b/src/ImageUploaderWidget.ts index e9298f55..501ad951 100644 --- a/src/ImageUploaderWidget.ts +++ b/src/ImageUploaderWidget.ts @@ -79,6 +79,7 @@ class ImageUploaderWidget { } closePreviewModal = () => { + document.body.style.overflow = 'auto'; const modal = document.getElementById('iuw-modal-element'); if (modal) { modal.classList.remove('visible'); @@ -140,6 +141,7 @@ class ImageUploaderWidget { setTimeout(() => { modal.classList.add('visible'); modal.classList.remove('hide'); + document.body.style.overflow = 'hidden'; }, 50); return; } From 64afcaa3b15a80c833d3b38d6bd8af5359f1f1fe Mon Sep 17 00:00:00 2001 From: Eduardo Oliveira Date: Thu, 30 Dec 2021 21:19:44 -0300 Subject: [PATCH 7/9] add prefer dark color scheme colors --- src/styles/_variables.scss | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 1ea66857..f92ba37a 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -17,6 +17,21 @@ body { --iuw-modal-closebutton-shadow: rgba(0, 0, 0, 0.2); @media (prefers-color-scheme: dark) { + --iuw-background: #121212; + --iuw-border-color: #CCC; + --iuw-color: #333; + --iuw-placeholder-text-color: #AAA; + --iuw-placeholder-destak-color: #417690; + --iuw-dropzone-background: rgba(0, 0, 0, 0.8); + --iuw-image-preview-border: #333; + --iuw-image-preview-shadow: rgba(0, 0, 0, 0.3); + --iuw-add-image-background: #333; + --iuw-add-image-color: #CCC; + --iuw-modal-overlay: rgba(0, 0, 0, 0.6); + --iuw-modal-image-background: #FFF; + --iuw-modal-closebutton-background: #FFF; + --iuw-modal-closebutton-color: #333; + --iuw-modal-closebutton-shadow: rgba(0, 0, 0, 0.2); } } From ce928209c4713fa43545aba97c29aa4377113e2e Mon Sep 17 00:00:00 2001 From: Eduardo Oliveira Date: Thu, 30 Dec 2021 21:23:11 -0300 Subject: [PATCH 8/9] bump setup version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 89cf816e..7ea829e7 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name='django-image-uploader-widget', - version='0.1.0', + version='0.1.1', description='Simple Image Uploader Widget for Django-Admin', long_description=readme, long_description_content_type='text/markdown', From aa707593df07786c51698effa468e2ce192cf498 Mon Sep 17 00:00:00 2001 From: Eduardo Oliveira Date: Thu, 30 Dec 2021 21:23:23 -0300 Subject: [PATCH 9/9] bump package.json version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3c49d8b..08ecd0f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "django-image-uploader-widget", - "version": "0.1.0", + "version": "0.1.1", "main": "index.js", "repository": "https://github.com/EduardoJM/django-image-uploader-widget.git", "author": "Eduardo Oliveira ",