web menu gui and backend

This commit is contained in:
Gerome Matilla 2020-06-04 12:15:59 +08:00
parent b117869435
commit bef2769676
6 changed files with 606 additions and 7 deletions

View File

@ -15,6 +15,7 @@
@import url('theme-engine.css');
@import url('weather-screen.css');
@import url('weather-settings.css');
@import url('web-menu.css');
:root {
/* Colors */

245
css/web-menu.css Normal file
View File

@ -0,0 +1,245 @@
#webMenu {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
margin: 0;
background: var(--panel-bg);
z-index: 0;
overflow: hidden;
backdrop-filter: blur(var(--blur-strength));
padding-top: 6vh;
padding-bottom: 6vh;
padding-left: 12vw;
padding-right: 12vw;
/*Dont increase the geometry by using padding*/
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
/*Disable user touch/select on text elements*/
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
/*Transitions*/
opacity: 0;
transform: scale(0);
transition: transform var(--transition-speed),
opacity var(--transition-speed),
z-index var(--transition-speed);
}
@media screen and (max-width: 580px) {
#webMenu {
padding-top: 6vh;
padding-bottom: 0vh;
padding-left: 18vw;
padding-right: 18vw;
}
}
@media screen and (max-height: 799px) {
#webMenu {
padding-top: 40px;
}
}
/*Show web menu*/
.showWebMenu {
transform: scale(1) !important;
opacity: 1 !important;
z-index: 3 !important;
}
#webMenuContainer {
background: transparent;
max-height: 100%;
overflow: hidden;
}
#webMenuSearchBox {
background: var(--base-container);
text-align: center;
font-family: roboto-bold;
color: var(--panel-color);
border: none;
border-radius: 6px;
width: 25%;
height: 36px;
margin: 0;
}
@media screen and (max-width: 580px) {
#webMenuSearchBox {
width: 50vw;
}
}
#webMenuSearchBoxContainer {
/*Center horizontally*/
position: relative;
display: flex;
flex-flow: column wrap;
align-items: center;
margin-bottom: 20px;
}
/*Web menu list*/
/*UL*/
#webMenuList {
list-style-type: none;
padding: 0;
margin: 0 auto;
text-align: justify;
background: transparent;
}
/*List*/
#webMenuList li {
/*Align list horizontally*/
display: inline-block;
}
@media screen and (max-width: 580px) {
#webMenuList li {
display: inline;
}
}
#webMenuList li.selected {
background: var(--base-active-bg);
border-radius: var(--rounded-radius);
transition: transform var(--transition-speed);
}
/*Child of li*/
.webItem {
background: transparent;
width: 128px;
height: 128px;
margin: 5px;
cursor: pointer;
border-radius: var(--rounded-radius);
}
.webItem:hover {
background: var(--base-hover-bg);
}
.webItem:active {
background: var(--base-active-bg);
}
.webItemFocus {
width: 128px;
height: 128px;
margin: 5px;
border-radius: var(--rounded-radius);
background: var(--base-hover-bg);
}
.webItemFocus:hover {
background: var(--base-hover-bg);
}
.webItemFocus:active {
background: var(--base-active-bg);
}
/*Contains web icon and label*/
.webItemContainer {
/*Align vertically*/
margin:0 auto;
position: relative;
top: 50%;
-webkit-transform: translateY(-50%);
/*Align horizontally*/
display: flex;
flex-direction: row;
justify-content: center;
padding: 5px;
}
/*Web icon container*/
.webItemIconContainer {
position: relative;
display: flex;
flex-flow: column wrap;
align-items: center;
}
.webItemIcon {
height: 64px;
width: 64px;
margin-bottom: 0;
}
/*Web label/name*/
.webItemName {
text-align: center;
font-size: 11pt;
font-family: roboto;
word-wrap: break-word;
color: var(--base-color);
}
#webMenuListContainer {
position: relative;
max-height: 70vh;
display: flex;
justify-content: center;
overflow-y: scroll;
/*scrollbar-width: none !important;
-ms-overflow-style: none !important;*/
/*Fade transparency*/
/*-webkit-mask-image: linear-gradient(to bottom, black 85%, transparent 100%);*/
/*mask-image: linear-gradient(to bottom, black 85%, transparent 100%);*/
}
/*Hide scrollbar*/
/*#webMenuListContainer::-webkit-scrollbar {
display: none;
}*/
/*Stretch list item if screen width < 580px*/
@media screen and (max-width: 580px) {
#webMenuList {
flex-grow: 1;
}
.webItem {
width: auto;
}
.webItem:hover {
-ms-transform: scale(1);
-webkit-transform: scale(1);
transform: scale(1);
}
.webItemFocus {
-ms-transform: scale(1);
-webkit-transform: scale(1);
transform: scale(1);
}
}

