From 2478d81ee4a19b0a544686b32713ac7a4b9862ef Mon Sep 17 00:00:00 2001 From: Colin Dawson Date: Fri, 13 Mar 2026 23:11:37 +0000 Subject: [PATCH] Started working on added task icons to the application. --- .../common/FontAwesomeStringIcon.tsx | 105 ++++++++++++++++++ .../services/WorkflowTemplateService.ts | 1 + 2 files changed, 106 insertions(+) create mode 100644 src/components/common/FontAwesomeStringIcon.tsx diff --git a/src/components/common/FontAwesomeStringIcon.tsx b/src/components/common/FontAwesomeStringIcon.tsx new file mode 100644 index 0000000..0ec97ee --- /dev/null +++ b/src/components/common/FontAwesomeStringIcon.tsx @@ -0,0 +1,105 @@ +import React from "react"; +import { + library, + type IconName, + type IconPrefix, + type IconProp, +} from "@fortawesome/fontawesome-svg-core"; +import { fas } from "@fortawesome/free-solid-svg-icons"; +import { fad } from "@fortawesome/pro-duotone-svg-icons"; +import { fal } from "@fortawesome/pro-light-svg-icons"; +import { far } from "@fortawesome/pro-regular-svg-icons"; +import { fat } from "@fortawesome/pro-thin-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +type FontAwesomeStringIconProps = { + icon?: string; + className?: string; +}; + +const iconPrefixByToken: Record = { + "fa-brands": "fab", + fab: "fab", + "fa-duotone": "fad", + fad: "fad", + "fa-light": "fal", + fal: "fal", + "fa-regular": "far", + far: "far", + "fa-solid": "fas", + fas: "fas", + "fa-thin": "fat", + fat: "fat", +}; + +let hasRegisteredIconPacks = false; + +function ensureIconPacksRegistered(): void { + if (hasRegisteredIconPacks) { + return; + } + + library.add(fas, far, fal, fat, fad); + hasRegisteredIconPacks = true; +} + +function toKebabCase(value: string): string { + return value + .replace(/^fa/, "") + .replace(/([a-z0-9])([A-Z])/g, "$1-$2") + .replace(/_/g, "-") + .replace(/\s+/g, "-") + .replace(/^-+/, "") + .toLowerCase(); +} + +function parseIcon(icon?: string): IconProp | undefined { + if (!icon) { + return undefined; + } + + const tokens = icon.trim().split(/\s+/).filter(Boolean); + let prefix: IconPrefix = "fas"; + let iconName = ""; + + tokens.forEach((token) => { + const mappedPrefix = iconPrefixByToken[token]; + if (mappedPrefix) { + prefix = mappedPrefix; + return; + } + + if (token.startsWith("fa-")) { + iconName = token.slice(3); + return; + } + + if (!iconName) { + iconName = token; + } + }); + + if (!iconName) { + iconName = toKebabCase(icon); + } else if (!iconName.includes("-")) { + iconName = toKebabCase(iconName); + } + + return iconName ? [prefix, iconName as IconName] : undefined; +} + +const FontAwesomeStringIcon: React.FC = ({ + icon, + className, +}) => { + const resolvedIcon = React.useMemo(() => parseIcon(icon), [icon]); + + if (!resolvedIcon) { + return null; + } + + ensureIconPacksRegistered(); + return ; +}; + +export default FontAwesomeStringIcon; diff --git a/src/modules/manager/workflowTemplates/services/WorkflowTemplateService.ts b/src/modules/manager/workflowTemplates/services/WorkflowTemplateService.ts index 8c88d14..98ebeb1 100644 --- a/src/modules/manager/workflowTemplates/services/WorkflowTemplateService.ts +++ b/src/modules/manager/workflowTemplates/services/WorkflowTemplateService.ts @@ -46,6 +46,7 @@ export interface CreateWorkflowTemplateVersion extends FormData { export interface TaskMetadata { taskType: string; displayName: string; + icon: string; capabilities: string[]; outcomeLabel: string; outcomes: string[];