Skip to content

Commit 4168987

Browse files
authored
メッセージにホバーしたときに色が変わるようにし、右クリックでドロップダウンメニューを出せるようにした (#3)
1 parent a119fcb commit 4168987

File tree

2 files changed

+86
-33
lines changed

2 files changed

+86
-33
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script lang="ts">
2+
import type { Snippet } from "svelte";
3+
4+
const {
5+
children,
6+
x,
7+
y,
8+
visible,
9+
}: { children: Snippet; x: number; y: number; visible: boolean } = $props();
10+
</script>
11+
12+
{#if visible}
13+
<div style={`top: ${y}px; left: ${x}px;}`} class="absolute z-10">
14+
{@render children()}
15+
</div>
16+
{/if}

packages/client/src/components/chat/MessageList.svelte

Lines changed: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import type { Doc } from "@packages/convex/src/convex/_generated/dataModel";
44
import { useQuery } from "convex-svelte";
55
import { onMount } from "svelte";
6+
import MessageDropdown from "./MessageDropdown.svelte";
67
78
interface Props {
89
channelId: Id<"channels">;
@@ -43,46 +44,82 @@
4344
onMount(() => {
4445
scrollToBottom();
4546
});
47+
48+
let clientX = $state(0);
49+
let clientY = $state(0);
50+
let visibleDropdown = $state<Id<"messages"> | null>(null);
51+
document.addEventListener("click", () => {
52+
visibleDropdown = null;
53+
});
4654
</script>
4755

4856
<div bind:this={messagesContainer} class="flex-1 space-y-2 overflow-y-auto p-4">
4957
{#if messages.data}
5058
{#each messages.data as message (message._id)}
51-
{#if message.parentId && messages.data.find((m) => m._id === message.parentId)}
52-
<div class="flex items-center gap-2">
53-
<span class="text-base-content/60 text-xs">返信</span>
54-
<span class="text-primary font-semibold"
55-
>{messagesById.get(message.parentId)?.author}</span
56-
>
57-
<span class="text-base-content/60 text-xs">
58-
{messagesById.get(message.parentId)?.content}
59-
</span>
60-
</div>
61-
{/if}
62-
<div class="group relative flex flex-col">
63-
<div class="flex items-baseline gap-2">
64-
<span class="text-primary font-semibold">{message.author}</span>
65-
<span class="text-base-content/60 text-xs">
66-
{formatTime(message.createdAt)}
67-
</span>
68-
</div>
69-
<div class="text-base-content ml-0 whitespace-pre-wrap">
70-
{message.content}
71-
</div>
72-
<div
73-
class="bg-base-100 absolute top-0 right-4 -translate-y-1/2 rounded-md border opacity-0 group-hover:opacity-100"
59+
{#snippet dropdownContent()}
60+
<ul
61+
class="menu dropdown-content bg-base-100 absolute z-[1] w-40 rounded-md border p-2 shadow"
7462
>
75-
<div class="dropdown dropdown-end">
76-
<button class="btn btn-ghost btn-sm p-2" tabindex="0"> ⋮ </button>
77-
<ul
78-
tabindex="0"
79-
role="menu"
80-
class="menu dropdown-content bg-base-100 z-[1] w-40 rounded-md border p-2 shadow"
63+
<li>
64+
<button onclick={() => (replyingTo = message)}>返信</button>
65+
</li>
66+
</ul>
67+
{/snippet}
68+
<MessageDropdown
69+
x={clientX}
70+
y={clientY}
71+
visible={visibleDropdown === message._id}
72+
>
73+
{@render dropdownContent()}
74+
</MessageDropdown>
75+
76+
<div
77+
role="button"
78+
tabindex="0"
79+
class="p-1 hover:bg-sky-900"
80+
oncontextmenu={(e) => {
81+
e.preventDefault();
82+
clientX = e.clientX;
83+
clientY = e.clientY;
84+
visibleDropdown = message._id;
85+
}}
86+
>
87+
{#if message.parentId && messages.data.find((m) => m._id === message.parentId)}
88+
<div class="flex items-center gap-2">
89+
<span class="text-base-content/60 text-xs">返信</span>
90+
<span class="text-primary font-semibold"
91+
>{messagesById.get(message.parentId)?.author}</span
8192
>
82-
<li>
83-
<button onclick={() => (replyingTo = message)}>返信</button>
84-
</li>
85-
</ul>
93+
<span class="text-base-content/60 text-xs">
94+
{messagesById.get(message.parentId)?.content}
95+
</span>
96+
</div>
97+
{/if}
98+
<div class="group relative flex flex-col">
99+
<div class="flex items-baseline gap-2">
100+
<span class="text-primary font-semibold">{message.author}</span>
101+
<span class="text-base-content/60 text-xs">
102+
{formatTime(message.createdAt)}
103+
</span>
104+
</div>
105+
<div class="text-base-content ml-0 whitespace-pre-wrap">
106+
{message.content}
107+
</div>
108+
<div
109+
class="bg-base-100 absolute top-4 right-4 -translate-y-1/2 rounded-md border opacity-0 group-hover:opacity-100"
110+
>
111+
<div class="dropdown dropdown-end">
112+
<button class="btn btn-ghost btn-sm p-2" tabindex="0"> ⋮ </button>
113+
<ul
114+
tabindex="0"
115+
role="menu"
116+
class="menu dropdown-content bg-base-100 z-[1] w-40 rounded-md border p-2 shadow"
117+
>
118+
<li>
119+
<button onclick={() => (replyingTo = message)}>返信</button>
120+
</li>
121+
</ul>
122+
</div>
86123
</div>
87124
</div>
88125
</div>

0 commit comments

Comments
 (0)