mirror of
https://codeberg.org/vlw/victorwesterlund.com.git
synced 2025-09-14 11:33:41 +02:00
dev21w36b
This commit is contained in:
parent
ff474e1786
commit
62cdb7aae5
8 changed files with 176 additions and 24 deletions
|
@ -23,7 +23,7 @@ body.modalActive main .screen {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -31,13 +31,46 @@ body.modalActive main .screen {
|
||||||
padding: var(--padding);
|
padding: var(--padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal + .modal {
|
||||||
|
transition: var(--transition) backdrop-filter;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
.modal.active {
|
.modal.active {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
background-color: rgba(var(--comp-inverted),.4);
|
background-color: rgba(var(--comp-inverted),.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal .button {
|
.modal .button {
|
||||||
background-color: rgba(var(--comp-inverted),.3);
|
background-color: rgba(var(--comp-inverted),.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .button p {
|
||||||
|
color: var(--color-inverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner.logo {
|
||||||
|
--size: 10vw;
|
||||||
|
--anim-speed: 1s;
|
||||||
|
align-self: center;
|
||||||
|
margin-top: var(--padding);
|
||||||
|
margin-left: calc((var(--size) / 2) * -1);
|
||||||
|
animation: logoSpinner var(--anim-speed) infinite alternate linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error:first-line {
|
||||||
|
font-size: 15vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes logoSpinner {
|
||||||
|
to {
|
||||||
|
opacity: .1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -- Cards -- */
|
/* -- Cards -- */
|
||||||
|
@ -45,18 +78,25 @@ body.modalActive main .screen {
|
||||||
.modal.card .inner {
|
.modal.card .inner {
|
||||||
transition: var(--transition) transform, var(--transition) opacity;
|
transition: var(--transition) transform, var(--transition) opacity;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: var(--color-contrast);
|
background-color: var(--color-background);
|
||||||
width: calc(100vw - var(--padding));
|
width: calc(100vw - var(--padding));
|
||||||
max-width: 600px;
|
max-width: 500px;
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: var(--padding);
|
padding: var(--padding);
|
||||||
transform: translateY(1vh);
|
transform: translateY(1vh);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal.card.active .inner {
|
.modal.card.active .inner {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal.card .button[data-action="close"] {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
|
@ -8,15 +8,36 @@ class Debug {
|
||||||
list() {
|
list() {
|
||||||
const functions = [
|
const functions = [
|
||||||
"list",
|
"list",
|
||||||
|
"toggleMenu",
|
||||||
"openContactsModal"
|
"openContactsModal"
|
||||||
];
|
];
|
||||||
console.log("Available functions:",functions.map(f => `window._debug.${f}();`));
|
console.log("Available functions:",functions.map(f => `window._debug.${f}();`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleMenu() {
|
||||||
|
document.getElementsByClassName("hamburger")[0].click();
|
||||||
|
}
|
||||||
|
|
||||||
openContactsModal() {
|
openContactsModal() {
|
||||||
document.getElementsByClassName("hamburger")[0].click();
|
document.getElementsByClassName("hamburger")[0].click();
|
||||||
document.querySelector("div[data-action='openContactCard']").click();
|
document.querySelector("div[data-action='openContactCard']").click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
demoCard() {
|
||||||
|
const module = import("./Modals.mjs");
|
||||||
|
const interactions = {
|
||||||
|
hello: () => {
|
||||||
|
console.log("Hello world");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.then(modals => {
|
||||||
|
const card = new modals.Card(interactions);
|
||||||
|
card.inner.style.height = "80vh";
|
||||||
|
card.inner.insertAdjacentHTML("afterbegin","<p>Hello world</p>");
|
||||||
|
card.open();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default window._debug = new Debug();
|
export default window._debug = new Debug();
|
|
@ -17,25 +17,25 @@ class Modal extends Interaction {
|
||||||
super(interactions,element);
|
super(interactions,element);
|
||||||
|
|
||||||
this.element = this.applyTemplate(element);
|
this.element = this.applyTemplate(element);
|
||||||
this.importStyleSheet();
|
|
||||||
document.body.appendChild(this.element);
|
document.body.appendChild(this.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import the companion CSS rules
|
// Fetch page html from "assets/pages"
|
||||||
importStyleSheet() {
|
async getPage(page) {
|
||||||
let sheet = "assets/css/modal.css";
|
const url = `assets/pages/${page}.html`;
|
||||||
const element = document.createElement("link");
|
const response = await fetch(url);
|
||||||
|
if(!response.ok) {
|
||||||
// Exit if the stylesheet has already been imported
|
throw new Error(`Modal: Failed to fetch page "${page}"`);
|
||||||
if(document.head.querySelector("link[data-async-modalcss]")) {
|
}
|
||||||
return false;
|
return response.text();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import the stylesheet with a link tag
|
insertHTML(element) {
|
||||||
element.setAttribute("rel","stylesheet");
|
this.inner.insertAdjacentHTML("afterbegin",element);
|
||||||
element.setAttribute("href",sheet);
|
}
|
||||||
element.setAttribute("data-async-modalcss","");
|
|
||||||
document.head.appendChild(element);
|
insertElement(element) {
|
||||||
|
this.inner.insertAdjacentElement("afterbegin",element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply a modal template to the provided element
|
// Apply a modal template to the provided element
|
||||||
|
@ -83,10 +83,6 @@ export class Card extends Modal {
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
setContent(content) {
|
|
||||||
this.element.insertAdjacentHTML("beforeend",content);
|
|
||||||
}
|
|
||||||
|
|
||||||
init(slim) {
|
init(slim) {
|
||||||
this.element.classList.add("card");
|
this.element.classList.add("card");
|
||||||
this.element.classList.add("center");
|
this.element.classList.add("center");
|
||||||
|
@ -100,7 +96,22 @@ export class Card extends Modal {
|
||||||
this.inner.appendChild(closeButtonElement);
|
this.inner.appendChild(closeButtonElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open page from "assets/pages"
|
||||||
openPage(page) {
|
openPage(page) {
|
||||||
|
// Show a spinner while fetching
|
||||||
|
const spinner = document.createElement("div");
|
||||||
|
spinner.classList = "logo spinner";
|
||||||
|
this.insertElement(spinner);
|
||||||
this.open();
|
this.open();
|
||||||
|
|
||||||
|
// Fetch the requested page
|
||||||
|
this.getPage(page).then(html => {
|
||||||
|
this.insertHTML(html);
|
||||||
|
}).catch(error => {
|
||||||
|
const element = document.createElement("p");
|
||||||
|
element.classList.add("error");
|
||||||
|
element.innerText = "🤯\nSomething went wrong";
|
||||||
|
this.insertElement(element);
|
||||||
|
}).finally(() => destroy(spinner));
|
||||||
}
|
}
|
||||||
}
|
}
|
42
public/assets/js/modules/Preload.mjs
Normal file
42
public/assets/js/modules/Preload.mjs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright © Victor Westerlund - No libraries! 😲
|
||||||
|
|
||||||
|
// Load assets for later use on this page.
|
||||||
|
// This implements a hybrid of the link types "preload" and "prefetch"
|
||||||
|
export default class Preload {
|
||||||
|
constructor(assets) {
|
||||||
|
this.scripts = [];
|
||||||
|
this.stylesheets = [];
|
||||||
|
|
||||||
|
// Get the type of asset from the file extension
|
||||||
|
assets.forEach(asset => {
|
||||||
|
const components = asset.split(".");
|
||||||
|
const extension = components[components.length - 1];
|
||||||
|
switch(extension) {
|
||||||
|
case "mjs":
|
||||||
|
this.scripts.push(asset);
|
||||||
|
break;
|
||||||
|
case "css":
|
||||||
|
this.stylesheets.push(asset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Append tags when DOM is ready
|
||||||
|
window.addEventListener("DOMContentLoaded",() => this.import());
|
||||||
|
}
|
||||||
|
|
||||||
|
import() {
|
||||||
|
this.scripts.forEach(script => {
|
||||||
|
const element = document.createElement("script");
|
||||||
|
element.setAttribute("type","module");
|
||||||
|
element.src = "assets/js/" + script;
|
||||||
|
document.body.appendChild(element);
|
||||||
|
});
|
||||||
|
this.stylesheets.forEach(sheet => {
|
||||||
|
const element = document.createElement("link");
|
||||||
|
element.setAttribute("rel","stylesheet");
|
||||||
|
element.href = "assets/css/" + sheet;
|
||||||
|
document.head.appendChild(element);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,15 @@
|
||||||
// Copyright © Victor Westerlund - No libraries! 😲
|
// Copyright © Victor Westerlund - No libraries! 😲
|
||||||
|
import { default as Preload } from "./modules/Preload.mjs";
|
||||||
import { default as Interaction } from "./modules/UI.mjs";
|
import { default as Interaction } from "./modules/UI.mjs";
|
||||||
import { default as Debug } from "./modules/Debugging.mjs";
|
import { default as Debug } from "./modules/Debugging.mjs";
|
||||||
|
|
||||||
|
// Load these assets when the DOM is ready (not needed right away)
|
||||||
|
const preload = new Preload([
|
||||||
|
"modules/Modals.mjs",
|
||||||
|
"modules/Components.mjs",
|
||||||
|
"modal.css"
|
||||||
|
]);
|
||||||
|
|
||||||
// All default interactions
|
// All default interactions
|
||||||
const interactions = {
|
const interactions = {
|
||||||
toggleMenu: () => {
|
toggleMenu: () => {
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<style>
|
||||||
|
.contact {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: repeat(2, 1fr);
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: var(--padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact .item {
|
||||||
|
height: 140px;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
background-color: rgba(var(--comp-inverted),.05);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="contact">
|
||||||
|
<div class="item" data-action="hello" data-value="signal">
|
||||||
|
<p>Signal</p>
|
||||||
|
</div>
|
||||||
|
<div class="item" data-action="getContact" data-value="email">
|
||||||
|
<p>E-Mail</p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
<p>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -3,12 +3,15 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
<title>Victor Westerlund</title>
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="description" content="Full-stack web developer from Stockholm, Sweden.">
|
<meta name="description" content="Full-stack web developer from Stockholm, Sweden.">
|
||||||
<meta name="theme-color" content="">
|
<meta name="theme-color" content="red">
|
||||||
|
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#212121">
|
||||||
|
|
||||||
<link href="assets/img/favicon.png" rel="icon">
|
<link href="assets/img/favicon.png" rel="icon">
|
||||||
<link href="assets/css/style.css" rel="stylesheet">
|
<link href="assets/css/style.css" rel="stylesheet">
|
||||||
<title>Victor Westerlund</title>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
|
|
Loading…
Add table
Reference in a new issue