feat(ui): add Button, Modal, Spinner, Toast, and Tooltip components with styles
All checks were successful
Build and Release / build-and-release (push) Successful in 13m12s
All checks were successful
Build and Release / build-and-release (push) Successful in 13m12s
- Implemented Button component with various props for customization. - Created Modal component with header, content, and footer subcomponents. - Added Spinner component for loading indicators. - Developed Toast component for displaying notifications. - Introduced Tooltip component for contextual hints with keyboard shortcuts. - Added corresponding CSS modules for styling each component. - Updated index file to export new components. - Configured TypeScript settings for the UI package.
This commit is contained in:
85
packages/shared/src/components/layout/GuildsLayout.tsx
Normal file
85
packages/shared/src/components/layout/GuildsLayout.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useQuery } from 'convex/react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { api } from '../../../../../convex/_generated/api';
|
||||
import { FileUploadDropZone } from '../channel/FileUploadDropZone';
|
||||
import { DMLayout } from '../dm/DMLayout';
|
||||
import { GuildList } from './GuildList';
|
||||
import { GuildNavbar } from './GuildNavbar';
|
||||
import { MobileBottomNav } from './MobileBottomNav';
|
||||
import { UserArea } from './UserArea';
|
||||
import styles from './GuildsLayout.module.css';
|
||||
|
||||
interface GuildsLayoutProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the current pathname to decide which mobile view to show. At
|
||||
* ≤768px exactly one of the three columns is visible at a time.
|
||||
* /channels/@me → nav (DM list)
|
||||
* /channels/@me/:channelId → chat
|
||||
* /channels/:serverId → nav (channel list)
|
||||
* /channels/:serverId/:channelId → chat
|
||||
* /channels/you → you (mobile profile tab)
|
||||
*/
|
||||
function detectMobileMode(pathname: string): 'nav' | 'chat' | 'you' {
|
||||
const segments = pathname.split('/').filter(Boolean);
|
||||
if (segments[0] !== 'channels') return 'nav';
|
||||
if (segments[1] === 'you') return 'you';
|
||||
return segments.length >= 3 && !!segments[2] ? 'chat' : 'nav';
|
||||
}
|
||||
|
||||
export function GuildsLayout({ children }: GuildsLayoutProps) {
|
||||
const location = useLocation();
|
||||
const segments = location.pathname.split('/').filter(Boolean);
|
||||
// /channels/:serverId/... — serverId is segments[1], '@me' for DMs
|
||||
const serverId = segments[0] === 'channels' ? segments[1] : undefined;
|
||||
const channelId = segments[0] === 'channels' ? segments[2] : undefined;
|
||||
const hasServer = !!serverId && serverId !== '@me' && serverId !== 'you';
|
||||
const mobileMode = detectMobileMode(location.pathname);
|
||||
|
||||
// Look up the current channel's name + type so the drop overlay
|
||||
// reads "Upload to #channel-name". Drops are disabled when there's
|
||||
// no channel in the URL (DM home, `/you` profile) and on voice
|
||||
// channels — they have no composer to attach to.
|
||||
const channel = useQuery(
|
||||
api.channels.get,
|
||||
channelId ? { id: channelId as any } : 'skip',
|
||||
);
|
||||
const channelName = (channel && (channel as any).name) || 'channel';
|
||||
const channelType = (channel && (channel as any).type) || undefined;
|
||||
const dropDisabled = !channelId || channelType === 'voice' || channelType === 'category';
|
||||
|
||||
return (
|
||||
<FileUploadDropZone
|
||||
channelName={channelName}
|
||||
disabled={dropDisabled}
|
||||
onDropFiles={(files) => {
|
||||
if (!channelId) return;
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('brycord:drop-files', {
|
||||
detail: { channelId, files },
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className={styles.wrapper} data-mobile-mode={mobileMode}>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.guildList}>
|
||||
<GuildList />
|
||||
</div>
|
||||
<div className={styles.sidebar}>
|
||||
{hasServer ? <GuildNavbar /> : <DMLayout />}
|
||||
</div>
|
||||
<div className={styles.sidebarDivider} />
|
||||
<div className={styles.content}>{children}</div>
|
||||
<div className={styles.userAreaWrapper}>
|
||||
<UserArea />
|
||||
</div>
|
||||
</div>
|
||||
<MobileBottomNav />
|
||||
</div>
|
||||
</FileUploadDropZone>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user