mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 14:36:31 +00:00
fix(plugins): fold resolvePluginIcon into PluginIcon
pluginIcon.ts and PluginIcon.tsx resolve to the same file on the case-insensitive filesystems dev checkouts commonly sit on (Windows, macOS) — tsc sees both casings of one module and fails with TS1149 in every importer. Keep the resolver and the component in one module.
This commit is contained in:
@@ -5,7 +5,7 @@ import { useSettingsStore } from '../../store/settingsStore'
|
||||
import { useTranslation } from '../../i18n'
|
||||
import { LayoutGrid, CalendarDays, Globe, Compass, Bookmark, Plus } from 'lucide-react'
|
||||
import type { LucideIcon } from 'lucide-react'
|
||||
import { resolvePluginIcon } from '../shared/pluginIcon'
|
||||
import { resolvePluginIcon } from '../shared/PluginIcon'
|
||||
|
||||
const ADDON_NAV: Record<string, { icon: LucideIcon; labelKey: string }> = {
|
||||
vacay: { icon: CalendarDays, labelKey: 'admin.addons.catalog.vacay.name' },
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useTranslation } from '../../i18n'
|
||||
import { Plane, LogOut, Settings, ChevronDown, Shield, ArrowLeft, Users, Moon, Sun, Monitor, CalendarDays, Briefcase, Globe, Compass, BookOpen, Bookmark } from 'lucide-react'
|
||||
import type { LucideIcon } from 'lucide-react'
|
||||
import InAppNotificationBell from './InAppNotificationBell.tsx'
|
||||
import { resolvePluginIcon } from '../shared/pluginIcon'
|
||||
import { resolvePluginIcon } from '../shared/PluginIcon'
|
||||
|
||||
const ADDON_ICONS: Record<string, LucideIcon> = { CalendarDays, Briefcase, Globe, Compass, Bookmark }
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { render } from '@testing-library/react'
|
||||
import { Blocks, Stethoscope } from 'lucide-react'
|
||||
import { resolvePluginIcon } from './pluginIcon'
|
||||
import PluginIcon from './PluginIcon'
|
||||
import PluginIcon, { resolvePluginIcon } from './PluginIcon'
|
||||
|
||||
describe('resolvePluginIcon', () => {
|
||||
it('resolves a lucide name outside the old 14-icon allowlist', () => {
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
import type { CSSProperties } from 'react'
|
||||
import { resolvePluginIcon } from './pluginIcon'
|
||||
import * as LucideIcons from 'lucide-react'
|
||||
import { Blocks, type LucideIcon } from 'lucide-react'
|
||||
|
||||
// Lucide names are PascalCase. Restricting the lookup to that shape keeps a manifest
|
||||
// from reaching lucide's non-icon exports (`createLucideIcon`, `icons`, `default`,
|
||||
// `module.exports`), which are all lowercase-initial.
|
||||
const LUCIDE_ICON_NAME = /^[A-Z][A-Za-z0-9]*$/
|
||||
|
||||
/**
|
||||
* Resolves a plugin manifest's `icon` (a lucide icon name, e.g. "Stethoscope") to the
|
||||
* matching lucide component, falling back to Blocks for a missing or unknown name.
|
||||
*
|
||||
* Note lucide icons are forwardRef objects, not functions — never narrow this with
|
||||
* `typeof x === 'function'`. The lookup returns a module-level reference, so the
|
||||
* component identity is stable across renders and is safe to call inline while
|
||||
* building nav/tab arrays.
|
||||
*/
|
||||
export function resolvePluginIcon(name: string | null | undefined): LucideIcon {
|
||||
if (!name || !LUCIDE_ICON_NAME.test(name)) return Blocks
|
||||
const icon = (LucideIcons as unknown as Record<string, LucideIcon | undefined>)[name]
|
||||
return icon ?? Blocks
|
||||
}
|
||||
|
||||
/** Renders the lucide icon a plugin declares in its manifest (Blocks if unknown). */
|
||||
export default function PluginIcon({ name, size = 20, className, style }: {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import * as LucideIcons from 'lucide-react'
|
||||
import { Blocks, type LucideIcon } from 'lucide-react'
|
||||
|
||||
// Lucide names are PascalCase. Restricting the lookup to that shape keeps a manifest
|
||||
// from reaching lucide's non-icon exports (`createLucideIcon`, `icons`, `default`,
|
||||
// `module.exports`), which are all lowercase-initial.
|
||||
const LUCIDE_ICON_NAME = /^[A-Z][A-Za-z0-9]*$/
|
||||
|
||||
/**
|
||||
* Resolves a plugin manifest's `icon` (a lucide icon name, e.g. "Stethoscope") to the
|
||||
* matching lucide component, falling back to Blocks for a missing or unknown name.
|
||||
*
|
||||
* Note lucide icons are forwardRef objects, not functions — never narrow this with
|
||||
* `typeof x === 'function'`. The lookup returns a module-level reference, so the
|
||||
* component identity is stable across renders and is safe to call inline while
|
||||
* building nav/tab arrays.
|
||||
*/
|
||||
export function resolvePluginIcon(name: string | null | undefined): LucideIcon {
|
||||
if (!name || !LUCIDE_ICON_NAME.test(name)) return Blocks
|
||||
const icon = (LucideIcons as unknown as Record<string, LucideIcon | undefined>)[name]
|
||||
return icon ?? Blocks
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { useSettingsStore } from '../../store/settingsStore'
|
||||
import { getCached, fetchPhoto } from '../../services/photoService'
|
||||
import { useToast } from '../../components/shared/Toast'
|
||||
import { Map, Ticket, PackageCheck, Wallet, FolderOpen, Users, Train } from 'lucide-react'
|
||||
import { resolvePluginIcon } from '../../components/shared/pluginIcon'
|
||||
import { resolvePluginIcon } from '../../components/shared/PluginIcon'
|
||||
import { useTranslation, translateApiError } from '../../i18n'
|
||||
import { addonsApi, accommodationsApi, authApi, tripsApi, assignmentsApi, healthApi, airtrailApi, mapsApi, placesApi } from '../../api/client'
|
||||
import { parsedItemToDraft, isTransportItem, type BookingReviewDraft } from '../../components/Planner/parsedItemToDraft'
|
||||
|
||||
Reference in New Issue
Block a user