View File

@ -167,9 +167,6 @@
</div>
<div class="dashboardOverlay" id="dashboardOverlay"></div>
<!-- Weather screen -->
<div id="weatherScreen">
<div id="weatherScreenContainer">
@ -203,7 +200,21 @@
</div>
<!-- Web menu panel -->
<div id="webMenu">
<div id="webMenuContainer">
<div id="webMenuSearchBoxContainer">
<input type="text" id="webMenuSearchBox" autocomplete="off" placeholder="Type to search">
</div>
<div id="webMenuListContainer">
<ul id="webMenuList">
<!-- Javascript will generate a list here -->
</ul>
</div>
</div>
</div>
<script src="js/body-background-set.js"></script>
@ -219,5 +230,6 @@
<script src="js/theme-engine.js"></script>
<script src="js/weather-screen.js"></script>
<script src="js/weather-settings.js"></script>
<script src="js/web-menu.js"></script>
</body>
</html>

View File

@ -25,11 +25,11 @@ const hideDashboard = () => {
const toggleDashboard = () => {
if (rightDashboardVisibility) {
// Hide search box
// Hide dashboard
hideDashboard();
} else {
// Show search box
// Show dashboard
showDashboard();
}

View File

@ -66,8 +66,8 @@ const populateDock = () => {
'Launch',
'launch',
() => {
// Toggle web pad
alert('toggle web pad');
// Toggle web menu
toggleWebMenu();
}
);

341
js/web-menu.js Normal file
View File

@ -0,0 +1,341 @@
var webMenu = document.getElementById("webMenu");
var webMenuList = document.getElementById("webMenuList");
var webMenuListContainer = document.getElementById("webMenuListContainer");
var webMenuSearchBox = document.getElementById("webMenuSearchBox");
let webMenuVisibility = false;
let webItemFocus;
let webListIndex = 0;
// Create mouse event for passed li
const createMouseUpEvent = (li, url) => {
// Create a callback property for the passed li
li.callback = () => {
window.location.href = encodeURI(url);
}
// Create onmouseup event for the li
li.onmouseup = () => {
li.callback();
}
}
// Sort list alphabetically
const sortList = () => {
Array.from(webMenuList.getElementsByTagName("li"))
.sort((a, b) => a.textContent.localeCompare(b.textContent))
.forEach(li => webMenuList.appendChild(li));
}
// Populate web menu
const populateWebMenu = () => {
// Generate a list
for (i = 0; i < (webSites.length); i++) {
var site = webSites[i].site;
var icon = webSites[i].icon;
var url = webSites[i].url;
var li = document.createElement('li');
// Add mouseup event
createMouseUpEvent(li, url);
// Create an outer div, child of li
let webItemDiv = document.createElement('div')
webItemDiv.className = 'webItem';
// webItemDiv.tabitemIndex = '1';
webItemDiv.id = "id" + site;
// Create a second div, webItemContainer
var webItemContainer = document.createElement('div');
webItemContainer.className = 'webItemContainer';
// Create the innermost div, contains icon and label
var webItemBody = document.createElement('div');
webItemBody.className = 'webItemBody';
// Create div for webItemIcon
var webItemIconContainer = document.createElement('div');
webItemIconContainer.className = 'webItemIconContainer';
var webItemIcon = document.createElement('div');
webItemIcon.className = 'webItemIcon';
webItemIcon.style.background = "url('assets/webcons/" + icon + ".svg')";
webItemIcon.style.backgroundSize = 'cover';
// Create webItemName
var webItemName = document.createElement('div');
webItemName.className = 'webItemName';
webItemName.innerHTML = site;
// Append divs with heirarchy
webItemDiv.appendChild(webItemContainer);
webItemContainer.appendChild(webItemBody);
webItemIconContainer.appendChild(webItemIcon);
webItemBody.appendChild(webItemIconContainer);
webItemBody.appendChild(webItemName);
li.appendChild(webItemDiv);
webMenuList.appendChild(li);
}
// Call to sort list
sortList();
}
// Fuzzy search
String.prototype.fuzzy = (term, ratio) => {
var string = this.toLowerCase();
var compare = term.toLowerCase();
var matches = 0;
if (string.indexOf(compare) > -1) return true; // covers basic partial matches
for (var i = 0; i < compare.length; i++) {
string.indexOf(compare[i]) > -1 ? matches += 1 : matches -=1;
}
return (matches/this.length >= ratio || term == "");
};
// Search through the list
const filterWebList = () => {
var input, filter, ul, li, a, i, txtValue;
input = webMenuSearchBox;
filter = input.value.toUpperCase();
ul = webMenuList;
li = ul.getElementsByTagName('li');
// Loop through all list items, and focus if matches the search query
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByClassName("webItemName")[0];
txtValue = a.innerHTML || a.textContent || a.innerText;
// If an item match, hightlight it and focus
// if (txtValue.toUpperCase().indexOf(filter) !== -1) {
if (txtValue.toUpperCase().fuzzy(filter, 1) === true) {
// Unselect/Unhightlight old active
var oldWebItemFocus = webItemFocus;
var oldWebItemFocusChild = oldWebItemFocus.querySelector('.webItem');
oldWebItemFocusChild.classList.remove('webItemFocus');
// Update webItemFocus
webItemFocus = li[i];
// Update weblistindex
webListIndex = i;
// Get child
var webItemFocusChild = webItemFocus.querySelector('.webItem');
// Add webItemFocus class to child
webItemFocusChild.classList.add('webItemFocus');
// Scroll focus into active
webItemFocus.scrollIntoView();
}
}
}
// Type event on web mmenu search box
webMenuSearchBox.onkeydown = (event) => {
// Don't hijack keyboard navigation buttons (up, down, left, right)
if ((event.key === 'ArrowRight') || (event.key === 'ArrowDown') ||
(event.key === 'ArrowLeft') || (event.key === 'ArrowUp')) return;
if (event.key === 'Enter' && webItemFocus) {
// Run the focused li's callback
webItemFocus.callback();
// Hide web menu
// webMenuToggle();
} else if (event.key === 'Backspace' && webMenuSearchBox.value.length < 1) {
// Hide web menu if backspace is pressed and searchbox value is 0
// webMenuToggle();
} else if ((event.key === 'Escape') || (event.key === 'Alt')) {
// Ignore escape and alt key
return;
}
// Filter
filterWebList();
}
// Reset focus on web menu close
const focusReset = () => {
var oldWebItemFocus = webItemFocus;
var oldWebItemFocusChild = oldWebItemFocus.querySelector('.webItem');
oldWebItemFocusChild.classList.remove('webItemFocus');
webListIndex = 0;
}
// Get first item of ul
const getFirstItem = () => {
var ul = webMenuList;
var li = ul.getElementsByTagName('li');
// Focus on first item
webItemFocus = li[0];
// Get child
var webItemFocusChildren = webItemFocus.querySelector('.webItem');
// Add webItemFocus class
webItemFocusChildren.classList.add('webItemFocus');
}
// Show/Hide web menu
// const webMenuToggle = () => {
// webMenuVisible = !webMenuVisible;
// hideCenterContainer();
// rotateProfile();
// webMenu.classList.toggle("show");
// // Clear and unfocus searchbox
// if (!webMenuVisible) {
// webMenuSearchBox.value = '';
// webMenuSearchBox.blur();
// filterWebList();
// webMenuListContainer.scrollTop = 0;
// focusReset();
// getFirstItem();
// } else {
// // Focus
// webMenuSearchBox.focus();
// }
// if(weatherVisible && webMenuVisible) {
// weatherToggle();
// } else if (floatPanelVisible && webMenuVisible) {
// slideDashboard();
// return;
// }
// }
const showWebMenu = () => {
webMenu.classList.add('showWebMenu');
webMenuVisibility = !webMenuVisibility;
}
const hideWebMenu = () => {
webMenu.classList.remove('showWebMenu');
webMenuVisibility = !webMenuVisibility;
}
const toggleWebMenu = () => {
// If profile anim is still running,
// Return to avoid spam
if (profileAnimRunning) return;
// Rotate profile
rotateProfile();
if (webMenuVisibility) {
// Hide web menu
hideWebMenu();
} else {
// Show Web menu
showWebMenu();
}
console.log('toggle web menu');
}
// Remove class to focused item
const removeClass = (el, className) => {
// Remove webItemFocus class
var oldWebItemFocus = el.querySelector('.webItem');
oldWebItemFocus.classList.remove('webItemFocus');
};
// Add class to focused item
const addClass = (el, className) => {
var webItemFocusChild = el.querySelector('.webItem');
// Add webItemFocus class to child
webItemFocusChild.classList.add('webItemFocus');
// Scroll focus into active
webItemFocusChild.scrollIntoView();
};
// Keyboard navigation
webMenu.addEventListener(
'keydown',
(event) => {
var len = webMenuList.getElementsByTagName('li').length - 1;
// Right and Down
if((event.which === 39) || (event.which === 40)) {
// Clear web menu searchbox
webMenuSearchBox.value = '';
webListIndex++;
if (webItemFocus) {
removeClass(webItemFocus, 'webItemFocus');
next = webMenuList.getElementsByTagName('li')[webListIndex];
if(typeof next !== undefined && webListIndex <= len) {
webItemFocus = next;
} else {
webListIndex = 0;
webItemFocus = webMenuList.getElementsByTagName('li')[0];
}
addClass(webItemFocus, 'webItemFocus');
// console.log(webListIndex);
} else {
webListIndex = 0;
webItemFocus = webMenuList.getElementsByTagName('li')[0];
addClass(webItemFocus, 'webItemFocus');
}
}
// Up and left
else if ((event.which === 37) || (event.which === 38)) {
// Clear web menu searchbox
webMenuSearchBox.value = '';
if (webItemFocus) {
removeClass(webItemFocus, 'webItemFocus');
webListIndex--;
// console.log(webListIndex);
next = webMenuList.getElementsByTagName('li')[webListIndex];
if(typeof next !== undefined && webListIndex >= 0) {
webItemFocus = next;
} else {
webListIndex = len;
webItemFocus = webMenuList.getElementsByTagName('li')[len];
}
addClass(webItemFocus, 'webItemFocus');
} else {
webListIndex = 0;
webItemFocus = webMenuList.getElementsByTagName('li')[len];
addClass(webItemFocus, 'webItemFocus');
}
}
},
false
);
// Populate and get first child
const initWebMenu = () => {
populateWebMenu();
getFirstItem();
}
// Initialize web menu
window.onload = initWebMenu();