2023-01-11 18:15:31 +01:00
|
|
|
import {
|
|
|
|
DeesElement,
|
|
|
|
html,
|
|
|
|
property,
|
|
|
|
customElement,
|
|
|
|
cssManager,
|
|
|
|
css,
|
2023-08-07 20:02:18 +02:00
|
|
|
type CSSResult,
|
2023-08-07 19:13:29 +02:00
|
|
|
} from '@design.estate/dees-element';
|
2020-09-17 10:35:33 +00:00
|
|
|
|
2023-08-07 19:13:29 +02:00
|
|
|
import * as domtools from '@design.estate/dees-domtools';
|
2020-09-17 10:35:33 +00:00
|
|
|
|
2023-08-07 20:02:18 +02:00
|
|
|
import { icon, type IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
2020-12-02 17:11:04 +00:00
|
|
|
import {
|
|
|
|
faFacebook,
|
|
|
|
faGoogle,
|
|
|
|
faLinkedin,
|
|
|
|
faMedium,
|
2020-12-03 11:12:11 +00:00
|
|
|
faSlackHash,
|
2020-12-02 17:11:04 +00:00
|
|
|
faTwitter,
|
2022-07-14 23:29:25 +02:00
|
|
|
faInstagram,
|
|
|
|
faTiktok,
|
2020-12-02 17:11:04 +00:00
|
|
|
} from '@fortawesome/free-brands-svg-icons';
|
2020-12-01 22:24:54 +00:00
|
|
|
|
2023-01-11 17:19:22 +01:00
|
|
|
import {
|
2023-01-13 00:30:56 +01:00
|
|
|
faCopy as faCopyRegular,
|
2023-01-13 01:17:08 +01:00
|
|
|
faCircleCheck as faCircleCheckRegular,
|
|
|
|
faCircleXmark as faCircleXmarkRegular,
|
2023-01-11 17:19:22 +01:00
|
|
|
faMessage as faMessageRegular,
|
2023-01-13 00:30:56 +01:00
|
|
|
faPaste as faPasteRegular,
|
2023-01-11 17:19:22 +01:00
|
|
|
faSun as faSunRegular,
|
2023-09-20 18:57:54 +02:00
|
|
|
faTrashCan as faTrashCanRegular,
|
2023-01-11 17:19:22 +01:00
|
|
|
} from '@fortawesome/free-regular-svg-icons';
|
|
|
|
import {
|
2023-01-13 00:30:56 +01:00
|
|
|
faArrowRight as faArrowRightSolid,
|
2023-01-12 18:14:59 +01:00
|
|
|
faArrowUpRightFromSquare as faArrowUpRightFromSquareSolid,
|
2023-01-11 17:19:22 +01:00
|
|
|
faBell as faBellSolid,
|
|
|
|
faBug as faBugSolid,
|
2023-01-12 00:07:39 +01:00
|
|
|
faBuilding as faBuildingSolid,
|
2023-01-11 18:32:05 +01:00
|
|
|
faCaretLeft as faCaretLeftSolid,
|
2023-01-13 00:30:56 +01:00
|
|
|
faCaretRight as faCaretRightSolid,
|
|
|
|
faCheck as faCheckSolid,
|
2023-01-11 17:43:58 +01:00
|
|
|
faCircleInfo as faCircleInfoSolid,
|
2023-01-13 01:17:08 +01:00
|
|
|
faCircleCheck as faCircleCheckSolid,
|
|
|
|
faCircleXmark as faCircleXmarkSolid,
|
2023-09-14 19:43:26 +02:00
|
|
|
faClockRotateLeft as faClockRotateLeftSolid,
|
2023-01-13 00:30:56 +01:00
|
|
|
faCopy as faCopySolid,
|
2023-01-11 17:19:22 +01:00
|
|
|
faDesktop as faDesktopSolid,
|
2023-01-16 11:51:21 +01:00
|
|
|
faEye as faEyeSolid,
|
|
|
|
faEyeSlash as faEyeSlashSolid,
|
2023-10-12 15:43:10 +02:00
|
|
|
faFileInvoice as faFileInvoiceSolid,
|
|
|
|
faFileInvoiceDollar as faFileInvoiceDollarSolid,
|
2024-01-15 19:42:15 +01:00
|
|
|
faGear as faGearSolid,
|
2023-01-11 17:43:58 +01:00
|
|
|
faGrip as faGripSolid,
|
2024-01-21 22:37:39 +01:00
|
|
|
faMagnifyingGlass as faMagnifyingGlassSolid,
|
2023-01-11 17:43:58 +01:00
|
|
|
faMessage as faMessageSolid,
|
2023-10-12 15:43:10 +02:00
|
|
|
faMoneyCheckDollar as faMoneyCheckDollarSolid,
|
2023-03-09 17:08:19 +01:00
|
|
|
faMugHot as faMugHotSolid,
|
2023-01-13 00:30:56 +01:00
|
|
|
faMinus as faMinusSolid,
|
2024-01-22 17:12:58 +01:00
|
|
|
faNetworkWired as faNetworkWiredSolid,
|
2024-01-18 02:08:19 +01:00
|
|
|
faPaperclip as faPaperclipSolid,
|
2023-01-13 00:30:56 +01:00
|
|
|
faPaste as faPasteSolid,
|
|
|
|
faPenToSquare as faPenToSquareSolid,
|
2023-09-22 13:15:34 +02:00
|
|
|
faPlus as faPlusSolid,
|
2023-10-12 15:43:10 +02:00
|
|
|
faReceipt as faReceiptSolid,
|
2023-01-11 17:19:22 +01:00
|
|
|
faRss as faRssSolid,
|
|
|
|
faUsers as faUsersSolid,
|
2023-01-11 17:50:37 +01:00
|
|
|
faShare as faShareSolid,
|
2023-01-11 17:19:22 +01:00
|
|
|
faSun as faSunSolid,
|
2024-01-22 17:12:58 +01:00
|
|
|
faTerminal as faTerminalSolid,
|
2023-09-20 18:57:54 +02:00
|
|
|
faTrash as faTrashSolid,
|
|
|
|
faTrashCan as faTrashCanSolid,
|
2023-10-12 15:43:10 +02:00
|
|
|
faWallet as faWalletSolid,
|
2023-01-11 20:55:06 +01:00
|
|
|
faXmark as faXmarkSolid,
|
2023-01-11 17:19:22 +01:00
|
|
|
} from '@fortawesome/free-solid-svg-icons';
|
2023-09-20 18:57:54 +02:00
|
|
|
import { demoFunc } from './dees-icon.demo.js';
|
2020-12-02 17:11:04 +00:00
|
|
|
|
2025-04-13 17:32:44 +00:00
|
|
|
// Import Lucide icons and the createElement function
|
|
|
|
import * as lucideIcons from 'lucide';
|
|
|
|
import { createElement } from 'lucide';
|
|
|
|
|
|
|
|
// Collect FontAwesome icons
|
|
|
|
const faIcons = {
|
2020-12-02 17:11:04 +00:00
|
|
|
// normal
|
2023-01-13 00:30:56 +01:00
|
|
|
arrowRight: faArrowRightSolid,
|
2023-01-12 18:14:59 +01:00
|
|
|
arrowUpRightFromSquare: faArrowUpRightFromSquareSolid,
|
2023-01-11 17:19:22 +01:00
|
|
|
bell: faBellSolid,
|
|
|
|
bug: faBugSolid,
|
2023-01-12 00:07:39 +01:00
|
|
|
building: faBuildingSolid,
|
2023-01-11 18:32:05 +01:00
|
|
|
caretLeft: faCaretLeftSolid,
|
2023-01-13 00:30:56 +01:00
|
|
|
caretRight: faCaretRightSolid,
|
|
|
|
check: faCheckSolid,
|
2023-01-13 01:17:08 +01:00
|
|
|
circleInfo: faCircleInfoSolid,
|
|
|
|
circleCheck: faCircleCheckRegular,
|
|
|
|
circleCheckSolid: faCircleCheckSolid,
|
|
|
|
circleXmark: faCircleXmarkRegular,
|
|
|
|
circleXmarkSolid: faCircleXmarkSolid,
|
2023-09-14 19:43:26 +02:00
|
|
|
clockRotateLeft: faClockRotateLeftSolid,
|
2023-01-13 00:30:56 +01:00
|
|
|
copy: faCopyRegular,
|
|
|
|
copySolid: faCopySolid,
|
2023-01-11 17:19:22 +01:00
|
|
|
desktop: faDesktopSolid,
|
2023-01-16 11:51:21 +01:00
|
|
|
eye: faEyeSolid,
|
|
|
|
eyeSlash: faEyeSlashSolid,
|
2023-10-12 15:43:10 +02:00
|
|
|
fileInvoice: faFileInvoiceSolid,
|
|
|
|
fileInvoiceDoller: faFileInvoiceDollarSolid,
|
2024-01-15 19:42:15 +01:00
|
|
|
gear: faGearSolid,
|
2023-01-11 17:19:22 +01:00
|
|
|
grip: faGripSolid,
|
2024-01-21 22:37:39 +01:00
|
|
|
magnifyingGlass: faMagnifyingGlassSolid,
|
2023-01-11 17:19:22 +01:00
|
|
|
message: faMessageRegular,
|
2023-01-11 17:43:58 +01:00
|
|
|
messageSolid: faMessageSolid,
|
2023-10-12 15:43:10 +02:00
|
|
|
moneyCheckDollar: faMoneyCheckDollarSolid,
|
2023-03-09 17:08:19 +01:00
|
|
|
mugHot: faMugHotSolid,
|
2023-01-13 00:30:56 +01:00
|
|
|
minus: faMinusSolid,
|
2024-01-22 17:12:58 +01:00
|
|
|
networkWired: faNetworkWiredSolid,
|
2024-01-18 02:08:19 +01:00
|
|
|
paperclip: faPaperclipSolid,
|
2023-01-13 00:30:56 +01:00
|
|
|
paste: faPasteRegular,
|
|
|
|
pasteSolid: faPasteSolid,
|
|
|
|
penToSquare: faPenToSquareSolid,
|
2023-09-22 13:15:34 +02:00
|
|
|
plus: faPlusSolid,
|
2023-10-12 15:43:10 +02:00
|
|
|
receipt: faReceiptSolid,
|
2023-01-11 17:19:22 +01:00
|
|
|
rss: faRssSolid,
|
2023-01-11 17:50:37 +01:00
|
|
|
share: faShareSolid,
|
2023-01-11 17:19:22 +01:00
|
|
|
sun: faSunRegular,
|
|
|
|
sunSolid: faSunSolid,
|
2024-01-22 17:12:58 +01:00
|
|
|
terminal: faTerminalSolid,
|
2023-09-20 18:57:54 +02:00
|
|
|
trash: faTrashSolid,
|
|
|
|
trashSolid: faTrashSolid,
|
|
|
|
trashCan: faTrashCanRegular,
|
|
|
|
trashCanSolid: faTrashCanSolid,
|
|
|
|
users: faUsersSolid,
|
2023-10-12 15:43:10 +02:00
|
|
|
wallet: faWalletSolid,
|
2023-01-11 20:55:06 +01:00
|
|
|
xmark: faXmarkSolid,
|
2020-12-02 17:11:04 +00:00
|
|
|
// brands
|
2020-12-01 22:49:44 +00:00
|
|
|
facebook: faFacebook,
|
2020-12-02 17:11:04 +00:00
|
|
|
google: faGoogle,
|
2022-07-14 23:29:25 +02:00
|
|
|
instagram: faInstagram,
|
2020-12-01 22:49:44 +00:00
|
|
|
linkedin: faLinkedin,
|
2020-12-02 17:11:04 +00:00
|
|
|
medium: faMedium,
|
2020-12-03 11:12:11 +00:00
|
|
|
slack: faSlackHash,
|
2022-07-14 23:29:25 +02:00
|
|
|
tiktok: faTiktok,
|
2020-12-02 17:11:04 +00:00
|
|
|
twitter: faTwitter,
|
2020-12-01 22:24:54 +00:00
|
|
|
};
|
|
|
|
|
2025-04-13 17:32:44 +00:00
|
|
|
// Create a string literal type for all FA icons
|
|
|
|
type FAIconKey = keyof typeof faIcons;
|
|
|
|
|
|
|
|
// Create union types for the icons with prefixes
|
|
|
|
export type IconWithPrefix = `fa:${FAIconKey}` | `lucide:${string}`;
|
|
|
|
|
|
|
|
// Export only FontAwesome icons directly
|
|
|
|
export const icons = {
|
|
|
|
fa: faIcons
|
|
|
|
};
|
|
|
|
|
|
|
|
// Legacy type for backward compatibility
|
|
|
|
export type TIconKey = FAIconKey | `lucide:${string}`;
|
|
|
|
|
|
|
|
// Use a global static cache for all icons to reduce rendering
|
|
|
|
const iconCache = new Map<string, string>();
|
|
|
|
|
|
|
|
// Clear cache items occasionally to prevent memory leaks
|
|
|
|
const MAX_CACHE_SIZE = 500;
|
|
|
|
function limitCacheSize() {
|
|
|
|
if (iconCache.size > MAX_CACHE_SIZE) {
|
|
|
|
// Remove oldest entries (first 20% of items)
|
|
|
|
const keysToDelete = Array.from(iconCache.keys()).slice(0, MAX_CACHE_SIZE / 5);
|
|
|
|
keysToDelete.forEach(key => iconCache.delete(key));
|
|
|
|
}
|
|
|
|
}
|
2023-09-04 19:28:50 +02:00
|
|
|
|
2021-03-06 15:48:02 +00:00
|
|
|
declare global {
|
|
|
|
interface HTMLElementTagNameMap {
|
|
|
|
'dees-icon': DeesIcon;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-17 10:35:33 +00:00
|
|
|
@customElement('dees-icon')
|
2021-11-26 20:06:09 +01:00
|
|
|
export class DeesIcon extends DeesElement {
|
2023-09-20 18:57:54 +02:00
|
|
|
public static demo = demoFunc;
|
2020-09-17 10:35:33 +00:00
|
|
|
|
2025-04-13 17:32:44 +00:00
|
|
|
/**
|
|
|
|
* @deprecated Use the `icon` property instead with format "fa:iconName" or "lucide:iconName"
|
|
|
|
*/
|
|
|
|
@property({
|
|
|
|
type: String,
|
|
|
|
converter: {
|
|
|
|
// Convert attribute string to property (for reflected attributes)
|
|
|
|
fromAttribute: (value: string): TIconKey => value as TIconKey,
|
|
|
|
// Convert property to attribute (for reflection)
|
|
|
|
toAttribute: (value: TIconKey): string => value
|
|
|
|
}
|
|
|
|
})
|
|
|
|
public iconFA?: TIconKey;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The preferred icon property. Use format "fa:iconName" or "lucide:iconName"
|
|
|
|
* Examples: "fa:check", "lucide:menu"
|
|
|
|
*/
|
2023-01-12 18:14:59 +01:00
|
|
|
@property({
|
2025-04-13 17:32:44 +00:00
|
|
|
type: String,
|
|
|
|
converter: {
|
|
|
|
fromAttribute: (value: string): IconWithPrefix => value as IconWithPrefix,
|
|
|
|
toAttribute: (value: IconWithPrefix): string => value
|
|
|
|
}
|
2023-01-12 18:14:59 +01:00
|
|
|
})
|
2025-04-13 17:32:44 +00:00
|
|
|
public icon?: IconWithPrefix;
|
2020-09-17 10:35:33 +00:00
|
|
|
|
2025-04-13 17:32:44 +00:00
|
|
|
@property({ type: Number })
|
2023-01-11 20:52:37 +01:00
|
|
|
public iconSize: number;
|
2020-12-01 22:24:54 +00:00
|
|
|
|
2025-04-13 17:32:44 +00:00
|
|
|
@property({ type: String })
|
|
|
|
public color: string = 'currentColor';
|
|
|
|
|
|
|
|
@property({ type: Number })
|
|
|
|
public strokeWidth: number = 2;
|
|
|
|
|
|
|
|
// For tracking when we need to re-render
|
|
|
|
private lastIcon: IconWithPrefix | TIconKey | null = null;
|
|
|
|
private lastIconSize: number | null = null;
|
|
|
|
private lastColor: string | null = null;
|
|
|
|
private lastStrokeWidth: number | null = null;
|
|
|
|
|
2020-09-17 10:35:33 +00:00
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
domtools.elementBasic.setup();
|
|
|
|
}
|
|
|
|
|
2025-04-13 17:32:44 +00:00
|
|
|
/**
|
|
|
|
* Gets the effective icon value, supporting both the new `icon` property
|
|
|
|
* and the legacy `iconFA` property for backward compatibility.
|
|
|
|
* Prefers `icon` if both are set.
|
|
|
|
*/
|
|
|
|
private getEffectiveIcon(): IconWithPrefix | TIconKey | null {
|
|
|
|
// Prefer the new API
|
|
|
|
if (this.icon) {
|
|
|
|
return this.icon;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fall back to the old API
|
|
|
|
if (this.iconFA) {
|
|
|
|
// If iconFA is already in the proper format (lucide:name), use it directly
|
|
|
|
if (this.iconFA.startsWith('lucide:')) {
|
|
|
|
return this.iconFA;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For FontAwesome icons with no prefix, add the prefix
|
|
|
|
return `fa:${this.iconFA}` as IconWithPrefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses an icon string into its type and name parts
|
|
|
|
* @param iconStr The icon string in format "type:name"
|
|
|
|
* @returns Object with type and name properties
|
|
|
|
*/
|
|
|
|
private parseIconString(iconStr: string): { type: 'fa' | 'lucide', name: string } {
|
|
|
|
if (iconStr.startsWith('fa:')) {
|
|
|
|
return {
|
|
|
|
type: 'fa',
|
|
|
|
name: iconStr.substring(3) // Remove 'fa:' prefix
|
|
|
|
};
|
|
|
|
} else if (iconStr.startsWith('lucide:')) {
|
|
|
|
return {
|
|
|
|
type: 'lucide',
|
|
|
|
name: iconStr.substring(7) // Remove 'lucide:' prefix
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
// For backward compatibility, assume FontAwesome if no prefix
|
|
|
|
return {
|
|
|
|
type: 'fa',
|
|
|
|
name: iconStr
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private renderLucideIcon(iconName: string): string {
|
|
|
|
// Create a cache key based on all visual properties
|
|
|
|
const cacheKey = `lucide:${iconName}:${this.iconSize}:${this.color}:${this.strokeWidth}`;
|
|
|
|
|
|
|
|
// Check if we already have this icon in the cache
|
|
|
|
if (iconCache.has(cacheKey)) {
|
|
|
|
return iconCache.get(cacheKey) || '';
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Get the Pascal case icon name (Menu instead of menu)
|
|
|
|
const pascalCaseName = iconName.charAt(0).toUpperCase() + iconName.slice(1);
|
|
|
|
|
|
|
|
// Check if the icon exists in lucideIcons
|
|
|
|
if (!lucideIcons[pascalCaseName]) {
|
|
|
|
console.warn(`Lucide icon '${pascalCaseName}' not found in lucideIcons object`);
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use the exact pattern from Lucide documentation
|
|
|
|
const svgElement = createElement(lucideIcons[pascalCaseName], {
|
|
|
|
color: this.color,
|
|
|
|
size: this.iconSize,
|
|
|
|
strokeWidth: this.strokeWidth
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!svgElement) {
|
|
|
|
console.warn(`createElement returned empty result for ${pascalCaseName}`);
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the HTML
|
|
|
|
const result = svgElement.outerHTML;
|
|
|
|
|
|
|
|
// Cache the result for future use
|
|
|
|
iconCache.set(cacheKey, result);
|
|
|
|
limitCacheSize();
|
|
|
|
|
|
|
|
return result;
|
|
|
|
} catch (error) {
|
|
|
|
console.error(`Error rendering Lucide icon ${iconName}:`, error);
|
|
|
|
|
|
|
|
// Create a fallback SVG with the icon name
|
|
|
|
return `<svg xmlns="http://www.w3.org/2000/svg" width="${this.iconSize}" height="${this.iconSize}" viewBox="0 0 24 24" fill="none" stroke="${this.color}" stroke-width="${this.strokeWidth}" stroke-linecap="round" stroke-linejoin="round">
|
|
|
|
<text x="50%" y="50%" font-size="6" text-anchor="middle" dominant-baseline="middle" fill="${this.color}">${iconName}</text>
|
|
|
|
</svg>`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-11 18:15:31 +01:00
|
|
|
public static styles = [
|
|
|
|
cssManager.defaultStyles,
|
|
|
|
css`
|
|
|
|
:host {
|
2025-04-13 17:32:44 +00:00
|
|
|
display: inline-flex;
|
2023-01-11 20:52:37 +01:00
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
2025-04-13 17:32:44 +00:00
|
|
|
line-height: 1;
|
|
|
|
vertical-align: middle;
|
2023-01-11 18:15:31 +01:00
|
|
|
}
|
2025-04-13 17:32:44 +00:00
|
|
|
|
|
|
|
/* Improve rendering performance */
|
|
|
|
#iconContainer svg {
|
|
|
|
display: block;
|
|
|
|
height: 100%;
|
|
|
|
width: 100%;
|
|
|
|
will-change: transform; /* Helps with animations */
|
|
|
|
contain: strict; /* Performance optimization */
|
2023-01-12 19:19:31 +01:00
|
|
|
}
|
2023-01-11 18:15:31 +01:00
|
|
|
`,
|
|
|
|
];
|
|
|
|
|
2020-09-17 10:35:33 +00:00
|
|
|
public render() {
|
|
|
|
return html`
|
|
|
|
${domtools.elementBasic.styles}
|
|
|
|
<style>
|
2025-04-13 17:32:44 +00:00
|
|
|
#iconContainer {
|
|
|
|
width: ${this.iconSize}px;
|
2023-01-11 17:19:22 +01:00
|
|
|
height: ${this.iconSize}px;
|
2020-09-17 10:35:33 +00:00
|
|
|
}
|
|
|
|
</style>
|
2023-01-11 17:19:22 +01:00
|
|
|
<div id="iconContainer"></div>
|
2020-09-17 10:35:33 +00:00
|
|
|
`;
|
|
|
|
}
|
2020-12-01 22:24:54 +00:00
|
|
|
|
2025-04-13 17:32:44 +00:00
|
|
|
public updated() {
|
|
|
|
// If size is not specified, use font size as a base
|
2023-01-11 20:52:37 +01:00
|
|
|
if (!this.iconSize) {
|
|
|
|
this.iconSize = parseInt(globalThis.getComputedStyle(this).fontSize.replace(/\D/g,''));
|
|
|
|
}
|
2025-04-13 17:32:44 +00:00
|
|
|
|
|
|
|
// Get the effective icon (either from icon or iconFA property)
|
|
|
|
const effectiveIcon = this.getEffectiveIcon();
|
|
|
|
|
|
|
|
// Check if we actually need to update the icon
|
|
|
|
// This prevents unnecessary DOM operations when properties haven't changed
|
|
|
|
if (this.lastIcon === effectiveIcon &&
|
|
|
|
this.lastIconSize === this.iconSize &&
|
|
|
|
this.lastColor === this.color &&
|
|
|
|
this.lastStrokeWidth === this.strokeWidth) {
|
|
|
|
return; // No visual changes - skip update
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update our "last properties" for future change detection
|
|
|
|
this.lastIcon = effectiveIcon;
|
|
|
|
this.lastIconSize = this.iconSize;
|
|
|
|
this.lastColor = this.color;
|
|
|
|
this.lastStrokeWidth = this.strokeWidth;
|
|
|
|
|
|
|
|
const container = this.shadowRoot?.querySelector('#iconContainer');
|
|
|
|
if (!container || !effectiveIcon) return;
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Parse the icon string to get type and name
|
|
|
|
const { type, name } = this.parseIconString(effectiveIcon);
|
|
|
|
|
|
|
|
if (type === 'lucide') {
|
|
|
|
// For Lucide, use direct DOM manipulation as shown in the docs
|
|
|
|
// This approach avoids HTML string issues
|
|
|
|
container.innerHTML = ''; // Clear container
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Convert to PascalCase
|
|
|
|
const pascalCaseName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
|
|
|
|
|
|
if (lucideIcons[pascalCaseName]) {
|
|
|
|
// Use the documented pattern from Lucide docs
|
|
|
|
const svgElement = createElement(lucideIcons[pascalCaseName], {
|
|
|
|
color: this.color,
|
|
|
|
size: this.iconSize,
|
|
|
|
strokeWidth: this.strokeWidth
|
|
|
|
});
|
|
|
|
|
|
|
|
if (svgElement) {
|
|
|
|
// Directly append the element
|
|
|
|
container.appendChild(svgElement);
|
|
|
|
return; // Exit early since we've added the element
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we reach here, something went wrong
|
|
|
|
throw new Error(`Could not create element for ${pascalCaseName}`);
|
|
|
|
} catch (error) {
|
|
|
|
console.error(`Error rendering Lucide icon:`, error);
|
|
|
|
|
|
|
|
// Fall back to the string-based approach
|
|
|
|
const iconHtml = this.renderLucideIcon(name);
|
|
|
|
if (iconHtml) {
|
|
|
|
container.innerHTML = iconHtml;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Use FontAwesome rendering via HTML string
|
|
|
|
const faIcon = icons.fa[name as FAIconKey];
|
|
|
|
if (faIcon) {
|
|
|
|
const iconHtml = icon(faIcon).html[0];
|
|
|
|
container.innerHTML = iconHtml;
|
|
|
|
} else {
|
|
|
|
console.warn(`FontAwesome icon not found: ${name}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error(`Error updating icon ${effectiveIcon}:`, error);
|
2020-12-01 22:24:54 +00:00
|
|
|
}
|
|
|
|
}
|
2025-04-13 17:32:44 +00:00
|
|
|
|
|
|
|
// Clean up resources when element is removed
|
|
|
|
async disconnectedCallback() {
|
|
|
|
super.disconnectedCallback();
|
|
|
|
|
|
|
|
// Clear our references
|
|
|
|
this.lastIcon = null;
|
|
|
|
this.lastIconSize = null;
|
|
|
|
this.lastColor = null;
|
|
|
|
this.lastStrokeWidth = null;
|
|
|
|
}
|
|
|
|
}
|