Commit a126dbfd authored by Jacek Lebioda's avatar Jacek Lebioda
Browse files

feat: new landing page

parent 36adae17
Pipeline #35150 passed with stages
in 2 minutes and 11 seconds
<!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>
: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
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");