Feature Enhancement: Enable Icon Integration for Custom Components (#1433)

This pull request introduces a significant enhancement to our project,
specifically aimed at empowering developers to seamlessly integrate
icons into custom components. The goal is to provide a flexible and
intuitive way to enhance UI elements with visual cues, thereby improving
user experience and interface aesthetics.
This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-02-16 08:49:56 -03:00 • committed by GitHub
commit 9928919f6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 43 additions and 18 deletions

View file

@ -6,6 +6,7 @@ from typing import Any, ClassVar, Optional
import emoji import emoji
from cachetools import TTLCache, cachedmethod from cachetools import TTLCache, cachedmethod
from fastapi import HTTPException from fastapi import HTTPException
from langflow.interface.custom.code_parser import CodeParser from langflow.interface.custom.code_parser import CodeParser
from langflow.utils import validate from langflow.utils import validate
@ -20,7 +21,9 @@ class ComponentFunctionEntrypointNameNullError(HTTPException):
class Component: class Component:
ERROR_CODE_NULL: ClassVar[str] = "Python code must be provided." ERROR_CODE_NULL: ClassVar[str] = "Python code must be provided."
ERROR_FUNCTION_ENTRYPOINT_NAME_NULL: ClassVar[str] = "The name of the entrypoint function must be provided." ERROR_FUNCTION_ENTRYPOINT_NAME_NULL: ClassVar[str] = (
"The name of the entrypoint function must be provided."
)
code: Optional[str] = None code: Optional[str] = None
_function_entrypoint_name: str = "build" _function_entrypoint_name: str = "build"
@ -96,11 +99,12 @@ class Component:
# we are going to use the emoji library to validate the emoji # we are going to use the emoji library to validate the emoji
# emojis can be defined using the :emoji_name: syntax # emojis can be defined using the :emoji_name: syntax
if not value.startswith(":") or not value.endswith(":"): if not value.startswith(":") or not value.endswith(":"):
raise ValueError("Invalid emoji. Please use the :emoji_name: syntax.") warnings.warn("Invalid emoji. Please use the :emoji_name: syntax.")
return value
emoji_value = emoji.emojize(value, variant="emoji_type") emoji_value = emoji.emojize(value, variant="emoji_type")
if value == emoji_value: if value == emoji_value:
raise ValueError(f"Invalid emoji. {value} is not a valid emoji.") warnings.warn(f"Invalid emoji. {value} is not a valid emoji.")
return value
return emoji_value return emoji_value
def build(self, *args: Any, **kwargs: Any) -> Any: def build(self, *args: Any, **kwargs: Any) -> Any:

View file

@ -1,4 +1,4 @@
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { NodeToolbar } from "reactflow"; import { NodeToolbar } from "reactflow";
import ShadTooltip from "../../components/ShadTooltipComponent"; import ShadTooltip from "../../components/ShadTooltipComponent";
import Tooltip from "../../components/TooltipComponent"; import Tooltip from "../../components/TooltipComponent";
@ -107,6 +107,39 @@ export default function GenericNode({
const nameEditable = data.node?.flow || data.type === "CustomComponent"; const nameEditable = data.node?.flow || data.type === "CustomComponent";
const emojiRegex = /\p{Emoji}/u;
const isEmoji = emojiRegex.test(data?.node?.icon!);
const iconNodeRender = useCallback(() => {
const iconElement = data?.node?.icon;
const iconColor = nodeColors[types[data.type]];
const iconName =
iconElement || (data.node?.flow ? "group_components" : name);
const iconClassName = `generic-node-icon ${
!showNode ? "absolute inset-x-6 h-12 w-12" : ""
}`;
if (iconElement && isEmoji) {
return nodeIconFragment(iconElement);
} else {
return checkNodeIconFragment(iconColor, iconName, iconClassName);
}
}, [data, isEmoji, name, showNode]);
const nodeIconFragment = (icon) => {
return <span className="text-lg">{icon}</span>;
};
const checkNodeIconFragment = (iconColor, iconName, iconClassName) => {
return (
<IconComponent
name={iconName}
className={iconClassName}
iconColor={iconColor}
/>
);
};
return ( return (
<> <>
<NodeToolbar> <NodeToolbar>
@ -156,19 +189,7 @@ export default function GenericNode({
(!showNode && "justify-center") (!showNode && "justify-center")
} }
> >
{data?.node?.icon ? ( {iconNodeRender()}
<span className="text-lg">{data?.node?.icon}</span>
) : (
<IconComponent
name={data.node?.flow ? "group_components" : name}
className={
"generic-node-icon " +
(!showNode ? "absolute inset-x-6 h-12 w-12" : "")
}
iconColor={`${nodeColors[types[data.type]]}`}
/>
)}
{showNode && ( {showNode && (
<div className="generic-node-tooltip-div"> <div className="generic-node-tooltip-div">
{nameEditable && inputName ? ( {nameEditable && inputName ? (