feat: alert & discord components

This commit is contained in:
iCrawl
2022-10-07 17:49:03 +02:00
parent 9f68d73706
commit 5730866073
27 changed files with 113 additions and 123 deletions

View 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>
);
}

View 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>
);
}

View 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>
</>
);
}

View File

@@ -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>
</>
);
}

View 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>
);
}

View 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>
);
}

View File

@@ -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>
);
}

View File

@@ -0,0 +1,7 @@
export interface IDiscordMessageEmbedFooter {
content: string;
}
export function DiscordMessageEmbedFooter({ content }: IDiscordMessageEmbedFooter) {
return <div className="mt-2 text-xs">{content}</div>;
}

View File

@@ -0,0 +1,7 @@
export interface IDiscordMessageEmbedTitle {
title: string;
}
export function DiscordMessageEmbedTitle({ title }: IDiscordMessageEmbedTitle) {
return <div className="mt-2 font-medium">{title}</div>;
}

View File

@@ -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>
);
}

View 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>
);
}

View 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>
);
}

View File

@@ -1 +1,14 @@
export * from './components/Alert.jsx';
export * from './components/Section.jsx';
export * from './components/discord/Message.jsx';
export * from './components/discord/MessageAuthor.jsx';
export * from './components/discord/MessageAuthorReply.jsx';
export * from './components/discord/MessageBaseReply.jsx';
export * from './components/discord/MessageEmbed.jsx';
export * from './components/discord/MessageEmbedAuthor.jsx';
export * from './components/discord/MessageEmbedFooter.jsx';
export * from './components/discord/MessageEmbedTitle.jsx';
export * from './components/discord/MessageInteraction.jsx';
export * from './components/discord/MessageReply.jsx';
export * from './components/discord/Messages.jsx';