mirror of
https://codeberg.org/vlw/victorwesterlund.com.git
synced 2025-09-14 11:33:41 +02:00
dev21w36c
This commit is contained in:
parent
62cdb7aae5
commit
821ed27776
10 changed files with 123 additions and 67 deletions
|
@ -6,11 +6,13 @@ body {
|
||||||
|
|
||||||
body main .screen {
|
body main .screen {
|
||||||
transition: var(--transition) transform;
|
transition: var(--transition) transform;
|
||||||
|
transition-delay: calc(var(--transition) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
body.modalActive main .screen {
|
body .modal.active ~ main .screen {
|
||||||
transition: 300ms;
|
transition: var(--transition);
|
||||||
transform: scale(.9,.95);
|
transition-delay: 1ms;
|
||||||
|
transform: scale(.95);
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
@ -31,11 +33,6 @@ 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);
|
||||||
|
|
|
@ -91,6 +91,10 @@ main.active {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.dark .screen.dark {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
.screen .content {
|
.screen .content {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: calc(var(--padding) * 1.5);
|
padding: calc(var(--padding) * 1.5);
|
||||||
|
|
|
@ -5,10 +5,6 @@ class Component {
|
||||||
constructor(tag) {
|
constructor(tag) {
|
||||||
this.element = document.createElement(tag); // Root element
|
this.element = document.createElement(tag); // Root element
|
||||||
}
|
}
|
||||||
|
|
||||||
getElement() {
|
|
||||||
return this.element;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⬇ UI Components ⬇
|
// ⬇ UI Components ⬇
|
||||||
|
|
|
@ -38,6 +38,20 @@ class Debug {
|
||||||
card.open();
|
card.open();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invalidCard() {
|
||||||
|
const module = import("./Modals.mjs");
|
||||||
|
const interactions = {
|
||||||
|
hello: () => {
|
||||||
|
console.log("Hello world");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.then(modals => {
|
||||||
|
const card = new modals.Card(interactions);
|
||||||
|
card.openPage("invalid_card");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default window._debug = new Debug();
|
export default window._debug = new Debug();
|
|
@ -17,12 +17,13 @@ class Modal extends Interaction {
|
||||||
super(interactions,element);
|
super(interactions,element);
|
||||||
|
|
||||||
this.element = this.applyTemplate(element);
|
this.element = this.applyTemplate(element);
|
||||||
document.body.appendChild(this.element);
|
this.element.close = () => this.close(); // Bind close to element prototype
|
||||||
|
document.body.insertAdjacentElement("afterbegin",this.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch page html from "assets/pages"
|
// Fetch page html from "assets/pages"
|
||||||
async getPage(page) {
|
async getPage(page) {
|
||||||
const url = `assets/pages/${page}.html`;
|
const url = `assets/pages/${page}`;
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
if(!response.ok) {
|
if(!response.ok) {
|
||||||
throw new Error(`Modal: Failed to fetch page "${page}"`);
|
throw new Error(`Modal: Failed to fetch page "${page}"`);
|
||||||
|
@ -57,18 +58,11 @@ class Modal extends Interaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
document.body.classList.add("modalActive");
|
|
||||||
setTimeout(() => this.element.classList.add("active"),this.transition / 2);
|
setTimeout(() => this.element.classList.add("active"),this.transition / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the modal and remove it from the DOM
|
// Close the modal and remove it from the DOM
|
||||||
close() {
|
close() {
|
||||||
const activeModals = document.getElementsByClassName("modal");
|
|
||||||
if(!activeModals || activeModals.length === 1) {
|
|
||||||
// Remove active effects if all modals have been closed
|
|
||||||
setTimeout(() => document.body.classList.remove("modalActive"),this.transition / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.element.classList.remove("active");
|
this.element.classList.remove("active");
|
||||||
setTimeout(() => destroy(this.element),this.transition + 1); // Wait for transition
|
setTimeout(() => destroy(this.element),this.transition + 1); // Wait for transition
|
||||||
}
|
}
|
||||||
|
@ -90,10 +84,27 @@ export class Card extends Modal {
|
||||||
text: "close",
|
text: "close",
|
||||||
action: "close"
|
action: "close"
|
||||||
});
|
});
|
||||||
const closeButtonElement = closeButton.getElement();
|
|
||||||
|
|
||||||
this.bind(closeButtonElement);
|
this.bind(closeButton.element);
|
||||||
this.inner.appendChild(closeButtonElement);
|
this.inner.appendChild(closeButton.element);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(message) {
|
||||||
|
const oops = document.createElement("p");
|
||||||
|
const infoButton = new Button({
|
||||||
|
text: "more info"
|
||||||
|
});
|
||||||
|
|
||||||
|
oops.classList.add("error");
|
||||||
|
oops.innerText = "🤯\nSomething went wrong";
|
||||||
|
|
||||||
|
infoButton.element.addEventListener("click",() => {
|
||||||
|
oops.innerText = "📋\n" + message;
|
||||||
|
destroy(infoButton.element);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.insertElement(infoButton.element);
|
||||||
|
this.insertElement(oops);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open page from "assets/pages"
|
// Open page from "assets/pages"
|
||||||
|
@ -107,11 +118,9 @@ export class Card extends Modal {
|
||||||
// Fetch the requested page
|
// Fetch the requested page
|
||||||
this.getPage(page).then(html => {
|
this.getPage(page).then(html => {
|
||||||
this.insertHTML(html);
|
this.insertHTML(html);
|
||||||
}).catch(error => {
|
this.bindAll(this.inner);
|
||||||
const element = document.createElement("p");
|
})
|
||||||
element.classList.add("error");
|
.catch(error => this.error(error))
|
||||||
element.innerText = "🤯\nSomething went wrong";
|
.finally(() => destroy(spinner));
|
||||||
this.insertElement(element);
|
|
||||||
}).finally(() => destroy(spinner));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,17 +17,31 @@ export default class Interaction extends Logging {
|
||||||
this.interactions = interactions;
|
this.interactions = interactions;
|
||||||
this.attribute = "data-action"; // Target elements with this attribute
|
this.attribute = "data-action"; // Target elements with this attribute
|
||||||
|
|
||||||
// Bind listeners to the target attribute within the provided scope
|
this.bindAll(scope);
|
||||||
const elements = scope.querySelectorAll(`[${this.attribute}]`);
|
}
|
||||||
|
|
||||||
|
// Bind event listeners to this element
|
||||||
|
bind(element) {
|
||||||
|
if(element.hasAttribute("data-bound") || !element.hasAttribute(this.attribute)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
element.addEventListener("click",event => this.pointerEvent(event));
|
||||||
|
element.setAttribute("data-bound","");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all elements with the target attribute in scope
|
||||||
|
getAll(scope) {
|
||||||
|
return scope.querySelectorAll(`[${this.attribute}]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind listeners to all attributed elements within scope
|
||||||
|
bindAll(scope) {
|
||||||
|
const elements = this.getAll(scope);
|
||||||
for(const element of elements) {
|
for(const element of elements) {
|
||||||
this.bind(element);
|
this.bind(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bind(element) {
|
|
||||||
element.addEventListener("click",event => this.pointerEvent(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the page theme color (and the theme-color meta tag)
|
// Set the page theme color (and the theme-color meta tag)
|
||||||
setThemeColor(color) {
|
setThemeColor(color) {
|
||||||
const meta = document.head.querySelector("meta[name='theme-color']");
|
const meta = document.head.querySelector("meta[name='theme-color']");
|
||||||
|
|
|
@ -23,14 +23,19 @@ const interactions = {
|
||||||
openContactCard: () => {
|
openContactCard: () => {
|
||||||
const module = import("./modules/Modals.mjs");
|
const module = import("./modules/Modals.mjs");
|
||||||
const interactions = {
|
const interactions = {
|
||||||
hello: () => {
|
getContact: (event) => {
|
||||||
console.log("Hello world");
|
const service = event.target.getAttribute("data-value");
|
||||||
|
module.then(modals => {
|
||||||
|
event.target.closest(".modal").close();
|
||||||
|
const card = new modals.Card(interactions);
|
||||||
|
card.openPage(service);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.then(modals => {
|
module.then(modals => {
|
||||||
const card = new modals.Card(interactions);
|
const card = new modals.Card(interactions);
|
||||||
card.openPage("contact_card");
|
card.openPage("contact");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,4 +58,5 @@ function updateTheme() {
|
||||||
|
|
||||||
// Set the current page theme, and listen for changes
|
// Set the current page theme, and listen for changes
|
||||||
theme.addEventListener("change",updateTheme);
|
theme.addEventListener("change",updateTheme);
|
||||||
updateTheme();
|
updateTheme();
|
||||||
|
window._debug.openContactsModal();
|
40
public/assets/pages/contact.html
Normal file
40
public/assets/pages/contact.html
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<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);
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: var(--padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact .item * {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact .item img {
|
||||||
|
height: 70%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="contact">
|
||||||
|
<div class="item center" data-action="getContact" data-value="contact_signal">
|
||||||
|
<img src="assets/img/icons/signal.svg"/>
|
||||||
|
<p>Signal</p>
|
||||||
|
</div>
|
||||||
|
<div class="item center" data-action="getContact" data-value="contact_email">
|
||||||
|
<p>E-Mail</p>
|
||||||
|
</div>
|
||||||
|
<div class="item center" data-action="getContact">
|
||||||
|
<p>
|
||||||
|
</div>
|
||||||
|
<div class="item center" data-action="getContact">
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,27 +0,0 @@
|
||||||
<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
public/assets/pages/contact_signal.html
Normal file
3
public/assets/pages/contact_signal.html
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
<img src="assets/img/icons/signal.svg"/>
|
Loading…
Add table
Reference in a new issue