From 5c592c31732fd69e437600a792abbbcf504acd13 Mon Sep 17 00:00:00 2001 From: Jenny Bonsak Date: Mon, 3 Jul 2023 17:00:27 +0200 Subject: [PATCH] frontpage: add vanilla js version of communities carousel --- .../zenodo-rdm/modules/transition.overrides | 88 ++++++++++++ .../zenodo-rdm/modules/transition.variables | 11 ++ invenio.cfg | 1 + .../js/zenodo_rdm/src/communities-carousel.js | 134 ++++++++++++++++++ site/zenodo_rdm/views.py | 8 ++ site/zenodo_rdm/webpack.py | 1 + .../semantic-ui/zenodo_rdm/frontpage.html | 7 + .../zenodo_rdm/macros/carousel_item.html | 58 ++++++++ .../macros/communities_carousel_slides.html | 35 +++++ .../zenodo_rdm/macros/creators.html | 2 +- .../macros/featured_communities_carousel.html | 75 ++++++++++ .../macros/recent_uploads_list.html | 2 +- .../zenodo_rdm/macros/record_item.html | 2 +- 13 files changed, 421 insertions(+), 3 deletions(-) create mode 100644 assets/less/zenodo-rdm/modules/transition.overrides create mode 100644 assets/less/zenodo-rdm/modules/transition.variables create mode 100644 site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/communities-carousel.js create mode 100644 templates/semantic-ui/zenodo_rdm/macros/carousel_item.html create mode 100644 templates/semantic-ui/zenodo_rdm/macros/communities_carousel_slides.html create mode 100644 templates/semantic-ui/zenodo_rdm/macros/featured_communities_carousel.html diff --git a/assets/less/zenodo-rdm/modules/transition.overrides b/assets/less/zenodo-rdm/modules/transition.overrides new file mode 100644 index 00000000..ca9645d8 --- /dev/null +++ b/assets/less/zenodo-rdm/modules/transition.overrides @@ -0,0 +1,88 @@ +/*********************************************** + Zenodo RDM Transition Overrides +***********************************************/ + +#carousel-container { + position: relative; + min-height: @carouselPageHeightComputer; + width: 100%; + + @media screen and (max-width: @largestTabletScreen) { + min-height: @carouselPageHeightTablet; + } + + @media screen and (max-width: @largestMobileScreen) { + min-height: @carouselPageHeightMobile; + } + + + #carousel-slides.ui.items { + position: relative; + width: max-content; + min-height: @carouselPageHeightComputer; + + @media screen and (max-width: @largestTabletScreen) { + min-height: @carouselPageHeightTablet; + } + + @media screen and (max-width: @largestMobileScreen) { + min-height: @carouselPageHeightMobile; + } + + @media screen and (min-width: @computerBreakpoint) { + margin-top: 0 !important; + } + + > .item.slide { + position: relative; + width: @carouselItemWidthComputer; + margin: 0 !important; + padding: 0 1rem; + + @media screen and (max-width: @largestTabletScreen) { + width: @carouselItemWidthTablet; + } + @media screen and (max-width: @largestMobileScreen) { + text-align: center; + width: @carouselItemWidthMobile; + } + + &.visible { + animation: fadein .3s; + animation-fill-mode: forwards; + } + &.hidden { + animation: fadeout .3s; + animation-fill-mode: forwards; + } + + .ui.mini.button { + @media screen and (max-width: @largestMobileScreen) { + margin-bottom: .5rem; + } + } + } + + } +} + + + +@keyframes fadein { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} +@keyframes fadeout { + from { + opacity: 1; + } + + to { + opacity: 0; + } +} \ No newline at end of file diff --git a/assets/less/zenodo-rdm/modules/transition.variables b/assets/less/zenodo-rdm/modules/transition.variables new file mode 100644 index 00000000..d2dad721 --- /dev/null +++ b/assets/less/zenodo-rdm/modules/transition.variables @@ -0,0 +1,11 @@ +/*********************************************** + Invenio App RDM Transition Variables +***********************************************/ + +@carouselPageHeightComputer: 15rem; +@carouselPageHeightTablet: 18rem; +@carouselPageHeightMobile: 40rem; + +@carouselItemWidthComputer: 68rem; +@carouselItemWidthTablet: 40rem; +@carouselItemWidthMobile: 15rem; \ No newline at end of file diff --git a/invenio.cfg b/invenio.cfg index 78bf1f13..d77402e4 100644 --- a/invenio.cfg +++ b/invenio.cfg @@ -83,6 +83,7 @@ APP_DEFAULT_SECURE_HEADERS = { 'strict_transport_security_preload': False, } +APP_RDM_ROUTES["index"] = ("/", frontpage_view_function) # Celery # ====== diff --git a/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/communities-carousel.js b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/communities-carousel.js new file mode 100644 index 00000000..b9f6b648 --- /dev/null +++ b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/communities-carousel.js @@ -0,0 +1,134 @@ +document.addEventListener("DOMContentLoaded", () => initCommunityCarousel()); + +function initCommunityCarousel() { + const carousel = document.getElementById("communities-carousel"); + const slidesContainer = document.getElementById("carousel-slides"); + const carouselSlides = slidesContainer.querySelectorAll(".item.slide"); + const prevSlideBtn = document.getElementById("prev-slide-btn"); + const nextSlideBtn = document.getElementById("next-slide-btn"); + + const animationSpeed = parseInt(carousel.dataset.animationSpeed); + const intervalDelay = parseInt(carousel.dataset.intervalDelay); + + const numSlides = carouselSlides.length; + const slideWidth = carouselSlides[0].offsetWidth; + + const minIndex = 0; + const maxIndex = numSlides - 1; + var activeIndex = 0; + + var transitionCompleted = true; + + carouselSlides.forEach((slide, index) => { + // Remove all inactive slides from the DOM. + if (index !== activeIndex) slide.remove(); + }); + + /** + * Switches carousel slide + * @param {string} direction Direction to slide - left or right + */ + const slide = (direction) => { + const currentSlide = slidesContainer.querySelector(".item.slide"); + const slideLeft = direction === "left"; + + if (transitionCompleted) { + transitionCompleted = false; + const prevVisibleIndex = slideLeft ? 0 : 1; + const currentVisibleIndex = slideLeft ? 1 : 0; + const slideAmount = -slideWidth; + + if (direction === "left") { + activeIndex++; + if (activeIndex > maxIndex) { + activeIndex = minIndex; + } + + // Add active slide after currently visible slide + currentSlide.after(carouselSlides[activeIndex]); + + // Translate the slides-container such that the active slide comes to center. + slidesContainer.style.transitionDuration = `${animationSpeed}ms`; + slidesContainer.style.transform = `translateX(${slideAmount}px)`; + } else { + activeIndex--; + if (activeIndex < minIndex) { + activeIndex = maxIndex; + } + + // Add active slide before currently visible slide + currentSlide.before(carouselSlides[activeIndex]); + + // Translate the slides-container such that the previously active slide stays in center. + slidesContainer.style.transition = "transform .001ms"; + slidesContainer.style.transform = `translateX(${slideAmount}px)`; + } + + const currentSlides = slidesContainer.querySelectorAll(".item.slide"); + + // Control slide visibility by adding/removing classes + currentSlides[prevVisibleIndex].classList.remove("visible"); + currentSlides[prevVisibleIndex].classList.add("hidden"); + + currentSlides[currentVisibleIndex].classList.add("visible"); + currentSlides[currentVisibleIndex].classList.remove("hidden"); + + // Transition-end event handler, runs once when slides-container transition is finished + const transitionEndHandler = (direction) => { + if (direction === "left") { + // Tranlate the slides-container such that the active item will be + // in center once the previously active item is removed, then remove that item + slidesContainer.style.transitionDuration = "0s"; + slidesContainer.style.transform = "translateX(0px)"; + currentSlides[prevVisibleIndex].remove(); + } else { + const removePreviousSlide = () => { + currentSlides[prevVisibleIndex].remove(); + slidesContainer.removeEventListener("transitionend", removePreviousSlide); + }; + + // Remove previous slide once the second transition is completed + slidesContainer.addEventListener("transitionend", removePreviousSlide, { + once: true, + }); + + // Translate slides-container to center + slidesContainer.style.transitionDuration = `${animationSpeed}ms`; + slidesContainer.style.transform = "translateX(0px)"; + } + + transitionCompleted = true; + slidesContainer.removeEventListener("transitionend", transitionEndHandler); + }; + + slidesContainer.addEventListener( + "transitionend", + () => transitionEndHandler(direction), + { once: true } + ); + } + }; + + // Run carousel automatically on page load + const setCarouselTimer = () => setInterval(() => slide("left"), intervalDelay); + var carouselTimer = setCarouselTimer(); + + // Pause carousel on focus + carousel.addEventListener("focusin", () => { + clearInterval(carouselTimer); + }); + carousel.addEventListener("focusout", () => { + carouselTimer = setCarouselTimer(); + }); + + // Navigation arrow event handlers + prevSlideBtn.addEventListener("click", () => slide("right")); + prevSlideBtn.addEventListener("keydown", (event) => { + event.key === "Enter" && slide("right"); + }); + + nextSlideBtn.addEventListener("click", () => slide("left")); + nextSlideBtn.addEventListener("keydown", (event) => { + event.key === "Enter" && slide("left"); + }); +} diff --git a/site/zenodo_rdm/views.py b/site/zenodo_rdm/views.py index 74a0fdea..a24c61fd 100644 --- a/site/zenodo_rdm/views.py +++ b/site/zenodo_rdm/views.py @@ -7,6 +7,7 @@ """Additional views.""" from flask import Blueprint, current_app, g, render_template +from invenio_communities.proxies import current_communities from invenio_rdm_records.proxies import current_rdm_records from invenio_rdm_records.resources.serializers import UIJSONSerializer from invenio_records_resources.resources.records.utils import search_preference @@ -33,10 +34,17 @@ def frontpage_view_function(): record_ui = UIJSONSerializer().dump_obj(record) records_ui.append(record_ui) + featured_communities = current_communities.service.featured_search( + identity=g.identity, + params=None, + search_preference=search_preference(), + ) + return render_template( current_app.config["THEME_FRONTPAGE_TEMPLATE"], show_intro_section=current_app.config["THEME_SHOW_FRONTPAGE_INTRO_SECTION"], recent_uploads=records_ui, + featured_communities=featured_communities, ) diff --git a/site/zenodo_rdm/webpack.py b/site/zenodo_rdm/webpack.py index 41b05a45..111ced77 100644 --- a/site/zenodo_rdm/webpack.py +++ b/site/zenodo_rdm/webpack.py @@ -22,6 +22,7 @@ # Add your webpack entrypoints "zenodo-rdm-support": "./js/zenodo_rdm/src/support/support.js", "zenodo-rdm-citations": "./js/zenodo_rdm/src/citations/index.js", + "zenodo-rdm-communities-carousel": "./js/zenodo_rdm/src/communities-carousel.js", }, dependencies={ "@babel/runtime": "^7.9.0", diff --git a/templates/semantic-ui/zenodo_rdm/frontpage.html b/templates/semantic-ui/zenodo_rdm/frontpage.html index cb8536cd..d1856a83 100644 --- a/templates/semantic-ui/zenodo_rdm/frontpage.html +++ b/templates/semantic-ui/zenodo_rdm/frontpage.html @@ -23,12 +23,19 @@ -#} {% from "zenodo_rdm/macros/recent_uploads_list.html" import recent_uploads_list %} +{% from "zenodo_rdm/macros/featured_communities_carousel.html" import featured_communities_carousel %} {%- extends "invenio_app_rdm/frontpage.html" %} {%- block page_header %} {%- include "zenodo_rdm/header_frontpage.html" %} {%- endblock page_header %} +{%- block top_banner %} + {% if featured_communities %} + {{ featured_communities_carousel(communities=featured_communities) }} + {% endif %} +{%- endblock top_banner %} + {% block main_column_content %} {% if recent_uploads %} {{ recent_uploads_list(records=recent_uploads) }} diff --git a/templates/semantic-ui/zenodo_rdm/macros/carousel_item.html b/templates/semantic-ui/zenodo_rdm/macros/carousel_item.html new file mode 100644 index 00000000..e49c7289 --- /dev/null +++ b/templates/semantic-ui/zenodo_rdm/macros/carousel_item.html @@ -0,0 +1,58 @@ +{# +# This file is part of Zenodo. +# Copyright (C) 2023 CERN. +# +# Zenodo is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# Zenodo is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Zenodo; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307, USA. +# +# In applying this license, CERN does not +# waive the privileges and immunities granted to it by virtue of its status +# as an Intergovernmental Organization or submit itself to any jurisdiction. +-#} + +{% macro carousel_item(community=None, index=0) %} +
+ +
+ + +

