refactor: Enhance InputList Component UI/UX (#5726)
* [LFOSS-74]: input list UI improvements * ✨ (frontend): Add new component 'DeleteButtonInputList' to improve user experience by providing a button to delete items in a list 📝 (frontend): Comment out unused code related to duplicating input items for now, to be revisited in a future release 📝 (frontend): Add a comment to indicate that the 'DropdownMenuInputList' feature will be added back in a future release 📝 (frontend): Update CSS styling for 'hit-area-icon' class to adjust size to h-7 and w-7 for consistency 📝 (frontend): Adjust color values in CSS to improve visual appearance, changing smooth-red to a new shade * ✨ (keypairListComponent/index.tsx): simplify conditional styling logic for list items to improve readability and maintainability * 📝 (delete-button-input-list.tsx): Remove unused imports and update function parameter type for removeInput to specify React.MouseEvent 📝 (delete-button-input-list.tsx): Update button className and IconComponent className for better styling and readability 📝 (delete-button-input-list.tsx): Update ICON_STROKE_WIDTH constant value from 1.25 to 1.5 for better icon appearance 📝 (index.tsx): Update InputListComponent to render DeleteButtonInputList component conditionally based on value length 📝 (constants.ts): Update ICON_STROKE_WIDTH constant value from 1.25 to 1.5 for better icon appearance 📝 (inputListComponent.spec.ts): Update test cases to use updated test IDs for delete buttons and adjust expected input values to match changes in component behavior * [autofix.ci] apply automated fixes * 🔧 (applies.css): adjust positioning of input-slider-text to fix alignment issue and improve user experience * 🐛 (button-input-list.tsx): fix button position by adjusting translate-x value to align properly on the page * ✨ (index.tsx): add visual indicator for focused input element in the list to improve user experience --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
This commit is contained in:
parent
317c743906
commit
5c3c680c41
9 changed files with 116 additions and 80 deletions
|
|
@ -219,7 +219,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "code",
|
||||
"value": "import re\n\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Load and retrive data from specified URLs.\"\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.\",\n options=[\"Text\", \"Raw HTML\"],\n value=\"Text\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\" # optional protocol\n r\"(www\\.)?\" # optional www\n r\"([a-zA-Z0-9.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,})?\" # top-level domain\n r\"(:\\d+)?\" # optional port\n r\"(\\/[^\\s]*)?$\", # optional path\n re.IGNORECASE,\n )\n\n if not url_regex.match(string):\n msg = f\"Invalid URL: {string}\"\n raise ValueError(msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n if self.format == \"Raw HTML\":\n loader = AsyncHtmlLoader(web_path=urls, encoding=\"utf-8\")\n else:\n loader = WebBaseLoader(web_paths=urls, encoding=\"utf-8\")\n docs = loader.load()\n data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n\n def as_dataframe(self) -> DataFrame:\n return DataFrame(self.fetch_content())\n"
|
||||
"value": "import re\n\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Load and retrive data from specified URLs.\"\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.\",\n options=[\"Text\", \"Raw HTML\"],\n value=\"Text\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\" # optional protocol\n r\"(www\\.)?\" # optional www\n r\"([a-zA-Z0-9.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,})?\" # top-level domain\n r\"(:\\d+)?\" # optional port\n r\"(\\/[^\\s]*)?$\", # optional path\n re.IGNORECASE,\n )\n\n if not url_regex.match(string):\n msg = f\"Invalid URL: {string}\"\n raise ValueError(msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n if self.format == \"Raw HTML\":\n loader = AsyncHtmlLoader(web_path=urls, encoding=\"utf-8\")\n else:\n loader = WebBaseLoader(web_paths=urls, encoding=\"utf-8\")\n docs = loader.load()\n data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n\n def as_dataframe(self) -> DataFrame:\n return DataFrame(self.fetch_content())\n"
|
||||
},
|
||||
"format": {
|
||||
"_input_type": "DropdownInput",
|
||||
|
|
|
|||
|
|
@ -1686,7 +1686,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "code",
|
||||
"value": "import re\n\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Load and retrive data from specified URLs.\"\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.\",\n options=[\"Text\", \"Raw HTML\"],\n value=\"Text\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\" # optional protocol\n r\"(www\\.)?\" # optional www\n r\"([a-zA-Z0-9.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,})?\" # top-level domain\n r\"(:\\d+)?\" # optional port\n r\"(\\/[^\\s]*)?$\", # optional path\n re.IGNORECASE,\n )\n\n if not url_regex.match(string):\n msg = f\"Invalid URL: {string}\"\n raise ValueError(msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n if self.format == \"Raw HTML\":\n loader = AsyncHtmlLoader(web_path=urls, encoding=\"utf-8\")\n else:\n loader = WebBaseLoader(web_paths=urls, encoding=\"utf-8\")\n docs = loader.load()\n data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n\n def as_dataframe(self) -> DataFrame:\n return DataFrame(self.fetch_content())\n"
|
||||
"value": "import re\n\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Load and retrive data from specified URLs.\"\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.\",\n options=[\"Text\", \"Raw HTML\"],\n value=\"Text\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\" # optional protocol\n r\"(www\\.)?\" # optional www\n r\"([a-zA-Z0-9.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,})?\" # top-level domain\n r\"(:\\d+)?\" # optional port\n r\"(\\/[^\\s]*)?$\", # optional path\n re.IGNORECASE,\n )\n\n if not url_regex.match(string):\n msg = f\"Invalid URL: {string}\"\n raise ValueError(msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n if self.format == \"Raw HTML\":\n loader = AsyncHtmlLoader(web_path=urls, encoding=\"utf-8\")\n else:\n loader = WebBaseLoader(web_paths=urls, encoding=\"utf-8\")\n docs = loader.load()\n data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n\n def as_dataframe(self) -> DataFrame:\n return DataFrame(self.fetch_content())\n"
|
||||
},
|
||||
"format": {
|
||||
"_input_type": "DropdownInput",
|
||||
|
|
@ -1832,7 +1832,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "code",
|
||||
"value": "import re\n\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Load and retrive data from specified URLs.\"\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.\",\n options=[\"Text\", \"Raw HTML\"],\n value=\"Text\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\" # optional protocol\n r\"(www\\.)?\" # optional www\n r\"([a-zA-Z0-9.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,})?\" # top-level domain\n r\"(:\\d+)?\" # optional port\n r\"(\\/[^\\s]*)?$\", # optional path\n re.IGNORECASE,\n )\n\n if not url_regex.match(string):\n msg = f\"Invalid URL: {string}\"\n raise ValueError(msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n if self.format == \"Raw HTML\":\n loader = AsyncHtmlLoader(web_path=urls, encoding=\"utf-8\")\n else:\n loader = WebBaseLoader(web_paths=urls, encoding=\"utf-8\")\n docs = loader.load()\n data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n\n def as_dataframe(self) -> DataFrame:\n return DataFrame(self.fetch_content())\n"
|
||||
"value": "import re\n\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Load and retrive data from specified URLs.\"\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.\",\n options=[\"Text\", \"Raw HTML\"],\n value=\"Text\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\" # optional protocol\n r\"(www\\.)?\" # optional www\n r\"([a-zA-Z0-9.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,})?\" # top-level domain\n r\"(:\\d+)?\" # optional port\n r\"(\\/[^\\s]*)?$\", # optional path\n re.IGNORECASE,\n )\n\n if not url_regex.match(string):\n msg = f\"Invalid URL: {string}\"\n raise ValueError(msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n if self.format == \"Raw HTML\":\n loader = AsyncHtmlLoader(web_path=urls, encoding=\"utf-8\")\n else:\n loader = WebBaseLoader(web_paths=urls, encoding=\"utf-8\")\n docs = loader.load()\n data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n\n def as_dataframe(self) -> DataFrame:\n return DataFrame(self.fetch_content())\n"
|
||||
},
|
||||
"format": {
|
||||
"_input_type": "DropdownInput",
|
||||
|
|
@ -1984,7 +1984,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "code",
|
||||
"value": "import re\n\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Load and retrive data from specified URLs.\"\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.\",\n options=[\"Text\", \"Raw HTML\"],\n value=\"Text\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\" # optional protocol\n r\"(www\\.)?\" # optional www\n r\"([a-zA-Z0-9.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,})?\" # top-level domain\n r\"(:\\d+)?\" # optional port\n r\"(\\/[^\\s]*)?$\", # optional path\n re.IGNORECASE,\n )\n\n if not url_regex.match(string):\n msg = f\"Invalid URL: {string}\"\n raise ValueError(msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n if self.format == \"Raw HTML\":\n loader = AsyncHtmlLoader(web_path=urls, encoding=\"utf-8\")\n else:\n loader = WebBaseLoader(web_paths=urls, encoding=\"utf-8\")\n docs = loader.load()\n data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n\n def as_dataframe(self) -> DataFrame:\n return DataFrame(self.fetch_content())\n"
|
||||
"value": "import re\n\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Load and retrive data from specified URLs.\"\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.\",\n options=[\"Text\", \"Raw HTML\"],\n value=\"Text\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\" # optional protocol\n r\"(www\\.)?\" # optional www\n r\"([a-zA-Z0-9.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,})?\" # top-level domain\n r\"(:\\d+)?\" # optional port\n r\"(\\/[^\\s]*)?$\", # optional path\n re.IGNORECASE,\n )\n\n if not url_regex.match(string):\n msg = f\"Invalid URL: {string}\"\n raise ValueError(msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n if self.format == \"Raw HTML\":\n loader = AsyncHtmlLoader(web_path=urls, encoding=\"utf-8\")\n else:\n loader = WebBaseLoader(web_paths=urls, encoding=\"utf-8\")\n docs = loader.load()\n data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n\n def as_dataframe(self) -> DataFrame:\n return DataFrame(self.fetch_content())\n"
|
||||
},
|
||||
"format": {
|
||||
"_input_type": "DropdownInput",
|
||||
|
|
|
|||
|
|
@ -2640,7 +2640,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "code",
|
||||
"value": "import re\n\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Load and retrive data from specified URLs.\"\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.\",\n options=[\"Text\", \"Raw HTML\"],\n value=\"Text\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\" # optional protocol\n r\"(www\\.)?\" # optional www\n r\"([a-zA-Z0-9.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,})?\" # top-level domain\n r\"(:\\d+)?\" # optional port\n r\"(\\/[^\\s]*)?$\", # optional path\n re.IGNORECASE,\n )\n\n if not url_regex.match(string):\n msg = f\"Invalid URL: {string}\"\n raise ValueError(msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n if self.format == \"Raw HTML\":\n loader = AsyncHtmlLoader(web_path=urls, encoding=\"utf-8\")\n else:\n loader = WebBaseLoader(web_paths=urls, encoding=\"utf-8\")\n docs = loader.load()\n data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n\n def as_dataframe(self) -> DataFrame:\n return DataFrame(self.fetch_content())\n"
|
||||
"value": "import re\n\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Load and retrive data from specified URLs.\"\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.\",\n options=[\"Text\", \"Raw HTML\"],\n value=\"Text\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\" # optional protocol\n r\"(www\\.)?\" # optional www\n r\"([a-zA-Z0-9.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,})?\" # top-level domain\n r\"(:\\d+)?\" # optional port\n r\"(\\/[^\\s]*)?$\", # optional path\n re.IGNORECASE,\n )\n\n if not url_regex.match(string):\n msg = f\"Invalid URL: {string}\"\n raise ValueError(msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n if self.format == \"Raw HTML\":\n loader = AsyncHtmlLoader(web_path=urls, encoding=\"utf-8\")\n else:\n loader = WebBaseLoader(web_paths=urls, encoding=\"utf-8\")\n docs = loader.load()\n data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n\n def as_dataframe(self) -> DataFrame:\n return DataFrame(self.fetch_content())\n"
|
||||
},
|
||||
"format": {
|
||||
"_input_type": "DropdownInput",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
import IconComponent from "@/components/common/genericIconComponent";
|
||||
import { ICON_STROKE_WIDTH } from "@/constants/constants";
|
||||
import { cn } from "@/utils/utils";
|
||||
import { getTestId } from "../helpers/get-test-id";
|
||||
|
||||
export const DeleteButtonInputList = ({
|
||||
index,
|
||||
removeInput,
|
||||
disabled,
|
||||
editNode,
|
||||
componentName,
|
||||
}: {
|
||||
index: number;
|
||||
removeInput: (e: React.MouseEvent) => void;
|
||||
disabled: boolean;
|
||||
editNode: boolean;
|
||||
componentName: string;
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
disabled={disabled}
|
||||
onClick={removeInput}
|
||||
data-testid={getTestId("delete", index, editNode, componentName)}
|
||||
className={cn(
|
||||
"hit-area-icon delete-btn-group flex items-center justify-center",
|
||||
disabled
|
||||
? "pointer-events-none bg-background hover:bg-background"
|
||||
: "bg-background hover:bg-smooth-red hover:text-destructive",
|
||||
editNode && "h-4 w-4",
|
||||
)}
|
||||
>
|
||||
<IconComponent
|
||||
name="X"
|
||||
className={cn(
|
||||
"icon-size justify-self-center text-muted-foreground",
|
||||
!disabled &&
|
||||
"hover:cursor-pointer [.delete-btn-group:hover_&]:text-destructive",
|
||||
)}
|
||||
strokeWidth={ICON_STROKE_WIDTH}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteButtonInputList;
|
||||
|
|
@ -10,6 +10,7 @@ import { GRADIENT_CLASS } from "@/constants/constants";
|
|||
import { cn } from "../../../../../utils/utils";
|
||||
import { getPlaceholder } from "../../helpers/get-placeholder-disabled";
|
||||
import { InputListComponentType, InputProps } from "../../types";
|
||||
import { DeleteButtonInputList } from "./components/delete-button-input-list";
|
||||
|
||||
export default function InputListComponent({
|
||||
value = [""],
|
||||
|
|
@ -66,16 +67,16 @@ export default function InputListComponent({
|
|||
[value, handleOnNewValue],
|
||||
);
|
||||
|
||||
const handleDuplicateInput = useCallback(
|
||||
(index: number, e: React.MouseEvent | KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
const newInputList = _.cloneDeep(value);
|
||||
newInputList.splice(index, 0, newInputList[index]);
|
||||
handleOnNewValue({ value: newInputList });
|
||||
setDropdownOpen(null);
|
||||
},
|
||||
[value, handleOnNewValue],
|
||||
);
|
||||
// const handleDuplicateInput = useCallback(
|
||||
// (index: number, e: React.MouseEvent | KeyboardEvent) => {
|
||||
// e.preventDefault();
|
||||
// const newInputList = _.cloneDeep(value);
|
||||
// newInputList.splice(index, 0, newInputList[index]);
|
||||
// handleOnNewValue({ value: newInputList });
|
||||
// setDropdownOpen(null);
|
||||
// },
|
||||
// [value, handleOnNewValue],
|
||||
// );
|
||||
|
||||
return (
|
||||
<div className={cn("w-full", editNode && "max-h-52")}>
|
||||
|
|
@ -92,16 +93,29 @@ export default function InputListComponent({
|
|||
|
||||
<div className="mt-2 flex w-full flex-col gap-3">
|
||||
{value.map((singleValue, index) => (
|
||||
<div key={index} className="relative flex w-full items-center gap-2">
|
||||
<div className="group flex flex-1 items-center rounded-md bg-background">
|
||||
<div key={index} className="flex w-full items-center">
|
||||
{focusedIndex !== index && !disabled && (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute z-50 h-6 w-16",
|
||||
editNode ? "translate-x-[12rem]" : "translate-x-[11.1rem]",
|
||||
)}
|
||||
style={{
|
||||
pointerEvents: "none",
|
||||
background: GRADIENT_CLASS,
|
||||
}}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
<div className="group relative flex-1">
|
||||
<Input
|
||||
ref={index === 0 ? inputRef : null}
|
||||
disabled={disabled}
|
||||
type="text"
|
||||
value={singleValue}
|
||||
className={cn(
|
||||
"w-full text-primary",
|
||||
editNode ? "input-edit-node pr-6" : "pr-10",
|
||||
"w-full pr-10 text-primary",
|
||||
editNode ? "input-edit-node" : "",
|
||||
disabled ? "disabled-state" : "",
|
||||
)}
|
||||
placeholder={getPlaceholder(disabled, placeholder)}
|
||||
|
|
@ -113,20 +127,20 @@ export default function InputListComponent({
|
|||
onBlur={() => setFocusedIndex(null)}
|
||||
/>
|
||||
|
||||
{focusedIndex !== index && !disabled && (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute h-6 w-16",
|
||||
editNode ? "translate-x-[11rem]" : "translate-x-[11.1rem]",
|
||||
)}
|
||||
style={{
|
||||
pointerEvents: "none",
|
||||
background: GRADIENT_CLASS,
|
||||
}}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{value.length > 1 && (
|
||||
<div className="absolute right-2 top-1/2 -translate-y-1/2">
|
||||
<DeleteButtonInputList
|
||||
index={index}
|
||||
removeInput={(e) => removeInput(index, e)}
|
||||
disabled={disabled}
|
||||
editNode={editNode}
|
||||
componentName={componentName || ""}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/*
|
||||
We will add this back in a future release
|
||||
{!disabled && (
|
||||
<DropdownMenuInputList
|
||||
index={index}
|
||||
|
|
@ -137,7 +151,7 @@ export default function InputListComponent({
|
|||
removeInput={removeInput}
|
||||
canDelete={value.length > 1}
|
||||
/>
|
||||
)}
|
||||
)} */}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -998,7 +998,7 @@ export const GRADIENT_CLASS =
|
|||
|
||||
export const RECEIVING_INPUT_VALUE = "Receiving input";
|
||||
|
||||
export const ICON_STROKE_WIDTH = 1.25;
|
||||
export const ICON_STROKE_WIDTH = 1.5;
|
||||
|
||||
export const DEFAULT_PLACEHOLDER = "Type something...";
|
||||
|
||||
|
|
|
|||
|
|
@ -1254,7 +1254,7 @@
|
|||
}
|
||||
|
||||
.hit-area-icon {
|
||||
@apply h-8 w-8 rounded-md;
|
||||
@apply h-7 w-7 rounded-md;
|
||||
}
|
||||
|
||||
.node-toolbar-buttons {
|
||||
|
|
@ -1274,7 +1274,7 @@
|
|||
}
|
||||
|
||||
.input-slider-text {
|
||||
@apply absolute bottom-[4.2rem] right-3 w-14 cursor-text rounded-sm px-2 py-[1px] text-center hover:ring-[1px] hover:ring-slider-input-border;
|
||||
@apply absolute right-3 w-14 -translate-y-3.5 cursor-text rounded-sm px-2 py-[1px] text-center hover:ring-[1px] hover:ring-slider-input-border;
|
||||
}
|
||||
|
||||
.btn-add-input-list {
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@
|
|||
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||
--placeholder: 240 5% 64.9%;
|
||||
--hard-zinc: 240 5.2% 33.9%;
|
||||
--smooth-red: 0 93.3% 94.1%;
|
||||
--smooth-red: 0 74.7% 15.5%;
|
||||
|
||||
--datatype-pink: 327.4 87.1% 81.8%;
|
||||
--datatype-pink-foreground: 333.3 71.4% 50.6%;
|
||||
|
|
|
|||
|
|
@ -54,20 +54,16 @@ test(
|
|||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.getByTestId("input-list-dropdown-menu-0-edit").click();
|
||||
|
||||
await page.getByTestId("input-list-dropdown-menu-0-delete").click();
|
||||
await page.getByTestId("input-list-delete-btn-edit_urls-0").click();
|
||||
|
||||
expect(
|
||||
await page.getByTestId("input-list-dropdown-menu-2-edit").count(),
|
||||
await page.getByTestId("input-list-delete-btn-edit_urls-2").count(),
|
||||
).toBe(0);
|
||||
|
||||
await page.getByTestId("input-list-dropdown-menu-1-edit").click();
|
||||
|
||||
await page.getByTestId("input-list-dropdown-menu-1-delete").click();
|
||||
await page.getByTestId("input-list-delete-btn-edit_urls-1").click();
|
||||
|
||||
expect(
|
||||
await page.getByTestId("input-list-dropdown-menu-1-edit").count(),
|
||||
await page.getByTestId("input-list-delete-btn-edit_urls-1").count(),
|
||||
).toBe(0);
|
||||
|
||||
await page.getByText("Close").last().click();
|
||||
|
|
@ -76,35 +72,32 @@ test(
|
|||
await page.getByTestId("input-list-plus-btn_urls-0").click();
|
||||
await page.getByTestId("input-list-plus-btn_urls-0").click();
|
||||
|
||||
expect(
|
||||
await page.getByTestId("input-list-dropdown-menu-0-view").count(),
|
||||
).toBe(1);
|
||||
expect(await page.getByTestId("input-list-delete-btn_urls-0").count()).toBe(
|
||||
1,
|
||||
);
|
||||
|
||||
expect(
|
||||
await page.getByTestId("input-list-dropdown-menu-1-view").count(),
|
||||
).toBe(1);
|
||||
expect(await page.getByTestId("input-list-delete-btn_urls-1").count()).toBe(
|
||||
1,
|
||||
);
|
||||
|
||||
expect(
|
||||
await page.getByTestId("input-list-dropdown-menu-2-view").count(),
|
||||
).toBe(1);
|
||||
expect(await page.getByTestId("input-list-delete-btn_urls-2").count()).toBe(
|
||||
1,
|
||||
);
|
||||
|
||||
expect(
|
||||
await page.getByTestId("input-list-dropdown-menu-3-view").count(),
|
||||
).toBe(1);
|
||||
expect(await page.getByTestId("input-list-delete-btn_urls-3").count()).toBe(
|
||||
1,
|
||||
);
|
||||
|
||||
expect(
|
||||
await page.getByTestId("input-list-dropdown-menu-4-view").count(),
|
||||
).toBe(0);
|
||||
|
||||
await page.getByTestId("input-list-dropdown-menu-0-view").click();
|
||||
await page.getByTestId("input-list-dropdown-menu-0-duplicate").click();
|
||||
expect(await page.getByTestId("input-list-delete-btn_urls-4").count()).toBe(
|
||||
0,
|
||||
);
|
||||
|
||||
expect(await page.getByTestId("inputlist_str_urls_0").inputValue()).toBe(
|
||||
"test1 test1 test1 test1",
|
||||
);
|
||||
|
||||
expect(await page.getByTestId("inputlist_str_urls_1").inputValue()).toBe(
|
||||
"test1 test1 test1 test1",
|
||||
"",
|
||||
);
|
||||
|
||||
await page.getByTestId("edit-button-modal").click();
|
||||
|
|
@ -115,22 +108,6 @@ test(
|
|||
|
||||
expect(
|
||||
await page.getByTestId("inputlist_str_edit_urls_1").inputValue(),
|
||||
).toBe("test1 test1 test1 test1");
|
||||
|
||||
await page.getByTestId("input-list-dropdown-menu-1-edit").click();
|
||||
|
||||
await page.getByTestId("input-list-dropdown-menu-1-duplicate").click();
|
||||
|
||||
expect(
|
||||
await page.getByTestId("inputlist_str_edit_urls_0").inputValue(),
|
||||
).toBe("test1 test1 test1 test1");
|
||||
|
||||
expect(
|
||||
await page.getByTestId("inputlist_str_edit_urls_1").inputValue(),
|
||||
).toBe("test1 test1 test1 test1");
|
||||
|
||||
expect(
|
||||
await page.getByTestId("inputlist_str_edit_urls_2").inputValue(),
|
||||
).toBe("test1 test1 test1 test1");
|
||||
).toBe("");
|
||||
},
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue