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:
commit
9928919f6d
2 changed files with 43 additions and 18 deletions
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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 ? (
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue