Skip to content

Commit 0334a04

Browse files
authored
Add new features to chat window (#117)
2 parents cb2024b + 7cf2ce2 commit 0334a04

File tree

8 files changed

+287
-31
lines changed

8 files changed

+287
-31
lines changed

src/lib/docs/ComponentPage.svelte

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<script lang="ts">
2+
import type { Snippet } from "svelte";
3+
import ComponentPageCard from "./ComponentPageCard.svelte";
4+
5+
let {
6+
title,
7+
description,
8+
demoComponent,
9+
features,
10+
props
11+
}: {
12+
title: string;
13+
description?: string;
14+
demoComponent?: Snippet;
15+
features?: Snippet;
16+
props?: Array<{
17+
name: string;
18+
type: string;
19+
default: string;
20+
description: string;
21+
}>;
22+
} = $props();
23+
</script>
24+
25+
<div class="flex flex-col gap-4">
26+
<div class="text-4xl tracking-wider">{title}</div>
27+
{#if description}
28+
<div>{description}</div>
29+
{/if}
30+
{#if demoComponent}
31+
<ComponentPageCard>
32+
{@render demoComponent()}
33+
</ComponentPageCard>
34+
{/if}
35+
{#if props}
36+
<div class="text-2xl tracking-wider mt-12">Props</div>
37+
<ComponentPageCard>
38+
<table class="table table-zebra">
39+
<thead>
40+
<tr>
41+
<th>Option</th>
42+
<th>Type</th>
43+
<th>Default</th>
44+
<th>Description</th>
45+
</tr>
46+
</thead>
47+
<tbody>
48+
{#each props as prop}
49+
<tr>
50+
<td>{prop.name}</td>
51+
<td>{prop.type}</td>
52+
<td>{prop.default}</td>
53+
<td>{prop.description}</td>
54+
</tr>
55+
{/each}
56+
</tbody>
57+
</table>
58+
</ComponentPageCard>
59+
{/if}
60+
{#if features}
61+
<div class="text-2xl tracking-wider mt-12">Features</div>
62+
{@render features()}
63+
{/if}
64+
</div>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<div class="card card-bordered card-compact shadow-lg bg-white">
2+
<div class="card-body">
3+
<slot />
4+
</div>
5+
</div>
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+
let { title, description, children }: { title: string; description?: string; children: Snippet } =
5+
$props();
6+
</script>
7+
8+
<div class="flex flex-col gap-4">
9+
<div class="p-2">{title}</div>
10+
{#if description}
11+
<div>{description}</div>
12+
{/if}
13+
<div class="p-4">
14+
{@render children()}
15+
</div>
16+
</div>

src/lib/docs/ComponentSubHeader.svelte

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
let { title, description }: { title: string; description?: string } = $props();
33
</script>
44

5-
<div class="flex flex-col gap-4 mt-8">
6-
<div class="text-xl font-semibold tracking-wider">{title}</div>
7-
<div>{description}</div>
5+
<div class="flex flex-col gap-4 p-4 rounded bg-gray-200">
6+
<div class="text-xl tracking-wider">{title}</div>
7+
{#if description}
8+
<div>{description}</div>
9+
{/if}
810
</div>

src/lib/modules/chat/ChatWindow.svelte

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,45 @@
1414
import Date from "../utils/date/Date.svelte";
1515
import type { ChatMessage, ChatUser } from "./chat-message";
1616
17-
let { messages, users, meId }: { messages: ChatMessage[]; users: ChatUser[]; meId: string } =
18-
$props();
17+
let {
18+
messages = [],
19+
users = [],
20+
meId,
21+
showAvatars = "always"
22+
}: {
23+
messages: ChatMessage[];
24+
users: ChatUser[];
25+
meId: string;
26+
showAvatars?: "never" | "always" | "change";
27+
} = $props();
28+
29+
const scrollToBottom = (node, dependency) => {
30+
const scroll = () =>
31+
node.scroll({
32+
top: node.scrollHeight,
33+
behavior: "smooth"
34+
});
35+
scroll();
36+
37+
return { update: scroll };
38+
};
1939
</script>
2040

21-
<div class="w-full h-full overflow-hidden overflow-y-auto">
22-
{#each messages as message, i (message.id)}
41+
<div class="w-full h-full overflow-hidden overflow-y-auto" use:scrollToBottom={messages}>
42+
{#each messages as message, i}
2343
<div class="chat {message.senderId === meId ? 'chat-start' : 'chat-end'}">
24-
<div class="chat-image avatar">
25-
<div class="w-10 rounded-box">
26-
{#if messages[i + 1]?.senderId !== message.senderId}
27-
<img
28-
alt="Tailwind CSS chat bubble component"
29-
src={users.find((user) => user.id === message.senderId)?.avatarUrl}
30-
/>
31-
{/if}
44+
{#if showAvatars !== "never"}
45+
<div class="chat-image avatar">
46+
<div class="w-10 rounded-box">
47+
{#if showAvatars === "always" || (showAvatars === "change" && messages[i + 1]?.senderId !== message.senderId)}
48+
<img
49+
alt="Avatar"
50+
src={users?.find((user) => user.id === message.senderId)?.avatarUrl}
51+
/>
52+
{/if}
53+
</div>
3254
</div>
33-
</div>
55+
{/if}
3456
<div class="chat-bubble {message.senderId === meId ? 'chat-start' : 'chat-end'}">
3557
{message.text}
3658
</div>

src/routes/install/+page.svelte

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import ComponentCode from "$lib/docs/ComponentCode.svelte";
33
import ComponentHeader from "$lib/docs/ComponentHeader.svelte";
44
import ComponentSubHeader from "$lib/docs/ComponentSubHeader.svelte";
5+
6+
let selectedTab = "Vanilla";
57
</script>
68

79
<ComponentHeader
@@ -41,21 +43,57 @@
4143
class="tab"
4244
aria-label="Vanilla"
4345
checked
46+
onchange={() => (selectedTab = "Vanilla")}
4447
/>
4548
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
4649
<pre>&lt;script type=&quot;module&quot; src=&quot;https://cdn.skypack.dev/@smallstack/svelte-ui&quot;&gt;&lt;/script&gt;
4750
&lt;sui-table&gt;&lt;/sui-table&gt;
4851
</pre>
4952
</div>
50-
<input type="radio" name="install-wc-tabs" role="tab" class="tab" aria-label="Angular" />
53+
<input
54+
type="radio"
55+
name="install-wc-tabs"
56+
role="tab"
57+
class="tab"
58+
aria-label="Angular"
59+
onchange={() => (selectedTab = "Angular")}
60+
/>
5161
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
52-
<pre>TBD</pre>
62+
{#if selectedTab === "Angular"}
63+
<iframe
64+
src="https://stackblitz.com/edit/sui-message-thread-wc-angular?file=src%2Fmain.ts"
65+
width="100%"
66+
height="600px"
67+
title="Chat Window as a custom element"
68+
></iframe>
69+
{/if}
5370
</div>
54-
<input type="radio" name="install-wc-tabs" role="tab" class="tab" aria-label="React" />
71+
<input
72+
type="radio"
73+
name="install-wc-tabs"
74+
role="tab"
75+
class="tab"
76+
aria-label="React"
77+
onchange={() => (selectedTab = "React")}
78+
/>
5579
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
56-
<pre>TBD</pre>
80+
{#if selectedTab === "React"}
81+
<iframe
82+
src="https://stackblitz.com/edit/sui-message-thread-wc-react?file=src%2FApp.jsx"
83+
width="100%"
84+
height="600px"
85+
title="Chat Window as a custom element"
86+
></iframe>
87+
{/if}
5788
</div>
58-
<input type="radio" name="install-wc-tabs" role="tab" class="tab" aria-label="Vue" />
89+
<input
90+
type="radio"
91+
name="install-wc-tabs"
92+
role="tab"
93+
class="tab"
94+
aria-label="Vue"
95+
onchange={() => (selectedTab = "Vue")}
96+
/>
5997
<div role="tabpanel" class="tab-content bg-base-100 border-base-300 rounded-box p-6">
6098
<pre>TBD</pre>
6199
</div>

src/routes/modules/chat/chat-window/+page.svelte

Lines changed: 108 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<script lang="ts">
22
import { ONE_SECOND } from "$lib/constants/date.constants";
3-
import ComponentHeader from "$lib/docs/ComponentHeader.svelte";
3+
import ComponentPage from "$lib/docs/ComponentPage.svelte";
4+
import ComponentPageCard from "$lib/docs/ComponentPageCard.svelte";
5+
import ComponentPageFeature from "$lib/docs/ComponentPageFeature.svelte";
46
import ChatWindow from "$lib/modules/chat/ChatWindow.svelte";
57
68
const users = [
@@ -14,13 +16,18 @@
1416
name: "Obi-Wan Kenobi",
1517
avatarUrl:
1618
"https://img.icons8.com/external-flat-icons-inmotus-design/40/external-Obi-Wan-star-wars-flat-icons-inmotus-design.png"
19+
},
20+
{
21+
id: "3",
22+
name: "Yoda",
23+
avatarUrl: "https://img.icons8.com/color/40/yoda.png"
1724
}
1825
];
1926
2027
const messages = [
2128
{
2229
id: "1",
23-
text: "It was said that you would, destroy the Sith, not join them.",
30+
text: "It was said that you would destroy the Sith, not join them",
2431
senderId: "2",
2532
createdAt: Date.now() - 1000 * 60 * 9 + ONE_SECOND * 5
2633
},
@@ -48,22 +55,113 @@
4855
senderId: "1",
4956
createdAt: Date.now() - 1000 * 60 * 5 + ONE_SECOND * 10
5057
},
58+
{
59+
id: "6",
60+
text: "Great humor you have, young padawan",
61+
senderId: "3",
62+
createdAt: Date.now() - 1000 * 60 * 4 + ONE_SECOND * 20
63+
}
64+
];
65+
66+
const longMessages = $state([
67+
...messages,
5168
{
5269
id: "6",
5370
text: "Bubba to the bang bang boogie, boobie to the boogie - To the rhythm of the boogie the beat. Now, what you hear is not a test I'm rappin' to the beat - And me, the groove, and my friends are gonna try to move your feet - See, I am Wonder Mike, and I'd like to say hello - To the black, to the white, the red and the brown- The purple and yellow, but first, I gotta - Bang bang, the boogie to the boogie -- Say up jump the boogie to the bang bang boogie -Let's rock, you don't stop - Rock the rhythm that'll make your body rock - Well so far you've heard my voice but I brought two friends along - And the next on the mic is my man Hank - C'mon, Hank, sing that song, check it out - Well, I'm Imp the Dimp, the ladies' pimp - The women fight for my delight - But I'm the grandmaster with the three MCs - That shock the house for the young ladies - And when you come inside, into the front - You do the Freak, Spank, and do the Bump",
5471
senderId: "1",
5572
createdAt: Date.now()
5673
}
74+
]);
75+
76+
const props = [
77+
{
78+
name: "messages",
79+
type: "Array<ChatMessage>",
80+
default: "[]",
81+
description: "Array of messages to display"
82+
},
83+
{
84+
name: "users",
85+
type: "Array<ChatUser>",
86+
default: "[]",
87+
description: "Array of users to display avatars"
88+
},
89+
{
90+
name: "meId",
91+
type: "String",
92+
default: "",
93+
description: "Id of the user who is currently logged in and therefor shown on the left"
94+
},
95+
{
96+
name: "showAvatars",
97+
type: '"never" | "always" | "change"',
98+
default: '"always"',
99+
description: "When to show avatars"
100+
}
57101
];
58102
</script>
59103

60-
<ComponentHeader title="Chat Window"></ComponentHeader>
104+
<ComponentPage
105+
title="Message Thread"
106+
description="Shows a thread window as known from various messengers"
107+
{demoComponent}
108+
{features}
109+
{props}
110+
></ComponentPage>
111+
112+
{#snippet demoComponent()}
113+
<ChatWindow meId="1" {users} {messages}></ChatWindow>
114+
{/snippet}
115+
116+
{#snippet features()}
117+
<ComponentPageFeature title="Avatars">
118+
<div class="flex flex-row gap-4 justify-center items-stretch">
119+
<div class="flex flex-col items-center grow">
120+
<div class="text-lg font-mono">always</div>
121+
<ComponentPageCard>
122+
<ChatWindow meId="1" {users} {messages} showAvatars="always"></ChatWindow>
123+
</ComponentPageCard>
124+
</div>
125+
<div class="flex flex-col items-center grow">
126+
<div class="text-lg font-mono">change</div>
127+
<ComponentPageCard>
128+
<ChatWindow meId="1" {users} {messages} showAvatars="change"></ChatWindow>
129+
</ComponentPageCard>
130+
</div>
131+
<div class="flex flex-col items-center grow">
132+
<div class="text-lg font-mono">never</div>
133+
<ComponentPageCard>
134+
<ChatWindow meId="1" {users} {messages} showAvatars="never"></ChatWindow>
135+
</ComponentPageCard>
136+
</div>
137+
</div>
138+
</ComponentPageFeature>
61139

62-
<div class="mockup-phone">
63-
<div class="camera"></div>
64-
<div class="display">
65-
<div class="artboard phone-1 h-full pt-6">
66-
<ChatWindow meId="1" {users} {messages}></ChatWindow>
140+
<ComponentPageFeature title="Auto Scroll">
141+
<div class="flex flex-row gap-4 justify-center items-center">
142+
<div class="mockup-phone">
143+
<div class="camera"></div>
144+
<div class="display">
145+
<div class="artboard artboard-demo phone-1 p-1 pt-8">
146+
<ChatWindow meId="1" {users} messages={longMessages} showAvatars="always"></ChatWindow>
147+
</div>
148+
</div>
149+
</div>
150+
<div class="grow flex flex-row justify-center">
151+
<button
152+
class="btn btn-primary btn-lg"
153+
onclick={() => {
154+
longMessages.push({
155+
id: longMessages.length + 100 + "",
156+
text: "Lunch anyone?",
157+
senderId: "2",
158+
createdAt: Date.now()
159+
});
160+
}}
161+
>
162+
Add new Message
163+
</button>
164+
</div>
67165
</div>
68-
</div>
69-
</div>
166+
</ComponentPageFeature>
167+
{/snippet}

static/img/icon_angular.svg

Lines changed: 11 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)