This commit is contained in:
Daniel Ledda
2024-11-01 18:50:26 +01:00
parent 7539e6ed48
commit f8a0cd50f8
7 changed files with 143 additions and 203 deletions

View File

@@ -1,32 +1,78 @@
import { watchEffect, watch, onMounted, type CSSProperties, defineComponent, ref } from "vue";
import { nextTick, inject, provide, watch, type InjectionKey, onBeforeUnmount, watchEffect, onMounted, type Ref, type CSSProperties, defineComponent, ref } from "vue";
import { h as djh } from "@/util.ts";
const carrierStyle = {
opacity: "0",
display: "block",
pointerEvents: "none",
backgroundColor: "black",
border: "white solid 1px",
color: "white",
padding: "10px",
position: "absolute",
zIndex: "1",
overflow: "hidden",
height: "0",
width: "0",
transition: "opacity 200ms, height 200ms, width 200ms",
} satisfies CSSProperties;
type TooltipContext = {
show: (newText: string, x: number, y: number) => void,
hide: () => void,
};
const textCarrierStyle = {
fontSize: '16px',
fontFamily: "Roboto, serif",
display: "block",
overflow: "hidden",
} satisfies CSSProperties;
const tooltipContext = Symbol('tooltip') as InjectionKey<TooltipContext>;
const defaultWidth = 350;
export function setupTooltip(options: { carrier: Ref<HTMLElement | null> }) {
const { carrier } = options;
watchEffect(() => {
if (carrier.value) {
carrier.value.classList.add('tooltip-carrier');
carrier.value.appendChild(djh('div', { className: 'text-carrier' }));
}
});
const listener = (pos: { x: number, y: number }) => {
if (carrier.value && getComputedStyle(carrier.value).opacity !== "0") {
if (pos.x + 15 + carrier.value.clientWidth <= document.body.scrollWidth) {
carrier.value.style.left = (pos.x + 15) + "px";
} else {
carrier.value.style.left = (document.body.scrollWidth - carrier.value.clientWidth - 5) + "px";
}
if (pos.y + carrier.value.clientHeight <= document.body.scrollHeight) {
carrier.value.style.top = pos.y + "px";
} else {
carrier.value.style.top = (document.body.scrollHeight - carrier.value.clientHeight - 5) + "px";
}
}
};
const active = ref(false);
watch(active, async () => {
const tooltipCarrier = carrier.value;
if (tooltipCarrier) {
tooltipCarrier.style.opacity = active.value ? '1' : '0';
tooltipCarrier.style.width = active.value ? '350px' : '0';
await nextTick();
if (active.value) {
if (tooltipCarrier.firstChild?.nodeType === Node.ELEMENT_NODE) {
const computedHeight = getComputedStyle(tooltipCarrier.firstChild as Element).height;
tooltipCarrier.style.height = computedHeight;
}
} else {
tooltipCarrier.style.height = '0';
}
}
});
onMounted(() => document.addEventListener("mousemove", (e) => listener({ x: e.pageX, y: e.pageY })));
onBeforeUnmount(() => document.removeEventListener("mousemove", (e) => listener({ x: e.pageX, y: e.pageY })));
const ctx: TooltipContext = {
show(tooltip, x, y) {
if (carrier.value) {
carrier.value.firstChild!.textContent = tooltip;
}
active.value = true;
listener({ x, y });
},
hide() {
active.value = false;
},
};
provide(tooltipContext, ctx);
}
export default defineComponent({
name: "dj-sexy-tooltip",
name: "dj-tooltip",
props: {
tooltip: {
type: String,
@@ -34,47 +80,15 @@ export default defineComponent({
},
},
setup(props, { slots, attrs }) {
const active = ref(false);
const carrier = ref<HTMLElement | null>(null);
const textCarrier = ref<HTMLElement | null>(null);
onMounted(() => {
document.addEventListener("mousemove", (event) => {
const pos = { x: event.pageX, y: event.pageY };
if (carrier.value && getComputedStyle(carrier.value).opacity !== "0") {
if (pos.x + 15 + carrier.value.clientWidth <= document.body.scrollWidth) {
carrier.value.style.left = (pos.x + 15) + "px";
} else {
carrier.value.style.left = (document.body.scrollWidth - carrier.value.clientWidth - 5) + "px";
}
if (pos.y + carrier.value.clientHeight <= document.body.scrollHeight) {
carrier.value.style.top = pos.y + "px";
} else {
carrier.value.style.top = (document.body.scrollHeight - carrier.value.clientHeight - 5) + "px";
}
}
});
});
watchEffect(() => {
if (carrier.value) {
carrier.value.style.height = active.value ? '16px' : '0';
carrier.value.style.opacity = active.value ? '1' : '0';
carrier.value.style.width = active.value ? '350px' : '0';
}
});
const tooltip = inject(tooltipContext, () => { throw new Error('No tooltip context'); }, true);
return () => <>
<div class="tooltip-container" {...attrs}
onMouseenter={() => { active.value = true; }}
onMouseleave={() => { active.value = false; }}
>
<div class="tooltip-container"
{...attrs}
onMouseenter={(e) => tooltip.show(props.tooltip, e.pageX, e.pageY)}
onMouseleave={() => tooltip.hide()}>
{slots.default && <slots.default />}
</div>
<div style={carrierStyle} ref={carrier}>
<span style={textCarrierStyle}>{props.tooltip}</span>
</div>
</>;
},
});

View File

@@ -1,24 +1,50 @@
import { defineComponent } from "vue";
import { defineComponent, computed, ref, type Ref } from "vue";
import useHead from "@/useHead.ts";
import DJTooltip from "@/DJTooltip.tsx";
import DJTooltip, { setupTooltip } from "@/DJTooltip.tsx";
import DJEmail from "@/DJEmail.tsx";
export default defineComponent({
name: "app-root",
setup() {
useHead({ title: "DJ Ledda's Homepage" });
return () => (
const tooltipCarrier = ref<HTMLDivElement | null>(null);
setupTooltip({ carrier: tooltipCarrier });
const dude1Spinning = ref(false);
const dude2Spinning = ref(false);
function toggleDude(event: MouseEvent, dudeRef: Ref<boolean>) {
const dude = event.target as HTMLImageElement;
if (dudeRef.value) {
dude.addEventListener("animationiteration", function listener() {
dudeRef.value = false;
dude.removeEventListener("animationiteration", listener as EventListenerOrEventListenerObject);
});
} else {
dudeRef.value = true;
}
}
const shaking = computed(() => dude1Spinning.value || dude2Spinning.value);
return () => <>
<div ref={tooltipCarrier} class="tooltip-carrier" />
<div class="supercontainer">
<div class="shakeable">
<div class={{ shakeable: true, shakeMe: shaking.value }}>
<div class="title_name">
<DJTooltip tooltip="I wonder what he's listening to?">
<img src="/home/img/dj.gif" alt="dj legt krasse Mucke auf" class="dude" />
<img src="/home/img/dj.gif" alt="dj legt krasse Mucke auf"
class={{ dude: true, spinMe: dude1Spinning.value }}
onClick={ (e) => toggleDude(e, dude1Spinning)} />
</DJTooltip>
<DJTooltip tooltip="Easily the coolest guy out there.">
<span>DJ Ledda</span>
</DJTooltip>
<DJTooltip tooltip="I once heard this guy played at revs.">
<img src="/home/img/dj.gif" alt="dj laying down some sick beats" class="dude" />
<img src="/home/img/dj.gif" alt="dj laying down some sick beats"
class={{ dude: true, spinMe: dude2Spinning.value }}
onClick={ (e) => toggleDude(e, dude2Spinning) } />
</DJTooltip>
</div>
<div class="main">
@@ -71,6 +97,6 @@ export default defineComponent({
<div id="tooltipCarrier"></div>
</div>
</div>
);
</>;
},
});

View File

@@ -1,4 +1,4 @@
import { createSSRApp } from "vue";
import App from "@/home/App.tsx";
import DJHomeRoot from "@/home/DJHomeRoot.tsx";
createSSRApp(App).mount("#app-root");
createSSRApp(DJHomeRoot).mount("#app-root");

View File

@@ -1,7 +1,7 @@
import { createSSRApp } from "vue";
import App from "@/home/App.tsx";
import DJHomeRoot from "@/home/DJHomeRoot.tsx";
export default function createApp() {
const app = createSSRApp(App);
const app = createSSRApp(DJHomeRoot);
return { app, router: null };
}

View File

@@ -42,6 +42,7 @@ export function css(strs: TemplateStringsArray, ...vals: string[]) {
return sheet;
}
/*
export class DJElement extends HTMLElement {
static styles: CSSStyleSheet;
@@ -57,3 +58,4 @@ export class DJElement extends HTMLElement {
this.root.adoptedStyleSheets = statics.styles ? [statics.styles] : [];
}
}
*/