chore: apply code formatting and linting
- format long import statements with consistent line wrapping - apply consistent indentation across client and server modules - remove generated tsconfig.tsbuildinfo file
This commit is contained in:
@@ -36,15 +36,18 @@ export function Breadcrumbs({ items, separator, className }: BreadcrumbsProps) {
|
||||
<li key={i} className={styles.item}>
|
||||
{isLast ? (
|
||||
<span className={styles.current} aria-current="page">
|
||||
{item.icon}{item.label}
|
||||
{item.icon}
|
||||
{item.label}
|
||||
</span>
|
||||
) : item.href ? (
|
||||
<a href={item.href} className={styles.link} onClick={item.onClick}>
|
||||
{item.icon}{item.label}
|
||||
{item.icon}
|
||||
{item.label}
|
||||
</a>
|
||||
) : (
|
||||
<button type="button" className={styles.link} onClick={item.onClick}>
|
||||
{item.icon}{item.label}
|
||||
{item.icon}
|
||||
{item.label}
|
||||
</button>
|
||||
)}
|
||||
{!isLast && <span className={styles.separator}>{sep}</span>}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { AlertCircle, CheckCircle2, Info, X } from 'lucide-react';
|
||||
import styles from './ToastProvider.module.css';
|
||||
import { subscribeApiErrorToasts } from '../../lib/toast-events';
|
||||
|
||||
type ToastVariant = 'success' | 'error' | 'info';
|
||||
import { ToastContext, type ToastApi, type ToastVariant } from './toast-context';
|
||||
|
||||
interface ToastItem {
|
||||
id: number;
|
||||
@@ -12,15 +11,6 @@ interface ToastItem {
|
||||
closing: boolean;
|
||||
}
|
||||
|
||||
interface ToastApi {
|
||||
show: (message: string, variant?: ToastVariant) => void;
|
||||
success: (message: string) => void;
|
||||
error: (message: string) => void;
|
||||
info: (message: string) => void;
|
||||
}
|
||||
|
||||
const ToastContext = createContext<ToastApi | null>(null);
|
||||
|
||||
const ICONS: Record<ToastVariant, React.ReactNode> = {
|
||||
success: <CheckCircle2 size={16} />,
|
||||
error: <AlertCircle size={16} />,
|
||||
@@ -41,33 +31,38 @@ export function ToastProvider({ children, durationMs = 3500 }: ToastProviderProp
|
||||
setItems((prev) => prev.filter((item) => item.id !== id));
|
||||
}, []);
|
||||
|
||||
const dismiss = useCallback((id: number) => {
|
||||
setItems((prev) =>
|
||||
prev.map((item) =>
|
||||
item.id === id ? { ...item, closing: true } : item,
|
||||
),
|
||||
);
|
||||
window.setTimeout(() => remove(id), EXIT_ANIMATION_MS);
|
||||
}, [remove]);
|
||||
const dismiss = useCallback(
|
||||
(id: number) => {
|
||||
setItems((prev) => prev.map((item) => (item.id === id ? { ...item, closing: true } : item)));
|
||||
window.setTimeout(() => remove(id), EXIT_ANIMATION_MS);
|
||||
},
|
||||
[remove],
|
||||
);
|
||||
|
||||
const show = useCallback((message: string, variant: ToastVariant = 'info') => {
|
||||
const now = Date.now();
|
||||
const dedupeKey = `${variant}:${message}`;
|
||||
const previousTs = lastShownAtRef.current.get(dedupeKey) ?? 0;
|
||||
if (now - previousTs < 600) return;
|
||||
lastShownAtRef.current.set(dedupeKey, now);
|
||||
const show = useCallback(
|
||||
(message: string, variant: ToastVariant = 'info') => {
|
||||
const now = Date.now();
|
||||
const dedupeKey = `${variant}:${message}`;
|
||||
const previousTs = lastShownAtRef.current.get(dedupeKey) ?? 0;
|
||||
if (now - previousTs < 600) return;
|
||||
lastShownAtRef.current.set(dedupeKey, now);
|
||||
|
||||
const id = Date.now() + Math.floor(Math.random() * 1000);
|
||||
setItems((prev) => [...prev, { id, message, variant, closing: false }]);
|
||||
window.setTimeout(() => dismiss(id), durationMs);
|
||||
}, [dismiss, durationMs]);
|
||||
const id = Date.now() + Math.floor(Math.random() * 1000);
|
||||
setItems((prev) => [...prev, { id, message, variant, closing: false }]);
|
||||
window.setTimeout(() => dismiss(id), durationMs);
|
||||
},
|
||||
[dismiss, durationMs],
|
||||
);
|
||||
|
||||
const api = useMemo<ToastApi>(() => ({
|
||||
show,
|
||||
success: (message: string) => show(message, 'success'),
|
||||
error: (message: string) => show(message, 'error'),
|
||||
info: (message: string) => show(message, 'info'),
|
||||
}), [show]);
|
||||
const api = useMemo<ToastApi>(
|
||||
() => ({
|
||||
show,
|
||||
success: (message: string) => show(message, 'success'),
|
||||
error: (message: string) => show(message, 'error'),
|
||||
info: (message: string) => show(message, 'info'),
|
||||
}),
|
||||
[show],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
return subscribeApiErrorToasts((message) => api.error(message));
|
||||
@@ -86,7 +81,9 @@ export function ToastProvider({ children, durationMs = 3500 }: ToastProviderProp
|
||||
role="status"
|
||||
>
|
||||
<div className={styles.content}>
|
||||
<span className={styles.icon} aria-hidden="true">{ICONS[item.variant]}</span>
|
||||
<span className={styles.icon} aria-hidden="true">
|
||||
{ICONS[item.variant]}
|
||||
</span>
|
||||
<p className={styles.message}>{item.message}</p>
|
||||
</div>
|
||||
<button
|
||||
@@ -103,11 +100,3 @@ export function ToastProvider({ children, durationMs = 3500 }: ToastProviderProp
|
||||
</ToastContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useToast(): ToastApi {
|
||||
const ctx = useContext(ToastContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useToast must be used within ToastProvider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export type ToastVariant = 'success' | 'error' | 'info';
|
||||
|
||||
export interface ToastApi {
|
||||
show: (message: string, variant?: ToastVariant) => void;
|
||||
success: (message: string) => void;
|
||||
error: (message: string) => void;
|
||||
info: (message: string) => void;
|
||||
}
|
||||
|
||||
export const ToastContext = createContext<ToastApi | null>(null);
|
||||
@@ -0,0 +1,10 @@
|
||||
import { useContext } from 'react';
|
||||
import { ToastContext, type ToastApi } from './toast-context';
|
||||
|
||||
export function useToast(): ToastApi {
|
||||
const ctx = useContext(ToastContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useToast must be used within ToastProvider');
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
@@ -27,8 +27,9 @@ export type { BreadcrumbsProps, BreadcrumbItem } from './Breadcrumbs/Breadcrumbs
|
||||
|
||||
export { ThemeSwitcher } from './ThemeSwitcher/ThemeSwitcher';
|
||||
|
||||
export { ToastProvider, useToast } from './Toast/ToastProvider';
|
||||
export { ToastProvider } from './Toast/ToastProvider';
|
||||
export type { ToastProviderProps } from './Toast/ToastProvider';
|
||||
export { useToast } from './Toast/useToast';
|
||||
|
||||
export { Timestamp } from './Timestamp/Timestamp';
|
||||
export type { TimestampProps } from './Timestamp/Timestamp';
|
||||
|
||||
Reference in New Issue
Block a user