Skip to content

Instantly share code, notes, and snippets.

@wcheek
Last active May 1, 2025 06:18
Show Gist options
  • Save wcheek/254757146372e33bc0b53cc83d0184ab to your computer and use it in GitHub Desktop.
Save wcheek/254757146372e33bc0b53cc83d0184ab to your computer and use it in GitHub Desktop.
PrimeVue ChatApp ShowCase Options API to Composition API Conversion
<template>
<div
class="flex-1 h-full overflow-y-auto overflow-x-clip overflow-hidden flex border border-surface rounded-2xl"
>
<div class="w-4/12 xl:w-3/12 min-w-40 overflow-auto flex flex-col gap-6">
<div
class="flex flex-col gap-6 pt-3 pb-2 -mb-2 px-5 sticky top-0 bg-surface-0 dark:bg-surface-950 z-10"
>
<div class="flex items-center justify-between gap-6 text-color">
<div class="text-2xl font-medium lead">Chats</div>
<Button icon="pi pi-plus" text />
</div>
</div>
<div class="px-5">
<IconField iconPosition="left">
<InputIcon class="pi pi-search"> </InputIcon>
<InputText v-model="search" placeholder="Search" class="w-full" />
</IconField>
</div>
<div class="w-full px-5">
<SelectButton
v-model="value"
:options="options"
aria-labelledby="basic"
:pt="{
root: {
class: 'w-full',
},
pcbutton: {
root: {
class: 'flex-1',
},
},
}"
/>
</div>
<div class="flex-1 flex flex-col">
<div
v-for="chat in chats"
:key="chat.name"
class="flex items-center gap-2 p-4 cursor-pointer hover:bg-emphasis transition-all"
:class="{
'bg-emphasis': chat.name === activeChat,
}"
>
<div class="relative">
<div
v-if="chat.active !== undefined"
class="absolute top-0 right-0 p-[1px] bg-surface-0 dark:bg-surface-950 rounded-full flex items-center justify-center"
>
<Badge
:severity="chat.active ? 'success' : 'danger'"
class="p-1.5"
></Badge>
</div>
<Avatar
v-bind="
chat.image ? { image: chat.image } : { label: chat.capName }
"
:class="{
'!bg-primary-100 !text-primary-950': !chat.image,
}"
class="text-base font-medium flex"
size="large"
shape="circle"
/>
</div>
<div class="flex-1">
<div class="flex items-start gap-1 justify-between">
<div class="text-color font-medium leading-6">
{{ chat.name }}
</div>
<div class="text-sm text-muted-color leading-5">
{{ chat.time }}
</div>
</div>
<div class="flex items-center gap-5 justify-between mt-1">
<div class="text-muted-color text-sm leading-5 line-clamp-1">
{{ chat.lastMessage }}
</div>
<Badge
v-if="chat.unreadMessageCount > 0"
:value="chat.unreadMessageCount"
severity="contrast"
></Badge>
</div>
</div>
</div>
</div>
</div>
<div class="w-8/12 xl:w-6/12 border-x border-surface flex flex-col">
<div class="flex items-center p-4 gap-7 border-b border-surface">
<div class="flex items-center">
<Avatar
image="https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar-primetek.png"
class="mr-2 av"
size="large"
shape="circle"
/>
<div class="flex-1">
<div
class="text-color leading-6 cursor-pointer hover:text-muted-color-emphasis transition-colors"
>
PrimeTek
</div>
<div class="text-muted-color leading-5 line-clamp-1 mt-1">
Cody Fisher, Esther Howard, Jerome Bell, Kristin Watson, Ronald
Richards, Darrell Steward
</div>
</div>
</div>
<div class="flex items-center gap-2">
<Button icon="pi pi-phone" text />
<Button icon="pi pi-search" text />
<Button
type="button"
icon="pi pi-ellipsis-h"
text
@click="toggle"
aria-haspopup="true"
aria-controls="overlay_menu"
/>
<Menu ref="menu" id="overlay_menu" :model="menuItems" :popup="true" />
</div>
</div>
<div class="flex-1 overflow-y-auto flex flex-col gap-8 py-8 px-6">
<div
v-for="message in chatMessages"
:key="message.id"
class="flex items-start min-w-64 w-fit max-w-[60%]"
:class="{ 'ml-auto mr-0 flex-row-reverse': message.type === 'sent' }"
>
<div
class="flex items-center gap-2 sticky top-0 transition-all"
:class="{
'flex-row-reverse': message.type === 'sent',
}"
>
<Avatar
v-bind="
message.image
? { image: message.image }
: { label: message.capName }
"
:class="{
'bg-primary-100 text-primary-950': !message.image,
}"
class="w-10 h-10 text-sm font-medium"
shape="circle"
/>
<div>
<svg
:class="
message.type === 'received'
? 'fill-surface-100 dark:fill-surface-800'
: 'fill-primary rotate-180'
"
class=""
xmlns="http://www.w3.org/2000/svg"
width="7"
height="11"
viewBox="0 0 7 11"
fill="none"
>
<path
d="M1.79256 7.09551C0.516424 6.31565 0.516426 4.46224 1.79256 3.68238L7 0.500055L7 10.2778L1.79256 7.09551Z"
/>
</svg>
</div>
</div>
<div
:class="
message.type === 'received'
? 'flex-1 bg-surface-100 dark:bg-surface-800 px-2 py-1 rounded-lg'
: 'flex-1 bg-primary px-2 py-1 rounded-lg'
"
>
<p
:class="
message.type === 'received'
? 'text-color leading-6 mb-0'
: 'text-primary-contrast leading-6 mb-0'
"
>
{{ message.message }}
</p>
<div
v-if="message.attachment"
:class="
message.type === 'received'
? 'bg-surface-200 dark:bg-surface-700'
: 'bg-primary-emphasis'
"
class="mt-2 w-full rounded-lg mb-0.5 hover:opacity-75 transition-all"
>
<img
class="w-full h-auto block cursor-pointer"
:src="message.attachment"
alt="Message Image"
/>
</div>
</div>
</div>
</div>
<div
class="p-4 border-t border-surface flex items-end justify-between gap-2"
>
<div class="flex items-end gap-1 flex-1">
<Button icon="pi pi-face-smile" text />
<Button icon="pi pi-paperclip" text />
<Textarea
class="ml-1 flex-1 border-0 shadow-none max-h-32 min-h-9 bg-emphasis overflow-auto"
autoResize
rows="1"
placeholder="Write your message..."
/>
</div>
<Button icon="pi pi-send" />
</div>
</div>
<div class="w-3/12 xl:block hidden min-w-40 py-6 px-3 overflow-auto">
<div class="flex flex-col items-center justify-center">
<Avatar
image="https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar-primetek.png"
class="w-32 h-32"
size="xlarge"
shape="circle"
/>
<div class="leading-6 font-medium text-color mt-4 w-full text-center">
PrimeTek
</div>
<div class="leading-5 text-sm text-muted-color mt-1 w-full text-center">
@primetek
</div>
<div class="flex items-center justify-center flex-wrap gap-1 mt-4">
<Button
icon="pi pi-phone text-muted-color"
severity="secondary"
text
/>
<Button
icon="pi pi-video text-muted-color"
severity="secondary"
text
/>
<Button
icon="pi pi-sign-in text-muted-color"
severity="secondary"
text
/>
<Button
icon="pi pi-info-circle text-muted-color"
severity="secondary"
text
/>
<Button
type="button"
icon="pi pi-ellipsis-v text-muted-color"
severity="secondary"
text
@click="toggle"
aria-haspopup="true"
aria-controls="overlay_menu"
/>
<Menu ref="menu" id="overlay_menu" :model="menuItems" :popup="true" />
</div>
</div>
<div class="flex flex-col gap-4 mt-4">
<div class="flex items-center gap-2">
<i class="pi pi-bell text-color"></i>
<div class="leading-6 font-medium text-color flex-1">
Notification
</div>
<ToggleSwitch v-model="notification" />
</div>
<div class="flex items-center gap-2">
<i class="pi pi-volume-down text-color"></i>
<div class="leading-6 font-medium text-color flex-1">Sound</div>
<ToggleSwitch v-model="sound" />
</div>
<div class="flex items-center gap-2">
<i class="pi pi-download text-color"></i>
<div class="leading-6 font-medium text-color flex-1">
Save to downloads
</div>
<ToggleSwitch v-model="download" />
</div>
</div>
<div class="mt-6">
<div class="flex items-center gap-2">
<div class="flex-1 text-color leading-6 font-medium">Members</div>
<Button
label="See All"
class="text-sm py-0.5 px-2 text-muted-color"
text
/>
</div>
<div class="mt-4 flex flex-col gap-4">
<div
v-for="member in members"
:key="member.name"
class="flex items-center gap-2 cursor-pointer"
>
<Avatar
v-bind="
member.image
? { image: member.image }
: { label: member.capName }
"
:class="{
'bg-orange-100 text-orange-950': !member.image,
}"
class="font-medium text-xs"
shape="circle"
/>
<div
class="text-sm text-color hover:text-muted-color-emphasis transition-colors font-medium leading-5 flex-1"
>
{{ member.name }}
</div>
<i class="pi pi-chevron-right text-xs text-muted-color"></i>
</div>
</div>
</div>
<div class="mt-5">
<SelectButton
v-model="media"
:options="mediaOptions"
:pt="{
root: {
class: 'w-full',
},
pcbutton: {
root: {
class: 'flex-1',
},
},
}"
/>
<div class="mt-3 mb-5 grid grid-cols-3 gap-2">
<div
v-for="(media, index) in chatMedia"
:key="index"
class="bg-emphasis hover:opacity-70 transition-all flex-1 aspect-square rounded-lg border border-surface cursor-pointer"
>
<img
class="w-full h-full object-cover block"
:src="media"
alt="Media Image"
/>
</div>
<div
class="bg-emphasis hover:opacity-70 transition-all flex-1 aspect-square rounded-lg border border-surface cursor-pointer flex items-center justify-center"
>
<span class="text-muted-color font-medium">99+</span>
</div>
</div>
<Button
label="Show more"
icon="pi pi-arrow-right"
iconPos="right"
outlined
class="w-full text-left"
:pt="{
root: {
class: 'justify-between',
},
}"
/>
</div>
</div>
</div>
</template>
<script setup lang="ts">
const search = ref("");
const download = ref(false);
const notification = ref(true);
const sound = ref(false);
const value = ref("Chat");
const value2 = ref("");
const options = ref(["Chat", "Call"]);
const media = ref("Media");
const mediaOptions = ref(["Media", "Link", "Docs"]);
const activeChat = ref("PrimeTek Team");
const menuItems = ref([
{
label: "Group Info",
icon: "pi pi-info-circle",
},
{
label: "Leave group",
icon: "pi pi-sign-out",
},
]);
const chats = ref([
{
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar11.jpg",
name: "Cody Fisher",
capName: "CF",
active: true,
unreadMessageCount: 8,
time: "12.30",
lastMessage:
"Hey there! I've heard about PrimeVue. Any cool tips for getting started?",
},
{
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar-primetek.png",
name: "PrimeTek Team",
capName: "PT",
active: undefined,
unreadMessageCount: 0,
time: "11.15",
lastMessage: "Let's implement PrimeVue. Elevating our UI game! 🚀",
},
{
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar2.png",
name: "Jerome Bell",
capName: "JB",
active: true,
unreadMessageCount: 4,
time: "11.15",
lastMessage:
"Absolutely! PrimeVue's documentation is gold—simplifies our UI work.",
},
{
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar12.jpg",
name: "Robert Fox",
capName: "RF",
active: false,
unreadMessageCount: 0,
time: "11.15",
lastMessage:
"Interesting! PrimeVue sounds amazing. What's your favorite feature?",
},
{
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar13.jpg",
name: "Esther Howard",
capName: "EH",
active: true,
unreadMessageCount: 9,
time: "11.15",
lastMessage:
"Quick one, team! Anyone using PrimeVue for mobile app development?",
},
{
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar9.jpg",
name: "Darlene Robertson",
capName: "DR",
active: false,
unreadMessageCount: 0,
time: "11.15",
lastMessage:
"Just explored PrimeVue's themes. Can we talk about those stunning designs? 😍",
},
{
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar6.png",
name: "Ralph Edwards",
capName: "RE",
active: false,
unreadMessageCount: 0,
time: "11.15",
lastMessage:
"PrimeVue is a game-changer, right? What are your thoughts, folks?",
},
{
image: "",
name: "Ronald Richards",
capName: "RR",
active: false,
unreadMessageCount: 0,
time: "11.15",
lastMessage:
"Jumping in! PrimeVue's community forum is buzzing. Any engaging discussions?",
},
{
image: "",
name: "Kristin Watson",
capName: "KW",
active: false,
unreadMessageCount: 0,
time: "11.15",
lastMessage:
"Sharing a quick win-PrimeVue tutorials are leveling up my UI skills. 👩‍💻",
},
{
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar7.png",
name: "Darrell Steward",
capName: "DS",
active: false,
unreadMessageCount: 0,
time: "11.15",
lastMessage:
"Reflecting on PrimeVue's impact on our workflow. What's your take?",
},
]);
const chatMessages = ref([
{
id: 1,
attachment: "",
name: "",
image: "",
capName: "OS",
type: "received",
message: "Awesome! What's the standout feature?",
},
{
id: 2,
attachment: "",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar8.png",
capName: "A",
type: "received",
message: "PrimeVue rocks! Simplifies UI dev with versatile components.",
},
{
id: 3,
attachment: "",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar11.jpg",
capName: "A",
type: "received",
message: "Intriguing! Tell us more about its impact.",
},
{
id: 4,
attachment:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/message-image.png",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar2.png",
capName: "A",
type: "received",
message:
"It's design-neutral and compatible with Tailwind. Features accessible, high-grade components!",
},
{
id: 5,
attachment: "",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar5.png",
capName: "A",
type: "sent",
message: "Customizable themes, responsive design – UI excellence!",
},
{
id: 6,
attachment: "",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar8.png",
capName: "A",
type: "received",
message: "Love it! Fast-tracking our development is key.",
},
{
id: 7,
attachment: "",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar6.png",
capName: "A",
type: "received",
message: "Documentation rocks too – smooth integration for all.",
},
{
id: 8,
attachment: "",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar5.png",
capName: "B",
type: "sent",
message:
"The flexibility and ease of use are truly impressive. Have you explored the new components?",
},
{
id: 9,
attachment: "",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar12.jpg",
capName: "C",
type: "received",
message:
"Absolutely, the new calendar component has saved us a ton of development time!",
},
{
id: 10,
attachment: "",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar13.jpg",
capName: "D",
type: "received",
message:
"And the accessibility features are top-notch. It's great to see a library focusing on inclusivity.",
},
{
id: 11,
attachment:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/message-image.png",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar5.png",
capName: "E",
type: "sent",
message:
"I couldn't agree more. Plus, the documentation is incredibly thorough, which makes onboarding new team members a breeze.",
},
{
id: 12,
attachment: "",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar6.png",
capName: "F",
type: "received",
message:
"Do you have any tips for optimizing performance when using multiple complex components?",
},
{
id: 13,
attachment: "",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar11.jpg",
capName: "G",
type: "received",
message:
"Yes! Lazy loading and code splitting can make a huge difference, especially in larger applications.",
},
{
id: 14,
attachment: "",
name: "",
image: "",
capName: "HS",
type: "received",
message:
"I've also found that leveraging the component's internal state management capabilities can help streamline data flow and improve performance.",
},
{
id: 15,
attachment: "",
name: "",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar5.png",
capName: "H",
type: "sent",
message:
"That's great advice. It's amazing how much detail and thought has gone into making PrimeVue such a powerful tool for developers.",
},
]);
const chatMedia = ref([
"https://www.primefaces.org/cdn/primevue/images/landing/apps/chat-image1.png",
"https://www.primefaces.org/cdn/primevue/images/landing/apps/chat-image2.png",
"https://www.primefaces.org/cdn/primevue/images/landing/apps/chat-image3.png",
"https://www.primefaces.org/cdn/primevue/images/landing/apps/chat-image4.png",
"https://www.primefaces.org/cdn/primevue/images/landing/apps/chat-image5.png",
]);
const members = ref([
{
name: "Robin Jonas",
capName: "RJ",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar2.png",
},
{
name: "Cameron Williamson",
capName: "CW",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar11.jpg",
},
{
name: "Eleanor Pena",
capName: "EP",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar5.png",
},
{
name: "Arlene McCoy",
capName: "AM",
image:
"https://www.primefaces.org/cdn/primevue/images/landing/apps/avatar8.png",
},
{ name: "Dianne Russell", capName: "DR", image: "" },
]);
const menu = ref();
function toggle(event: any) {
menu.value.toggle(event);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment