This commit is contained in:
parent
e115ee0194
commit
49932fb183
15 changed files with 0 additions and 770 deletions
|
@ -1,10 +0,0 @@
|
||||||
.woodpecker
|
|
||||||
.dockerignore
|
|
||||||
.gitignore
|
|
||||||
.git
|
|
||||||
docker-compose.build.yml
|
|
||||||
node_modules/
|
|
||||||
.env.example
|
|
||||||
LICENSE.md
|
|
||||||
docker-compose.yml
|
|
||||||
README.md
|
|
29
index.html
29
index.html
|
@ -1,29 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<link rel="icon" href="/images/home-icon.png">
|
|
||||||
<meta name="viewport" content="width=device-width">
|
|
||||||
<link rel="manifest" href="/manifest.json">
|
|
||||||
<title>Pianello</title>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="/src/css/roboto.css">
|
|
||||||
<link rel="stylesheet" href="/src/css/index.css">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<header></header>
|
|
||||||
|
|
||||||
<main></main>
|
|
||||||
|
|
||||||
<footer></footer>
|
|
||||||
|
|
||||||
<!-- <wd-loader></wd-loader> -->
|
|
||||||
|
|
||||||
<script src="/src/components/bottom-app-bar.js" type="module"></script>
|
|
||||||
<script src="/src/js/index.js" type="module"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,80 +0,0 @@
|
||||||
import homeIconUrl from '../../static/images/home-icon.png';
|
|
||||||
import routesIconUrl from '../../static/images/routes-icon.png';
|
|
||||||
import settingsIconUrl from '../../static/images/settings-icon.png';
|
|
||||||
|
|
||||||
const template = `
|
|
||||||
<div>
|
|
||||||
<img src="${homeIconUrl}">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<img src="${routesIconUrl}">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<img src="${settingsIconUrl}">
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
const style = `
|
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
height: 68px;
|
|
||||||
width: 90%;
|
|
||||||
border-radius: 90px;
|
|
||||||
box-shadow: 0 0 50px #ccc;
|
|
||||||
background-color: var(--card-background-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host > div {
|
|
||||||
flex: 1;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
display: block;
|
|
||||||
height: 32px;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bottom app bar custom element.
|
|
||||||
*/
|
|
||||||
class BottomAppBar extends HTMLElement {
|
|
||||||
#elements = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.#createShadowDOM();
|
|
||||||
this.#setElements();
|
|
||||||
this.#addEventListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create shadow DOM.
|
|
||||||
*/
|
|
||||||
#createShadowDOM() {
|
|
||||||
this.attachShadow({mode: 'open'});
|
|
||||||
this.shadowRoot.innerHTML = `
|
|
||||||
<style>${style}</style>
|
|
||||||
${template}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set all shadow dom selectors.
|
|
||||||
*/
|
|
||||||
#setElements() {
|
|
||||||
this.elements = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
#addEventListeners() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define('bottom-app-bar', BottomAppBar);
|
|
||||||
|
|
||||||
export default BottomAppBar;
|
|
||||||
|
|
|
@ -1,223 +0,0 @@
|
||||||
const template = `
|
|
||||||
<div class="background">
|
|
||||||
<div class="loader">
|
|
||||||
<svg viewBox="0 0 32 32" width="32" height="32">
|
|
||||||
<circle id="spinner" cx="16" cy="16" r="14" fill="none"></circle>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div id="message"></div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
const style = `
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
opacity: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.6);
|
|
||||||
z-index: 99999;
|
|
||||||
transition: opacity 0.333s cubic-bezier(0, 0, 0.21, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader {
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
position: fixed;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#spinner {
|
|
||||||
box-sizing: border-box;
|
|
||||||
stroke: var(--accent-color);
|
|
||||||
stroke-width: 3px;
|
|
||||||
transform-origin: 50%;
|
|
||||||
animation: line 1.6s cubic-bezier(0.4, 0, 0.2, 1) infinite, rotate 1.6s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rotate {
|
|
||||||
from {
|
|
||||||
-webkit-transform: rotate(0);
|
|
||||||
transform: rotate(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
-webkit-transform: rotate(450deg);
|
|
||||||
transform: rotate(450deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes line {
|
|
||||||
0% {
|
|
||||||
stroke-dasharray: 2, 85.964;
|
|
||||||
-webkit-transform: rotate(0);
|
|
||||||
transform: rotate(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
50% {
|
|
||||||
stroke-dasharray: 65.973, 21.9911;
|
|
||||||
stroke-dashoffset: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
stroke-dasharray: 2, 85.964;
|
|
||||||
stroke-dashoffset: -65.973;
|
|
||||||
-webkit-transform: rotate(90deg);
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#message {
|
|
||||||
position: absolute;
|
|
||||||
height: 20%;
|
|
||||||
bottom: 0px;
|
|
||||||
width: 100%;
|
|
||||||
background-color: #212121;
|
|
||||||
border-bottom-left-radius: 8px;
|
|
||||||
border-bottom-right-radius: 8px;
|
|
||||||
color: #eee;
|
|
||||||
place-items: center;
|
|
||||||
display: none;
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#message.show {
|
|
||||||
display: grid !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host(.contained) .background {
|
|
||||||
position: absolute;
|
|
||||||
width: 250px;
|
|
||||||
height: 250px;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host(.contained) .loader {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spinner-style loader ce.
|
|
||||||
*/
|
|
||||||
class WDLoader extends HTMLElement {
|
|
||||||
#elements = {};
|
|
||||||
isVisible = false;
|
|
||||||
#counter = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.#createShadowDOM();
|
|
||||||
this.#setElements();
|
|
||||||
getComputedStyle(this.#elements.background).opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set message.
|
|
||||||
* @param {String} value
|
|
||||||
*/
|
|
||||||
set message(value) {
|
|
||||||
if (value && value !== '') {
|
|
||||||
this.#elements.message.innerHTML = value;
|
|
||||||
this.#elements.message.classList.add('show');
|
|
||||||
} else {
|
|
||||||
this.#elements.message.classList.remove('show');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {WDLoader}
|
|
||||||
*/
|
|
||||||
static get instance() {
|
|
||||||
let loader = document.querySelector('wd-loader');
|
|
||||||
|
|
||||||
if (loader) {
|
|
||||||
return loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
loader = new WDLoader();
|
|
||||||
document.body.appendChild(loader);
|
|
||||||
return loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create shadow DOM.
|
|
||||||
*/
|
|
||||||
#createShadowDOM() {
|
|
||||||
this.attachShadow({mode: 'open'});
|
|
||||||
this.shadowRoot.innerHTML = `
|
|
||||||
<style>${style}</style>
|
|
||||||
${template}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set all shadow dom selectors.
|
|
||||||
*/
|
|
||||||
#setElements() {
|
|
||||||
this.#elements = {
|
|
||||||
background: this.shadowRoot.querySelector('.background'),
|
|
||||||
message: this.shadowRoot.querySelector('#message'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show loader.
|
|
||||||
* @param {String} message
|
|
||||||
*/
|
|
||||||
show(message = '') {
|
|
||||||
this.#counter++;
|
|
||||||
this.style.display = 'block';
|
|
||||||
this.isVisible = true;
|
|
||||||
this.#elements.background.style.opacity = 1;
|
|
||||||
this.message = message;
|
|
||||||
getComputedStyle(this.#elements.background).opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide loader.
|
|
||||||
*/
|
|
||||||
hide() {
|
|
||||||
this.#counter--;
|
|
||||||
|
|
||||||
if (this.#counter > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const onLoaderTransitionEnd = (evt) => {
|
|
||||||
this.style.display = 'none';
|
|
||||||
};
|
|
||||||
|
|
||||||
this.#elements.background.addEventListener(
|
|
||||||
'transitionend',
|
|
||||||
onLoaderTransitionEnd.bind(this),
|
|
||||||
{once: true});
|
|
||||||
|
|
||||||
this.isVisible = false;
|
|
||||||
this.message = '';
|
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
this.#elements.background.style.opacity = 0;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define('wd-loader', WDLoader);
|
|
||||||
|
|
||||||
export default WDLoader;
|
|
|
@ -1,42 +0,0 @@
|
||||||
|
|
||||||
:root {
|
|
||||||
--footer-height: calc(68px + 20px + 20px);
|
|
||||||
--accent-color: #213c8b;
|
|
||||||
--pianello-red: #de0e1b;
|
|
||||||
--pianello-yellow: #f6ae04;
|
|
||||||
--pianello-blue: #213c8b;
|
|
||||||
--card-background-color: #f9f4f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0px;
|
|
||||||
background-color: #fff;
|
|
||||||
font-family: 'Roboto-Regular';
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
grid-template-rows: var(--footer-height) auto var(--footer-height);
|
|
||||||
}
|
|
||||||
|
|
||||||
header,
|
|
||||||
main,
|
|
||||||
footer {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bold {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
/* @import './app.css'; */
|
|
||||||
|
|
||||||
#welcome-message {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#route-cards {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.route-card {
|
|
||||||
flex: 1;
|
|
||||||
--route-card-radius: 45px;
|
|
||||||
border-radius: var(--route-card-radius);
|
|
||||||
box-shadow: 0 0 27px #ccc;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
width: 90%;
|
|
||||||
display: flex;
|
|
||||||
background-color: var(--card-background-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.route-card > * {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.route-card-left {
|
|
||||||
flex: 0 1 20%;
|
|
||||||
border-top-left-radius: var(--route-card-radius);
|
|
||||||
border-bottom-left-radius: var(--route-card-radius);
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.route-card-left img {
|
|
||||||
display: block;
|
|
||||||
width: 80%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.route-card-right {
|
|
||||||
flex: 0 1 20%;
|
|
||||||
border-top-right-radius: var(--route-card-radius);
|
|
||||||
border-bottom-right-radius: var(--route-card-radius);
|
|
||||||
background-image: url('/images/test-1.jpg');
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
#route-cards .route-card:nth-child(1) .route-card-left {
|
|
||||||
background-color: var(--pianello-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
#route-cards .route-card:nth-child(2) .route-card-left {
|
|
||||||
background-color: var(--pianello-yellow);
|
|
||||||
}
|
|
||||||
|
|
||||||
#route-cards .route-card:nth-child(3) .route-card-left {
|
|
||||||
background-color: var(--pianello-blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
.route-card .name {
|
|
||||||
display: grid;
|
|
||||||
height: 100%;
|
|
||||||
align-content: center;
|
|
||||||
padding: 20px;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.route-card .name > * {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
@import './app.css';
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
import Router from './router.js';
|
|
||||||
import WDLoader from '../components/wd-loader.js';
|
|
||||||
import robotoFontStyle from '../css/roboto.css?raw';
|
|
||||||
import appStyle from '../css/app.css?raw';
|
|
||||||
const appIconURL = new URL('../../static/images/home-icon.png', import.meta.url).href;
|
|
||||||
|
|
||||||
const app = {
|
|
||||||
title: 'Pianello',
|
|
||||||
meta: {
|
|
||||||
charset: 'utf-8',
|
|
||||||
viewport: 'width=device-width',
|
|
||||||
},
|
|
||||||
link: {
|
|
||||||
icon: appIconURL,
|
|
||||||
},
|
|
||||||
page: null,
|
|
||||||
router: new Router(),
|
|
||||||
loader: WDLoader.instance,
|
|
||||||
init: () => {
|
|
||||||
document.head.innerHTML = `
|
|
||||||
<meta charset="${app.meta.charset}">
|
|
||||||
<link rel="icon" href="${app.link.icon}">
|
|
||||||
<meta name="viewport" content="${app.meta.viewport}">
|
|
||||||
`;
|
|
||||||
document.title = app.title;
|
|
||||||
document.adoptedStyleSheets = [robotoFontStyle, appStyle]
|
|
||||||
.map((rawSheet) => {
|
|
||||||
const sheet = new CSSStyleSheet();
|
|
||||||
sheet.replaceSync(rawSheet);
|
|
||||||
return sheet;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default app;
|
|
|
@ -1,46 +0,0 @@
|
||||||
import app from './app.js';
|
|
||||||
import pageStyle from '../css/home-page.css?raw';
|
|
||||||
import BottomAppBar from '../components/bottom-app-bar.js';
|
|
||||||
|
|
||||||
class HomePage {
|
|
||||||
/**
|
|
||||||
* An object containing useful HTMLElement references.
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
#elements = {};
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.#addPageStyle();
|
|
||||||
this.#setElements();
|
|
||||||
this.#addEventListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
get elements() {
|
|
||||||
return this.#elements;
|
|
||||||
}
|
|
||||||
|
|
||||||
#addPageStyle() {
|
|
||||||
const sheet = new CSSStyleSheet();
|
|
||||||
sheet.replaceSync(pageStyle);
|
|
||||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Init `#elements` field that holds element references.
|
|
||||||
*/
|
|
||||||
#setElements() {
|
|
||||||
this.#elements = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
#addEventListeners() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default HomePage;
|
|
||||||
|
|
||||||
app.init();
|
|
||||||
app.page = new HomePage();
|
|
|
@ -1,13 +0,0 @@
|
||||||
import app from './app.js';
|
|
||||||
import homePageURL from '../pages/home-page.html?url';
|
|
||||||
|
|
||||||
app.init();
|
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
// Service worker build output path.
|
|
||||||
navigator.serviceWorker.register('/service-worker.js');
|
|
||||||
}
|
|
||||||
|
|
||||||
app.router.navigate(homePageURL);
|
|
||||||
});
|
|
158
src/js/router.js
158
src/js/router.js
|
@ -1,158 +0,0 @@
|
||||||
import app from './app.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Router
|
|
||||||
*/
|
|
||||||
class Router {
|
|
||||||
/**
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
this.host = location.host;
|
|
||||||
this.previousURL = null;
|
|
||||||
|
|
||||||
this.initListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add all event listeners handles.
|
|
||||||
*/
|
|
||||||
initListeners() {
|
|
||||||
document.addEventListener('click', this.onLinkClick.bind(this));
|
|
||||||
window.addEventListener('popstate', this.onPopState.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* window popstate event handler.
|
|
||||||
* @param {Event} event
|
|
||||||
*/
|
|
||||||
onPopState(event) {
|
|
||||||
this.navigate(location.href, event.state.scrollTop, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTMLAnchorElement click event handler.
|
|
||||||
* @param {Event} event
|
|
||||||
*/
|
|
||||||
async onLinkClick(event) {
|
|
||||||
const anchor = event.target.closest('a');
|
|
||||||
if (!anchor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (anchor.dataset.hasOwnProperty('external')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {href} = anchor;
|
|
||||||
const link = new URL(href);
|
|
||||||
|
|
||||||
// If it’s an external link, just navigate.
|
|
||||||
if (link.host !== this.host) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
this.navigate(link.toString())
|
|
||||||
.catch((error) => console.error(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the navigation.
|
|
||||||
* @param {string} link
|
|
||||||
* @param {number} scrollTop
|
|
||||||
* @param {boolean} pushState
|
|
||||||
* @return {Promise<number>} Fetch fragment status
|
|
||||||
*/
|
|
||||||
async navigate(link, scrollTop = 0, pushState = true) {
|
|
||||||
if (!link) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save current route for history pop event.
|
|
||||||
this.previousURL = location.href;
|
|
||||||
// Manually handle the scroll restoration.
|
|
||||||
history.scrollRestoration = 'manual';
|
|
||||||
|
|
||||||
if (pushState) {
|
|
||||||
history.replaceState({
|
|
||||||
scrollTop: document.scrollingElement.scrollTop,
|
|
||||||
}, '');
|
|
||||||
history.pushState({scrollTop}, '', link);
|
|
||||||
}
|
|
||||||
|
|
||||||
const linkUrl = new URL((link.startsWith('http')) ?
|
|
||||||
link :
|
|
||||||
`${location.origin}${link}`);
|
|
||||||
|
|
||||||
// Fetch new page and switch.
|
|
||||||
const oldHeader = document.querySelector('header');
|
|
||||||
const oldMain = document.querySelector('main');
|
|
||||||
const oldFooter = document.querySelector('footer');
|
|
||||||
const {
|
|
||||||
status,
|
|
||||||
header,
|
|
||||||
main,
|
|
||||||
footer,
|
|
||||||
script: newPageScript,
|
|
||||||
} = await this.fetchFragment(link);
|
|
||||||
|
|
||||||
if (status === 200) {
|
|
||||||
// Replace elements.
|
|
||||||
oldHeader?.parentNode?.replaceChild(header, oldHeader);
|
|
||||||
oldMain?.parentNode?.replaceChild(main, oldMain);
|
|
||||||
oldFooter?.parentNode?.replaceChild(footer, oldFooter);
|
|
||||||
const newScriptFilename = newPageScript?.src.split('/').slice(-1)[0];
|
|
||||||
app.loader.show();
|
|
||||||
try {
|
|
||||||
await app.page?.clear?.();
|
|
||||||
// String manipulation from static analysis.
|
|
||||||
const script = await import(
|
|
||||||
`../js/${newScriptFilename.replace('.js', '')}.js`);
|
|
||||||
// Enable current page re-navigation and avoid double page init.
|
|
||||||
// Check only on url.origin+url.pathname to allow
|
|
||||||
// re-navigation on same page with different url parameters.
|
|
||||||
const prevLinkUrl = new URL(this.previousURL);
|
|
||||||
if (!(app.page instanceof script.default) ||
|
|
||||||
(linkUrl.origin + linkUrl.pathname ===
|
|
||||||
prevLinkUrl.origin + prevLinkUrl.pathname)) {
|
|
||||||
// eslint-disable-next-line new-cap
|
|
||||||
app.page = new script.default();
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
console.error(ex);
|
|
||||||
} finally {
|
|
||||||
app.loader.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
document.scrollingElement.scrollTop = scrollTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set to auto in case user hits page refresh.
|
|
||||||
history.scrollRestoration = 'auto';
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch page and get main element;
|
|
||||||
* @param {string} link
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
async fetchFragment(link) {
|
|
||||||
const url = (link.startsWith('http')) ? link : `${location.origin}${link}`;
|
|
||||||
link = (new URL(url)).pathname;
|
|
||||||
|
|
||||||
const response = await fetch(link);
|
|
||||||
const template = document.createElement('template');
|
|
||||||
template.innerHTML = await response.text();
|
|
||||||
return {
|
|
||||||
status: response.status,
|
|
||||||
header: template.content.querySelector('header')?.cloneNode(true),
|
|
||||||
main: template.content.querySelector('main')?.cloneNode(true),
|
|
||||||
footer: template.content.querySelector('footer')?.cloneNode(true),
|
|
||||||
script: template.content.querySelector('#page-script')?.cloneNode(true),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Router;
|
|
|
@ -1,4 +0,0 @@
|
||||||
import {precacheAndRoute} from 'workbox-precaching';
|
|
||||||
|
|
||||||
precacheAndRoute(self.__WB_MANIFEST);
|
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<div id="welcome-message">Benvenuti a <span class="bold">Pianello Val Tidone</span></div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div id="route-cards">
|
|
||||||
<div class="route-card">
|
|
||||||
<div class="route-card-left">
|
|
||||||
<img src="../../static/images/app-bar-logo.png">
|
|
||||||
</div>
|
|
||||||
<div class="route-card-center">
|
|
||||||
<div class="name">
|
|
||||||
<div>Percorsi</div>
|
|
||||||
<div class="bold">naturalistici</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="route-card-right"></div>
|
|
||||||
</div>
|
|
||||||
<div class="route-card">
|
|
||||||
<div class="route-card-left">
|
|
||||||
<img src="../../static/images/app-bar-logo.png">
|
|
||||||
</div>
|
|
||||||
<div class="route-card-center">
|
|
||||||
<div class="name">
|
|
||||||
<div>Percorsi</div>
|
|
||||||
<div class="bold">storici</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="route-card-right"></div>
|
|
||||||
</div>
|
|
||||||
<div class="route-card">
|
|
||||||
<div class="route-card-left">
|
|
||||||
<img src="../../static/images/app-bar-logo.png">
|
|
||||||
</div>
|
|
||||||
<div class="route-card-center">
|
|
||||||
<div class="name">
|
|
||||||
<div>Percorsi</div>
|
|
||||||
<div class="bold">tradizionalistici</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="route-card-right"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<bottom-app-bar></bottom-app-bar>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script id="page-script" src="../js/home-page.js" type="module"></script>
|
|
||||||
</body>
|
|
Loading…
Reference in a new issue