diff --git a/_layouts/landing.html b/_layouts/landing.html new file mode 100644 index 0000000000000000000000000000000000000000..6760d9eaac7e106af62928ec86bb444d18df2d93 --- /dev/null +++ b/_layouts/landing.html @@ -0,0 +1,124 @@ +<!DOCTYPE html> +<html lang="{{ page.lang | default: site.lang | default: "en" }}"> + + {%- include head.html -%} + + <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" /> + <link href="{{ "assets/css/landing.css" | relative_url }}"" rel="stylesheet" /> +<body> + <div class="main"> + <div class="content-wrapper"> + + <header class="site-header" role="banner"> + <div> + + {%- if site.banner_link -%} + {%- assign banner_link = site.banner_link -%} + {%- else -%} + {%- assign banner_link = '/' | relative_url -%} + {%- endif -%} + + <div class="custom-header"> + <a href="{{ banner_link }}"><img class="img-uni-lu" + src="{{ '/assets/banners/uni-logo.svg' | relative_url }}" + type="image/svg+xml" + /></a> + <img class="img-banner img-banner-main" + src="{{ '/assets/banners/' | relative_url }}{{ site.banner }}/banner.svg" + type="image/svg+xml" + /> + <img class="img-banner img-banner-motto" + src="{{ '/assets/banners/' | relative_url }}{{ site.banner }}/motto.svg" + type="image/svg+xml" + /> + <img class="img-lcsb img-logo-{{ site.logo }}" + src="{{ '/assets/banners/' | relative_url }}{{ site.banner }}/logos.svg" + type="image/svg+xml" + /> + </div> + </div> + </header> + + <main class="page-content" aria-label="Content" style="margin-right: 0%"> + {% if site.internal %} + <div class="indicator tooltip" id="external-indicator"> + <i class="material-icons">lock_open</i> + <span class="tooltip-text"> + You are connected to the uni.lu network. <br />You see all the cards. + </span> + </div> + {% else %} + <div class="indicator tooltip" id="internal-indicator"> + <i class="material-icons">lock</i> + <span class="tooltip-text"> + You are <strong>not connected</strong> to the uni.lu network. <br />You see only publically-available cards. <br />In order to see all, please connect to the VPN. + </span> + </div> + {% endif %} + + <div class="wrapper"> + {%- if page.show_print_button -%} + <div class="print-button"> + <img src="{{ "assets/pdf.svg" | relative_url }}"> + <a href="javascript:window.print()"><span>Print the page</span></a> + </div> + {%- endif -%} + + {{ content }} + + <!-- index --> + + <form action="search" method="GET"> + <div class="search-bar"> + <input placeholder="What would you like to look for?" type="text" name="search_query" /> + <i class="large material-icons">search</i> + </div> + </form> + + <div class="container"> + <div class="left-inner-container" id="left-inner-container" ondrop="window.cardDropDiscard(event)" ondragover="window.allowDrop(event)"> + </div> + <div class="right-inner-container" id="right-inner-container" ondrop="window.cardDrop(event)" ondragover="window.allowDrop(event)"> + <a class="card-link" href="/?handbook"> + <div class="card-pinned card-red"> + <div class="card-header"> + <div class="card-icon"><i class="large material-icons">book</i></div> + </div> + <div class="card-content"> + <div class="card-title">Handbook</div> + </div> + </div> + </a> + <div id="drop-to-add" class="card-pinned card-grayed card-pulsate"> + <div class="card-header"> + <div class="card-icon"><i class="large material-icons">add</i></div> + </div> + <div class="card-content"> + <div class="card-title"> + <small>Drop to add...</small> + </div> + </div> + </div> + <div class="card-pinned card-grayed cursor-hand" onclick="clear_pinned()"> + <div class="card-header"> + <div class="card-icon"><i class="large material-icons">delete</i></div> + </div> + <div class="card-content"> + <div class="card-title"> + <small>Remove all...</small> + </div> + </div> + </div> + </div> + </div> + </div> + </main> + </div> + {%- include footer.html -%} +</div> +</body> +{%- include scripts.html -%} + +<script src="{{ "assets/js/landing.js" | relative_url }}"></script> + +</html> diff --git a/assets/css/landing.css b/assets/css/landing.css new file mode 100644 index 0000000000000000000000000000000000000000..e9d8c2fbe1a1324e95ed0efa2add57ce19868b6b --- /dev/null +++ b/assets/css/landing.css @@ -0,0 +1,286 @@ +:root { + --primary-color: rgb(0, 170, 220); + --secondary-color: #dddedd; + --red-color: rgb(255, 20, 11); + --tooltip-bg: rgb(25, 25, 25); + --border-radius: 25px; + --border-radius-pinned: 18px; + --title-height: 30px; + --animation-time: 300ms; +} +.wrapper { + padding-right: 15px; +} +.container { + font-family: Helvetica; + width: 100%; + height: 100%; + display: grid; + grid-template-columns: 5fr 1fr; +} +.container > .left-inner-container { + grid-column: 1; + height: 100%; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-content: flex-start; + padding-right: 10px; + border: solid 1px rgba(0,0,0,0); + border-right: solid 2px rgba(240, 240, 240, 0.7); +} +.full-bleed { + width: 100%; + grid-column: 1 / 4; +} +.container > .right-inner-container { + height: 100%; + border: solid 1px rgba(0,0,0,0); + grid-column: 2; + transition: border var(--animation-time) linear, background-color var(--animation-time) linear; + margin: 0px auto; + display: block; + padding-top: 12px; + width: 100%; + border-radius: 0px 20px 20px 0; + padding-left: 5px; +} +.droppable { + background: #eee; + border: solid 1px var(--secondary-color); +} +.cursor-hand { + cursor: pointer !important; +} +/* ==================================================== */ +/* Normal cards */ +/* ==================================================== */ +.card-link { + color: unset !important; + text-decoration: none; + cursor: pointer; +} +.card-link:hover { + text-decoration: none; +} +.card-link:hover > .card > .card-content { + border-color: var(--primary-color) !important; +} +.card { + margin: 10px; + margin-bottom: 20px; + width: 160px; + min-height: 100px; +} +.card.card-dragged { + height: 80px; +} +.card > .card-header { + height: 50%; + border: solid 3px var(--primary-color); + border-radius: var(--border-radius) var(--border-radius) 0 0; + background-color: white; + transition: background-color var(--animation-time) linear, border-style var(--animation-time) linear; +} +.card > .card-header:hover { + border-color: var(--primary-color); + background-color: var(--primary-color); +} +.card > .card-header:hover > .card-icon { + color: white; +} +.card > .card-header.card-dragged { + border-style: dotted !important; + border-color: #66c9e9 !important; + height: 150px; + border-radius: var(--border-radius); +} +.card > .card-header.card-dragged > .card-title { + background: none !important; + position: relative; + top: -35px; +} +.card > .card-content { + border: solid 3px var(--secondary-color); + border-top: 0; + border-radius: 0 0 var(--border-radius) var(--border-radius); + /* min-height: calc(50% - 20px); */ + min-height: 110px; + padding: 10px 10px 5px 10px; + text-align: -webkit-center; + transition: height var(--animation-time) linear, border-color var(--animation-time) linear; + background-color: white; + font-size: 15px; +} +.card > .card-content.card-dragged { + opacity: 0; + background: rgba(255,255,255,0.0); + height: 0px; +} +.card > .card-header > .card-icon { + height: calc(100% - var(--title-height)); + width: 80%; + margin: 0px auto; + display: block; + text-align: center; + color: var(--primary-color); + transition: color var(--animation-time) linear; +} +.card > .card-header > .card-icon i { + padding-top: 10px; + font-size: 4rem; +} +.card > .card-header > .card-title { + cursor: move; + --card-title-padding: 6px; + background-color: var(--primary-color); + color: white; + width: 100%; + height: calc(var(--title-height) - var(--card-title-padding)); + padding-top: var(--card-title-padding); + text-align: -webkit-center; + vertical-align: middle; + font-size: 14pt; +} +/* ==================================================== */ +/* Pinned cards */ +/* ==================================================== */ +.card-pinned { + width: 74px; + height: 72px; + margin-bottom: 60px; + margin-left: 25%; + cursor: pointer; +} +.card-pinned[draggable="True"] > .card-header > .card-title { + cursor: pointer; +} +.card-pinned > .card-header { + height: 100%; + border: solid 3px var(--primary-color); + border-radius: var(--border-radius-pinned); + background-color: white; + transition: background-color var(--animation-time) linear, border-style var(--animation-time) linear; +} +.card-pinned > .card-header > .card-icon { + text-align: center; + color: var(--primary-color); +} +.card-pinned > .card-header > .card-icon i { + padding-top: 10px; + font-size: 3rem; +} +.card-pinned > .card-content { + padding: 4px 0px; + font-size: smaller; + color: #777; + text-align: -webkit-center; +} +.card-pinned.card-red > .card-header { + border-color: var(--red-color); +} +.card-pinned.card-red > .card-header > .card-icon { + color: var(--red-color); +} +.card-pinned.card-grayed > .card-header { + border-color: #ccc; + border-style: dashed; + user-select: none; +} +#drop-to-add > * { + cursor: auto; +} +.card-pinned.card-grayed > .card-header > .card-icon { + color: #ccc; +} +.right-inner-container.droppable > .card-pinned.card-grayed.card-pulsate { + animation-name: stretch; + animation-duration: 0.6s; + animation-direction: alternate; + animation-iteration-count: infinite; + animation-play-state: running; +} +@keyframes stretch { + 0% { + transform: scale(.90); + } + 100% { + transform: scale(1.1); + } +} +/* ==================================================== */ +/* Search bar */ +/* ==================================================== */ +div.search-bar { + width: 60%; + margin-left: 20%; + display: flex; +} +.search-bar > input { + width: 100%; + margin: 0px 0px 40px 0px; + border-radius: 20px; + border: solid 2px #eee ; + padding: 8px 25px; + height: 15pt; + font-size: 16px; + transition: border var(--animation-time) linear, font-size var(--animation-time) linear; +} +.search-bar > i { + position: relative; + left: -40px; + top: 7px; +} +.search-bar > input:focus { + border-color: var(--primary-color); + outline: none; + font-size: larger; +} +.search-bar > input:active { + border-color: var(--primary-color); + outline: none; + font-size: larger; +} +/* ==================================================== */ +/* Internal/external indicator */ +/* ==================================================== */ +.indicator { + position: absolute; + top: 190px; + right: 75px; + color: #d6d6d6; + text-align: center; + cursor: pointer; +} +.indicator > i { + font-size: 36px; +} +/* ==================================================== */ +/* Tooltip */ +/* ==================================================== */ +.tooltip .tooltip-text { + visibility: hidden; + width: 400px; + right: 120%; + top: -15px; + background-color: var(--tooltip-bg); + color: var(--secondary-color); + text-align: center; + padding: 10px 0; + border-radius: 8px; + position: absolute; + z-index: 1; +} +.tooltip:hover .tooltip-text { + visibility: visible; +} +.tooltip .tooltip-text::after { + content: " "; + position: absolute; + top: 50%; + left: 100%; /* To the right of the tooltip */ + margin-top: -5px; + border-width: 5px; + border-style: solid; + border-color: transparent transparent transparent var(--tooltip-bg); +} \ No newline at end of file diff --git a/assets/js/landing.js b/assets/js/landing.js new file mode 100644 index 0000000000000000000000000000000000000000..b4a02ece12a7e96da97b8da098dac8245ef28f3e --- /dev/null +++ b/assets/js/landing.js @@ -0,0 +1,205 @@ +window.cards_limit = 6; +let $left_inner_container = document.getElementById("left-inner-container"); +let $right_inner_container = document.getElementById("right-inner-container"); +let $drop_to_add = document.getElementById("drop-to-add"); +// ============= DATA ======================================== +let pinned_cards_store = localStorage.getItem("pinnedCards"); +window.pinned_cards = [ +]; +// ============= DOM functions ======================================== +let create_card = function(icon, title, caption, link) { + let new_div = document.createElement('div'); + new_div.innerHTML = ` + <a class="card-link card-dynamic" href="${link}"> + <div class="card" draggable="True" ondragstart="window.cardDragStart(event)" ondragend="window.cardDragEnd(event)" data-icon='${icon}' data-title='${title}' data-caption='${caption}'> + <div class="card-header"> + <div class="card-icon">${icon}</div> + <div class="card-title">${title}</div> + </div> + <div class="card-content"> + <div class="card-caption">${caption}</div> + </div> + </div> + </a> + `; + return new_div; +}; +let create_pinned_card = function(icon, title, caption, link) { + let new_div = document.createElement('div'); + new_div.innerHTML = ` + <a class="card-link card-dynamic-pinned" href="${link}"> + <div class="card-pinned" draggable="True" ondragstart="window.pinnedCardDragStart(event)" ondragend="window.pinnedCardDragEnd(event)" data-icon='${icon}' data-title='${title}' data-caption='${caption}'> + <div class="card-header"> + <div class="card-icon">${icon}</div> + </div> + <div class="card-content"> + <div class="card-title">${title}</div> + </div> + </div> + </a> + `; + return new_div; +} +let attach_card = function(container, card) { + container.append(card); +} +let attach_pinned_card = function(container, card) { + let card_el = create_pinned_card(card.icon, card.title, card.caption); + let first_child = container.children[1]; + // might as well remove all children and rebuild the contents using window.pinned_cards + container.insertBefore(card_el, first_child); +} +// ============= Helper functions ======================================== +let list_contains = function(the_list, the_object, key) { + for (const element of the_list) { + if (the_object[key] === element[key]) + return true; + } + return false; +} +let remove_from_list = function(the_list, the_object, key) { + var index = -1; + for (var i = 0; i < the_list.length; i++) { + if (the_list[i][key] == the_object[key]) { + index = i; + break; + } + } + if (index != -1) { + the_list.splice(i, 1); + } +} +// ============= DRAG&DROP functions ======================================== +window.cardDragStart = function(event) { + // First, attach classes for animations + event.target.classList.add('card-dragged'); + event.target.children[0].classList.add('card-dragged'); + event.target.children[1].classList.add('card-dragged'); + $right_inner_container.classList.add('droppable'); + // Attach data about the card to drag&drop event + let card = { + "title": event.target.dataset['title'], + "icon": event.target.dataset['icon'], + "caption": event.target.dataset['caption'], + "link": event.target.dataset['link'], + "type": "big" + } + event.dataTransfer.setData("card", JSON.stringify(card)); + // Allow drag&drop + event.dataTransfer.effectAllowed = 'move'; + return true; +} +window.cardDragEnd = function(event) { + event.target.classList.remove('card-dragged'); + event.target.children[0].classList.remove('card-dragged'); + event.target.children[1].classList.remove('card-dragged'); + $right_inner_container.classList.remove('droppable'); + return true; +} +window.pinnedCardDragStart = function(event) { + $left_inner_container.classList.add('droppable'); + // Attach data about the card to drag&drop event + let card = { + "title": event.target.dataset['title'], + "icon": event.target.dataset['icon'], + "caption": event.target.dataset['caption'], + "link": event.target.dataset['link'], + "type": "small" + } + event.dataTransfer.setData("card", JSON.stringify(card)); + // Allow drag&drop + event.dataTransfer.effectAllowed = 'move'; + return true; +} +window.pinnedCardDragEnd = function(event) { + $left_inner_container.classList.remove('droppable'); + return true; +} +window.cardDrop = function(event) { + let serializedCard = event.dataTransfer.getData("card"); + let card = JSON.parse(serializedCard); + // Don't react to dropping small (shortcut) cards + if (card['type'] == "small") { + return; + } + // Add limit of pinned cards + if (window.pinned_cards.length >= window.cards_limit) { + return; + } + if (!list_contains(window.pinned_cards, card, "title")) { + window.pinned_cards.push(card); + localStorage.setItem("pinnedCards", JSON.stringify(pinned_cards)); + window.cards = window.cards.filter(function(el) { return el['title'] != card['title'];}); + rebuild_pinned_cards(); + rebuild_cards(); + } + refresh_drop_to_add_button(); +} +window.cardDropDiscard = function(event) { + let serializedCard = event.dataTransfer.getData("card"); + let card = JSON.parse(serializedCard); + if (card['type'] == "big") + return; + if (list_contains(window.pinned_cards, card, "title")) { + remove_from_list(window.pinned_cards, card, "title"); + localStorage.setItem("pinnedCards", JSON.stringify(pinned_cards)); + cards.push(card); + rebuild_pinned_cards(); + rebuild_cards(); + } + refresh_drop_to_add_button(); +} +window.allowDrop = function(event) { + event.preventDefault(); +} +window.rebuild_cards = function() { + $left_inner_container.replaceChildren(); + for (const card of window.cards) { + let card_el = create_card(card.icon, card.title, card.caption, card.link); + attach_card($left_inner_container, card_el); + } +} +window.rebuild_pinned_cards = function() { + var old_pinned_cards = document.getElementsByClassName("card-dynamic-pinned"); + while (old_pinned_cards.length > 0){ + old_pinned_cards[0].parentNode.removeChild(old_pinned_cards[0]); + } + for (const card of pinned_cards) { + let card_el = create_pinned_card(card.icon, card.title, card.caption, card.link); + attach_pinned_card($right_inner_container, card); + } +} +window.refresh_drop_to_add_button = function() { + if (window.pinned_cards.length == window.cards_limit) { + $drop_to_add.style.display = "none" + } else { + $drop_to_add.style.display = "block" + } +} +window.clear_pinned = function() { + for (card of pinned_cards) { + cards.push(card); + } + pinned_cards = []; + localStorage.setItem("pinnedCards", JSON.stringify(pinned_cards)); + rebuild_pinned_cards(); + rebuild_cards(); + refresh_drop_to_add_button(); +} +window.start_cards = function() { + // ============= Initialization ======================================== + if (pinned_cards_store == null) { + localStorage.setItem("pinnedCards", JSON.stringify(pinned_cards)); + } else { + pinned_cards = JSON.parse(pinned_cards_store); + // Sort alphabetically + pinned_cards = pinned_cards.sort(function(a, b) { if (a.title > b.title) return -1; else if (a.title < b.title) return 1; else return 0; }); + // Remove duplicates of pinned cards + for (const card of pinned_cards) { + remove_from_list(cards, card, "title"); + } + } + refresh_drop_to_add_button(); + rebuild_cards(); + rebuild_pinned_cards(); +} diff --git a/landing.md b/landing.md new file mode 100644 index 0000000000000000000000000000000000000000..c4a39cff253df3125427513b7c3d852a654f3eef --- /dev/null +++ b/landing.md @@ -0,0 +1,118 @@ +--- +layout: landing +title: Home +order: -1 +--- + +{% comment %} +The Bioinformatics Core and Lab Support teams assist researchers from the Luxembourg Centre for Systems Biomedicine ([LCSB](https://www.uni.lu/lcsb)) with the organization, management, and curation of research data through its R3 initiative. +The How-to cards are intended to provide practical guidance in implementing Data Management, Data Protection, IT setup, lab support, and others. +{% endcomment %} + +<script> + window.cards = [ + { + "title": "COVID-19", + "caption": "What is currently happening in relation to the pandemic?", + "icon": "<i class=\"large material-icons\">coronavirus</i>", + "link": "/?covid19", + }, + { + "title": "Newcomers", + "caption": "I joined the team recently. Help me!", + "icon": "<i class=\"large material-icons\">accessibility_new</i>", + "link": "/?newcomers", + }, + { + "title": "On&Off boarding", + "caption": "A new colleague joined or my current colleague will leave. What do I have to do?", + "icon": "<i class=\"large material-icons\">compare_arrows</i>", + "link": "/?onoff_boarding" + }, + { + "title": "General", + "caption": "I need information on LCSB operations", + "icon": "<i class=\"large material-icons\">sticky_note_2</i>", + "link": "/?general" + }, + { + "title": "Access", + "caption": "How can I get access to systems and infrastructure?", + "icon": "<i class=\"large material-icons\">login</i>", + "link": "/?access" + }, + { + "title": "Backup", + "caption": "Guidelines regarding backing up your data", + "icon": "<i class=\"large material-icons\">backup</i>", + "link": "/?backup" + }, + { + "title": "Contribute", + "caption": "I want to share my knowledge and expertise. How?", + "icon": "<i class=\"large material-icons\">add_circle</i>", + "link": "/?contribute" + }, + + { + "title": "Duty call", + "caption": "I am on call for critical equipment", + "icon": "<i class=\"large material-icons\">notifications_active</i>", + "link": "/?duty_call" + }, + { + "title": "Events", + "caption": "I am planning an event or a visit", + "icon": "<i class=\"large material-icons\">event</i>", + "link": "/?events" + }, + { + "title": "Exchange channels", + "caption": "I am looking to share data or information", + "icon": "<i class=\"large material-icons\">multiple_stop</i>", + "link": "/?exchange_channels" + }, + { + "title": "Finance", + "caption": "I need to get funds or claim expenses", + "icon": "<i class=\"large material-icons\">euro_symbol</i>", + "link": "/?finance" + }, + /* + { + "title": "Handbook", + "caption": "I am looking for a complete resource on everything I need to know", + "icon": "<i class=\"large material-icons\">book</i>", + "link": "/?handbook" + }, */ + { + "title": "Integrity", + "caption": "I want to ensure that my work and research is compliant and reusable", + "icon": "<i class=\"large material-icons\">vpn_key</i>", + "link": "/?integrity" + }, + { + "title": "Lab", + "caption": "I need information on wetlab equipment and procedures", + "icon": "<i class=\"large material-icons\">science</i>", + "link": "/?lab" + }, + { + "title": "Policies", + "caption": "What are all the important rules I need to know?", + "icon": "<i class=\"large material-icons\">book</i>", + "link": "/?policies" + }, + { + "title": "Publication", + "caption": "I want to publish my article", + "icon": "<i class=\"large material-icons\">menu_book</i>", + "link": "/?publication" + } + ]; + + document.addEventListener('DOMContentLoaded', function() { + window.start_cards(); + }, false); + +</script> \ No newline at end of file