fix(UI): manual import map for DynamicIcon to avoid huge bundle of Tabler icons

This commit is contained in:
Jake Turner 2026-04-02 19:03:39 +00:00 committed by Jake Turner
parent a14dd688fa
commit 1e4b7aea82
3 changed files with 113 additions and 20 deletions

View File

@ -1,6 +1,5 @@
import * as Icons from '@tabler/icons-react'
import classNames from '~/lib/classNames'
import DynamicIcon from './DynamicIcon'
import DynamicIcon, { DynamicIconName } from './DynamicIcon'
import StyledButton, { StyledButtonProps } from './StyledButton'
export type AlertProps = React.HTMLAttributes<HTMLDivElement> & {
@ -10,7 +9,7 @@ export type AlertProps = React.HTMLAttributes<HTMLDivElement> & {
children?: React.ReactNode
dismissible?: boolean
onDismiss?: () => void
icon?: keyof typeof Icons
icon?: DynamicIconName
variant?: 'standard' | 'bordered' | 'solid'
buttonProps?: StyledButtonProps
}
@ -27,7 +26,7 @@ export default function Alert({
buttonProps,
...props
}: AlertProps) {
const getDefaultIcon = (): keyof typeof Icons => {
const getDefaultIcon = (): DynamicIconName => {
switch (type) {
case 'warning':
return 'IconAlertTriangle'

View File

@ -1,36 +1,26 @@
import classNames from 'classnames'
import * as TablerIcons from '@tabler/icons-react'
import { icons } from '../lib/icons'
export type DynamicIconName = keyof typeof TablerIcons
export type { DynamicIconName } from '../lib/icons'
interface DynamicIconProps {
icon?: DynamicIconName
icon?: keyof typeof icons
className?: string
stroke?: number
onClick?: () => void
}
/**
* Renders a dynamic icon from the TablerIcons library based on the provided icon name.
* @param icon - The name of the icon to render.
* @param className - Optional additional CSS classes to apply to the icon.
* @param stroke - Optional stroke width for the icon.
* @returns A React element representing the icon, or null if no matching icon is found.
*/
const DynamicIcon: React.FC<DynamicIconProps> = ({ icon, className, stroke, onClick }) => {
if (!icon) return null
const Icon = TablerIcons[icon]
const Icon = icons[icon]
if (!Icon) {
console.warn(`Icon "${icon}" not found in TablerIcons.`)
console.warn(`Icon "${icon}" not found in icon map.`)
return null
}
return (
// @ts-ignore
<Icon className={classNames('h-5 w-5', className)} stroke={stroke || 2} onClick={onClick} />
)
return <Icon className={classNames('h-5 w-5', className)} strokeWidth={stroke ?? 2} onClick={onClick} />
}
export default DynamicIcon

104
admin/inertia/lib/icons.ts Normal file
View File

@ -0,0 +1,104 @@
import {
IconArrowUp,
IconBooks,
IconBrain,
IconChefHat,
IconCheck,
IconChevronLeft,
IconChevronRight,
IconCloudDownload,
IconCloudUpload,
IconCpu,
IconDatabase,
IconDownload,
IconHome,
IconLogs,
IconNotes,
IconPlayerPlay,
IconPlus,
IconRefresh,
IconRefreshAlert,
IconRobot,
IconSchool,
IconSettings,
IconTrash,
IconUpload,
IconWand,
IconWorld,
IconX,
IconAlertTriangle,
IconXboxX,
IconCircleCheck,
IconInfoCircle,
IconBug,
IconCopy,
IconServer,
IconMenu2,
IconArrowLeft,
IconArrowRight,
IconSun,
IconMoon,
IconStethoscope,
IconShieldCheck,
IconTool,
IconPlant,
IconCode,
IconMap,
} from '@tabler/icons-react'
/**
* An explicit import of used icons in the DynamicIcon component to ensure we get maximum tree-shaking
* while still providing us a nice DX with the DynamicIcon component and icon name inference.
* Only icons that are actually used by DynamicIcon should be added here. Yes, it does introduce
* some manual maintenance, but the bundle size benefits are worth it since we use a (relatively)
* very limited subset of the full Tabler Icons library.
*/
export const icons = {
IconAlertTriangle,
IconArrowLeft,
IconArrowRight,
IconArrowUp,
IconBooks,
IconBrain,
IconBug,
IconChefHat,
IconCheck,
IconChevronLeft,
IconChevronRight,
IconCircleCheck,
IconCloudDownload,
IconCloudUpload,
IconCode,
IconCopy,
IconCpu,
IconDatabase,
IconDownload,
IconHome,
IconInfoCircle,
IconLogs,
IconMap,
IconMenu2,
IconMoon,
IconNotes,
IconPlant,
IconPlayerPlay,
IconPlus,
IconRefresh,
IconRefreshAlert,
IconRobot,
IconSchool,
IconServer,
IconSettings,
IconShieldCheck,
IconStethoscope,
IconSun,
IconTool,
IconTrash,
IconUpload,
IconWand,
IconWorld,
IconX,
IconXboxX
} as const
export type DynamicIconName = keyof typeof icons