+ {{ community.metadata.description | truncate(length=300, end='...') }} +

+
+
+{% endmacro %} diff --git a/templates/semantic-ui/zenodo_rdm/macros/communities_carousel_slides.html b/templates/semantic-ui/zenodo_rdm/macros/communities_carousel_slides.html new file mode 100644 index 00000000..26100bde --- /dev/null +++ b/templates/semantic-ui/zenodo_rdm/macros/communities_carousel_slides.html @@ -0,0 +1,35 @@ +{# +# This file is part of Zenodo. +# Copyright (C) 2023 CERN. +# +# Zenodo is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# Zenodo is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Zenodo; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307, USA. +# +# In applying this license, CERN does not +# waive the privileges and immunities granted to it by virtue of its status +# as an Intergovernmental Organization or submit itself to any jurisdiction. +-#} + +{% from "zenodo_rdm/macros/carousel_item.html" import carousel_item %} + +{% macro communities_carousel_slides(communities) %} + +{% endmacro %} diff --git a/templates/semantic-ui/zenodo_rdm/macros/creators.html b/templates/semantic-ui/zenodo_rdm/macros/creators.html index 095b0f4d..24f94a26 100644 --- a/templates/semantic-ui/zenodo_rdm/macros/creators.html +++ b/templates/semantic-ui/zenodo_rdm/macros/creators.html @@ -1,6 +1,6 @@ {# # This file is part of Zenodo. -# Copyright (C) 2022 CERN. +# Copyright (C) 2023 CERN. # # Zenodo is free software; you can redistribute it # and/or modify it under the terms of the GNU General Public License as diff --git a/templates/semantic-ui/zenodo_rdm/macros/featured_communities_carousel.html b/templates/semantic-ui/zenodo_rdm/macros/featured_communities_carousel.html new file mode 100644 index 00000000..11e7e91e --- /dev/null +++ b/templates/semantic-ui/zenodo_rdm/macros/featured_communities_carousel.html @@ -0,0 +1,75 @@ +{# +# This file is part of Zenodo. +# Copyright (C) 2023 CERN. +# +# Zenodo is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# Zenodo is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Zenodo; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307, USA. +# +# In applying this license, CERN does not +# waive the privileges and immunities granted to it by virtue of its status +# as an Intergovernmental Organization or submit itself to any jurisdiction. +-#} + +{% from "zenodo_rdm/macros/communities_carousel_slides.html" import communities_carousel_slides %} + +{% macro featured_communities_carousel( + communities, + title=_("Featured communities"), + interval_delay="10000", + animation_speed="300" + ) +%} + + {{ webpack['zenodo-rdm-communities-carousel.js'] }} +{% endmacro %} + diff --git a/templates/semantic-ui/zenodo_rdm/macros/recent_uploads_list.html b/templates/semantic-ui/zenodo_rdm/macros/recent_uploads_list.html index 9aff1cfc..3adef3f8 100644 --- a/templates/semantic-ui/zenodo_rdm/macros/recent_uploads_list.html +++ b/templates/semantic-ui/zenodo_rdm/macros/recent_uploads_list.html @@ -1,6 +1,6 @@ {# # This file is part of Zenodo. -# Copyright (C) 2022 CERN. +# Copyright (C) 2023 CERN. # # Zenodo is free software; you can redistribute it # and/or modify it under the terms of the GNU General Public License as diff --git a/templates/semantic-ui/zenodo_rdm/macros/record_item.html b/templates/semantic-ui/zenodo_rdm/macros/record_item.html index 2f100189..027203a6 100644 --- a/templates/semantic-ui/zenodo_rdm/macros/record_item.html +++ b/templates/semantic-ui/zenodo_rdm/macros/record_item.html @@ -1,6 +1,6 @@ {# # This file is part of Zenodo. -# Copyright (C) 2022 CERN. +# Copyright (C) 2023 CERN. # # Zenodo is free software; you can redistribute it # and/or modify it under the terms of the GNU General Public License as