mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-15 19:13:31 +01:00
feat: alert & discord components
This commit is contained in:
57
packages/ui/src/lib/components/Alert.tsx
Normal file
57
packages/ui/src/lib/components/Alert.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { VscFlame, VscInfo, VscWarning } from 'react-icons/vsc';
|
||||
|
||||
export interface IAlert {
|
||||
title?: string | undefined;
|
||||
type: 'danger' | 'info' | 'success';
|
||||
}
|
||||
|
||||
function resolveType(type: IAlert['type']) {
|
||||
switch (type) {
|
||||
case 'danger': {
|
||||
return {
|
||||
text: 'text-red-500',
|
||||
border: 'border-red-500',
|
||||
icon: <VscWarning size={20} />,
|
||||
};
|
||||
}
|
||||
|
||||
case 'info': {
|
||||
return {
|
||||
text: 'text-blue-500',
|
||||
border: 'border-blue-500',
|
||||
icon: <VscInfo size={20} />,
|
||||
};
|
||||
}
|
||||
|
||||
case 'success': {
|
||||
return {
|
||||
text: 'text-green-500',
|
||||
border: 'border-green-500',
|
||||
icon: <VscFlame size={20} />,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function Alert({ title, type, children }: PropsWithChildren<IAlert>) {
|
||||
const { text, border, icon } = resolveType(type);
|
||||
|
||||
return (
|
||||
<div className="my-4">
|
||||
<div className="relative flex">
|
||||
<div className="p-4">{children}</div>
|
||||
<div className="absolute flex h-full w-full">
|
||||
<div className={`rounded-tl-1.5 rounded-bl-1.5 w-4 shrink-0 border-t-2 border-b-2 border-l-2 ${border}`} />
|
||||
<div className={`relative border-b-2 ${border}`}>
|
||||
<div className={`-translate-y-50% flex place-items-center gap-2 px-2 ${text}`}>
|
||||
{icon}
|
||||
<span className={`font-semibold ${text}`}>{title}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`rounded-tr-1.5 rounded-br-1.5 flex-1 border-t-2 border-b-2 border-r-2 ${border}`} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
60
packages/ui/src/lib/components/discord/Message.tsx
Normal file
60
packages/ui/src/lib/components/discord/Message.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { PropsWithChildren, ReactNode } from 'react';
|
||||
import { DiscordMessageAuthor, type IDiscordMessageAuthor } from './MessageAuthor.jsx';
|
||||
import { DiscordMessageInteraction, type IDiscordMessageInteraction } from './MessageInteraction.jsx';
|
||||
import { DiscordMessageReply, type IDiscordMessageReply } from './MessageReply.jsx';
|
||||
|
||||
export function DiscordMessage({
|
||||
reply,
|
||||
replyNode,
|
||||
interaction,
|
||||
interactionNode,
|
||||
author,
|
||||
authorNode,
|
||||
followUp,
|
||||
time,
|
||||
children,
|
||||
}: PropsWithChildren<{
|
||||
author?: IDiscordMessageAuthor | undefined;
|
||||
authorNode?: ReactNode | undefined;
|
||||
followUp?: boolean;
|
||||
interaction?: IDiscordMessageInteraction | undefined;
|
||||
interactionNode?: ReactNode | undefined;
|
||||
reply?: IDiscordMessageReply | undefined;
|
||||
replyNode?: ReactNode | undefined;
|
||||
time?: string | undefined;
|
||||
}>) {
|
||||
return (
|
||||
<div className="relative" id="outer-message-wrapper">
|
||||
<div
|
||||
className={`pl-18 hover:bg-[rgb(4_4_5)]/7 group py-0.5 pr-12 leading-snug ${followUp ? '' : 'mt-4'}`}
|
||||
id="message-wrapper"
|
||||
>
|
||||
{(reply || replyNode) && !followUp ? reply ? <DiscordMessageReply {...reply} /> : replyNode ?? null : null}
|
||||
{(interaction || interactionNode) && !(reply || replyNode) && !followUp ? (
|
||||
interaction ? (
|
||||
<DiscordMessageInteraction {...interaction} />
|
||||
) : (
|
||||
interactionNode ?? null
|
||||
)
|
||||
) : null}
|
||||
<div className="static" id="content-wrapper">
|
||||
{followUp ? (
|
||||
<span
|
||||
className="h-5.5 absolute left-0 mr-1 hidden w-[56px] cursor-default select-none text-right text-xs leading-loose text-[rgb(163_166_170)] group-hover:inline-block"
|
||||
id="time"
|
||||
>
|
||||
{time}
|
||||
</span>
|
||||
) : author ? (
|
||||
<DiscordMessageAuthor {...author} />
|
||||
) : (
|
||||
authorNode
|
||||
)}
|
||||
<div className="[&>p]:m-0 [&>p]:leading-snug text-white" id="message-content">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
31
packages/ui/src/lib/components/discord/MessageAuthor.tsx
Normal file
31
packages/ui/src/lib/components/discord/MessageAuthor.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
export interface IDiscordMessageAuthor {
|
||||
avatar: string;
|
||||
bot?: boolean;
|
||||
time: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export function DiscordMessageAuthor({ avatar, username, bot, time }: IDiscordMessageAuthor) {
|
||||
return (
|
||||
<>
|
||||
<img
|
||||
alt={`${username}'s avatar`}
|
||||
className="absolute left-[16px] mt-0.5 h-10 w-10 cursor-pointer select-none rounded-full"
|
||||
src={avatar}
|
||||
/>
|
||||
<h2 className="text-size-inherit m-0 font-medium leading-snug" id="user-info">
|
||||
<span className="mr-1" id="username">
|
||||
<span className="cursor-pointer text-base font-medium text-white hover:underline">{username}</span>
|
||||
{bot ? (
|
||||
<span className="bg-blurple vertical-top relative top-1 ml-1 rounded px-1 text-xs" id="bot">
|
||||
BOT
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
<span className="ml-1 cursor-default text-xs leading-snug text-[rgb(163_166_170)]" id="time">
|
||||
{time}
|
||||
</span>
|
||||
</h2>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export interface IDiscordMessageAuthorReply {
|
||||
avatar: string;
|
||||
bot?: boolean;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export function DiscordMessageAuthorReply({ avatar, bot, username }: IDiscordMessageAuthorReply) {
|
||||
return (
|
||||
<>
|
||||
<img alt={`${username}'s avatar`} className="mr-1 h-4 w-4 select-none rounded-full" src={avatar} />
|
||||
{bot ? (
|
||||
<div className="bg-blurple vertical-top mr-1 rounded px-1 text-xs" id="bot">
|
||||
BOT
|
||||
</div>
|
||||
) : null}
|
||||
<span className="mr-1 cursor-pointer select-none text-sm font-medium leading-snug text-white hover:underline">
|
||||
{username}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
20
packages/ui/src/lib/components/discord/MessageBaseReply.tsx
Normal file
20
packages/ui/src/lib/components/discord/MessageBaseReply.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { PropsWithChildren, ReactNode } from 'react';
|
||||
import { DiscordMessageAuthorReply, type IDiscordMessageAuthorReply } from './MessageAuthorReply.jsx';
|
||||
|
||||
export function DiscordMessageBaseReply({
|
||||
author,
|
||||
authorNode,
|
||||
children,
|
||||
}: PropsWithChildren<{ author?: IDiscordMessageAuthorReply | undefined; authorNode?: ReactNode | undefined }>) {
|
||||
return (
|
||||
<div
|
||||
className="before:rounded-tl-1.5 relative mb-1 flex place-items-center before:absolute before:left-[-36px] before:right-full before:top-[50%] before:bottom-0 before:mr-1 before:block before:border-l-2 before:border-t-2 before:border-[rgb(79_84_92)] before:content-none"
|
||||
id="reply-wrapper"
|
||||
>
|
||||
<div className="[&>span]:opacity-60 flex place-items-center">
|
||||
{author ? <DiscordMessageAuthorReply {...author} /> : authorNode}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
38
packages/ui/src/lib/components/discord/MessageEmbed.tsx
Normal file
38
packages/ui/src/lib/components/discord/MessageEmbed.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { PropsWithChildren, ReactNode } from 'react';
|
||||
import { DiscordMessageEmbedAuthor, type IDiscordMessageEmbedAuthor } from './MessageEmbedAuthor.jsx';
|
||||
import { DiscordMessageEmbedFooter, type IDiscordMessageEmbedFooter } from './MessageEmbedFooter.jsx';
|
||||
import { DiscordMessageEmbedTitle, type IDiscordMessageEmbedTitle } from './MessageEmbedTitle.jsx';
|
||||
|
||||
export interface IDiscordMessageEmbed {
|
||||
author?: IDiscordMessageEmbedAuthor | undefined;
|
||||
authorNode?: ReactNode | undefined;
|
||||
footer?: IDiscordMessageEmbedFooter | undefined;
|
||||
footerNode?: ReactNode | undefined;
|
||||
title?: IDiscordMessageEmbedTitle | undefined;
|
||||
titleNode?: ReactNode | undefined;
|
||||
}
|
||||
|
||||
export function DiscordMessageEmbed({
|
||||
author,
|
||||
authorNode,
|
||||
title,
|
||||
titleNode,
|
||||
children,
|
||||
footer,
|
||||
footerNode,
|
||||
}: PropsWithChildren<IDiscordMessageEmbed>) {
|
||||
return (
|
||||
<div className="py-0.5" id="outer-embed-wrapper">
|
||||
<div className="border-l-blurple grid max-w-max rounded border-l-4 bg-[rgb(47_49_54)]" id="embed-wrapper">
|
||||
<div className="max-w-128">
|
||||
<div className="pt-2 pr-4 pb-4 pl-3">
|
||||
{author ? <DiscordMessageEmbedAuthor {...author} /> : authorNode ?? null}
|
||||
{title ? <DiscordMessageEmbedTitle {...title} /> : titleNode ?? null}
|
||||
{children ? <div className="mt-2 text-sm">{children}</div> : null}
|
||||
{footer ? <DiscordMessageEmbedFooter {...footer} /> : footerNode ?? null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export interface IDiscordMessageEmbedAuthor {
|
||||
avatar: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export function DiscordMessageEmbedAuthor({ avatar, username }: IDiscordMessageEmbedAuthor) {
|
||||
return (
|
||||
<div className="mt-2 flex place-items-center">
|
||||
<img alt={`${username}'s avatar`} className="mr-2 h-6 w-6 select-none rounded-full" src={avatar} />
|
||||
<span className="text-sm font-medium hover:underline">{username}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface IDiscordMessageEmbedFooter {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export function DiscordMessageEmbedFooter({ content }: IDiscordMessageEmbedFooter) {
|
||||
return <div className="mt-2 text-xs">{content}</div>;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface IDiscordMessageEmbedTitle {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export function DiscordMessageEmbedTitle({ title }: IDiscordMessageEmbedTitle) {
|
||||
return <div className="mt-2 font-medium">{title}</div>;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import type { IDiscordMessageAuthorReply } from './MessageAuthorReply.jsx';
|
||||
import { DiscordMessageBaseReply } from './MessageBaseReply.jsx';
|
||||
|
||||
export interface IDiscordMessageInteraction {
|
||||
author?: IDiscordMessageAuthorReply | undefined;
|
||||
authorNode?: ReactNode | undefined;
|
||||
command?: string;
|
||||
}
|
||||
|
||||
export function DiscordMessageInteraction({ author, authorNode, command }: IDiscordMessageInteraction) {
|
||||
return (
|
||||
<DiscordMessageBaseReply author={author} authorNode={authorNode}>
|
||||
<span className="mr-1 select-none text-sm leading-snug text-white">used</span>
|
||||
<div className="text-blurple cursor-pointer text-sm leading-snug hover:underline">{command}</div>
|
||||
</DiscordMessageBaseReply>
|
||||
);
|
||||
}
|
||||
19
packages/ui/src/lib/components/discord/MessageReply.tsx
Normal file
19
packages/ui/src/lib/components/discord/MessageReply.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import type { IDiscordMessageAuthorReply } from './MessageAuthorReply.jsx';
|
||||
import { DiscordMessageBaseReply } from './MessageBaseReply.jsx';
|
||||
|
||||
export interface IDiscordMessageReply {
|
||||
author?: IDiscordMessageAuthorReply | undefined;
|
||||
authorNode?: ReactNode | undefined;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export function DiscordMessageReply({ author, authorNode, content }: IDiscordMessageReply) {
|
||||
return (
|
||||
<DiscordMessageBaseReply author={author} authorNode={authorNode}>
|
||||
<div className="cursor-pointer select-none text-sm leading-snug text-[rgb(163_166_170)] hover:text-white">
|
||||
{content}
|
||||
</div>
|
||||
</DiscordMessageBaseReply>
|
||||
);
|
||||
}
|
||||
9
packages/ui/src/lib/components/discord/Messages.tsx
Normal file
9
packages/ui/src/lib/components/discord/Messages.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
||||
export function DiscordMessages({ rounded, children }: PropsWithChildren<{ rounded?: boolean }>) {
|
||||
return (
|
||||
<div className={`pt-0.1 bg-[rgb(54_57_63)] pb-4 ${rounded ? 'rounded' : ''}`} id="messages-wrapper">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user