197 lines
4.8 KiB
Vue
197 lines
4.8 KiB
Vue
<template>
|
|
<component
|
|
:is="iconComponent"
|
|
v-if="iconComponent"
|
|
:size="size"
|
|
:stroke-width="strokeWidth"
|
|
:color="color"
|
|
class="lucide-icon"
|
|
/>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import * as icons from 'lucide-vue-next'
|
|
|
|
interface Props {
|
|
name: string
|
|
size?: number | string
|
|
strokeWidth?: number
|
|
color?: string
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
size: 24,
|
|
strokeWidth: 2,
|
|
color: 'currentColor'
|
|
})
|
|
|
|
// Convert kebab-case to PascalCase for icon component names
|
|
const toPascalCase = (str: string) => {
|
|
return str
|
|
.split('-')
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join('')
|
|
}
|
|
|
|
const iconComponent = computed(() => {
|
|
// Handle special cases and common mappings
|
|
const iconMap: Record<string, string> = {
|
|
'alert-circle': 'AlertCircle',
|
|
'chevron-down': 'ChevronDown',
|
|
'chevron-up': 'ChevronUp',
|
|
'x': 'X',
|
|
'check': 'Check',
|
|
'trending-up': 'TrendingUp',
|
|
'trending-down': 'TrendingDown',
|
|
'minus': 'Minus',
|
|
'search': 'Search',
|
|
'filter': 'Filter',
|
|
'calendar': 'Calendar',
|
|
'map-pin': 'MapPin',
|
|
'users': 'Users',
|
|
'clock': 'Clock',
|
|
'star': 'Star',
|
|
'grid': 'Grid',
|
|
'list': 'List',
|
|
'plus': 'Plus',
|
|
'user': 'User',
|
|
'mail': 'Mail',
|
|
'phone': 'Phone',
|
|
'globe': 'Globe',
|
|
'briefcase': 'Briefcase',
|
|
'building': 'Building',
|
|
'award': 'Award',
|
|
'shield': 'Shield',
|
|
'heart': 'Heart',
|
|
'edit': 'Edit',
|
|
'settings': 'Settings',
|
|
'log-out': 'LogOut',
|
|
'bell': 'Bell',
|
|
'home': 'Home',
|
|
'activity': 'Activity',
|
|
'message-square': 'MessageSquare',
|
|
'arrow-right': 'ArrowRight',
|
|
'external-link': 'ExternalLink',
|
|
'download': 'Download',
|
|
'upload': 'Upload',
|
|
'share': 'Share',
|
|
'copy': 'Copy',
|
|
'trash': 'Trash',
|
|
'eye': 'Eye',
|
|
'eye-off': 'EyeOff',
|
|
'lock': 'Lock',
|
|
'unlock': 'Unlock',
|
|
'camera': 'Camera',
|
|
'image': 'Image',
|
|
'video': 'Video',
|
|
'file-text': 'FileText',
|
|
'bar-chart': 'BarChart',
|
|
'pie-chart': 'PieChart',
|
|
'dollar-sign': 'DollarSign',
|
|
'credit-card': 'CreditCard',
|
|
'gift': 'Gift',
|
|
'bookmark': 'Bookmark',
|
|
'tag': 'Tag',
|
|
'folder': 'Folder',
|
|
'layers': 'Layers',
|
|
'zap': 'Zap',
|
|
'sun': 'Sun',
|
|
'moon': 'Moon',
|
|
'more-horizontal': 'MoreHorizontal',
|
|
'more-vertical': 'MoreVertical',
|
|
'menu': 'Menu',
|
|
'arrow-left': 'ArrowLeft',
|
|
'arrow-up': 'ArrowUp',
|
|
'arrow-down': 'ArrowDown',
|
|
'chevron-left': 'ChevronLeft',
|
|
'chevron-right': 'ChevronRight',
|
|
'check-circle': 'CheckCircle',
|
|
'x-circle': 'XCircle',
|
|
'alert-triangle': 'AlertTriangle',
|
|
'info': 'Info',
|
|
'help-circle': 'HelpCircle',
|
|
'loader': 'Loader',
|
|
'refresh-cw': 'RefreshCw',
|
|
'link': 'Link',
|
|
'paperclip': 'Paperclip',
|
|
'send': 'Send',
|
|
'inbox': 'Inbox',
|
|
'archive': 'Archive',
|
|
'flag': 'Flag',
|
|
'save': 'Save',
|
|
'wifi': 'Wifi',
|
|
'wifi-off': 'WifiOff',
|
|
'mic': 'Mic',
|
|
'mic-off': 'MicOff',
|
|
'volume': 'Volume',
|
|
'volume-x': 'VolumeX',
|
|
'play': 'Play',
|
|
'pause': 'Pause',
|
|
'skip-forward': 'SkipForward',
|
|
'skip-back': 'SkipBack',
|
|
'maximize': 'Maximize',
|
|
'minimize': 'Minimize',
|
|
'expand': 'Expand',
|
|
'compass': 'Compass',
|
|
'map': 'Map',
|
|
'navigation': 'Navigation',
|
|
'target': 'Target',
|
|
'crown': 'Crown',
|
|
'key': 'Key',
|
|
'code': 'Code',
|
|
'terminal': 'Terminal',
|
|
'database': 'Database',
|
|
'server': 'Server',
|
|
'cpu': 'Cpu',
|
|
'hard-drive': 'HardDrive',
|
|
'monitor': 'Monitor',
|
|
'smartphone': 'Smartphone',
|
|
'tablet': 'Tablet',
|
|
'watch': 'Watch',
|
|
'printer': 'Printer',
|
|
'headphones': 'Headphones',
|
|
'bluetooth': 'Bluetooth',
|
|
'battery': 'Battery',
|
|
'battery-charging': 'BatteryCharging',
|
|
'clipboard': 'Clipboard',
|
|
'hash': 'Hash',
|
|
'at-sign': 'AtSign',
|
|
'percent': 'Percent',
|
|
'thumbs-up': 'ThumbsUp',
|
|
'thumbs-down': 'ThumbsDown',
|
|
'smile': 'Smile',
|
|
'frown': 'Frown',
|
|
'coffee': 'Coffee',
|
|
'shopping-cart': 'ShoppingCart',
|
|
'shopping-bag': 'ShoppingBag',
|
|
'package': 'Package',
|
|
'truck': 'Truck',
|
|
'book': 'Book',
|
|
'book-open': 'BookOpen',
|
|
'feather': 'Feather',
|
|
'sliders': 'Sliders',
|
|
'toggle-left': 'ToggleLeft',
|
|
'toggle-right': 'ToggleRight',
|
|
'power': 'Power',
|
|
'log-in': 'LogIn',
|
|
'circle': 'Circle',
|
|
'square': 'Square',
|
|
'triangle': 'Triangle'
|
|
}
|
|
|
|
// Get the icon name from the map or convert from kebab-case
|
|
const iconName = iconMap[props.name] || toPascalCase(props.name)
|
|
|
|
// Return the icon component from lucide-vue-next
|
|
return (icons as any)[iconName] || (icons as any)[iconName + 'Icon'] || null
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.lucide-icon {
|
|
display: inline-block;
|
|
vertical-align: middle;
|
|
flex-shrink: 0;
|
|
}
|
|
</style> |