Files
carmanagement/homepage/src/components/foundation/Icon.tsx
T
root d7fb7b7a7b
Build & Deploy / Build & Push Docker Image (push) Failing after 47s
Build & Deploy / Deploy to VPS (push) Has been skipped
Test / API Unit Tests (push) Failing after 5m4s
Test / Marketplace Unit Tests (push) Failing after 4m55s
Test / Admin Unit Tests (push) Successful in 9m37s
Test / Dashboard Unit Tests (push) Successful in 9m37s
Test / API Integration Tests (push) Successful in 9m54s
redesign the homepage
2026-06-26 16:27:21 -04:00

140 lines
3.5 KiB
TypeScript

import { classNames } from '@/lib/components/classNames';
import type { ReactNode, SVGProps } from 'react';
import styles from './Icon.module.css';
export const approvedIconNames = [
'arrow-back',
'arrow-forward',
'calendar',
'car',
'check',
'chevron-down',
'chevron-forward',
'close',
'error',
'external',
'globe',
'info',
'menu',
'minus',
'plus',
'search',
'settings',
'spinner',
'warning',
] as const;
export type ApprovedIconName = (typeof approvedIconNames)[number];
export type IconSize = 'sm' | 'md' | 'lg';
export type IconDirectionBehavior = 'fixed' | 'mirror';
interface IconProps extends Omit<SVGProps<SVGSVGElement>, 'name'> {
name: ApprovedIconName;
size?: IconSize;
decorative?: boolean;
label?: string;
directionBehavior?: IconDirectionBehavior;
}
const paths: Record<ApprovedIconName, ReactNode> = {
'arrow-back': <path d="m15 18-6-6 6-6M9 12h10" />,
'arrow-forward': <path d="m9 18 6-6-6-6m6 6H5" />,
calendar: (
<>
<rect x="3" y="5" width="18" height="16" rx="2" />
<path d="M16 3v4M8 3v4M3 10h18" />
</>
),
car: (
<>
<path d="m5 11 2-5h10l2 5" />
<path d="M3 11h18v7H3zM6 18v2m12-2v2M7 14h.01M17 14h.01" />
</>
),
check: <path d="m5 12 4 4L19 6" />,
'chevron-down': <path d="m6 9 6 6 6-6" />,
'chevron-forward': <path d="m9 18 6-6-6-6" />,
close: <path d="M6 6l12 12M18 6 6 18" />,
error: (
<>
<circle cx="12" cy="12" r="9" />
<path d="M12 7v6m0 4h.01" />
</>
),
external: (
<>
<path d="M14 4h6v6M20 4l-9 9" />
<path d="M18 13v6a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6" />
</>
),
globe: (
<>
<circle cx="12" cy="12" r="9" />
<path d="M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18" />
</>
),
info: (
<>
<circle cx="12" cy="12" r="9" />
<path d="M12 11v6m0-10h.01" />
</>
),
menu: <path d="M4 7h16M4 12h16M4 17h16" />,
minus: <path d="M5 12h14" />,
plus: <path d="M12 5v14M5 12h14" />,
search: (
<>
<circle cx="11" cy="11" r="7" />
<path d="m16 16 5 5" />
</>
),
settings: (
<>
<circle cx="12" cy="12" r="3" />
<path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1A1.7 1.7 0 0 0 9 4.6 1.7 1.7 0 0 0 10 3V2.8h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9 1.7 1.7 0 0 0 1.6 1h.2v4H21a1.7 1.7 0 0 0-1.6 1Z" />
</>
),
spinner: <path d="M21 12a9 9 0 1 1-9-9" />,
warning: (
<>
<path d="M12 3 2.5 20h19L12 3Z" />
<path d="M12 9v5m0 3h.01" />
</>
),
};
export function Icon({
name,
size = 'md',
decorative = true,
label,
directionBehavior = 'fixed',
className,
...props
}: IconProps) {
const accessibleProps = decorative
? ({ 'aria-hidden': true } as const)
: ({ role: 'img', 'aria-label': label ?? name } as const);
return (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
focusable="false"
className={classNames(
styles.icon,
styles[size],
directionBehavior === 'mirror' && styles.mirror,
className,
)}
{...accessibleProps}
{...props}
>
{paths[name]}
</svg>
);
}