diff --git a/cds/modules/records/static/templates/cds_records/video/detail.html b/cds/modules/records/static/templates/cds_records/video/detail.html index c5ebe4ed6..33c5bc61c 100644 --- a/cds/modules/records/static/templates/cds_records/video/detail.html +++ b/cds/modules/records/static/templates/cds_records/video/detail.html @@ -344,7 +344,7 @@

Chapters

@@ -434,3 +434,42 @@

+ + +
+ +
+ + + + + +
+ + + + + +
+ + +
+
{{ chapterModal.currentChapter.title }}
+
{{ chapterModal.currentChapter.timestamp }}
+
+
+
diff --git a/cds/modules/theme/assets/bootstrap3/js/cds_records/cdsRecord.js b/cds/modules/theme/assets/bootstrap3/js/cds_records/cdsRecord.js index 1c865c8b2..6848b300d 100644 --- a/cds/modules/theme/assets/bootstrap3/js/cds_records/cdsRecord.js +++ b/cds/modules/theme/assets/bootstrap3/js/cds_records/cdsRecord.js @@ -64,12 +64,73 @@ function cdsRecordController($scope, $sce, $http, $timeout, $filter) { $scope.shortDescription = ""; $scope.fullDescription = ""; $scope.chapterFrames = {}; + $scope.chapterModal = { + open: false, + index: 0, + currentChapter: null, + currentFrameUrl: null, + }; const REQUEST_HEADERS = { "Content-Type": "application/json", "X-CSRFToken": getCookie("csrftoken"), }; + // Open modal at specific chapter index + $scope.openChapterModal = function (index) { + $scope.chapterModal.index = index; + $scope.chapterModal.open = true; + $scope.updateChapterModalContent(); + }; + + // Close modal + $scope.closeChapterModal = function () { + $scope.chapterModal.open = false; + }; + + // Build IIIF frame URL + $scope.getFrameUrl = function (frame) { + console.log("Getting frame URL for", frame); + if (!frame) return null; + return `/api/iiif/v2/${frame.bucket_id}:${frame.version_id}:${frame.key}/full/full/0/default.jpg`; + }; + + // Update modal contents after navigation + $scope.updateChapterModalContent = function () { + const chapter = $scope.chapters[$scope.chapterModal.index]; + const frame = $scope.chapterFrames[chapter.seconds]; + $scope.chapterModal.currentChapter = chapter; + $scope.chapterModal.currentFrameUrl = $scope.getFrameUrl(frame); + }; + + // Navigation + $scope.nextChapter = function ($event) { + $event.stopPropagation(); // prevent closing modal + if ($scope.chapterModal.index < $scope.chapters.length - 1) { + $scope.chapterModal.index++; + $scope.updateChapterModalContent(); + } + }; + + $scope.prevChapter = function ($event) { + $event.stopPropagation(); + if ($scope.chapterModal.index > 0) { + $scope.chapterModal.index--; + $scope.updateChapterModalContent(); + } + }; + + // Keyboard navigation for modal + document.addEventListener("keydown", function (e) { + if (!$scope.chapterModal.open) return; + + if (e.key === "ArrowRight") { + $scope.$apply(() => $scope.nextChapter(e)); + } else if (e.key === "ArrowLeft") { + $scope.$apply(() => $scope.prevChapter(e)); + } + }); + $scope.scrollToElement = function (id) { setTimeout(function () { const el = document.getElementById(id); diff --git a/cds/modules/theme/assets/bootstrap3/scss/cds/cds.scss b/cds/modules/theme/assets/bootstrap3/scss/cds/cds.scss index 28d5ec5a2..f4fecce65 100644 --- a/cds/modules/theme/assets/bootstrap3/scss/cds/cds.scss +++ b/cds/modules/theme/assets/bootstrap3/scss/cds/cds.scss @@ -1464,4 +1464,84 @@ div[cds-search-results] { .sharelink-start:has(input[type="checkbox"]:checked) .start-time { border-bottom: 1px solid #888; color: #333333; -} \ No newline at end of file +} + +/* Dark clickable overlay */ +.chapter-modal-overlay { + position: fixed; + inset: 0; + background: rgba(0,0,0,0.7); + z-index: 9998; +} + +/* Centering container (also click-to-close) */ +.chapter-modal-container { + position: fixed; + inset: 0; + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; +} + +.chapter-modal { + position: relative; + display: flex; + flex-direction: column; + align-items: center; +} + +.chapter-modal-image { + max-width: 80vw; + max-height: 80vh; + object-fit: contain; +} + +.chapter-modal-close { + position: absolute; + top: -40px; + right: -40px; + background: none; + border: none; + font-size: 24px; + color: white; + cursor: pointer; +} + +.chapter-modal-arrow { + position: absolute; + top: 50%; + font-size: 48px; + color: white; + cursor: pointer; + transition: opacity 0.2s; +} + +.chapter-modal-arrow:hover { + opacity: 0.6; +} + +.chapter-modal-arrow.left { + left: -40px; +} + +.chapter-modal-arrow.right { + right: -40px; +} + +/* Caption */ +.chapter-modal-caption { + margin-top: 12px; + text-align: center; + color: white; +} + +.chapter-modal-caption .title { + font-size: 18px; + font-weight: 600; +} + +.chapter-modal-caption .time { + opacity: 0.8; + font-size: 14px; +}