Compare commits

...

4 commits

Author SHA1 Message Date
704de034b7
feat(wip): start working on seeing if emitting events between component and parent works
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
2025-07-09 07:40:20 +02:00
fef618795d
feat: add custom context handling 2025-07-09 07:39:42 +02:00
950d27b2cf
feat(wip): add custom context menu 2025-07-09 07:38:50 +02:00
7ddc2acb02
feat: add message id as data field of Message component 2025-07-09 07:37:05 +02:00
5 changed files with 107 additions and 4 deletions

38
app.vue
View file

@ -6,9 +6,47 @@
</template>
<script lang="ts" setup>
import { ContextMenu } from '#components';
import { render } from 'vue';
const banner = useState("banner", () => false);
onMounted(() => {
document.removeEventListener("contextmenu", contextMenuHandler);
document.addEventListener("contextmenu", (e) => {
contextMenuHandler(e);
});
document.addEventListener("mousedown", (e) => {
console.log("click");
console.log("target:", e.target);
console.log(e.target instanceof HTMLDivElement);
if (e.target instanceof HTMLDivElement) {
const classes = e.target.classList;
console.log(classes);
if (classes.contains("message-data") || classes.contains("message-metadata") || classes.contains("message-text")) {
const closest = e.target.closest("div.message") as HTMLDivElement;
console.log("message ID:", closest.dataset.messageId);
}
}
const contextMenu = document.getElementById("context-menu");
if (contextMenu) {
contextMenu.remove();
}
});
});
function contextMenuHandler(e: MouseEvent) {
e.preventDefault();
console.log("Opened context menu");
console.log("Rendering new context menu");
const menuContainer = document.createElement("div");
menuContainer.id = "context-menu";
document.body.appendChild(menuContainer);
const contextMenu = h(ContextMenu, { cursorX: e.clientX, cursorY: e.clientY });
render(contextMenu, menuContainer);
console.log("Rendered");
}
</script>
<style>

View file

@ -0,0 +1,52 @@
<template>
<div v-for="item of menuItems" class="context-menu-item">
{{ item.name }}
</div>
</template>
<script lang="ts" setup>
const props = defineProps<{ cursorX: number, cursorY: number }>();
const menuItems = [
{ name: "Edit", callback: editMessage },
{ name: "Reply", callback: replyMessage }
];
onMounted(() => {
const contextMenu = document.getElementById("context-menu");
if (contextMenu) {
contextMenu.style.left = props.cursorX.toString() + "px";
contextMenu.style.top = props.cursorY.toString() + "px";
}
});
function editMessage() {
//
}
function replyMessage(id: string) {
//
}
</script>
<style>
#context-menu {
position: absolute;
display: flex;
flex-direction: column;
width: 10dvw;
border: .15rem solid cyan;
background-color: var(--background-color);
text-align: center;
}
.context-menu-item:hover {
background-color: rgb(50, 50, 50);
}
</style>

View file

@ -1,5 +1,5 @@
<template>
<div v-if="props.type == 'normal'" :id="props.last ? 'last-message' : undefined" class="message normal-message">
<div @click="emitId" v-if="props.type == 'normal'" :id="props.last ? 'last-message' : undefined" class="message normal-message" :data-message-id="props.messageId">
<div class="left-column">
<img v-if="props.img" class="message-author-avatar" :src="props.img" :alt="username" />
<Icon v-else name="lucide:user" class="message-author-avatar" />
@ -16,7 +16,7 @@
<div class="message-text" v-html="sanitized" tabindex="0"></div>
</div>
</div>
<div v-else ref="messageElement" :id="props.last ? 'last-message' : undefined" class="message grouped-message" :class="{ 'message-margin-bottom': props.marginBottom }">
<div v-else ref="messageElement" :id="props.last ? 'last-message' : undefined" class="message grouped-message" :class="{ 'message-margin-bottom': props.marginBottom }" :data-message-id="props.messageId">
<div class="left-column">
<span :class="{ 'invisible': dateHidden }" class="message-date side-message-date" :title="date.toString()">
{{ date.toLocaleTimeString(undefined, { timeStyle: "short" }) }}
@ -41,7 +41,8 @@ const props = defineProps<{
format: "12" | "24",
type: "normal" | "grouped",
marginBottom: boolean,
last: boolean
last: boolean,
messageId: string
}>();
const messageElement = ref<HTMLDivElement>();
@ -74,6 +75,11 @@ onMounted(async () => {
// showHover.value = !showHover.value;
//}
const nuxtApp = useNuxtApp();
function emitId() {
// nuxtApp.callHook()
}
</script>
<style scoped>

View file

@ -5,7 +5,7 @@
:text="message.message" :timestamp="messageTimestamps[message.uuid]" :img="message.user.avatar"
format="12" :type="messagesType[message.uuid]"
:margin-bottom="(messages[i + 1] && messagesType[messages[i + 1].uuid] == 'normal') ?? false"
:last="i == messages.length - 1" />
:last="i == messages.length - 1" :message-id="message.uuid" />
</div>
<div id="message-box" class="rounded-corners">
<form id="message-form" @submit="sendMessage">

7
types/hooks.ts Normal file
View file

@ -0,0 +1,7 @@
import type { RuntimeNuxtHooks } from 'nuxt/schema';
declare module "nuxt/schema" {
interface RuntimeNuxtHooks {
"app:message:right-clicked": (payload: { messageId: string }) => void
}
}