diff --git a/CHANGELOG.md b/CHANGELOG.md index cc9920f..a8f0f93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. +## 2.13.2 - 2026-09-11 + +### Changed + +- Refined the Media Library upload flow with a compact Add media modal, clearer toolbar controls, full-height scrolling, and more obvious selection feedback for media that is already in use. + ## 2.13.1 - 2026-09-11 ### Added diff --git a/build/package.player.json b/build/package.player.json index 929f964..cc0a2db 100644 --- a/build/package.player.json +++ b/build/package.player.json @@ -1,6 +1,6 @@ { "name": "pulse-signage-player", - "version": "2.13.1", + "version": "2.13.2", "private": false, "description": "Pulse Signage player application bundle", "engines": { diff --git a/build/package.web.json b/build/package.web.json index 33655b2..8ba7a77 100644 --- a/build/package.web.json +++ b/build/package.web.json @@ -1,6 +1,6 @@ { "name": "pulse-signage-web", - "version": "2.13.1", + "version": "2.13.2", "private": false, "description": "Pulse Signage web and bridge application bundle", "engines": { diff --git a/package-lock.json b/package-lock.json index 73757d1..d35e4be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pulse-signage", - "version": "2.13.1", + "version": "2.13.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pulse-signage", - "version": "2.13.1", + "version": "2.13.2", "dependencies": { "@sparticuz/chromium": "^149.0.0", "animate.css": "^4.1.1", diff --git a/package.json b/package.json index a16d62e..a7ecdfb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pulse-signage", - "version": "2.13.1", + "version": "2.13.2", "private": false, "description": "Pulse Signage application with MySQL and media storage", "engines": { diff --git a/src/web/public/css/theme-custom.css b/src/web/public/css/theme-custom.css index f93d6b6..5063b80 100644 --- a/src/web/public/css/theme-custom.css +++ b/src/web/public/css/theme-custom.css @@ -30,12 +30,16 @@ .media-gallery-toolbar { display: flex; - align-items: end; + align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1rem; } +.media-gallery-count { + flex: 0 0 auto; +} + .media-gallery-search { width: min(100%, 52rem); } @@ -113,6 +117,28 @@ font-weight: 700; } +.media-gallery-card-selection-disabled { + opacity: .72; +} + +.media-gallery-card-selection-disabled::before { + position: absolute; + top: .65rem; + left: .65rem; + z-index: 2; + padding: .25rem .5rem; + border-radius: .25rem; + background: var(--bs-secondary); + color: var(--bs-white); + content: 'In use'; + font-size: .75rem; + font-weight: 700; +} + +.media-gallery-selection-mode .media-gallery-card.media-gallery-card-selection-disabled { + cursor: not-allowed; +} + .media-gallery-selection-mode .media-gallery-card { cursor: pointer; } @@ -4069,7 +4095,7 @@ table.table thead th.sort-desc .table-sort-indicator { .media-gallery-scroll { flex: 1 1 auto; min-height: 0; - max-height: max(12rem, calc(100dvh - 29rem)); + max-height: none; overflow-x: hidden; overflow-y: auto; padding-right: .5rem; diff --git a/src/web/public/js/settings/media-library.js b/src/web/public/js/settings/media-library.js index ea6f871..92a43ba 100644 --- a/src/web/public/js/settings/media-library.js +++ b/src/web/public/js/settings/media-library.js @@ -12,6 +12,7 @@ const loadMoreWrap = gallery && gallery.querySelector('[data-media-gallery-load- const loadMoreButton = gallery && gallery.querySelector('[data-media-gallery-load-more]'); const selectModeButton = gallery && gallery.querySelector('[data-media-gallery-select-mode]'); const deleteSelectedButton = gallery && gallery.querySelector('[data-media-gallery-delete-selected]'); +const addMediaButton = gallery && gallery.querySelector('[data-media-library-add]'); let galleryItems = gallery ? Array.from(gallery.querySelectorAll('[data-media-gallery-column]')) : []; let visibleRowLimit = 4; let galleryOffset = gallery ? Number(gallery.dataset.mediaGalleryOffset || galleryItems.length) : 0; @@ -24,6 +25,7 @@ const uploadForm = document.querySelector('form[action="/media-library"]'); const uploadInput = document.querySelector('[data-media-library-upload]'); const uploadZone = document.querySelector('[data-media-library-upload-zone]'); const uploadName = document.querySelector('[data-media-library-upload-name]'); +const uploadModal = document.getElementById('media-library-upload-modal'); const cropModal = document.getElementById('media-library-crop-modal'); const cropImage = cropModal && cropModal.querySelector('#media-library-crop-image'); const cropApply = cropModal && cropModal.querySelector('[data-media-library-crop-apply]'); @@ -48,22 +50,28 @@ function showUploadMessage(message) { window.alert(message); } -function showUploadModal() { +function showModal(target) { if (window.pulseModal && typeof window.pulseModal.show === 'function') { - window.pulseModal.show(cropModal); + window.pulseModal.show(target); } else if (window.bootstrap && window.bootstrap.Modal) { - window.bootstrap.Modal.getOrCreateInstance(cropModal).show(); + window.bootstrap.Modal.getOrCreateInstance(target).show(); } } -function hideUploadModal() { +function hideModal(target) { if (window.pulseModal && typeof window.pulseModal.hide === 'function') { - window.pulseModal.hide(cropModal); + window.pulseModal.hide(target); } else if (window.bootstrap && window.bootstrap.Modal) { - window.bootstrap.Modal.getOrCreateInstance(cropModal).hide(); + window.bootstrap.Modal.getOrCreateInstance(target).hide(); } } +if (addMediaButton && uploadModal) { + addMediaButton.addEventListener('click', function () { + showModal(uploadModal); + }); +} + function clearCropper() { if (cropper) { cropper.destroy(); @@ -245,7 +253,7 @@ function setUploadFile(file, shouldCrop) { cropObjectUrl = URL.createObjectURL(file); cropImage.src = cropObjectUrl; if (cropFrame) cropFrame.classList.add('is-loading'); - showUploadModal(); + showModal(cropModal); } } @@ -296,7 +304,7 @@ if (uploadZone) { if (cropApply) { cropApply.addEventListener('click', function () { if (!cropper || !cropSourceFile || !uploadInput) { - hideUploadModal(); + hideModal(cropModal); return; } const outputType = cropSourceFile.type === 'image/jpeg' || cropSourceFile.type === 'image/webp' ? cropSourceFile.type : 'image/png'; @@ -309,7 +317,7 @@ if (cropApply) { const croppedFile = new File([blob], cropSourceFile.name.replace(/\.[^.]+$/, '') + extension, { type: outputType, lastModified: Date.now() }); clearCropper(); setUploadFile(croppedFile, false); - hideUploadModal(); + hideModal(cropModal); submitUpload(); }, outputType, 0.92); }); @@ -320,7 +328,7 @@ if (cropCancel) { clearCropper(); if (uploadInput) uploadInput.value = ''; if (uploadName) uploadName.textContent = 'No file selected'; - hideUploadModal(); + hideModal(cropModal); }); } @@ -406,7 +414,7 @@ function updateSearchResults() { function updateBulkDeleteActions() { if (selectModeButton) { selectModeButton.setAttribute('aria-pressed', selectionMode ? 'true' : 'false'); - selectModeButton.textContent = selectionMode ? 'Done' : 'Select'; + selectModeButton.textContent = selectionMode ? 'Cancel' : 'Select'; } if (deleteSelectedButton) { deleteSelectedButton.hidden = !selectionMode; @@ -415,7 +423,10 @@ function updateBulkDeleteActions() { galleryItems.forEach(function (item) { const id = String(item.dataset.mediaId || ''); const card = item.querySelector('.media-gallery-card'); - if (card) card.classList.toggle('media-gallery-card-selected', selectionMode && selectedAssetIds.has(id)); + if (card) { + card.classList.toggle('media-gallery-card-selected', selectionMode && selectedAssetIds.has(id)); + card.classList.toggle('media-gallery-card-selection-disabled', selectionMode && item.dataset.mediaUsed === 'true'); + } }); } @@ -487,7 +498,10 @@ if (gallery) { if (!column || !gallery.contains(column)) return; event.preventDefault(); event.stopPropagation(); - if (column.dataset.mediaUsed === 'true') return; + if (column.dataset.mediaUsed === 'true') { + showUploadMessage('This media is in use and cannot be selected.'); + return; + } const id = String(column.dataset.mediaId || ''); if (!id) return; if (selectedAssetIds.has(id)) selectedAssetIds.delete(id); @@ -519,7 +533,7 @@ if (loadMoreButton) { } updateSearchResults(); -function showModal() { +function showPreviewModal() { if (window.pulseModal && typeof window.pulseModal.show === 'function') { window.pulseModal.show(modal); } else if (window.bootstrap && window.bootstrap.Modal) { @@ -550,7 +564,7 @@ if (modal && body) { media.playsInline = true; } body.appendChild(media); - showModal(); + showPreviewModal(); }); modal.addEventListener('hidden.bs.modal', function () { diff --git a/src/web/views/settings/media-library/list.hbs b/src/web/views/settings/media-library/list.hbs index da339c1..f41bd16 100644 --- a/src/web/views/settings/media-library/list.hbs +++ b/src/web/views/settings/media-library/list.hbs @@ -1,43 +1,20 @@ -{{#if (hasPermission currentUser 'media-library.create')}} -
-

Upload media

-
-
-
- -
-
-
-
-{{/if}} - @@ -126,6 +105,30 @@ {{/modal-shell}} +{{#if (hasPermission currentUser 'media-library.create')}} +{{#> modal-shell modalId="media-library-upload-modal" modalLabelId="media-library-upload-modal-label" modalDialogClass="modal-dialog-centered modal-lg"}} + +
+ +
+{{/modal-shell}} +{{/if}} + {{#> modal-shell modalId="media-library-crop-modal" modalLabelId="media-library-crop-modal-label" modalDialogClass="modal-dialog-centered modal-xl" modalBackdropStatic=true modalKeyboardDisabled=true}}