feature: Improve Table customization to enhance ux on tool mode (#5216)
* refactor: Add field validation options to TableOptions * refactor: Add field validation options and trigger text/icon to TableMixin * refactor: Add field validation options and trigger text/icon to TableMixin * refactor: Add field validation options and trigger text/icon to TableMixin * update table trigger for toolmode usage * Refactor table trigger and field validation options - Updated the table trigger for toolmode usage - Added field validation options and trigger text/icon to TableMixin - Modified TableOptions to block certain actions and hide options * Refactor TableOptionsTypeAPI field names for blocking actions * Refactor TableOptions default values for blocking actions * Refactor TableOptions default values for blocking actions * Refactor TableOptions component to include tableOptions prop * Refactor table selection and pagination options * Refactor TOOL_TABLE_SCHEMA to disable sorting and filtering for the "name" and "description" fields * Refactor TableOptions to allow blocking hiding of fields * Refactor TableModal and TableNodeComponent to include support for block hiding columns * Refactor Column model to include support for different edit modes * Refactor TableOptions to include support for field parsers * Refactor TableOptions to include support for field parsers and blocking hiding of fields * Refactor TableOptions to include support for inline editing of fields * Refactor App.css to style large text inputs and text areas in AgGrid * update types * Update table modal to prevent closing the the modal while editing cell * Refactor string manipulation utilities to support parsing and transforming strings based on specified field parsers * add inline input support * Refactor TextModal component to remove close button in the footer * add field parser in context * format code * format code * Add disable_edit field to Column class * Refactor TableNodeComponent to exclude columns with disable_edit field from being editable * [autofix.ci] apply automated fixes * Fix casing in selector text for "Open table" in tableInputComponent tests --------- 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
13a468027b
commit
dffc2d51cd
17 changed files with 320 additions and 41 deletions
|
|
@ -1,3 +1,5 @@
|
|||
from langflow.schema.table import EditMode
|
||||
|
||||
TOOL_OUTPUT_NAME = "component_as_tool"
|
||||
TOOL_OUTPUT_DISPLAY_NAME = "Toolset"
|
||||
TOOLS_METADATA_INPUT_NAME = "tools_metadata"
|
||||
|
|
@ -7,11 +9,17 @@ TOOL_TABLE_SCHEMA = [
|
|||
"display_name": "Name",
|
||||
"type": "str",
|
||||
"description": "Specify the name of the output field.",
|
||||
"sortable": False,
|
||||
"filterable": False,
|
||||
"edit_mode": EditMode.INLINE,
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"display_name": "Description",
|
||||
"type": "str",
|
||||
"description": "Describe the purpose of the output field.",
|
||||
"sortable": False,
|
||||
"filterable": False,
|
||||
"edit_mode": EditMode.INLINE,
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ from langflow.schema.artifact import get_artifact_type, post_process_raw
|
|||
from langflow.schema.data import Data
|
||||
from langflow.schema.message import ErrorMessage, Message
|
||||
from langflow.schema.properties import Source
|
||||
from langflow.schema.table import FieldParserType, TableOptions
|
||||
from langflow.services.tracing.schema import Log
|
||||
from langflow.template.field.base import UNDEFINED, Input, Output
|
||||
from langflow.template.frontend_node.custom_components import ComponentFrontendNode
|
||||
|
|
@ -1183,8 +1184,22 @@ class Component(CustomComponent):
|
|||
|
||||
return TableInput(
|
||||
name=TOOLS_METADATA_INPUT_NAME,
|
||||
display_name="Tools Metadata",
|
||||
info="Use the table to configure the tools.",
|
||||
display_name="Toolset configuration",
|
||||
real_time_refresh=True,
|
||||
table_schema=TOOL_TABLE_SCHEMA,
|
||||
value=tool_data,
|
||||
trigger_icon="Hammer",
|
||||
trigger_text="Open toolset",
|
||||
table_options=TableOptions(
|
||||
block_add=True,
|
||||
block_delete=True,
|
||||
block_edit=True,
|
||||
block_sort=True,
|
||||
block_filter=True,
|
||||
block_hide=True,
|
||||
block_select=True,
|
||||
hide_options=True,
|
||||
field_parsers={"name": FieldParserType.SNAKE_CASE},
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from pydantic import (
|
|||
|
||||
from langflow.field_typing.range_spec import RangeSpec
|
||||
from langflow.inputs.validators import CoalesceBool
|
||||
from langflow.schema.table import Column, TableSchema
|
||||
from langflow.schema.table import Column, TableOptions, TableSchema
|
||||
|
||||
|
||||
class FieldTypes(str, Enum):
|
||||
|
|
@ -184,6 +184,9 @@ class SliderMixin(BaseModel):
|
|||
|
||||
class TableMixin(BaseModel):
|
||||
table_schema: TableSchema | list[Column] | None = None
|
||||
trigger_text: str = Field(default="Open table")
|
||||
trigger_icon: str = Field(default="Table")
|
||||
table_options: TableOptions | None = None
|
||||
|
||||
@field_validator("table_schema")
|
||||
@classmethod
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ class FormatterType(str, Enum):
|
|||
boolean = "boolean"
|
||||
|
||||
|
||||
class EditMode(str, Enum):
|
||||
MODAL = "modal"
|
||||
INLINE = "inline"
|
||||
|
||||
|
||||
class Column(BaseModel):
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
name: str
|
||||
|
|
@ -22,6 +27,8 @@ class Column(BaseModel):
|
|||
formatter: FormatterType | str | None = Field(default=None, alias="type")
|
||||
description: str | None = None
|
||||
default: str | None = None
|
||||
disable_edit: bool = Field(default=False)
|
||||
edit_mode: EditMode | None = Field(default=EditMode.MODAL)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def set_display_name(self):
|
||||
|
|
@ -48,3 +55,44 @@ class Column(BaseModel):
|
|||
|
||||
class TableSchema(BaseModel):
|
||||
columns: list[Column]
|
||||
|
||||
|
||||
class FieldValidatorType(str, Enum):
|
||||
"""Enum for field validation types."""
|
||||
|
||||
NO_SPACES = "no_spaces" # Prevents spaces in input
|
||||
LOWERCASE = "lowercase" # Forces lowercase
|
||||
UPPERCASE = "uppercase" # Forces uppercase
|
||||
EMAIL = "email" # Validates email format
|
||||
URL = "url" # Validates URL format
|
||||
ALPHANUMERIC = "alphanumeric" # Only letters and numbers
|
||||
NUMERIC = "numeric" # Only numbers
|
||||
ALPHA = "alpha" # Only letters
|
||||
PHONE = "phone" # Phone number format
|
||||
SLUG = "slug" # URL slug format (lowercase, hyphens)
|
||||
USERNAME = "username" # Alphanumeric with underscores
|
||||
PASSWORD = "password" # Minimum security requirements # noqa: S105
|
||||
|
||||
|
||||
class FieldParserType(str, Enum):
|
||||
"""Enum for field parser types."""
|
||||
|
||||
SNAKE_CASE = "snake_case"
|
||||
CAMEL_CASE = "camel_case"
|
||||
PASCAL_CASE = "pascal_case"
|
||||
KEBAB_CASE = "kebab_case"
|
||||
LOWERCASE = "lowercase"
|
||||
UPPERCASE = "uppercase"
|
||||
|
||||
|
||||
class TableOptions(BaseModel):
|
||||
block_add: bool = Field(default=False)
|
||||
block_delete: bool = Field(default=False)
|
||||
block_edit: bool = Field(default=False)
|
||||
block_sort: bool = Field(default=False)
|
||||
block_filter: bool = Field(default=False)
|
||||
block_hide: bool | list[str] = Field(default=False)
|
||||
block_select: bool = Field(default=False)
|
||||
hide_options: bool = Field(default=False)
|
||||
field_validators: dict[str, list[FieldValidatorType] | FieldValidatorType] | None = Field(default=None)
|
||||
field_parsers: dict[str, list[FieldParserType] | FieldParserType] | None = Field(default=None)
|
||||
|
|
|
|||
|
|
@ -197,3 +197,12 @@ code {
|
|||
appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ag-large-text-input.ag-text-area.ag-input-field {
|
||||
background-color: hsl(var(--background)) !important;
|
||||
}
|
||||
|
||||
.ag-large-text-input.ag-text-area.ag-input-field textarea {
|
||||
resize: none !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ export default function TableNodeComponent({
|
|||
columns,
|
||||
handleOnNewValue,
|
||||
disabled = false,
|
||||
table_options,
|
||||
trigger_icon = "Table",
|
||||
trigger_text = "Open Table",
|
||||
}: InputProps<any[], TableComponentType>): JSX.Element {
|
||||
const dataTypeDefinitions: {
|
||||
[cellDataType: string]: DataTypeDefinition<any>;
|
||||
|
|
@ -63,7 +66,7 @@ export default function TableNodeComponent({
|
|||
const agGrid = useRef<AgGridReact>(null);
|
||||
const componentColumns = columns
|
||||
? columns
|
||||
: generateBackendColumnsFromValue(value ?? []);
|
||||
: generateBackendColumnsFromValue(value ?? [], table_options);
|
||||
const AgColumns = FormatColumns(componentColumns);
|
||||
function setAllRows() {
|
||||
if (agGrid.current && !agGrid.current.api.isDestroyed()) {
|
||||
|
|
@ -100,16 +103,22 @@ export default function TableNodeComponent({
|
|||
function updateComponent() {
|
||||
setAllRows();
|
||||
}
|
||||
const editable = componentColumns.map((column) => {
|
||||
const isCustomEdit =
|
||||
column.formatter &&
|
||||
(column.formatter === "text" || column.formatter === "json");
|
||||
return {
|
||||
field: column.name,
|
||||
onUpdate: updateComponent,
|
||||
editableCell: isCustomEdit ? false : true,
|
||||
};
|
||||
});
|
||||
const editable = componentColumns
|
||||
.map((column) => {
|
||||
const isCustomEdit =
|
||||
column.formatter &&
|
||||
((column.formatter === "text" && column.edit_mode !== "inline") ||
|
||||
column.formatter === "json");
|
||||
return {
|
||||
field: column.name,
|
||||
onUpdate: updateComponent,
|
||||
editableCell: isCustomEdit ? false : true,
|
||||
};
|
||||
})
|
||||
.filter(
|
||||
(col) =>
|
||||
columns?.find((c) => c.name === col.field)?.disable_edit !== true,
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -119,6 +128,7 @@ export default function TableNodeComponent({
|
|||
>
|
||||
<div className="flex w-full items-center gap-3" data-testid={"div-" + id}>
|
||||
<TableModal
|
||||
tableOptions={table_options}
|
||||
dataTypeDefinitions={dataTypeDefinitions}
|
||||
autoSizeStrategy={{ type: "fitGridWidth", defaultMinWidth: 100 }}
|
||||
tableTitle={tableTitle}
|
||||
|
|
@ -127,10 +137,10 @@ export default function TableNodeComponent({
|
|||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||
setSelectedNodes(event.api.getSelectedNodes());
|
||||
}}
|
||||
rowSelection="multiple"
|
||||
rowSelection={table_options?.block_select ? undefined : "multiple"}
|
||||
suppressRowClickSelection={true}
|
||||
editable={editable}
|
||||
pagination={true}
|
||||
pagination={!table_options?.hide_options}
|
||||
addRow={addRow}
|
||||
onDelete={deleteRow}
|
||||
onDuplicate={duplicateRow}
|
||||
|
|
@ -138,6 +148,7 @@ export default function TableNodeComponent({
|
|||
className="h-full w-full"
|
||||
columnDefs={AgColumns}
|
||||
rowData={value}
|
||||
context={{ field_parsers: table_options?.field_parsers }}
|
||||
>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
|
|
@ -148,8 +159,11 @@ export default function TableNodeComponent({
|
|||
(disabled ? "pointer-events-none cursor-not-allowed" : "")
|
||||
}
|
||||
>
|
||||
<ForwardedIconComponent name="Table" className="mt-px h-4 w-4" />
|
||||
<span className="font-normal">Open Table</span>
|
||||
<ForwardedIconComponent
|
||||
name={trigger_icon}
|
||||
className="mt-px h-4 w-4"
|
||||
/>
|
||||
<span className="font-normal">{trigger_text}</span>
|
||||
</Button>
|
||||
</TableModal>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import IconComponent from "@/components/common/genericIconComponent";
|
||||
import ShadTooltip from "@/components/common/shadTooltipComponent";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { TableOptionsTypeAPI } from "@/types/api";
|
||||
import { cn } from "@/utils/utils";
|
||||
|
||||
export default function TableOptions({
|
||||
|
|
@ -10,6 +11,7 @@ export default function TableOptions({
|
|||
hasSelection,
|
||||
stateChange,
|
||||
addRow,
|
||||
tableOptions,
|
||||
}: {
|
||||
resetGrid: () => void;
|
||||
duplicateRow?: () => void;
|
||||
|
|
@ -17,11 +19,12 @@ export default function TableOptions({
|
|||
addRow?: () => void;
|
||||
hasSelection: boolean;
|
||||
stateChange: boolean;
|
||||
tableOptions?: TableOptionsTypeAPI;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div className={cn("absolute bottom-3 left-6")}>
|
||||
<div className="flex items-center gap-3">
|
||||
{addRow && (
|
||||
{addRow && !tableOptions?.block_add && (
|
||||
<div>
|
||||
<ShadTooltip content={"Add a new row"}>
|
||||
<Button data-testid="add-row-button" unstyled onClick={addRow}>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from "@/constants/constants";
|
||||
import { useDarkStore } from "@/stores/darkStore";
|
||||
import "@/style/ag-theme-shadcn.css"; // Custom CSS applied to the grid
|
||||
import { TableOptionsTypeAPI } from "@/types/api";
|
||||
import { cn } from "@/utils/utils";
|
||||
import { ColDef } from "ag-grid-community";
|
||||
import "ag-grid-community/styles/ag-grid.css"; // Mandatory CSS required by the grid
|
||||
|
|
@ -36,6 +37,7 @@ export interface TableComponentProps extends AgGridReactProps {
|
|||
onDelete?: () => void;
|
||||
onDuplicate?: () => void;
|
||||
addRow?: () => void;
|
||||
tableOptions?: TableOptionsTypeAPI;
|
||||
}
|
||||
|
||||
const TableComponent = forwardRef<
|
||||
|
|
@ -55,7 +57,7 @@ const TableComponent = forwardRef<
|
|||
let newCol = {
|
||||
...col,
|
||||
};
|
||||
if (props.onSelectionChanged && index === 0) {
|
||||
if (props.rowSelection && props.onSelectionChanged && index === 0) {
|
||||
newCol = {
|
||||
...newCol,
|
||||
checkboxSelection: true,
|
||||
|
|
@ -63,6 +65,17 @@ const TableComponent = forwardRef<
|
|||
headerCheckboxSelectionFilteredOnly: true,
|
||||
};
|
||||
}
|
||||
if (
|
||||
(typeof props.tableOptions?.block_hide === "boolean" &&
|
||||
props.tableOptions?.block_hide) ||
|
||||
(Array.isArray(props.tableOptions?.block_hide) &&
|
||||
props.tableOptions?.block_hide.includes(newCol.field ?? ""))
|
||||
) {
|
||||
newCol = {
|
||||
...newCol,
|
||||
lockVisible: true,
|
||||
};
|
||||
}
|
||||
if (
|
||||
(typeof props.editable === "boolean" && props.editable) ||
|
||||
(Array.isArray(props.editable) &&
|
||||
|
|
@ -95,6 +108,7 @@ const TableComponent = forwardRef<
|
|||
}
|
||||
return newCol;
|
||||
});
|
||||
console.log(colDef);
|
||||
// @ts-ignore
|
||||
const realRef: React.MutableRefObject<AgGridReact> =
|
||||
useRef<AgGridReact | null>(null);
|
||||
|
|
@ -170,6 +184,7 @@ const TableComponent = forwardRef<
|
|||
);
|
||||
}
|
||||
}
|
||||
console.log(colDef);
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
|
@ -206,8 +221,9 @@ const TableComponent = forwardRef<
|
|||
}
|
||||
}}
|
||||
/>
|
||||
{props.pagination && (
|
||||
{!props.tableOptions?.hide_options && (
|
||||
<TableOptions
|
||||
tableOptions={props.tableOptions}
|
||||
stateChange={columnStateChange}
|
||||
hasSelection={realRef.current?.api?.getSelectedRows().length > 0}
|
||||
duplicateRow={props.onDuplicate ? props.onDuplicate : undefined}
|
||||
|
|
|
|||
|
|
@ -177,6 +177,9 @@ export function ParameterRenderComponent({
|
|||
description={templateData.info || "Add or edit data"}
|
||||
columns={templateData?.table_schema?.columns}
|
||||
tableTitle={templateData?.display_name ?? "Table"}
|
||||
table_options={templateData?.table_options}
|
||||
trigger_icon={templateData?.trigger_icon}
|
||||
trigger_text={templateData?.trigger_text}
|
||||
/>
|
||||
);
|
||||
case "slider":
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { handleOnNewValueType } from "@/CustomNodes/hooks/use-handle-new-value";
|
||||
import { APIClassType, InputFieldType } from "@/types/api";
|
||||
import { APIClassType, InputFieldType, TableOptionsTypeAPI } from "@/types/api";
|
||||
import { RangeSpecType } from "@/types/components";
|
||||
import { ColumnField } from "@/types/utils/functions";
|
||||
|
||||
|
|
@ -28,6 +28,9 @@ export type TableComponentType = {
|
|||
description: string;
|
||||
tableTitle: string;
|
||||
columns?: ColumnField[];
|
||||
table_options?: TableOptionsTypeAPI;
|
||||
trigger_text?: string;
|
||||
trigger_icon?: string;
|
||||
};
|
||||
|
||||
export type FloatComponentType = {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ import TableComponent, {
|
|||
TableComponentProps,
|
||||
} from "@/components/core/parameterRenderComponent/components/tableComponent";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { TableOptionsTypeAPI } from "@/types/api";
|
||||
import { DialogClose } from "@radix-ui/react-dialog";
|
||||
import { ElementRef, forwardRef, useState } from "react";
|
||||
import { AgGridReact } from "ag-grid-react";
|
||||
import { ElementRef, ForwardedRef, forwardRef } from "react";
|
||||
import BaseModal from "../baseModal";
|
||||
|
||||
interface TableModalProps extends TableComponentProps {
|
||||
|
|
@ -12,18 +14,28 @@ interface TableModalProps extends TableComponentProps {
|
|||
description: string;
|
||||
disabled?: boolean;
|
||||
children: React.ReactNode;
|
||||
tableOptions?: TableOptionsTypeAPI;
|
||||
hideColumns?: boolean | string[];
|
||||
}
|
||||
|
||||
const TableModal = forwardRef<
|
||||
ElementRef<typeof TableComponent>,
|
||||
TableModalProps
|
||||
>(
|
||||
const TableModal = forwardRef<AgGridReact, TableModalProps>(
|
||||
(
|
||||
{ tableTitle, description, children, disabled, ...props }: TableModalProps,
|
||||
ref,
|
||||
ref: ForwardedRef<AgGridReact>,
|
||||
) => {
|
||||
return (
|
||||
<BaseModal disable={disabled}>
|
||||
<BaseModal
|
||||
onEscapeKeyDown={(e) => {
|
||||
if (
|
||||
(
|
||||
ref as React.RefObject<AgGridReact>
|
||||
)?.current?.api.getEditingCells().length
|
||||
) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
disable={disabled}
|
||||
>
|
||||
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={description}>
|
||||
<span className="pr-2">{tableTitle}</span>
|
||||
|
|
|
|||
|
|
@ -70,9 +70,6 @@ export default function TextModal({
|
|||
Save
|
||||
</Button>
|
||||
)}
|
||||
<Button className="flex gap-2 px-3" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
</BaseModal.Footer>
|
||||
</BaseModal>
|
||||
|
|
|
|||
|
|
@ -290,3 +290,40 @@ export type useMutationFunctionType<
|
|||
"mutationFn" | "mutationKey"
|
||||
>,
|
||||
) => UseMutationResult<Data, Error, Variables>;
|
||||
|
||||
export type FieldValidatorType =
|
||||
| "no_spaces"
|
||||
| "lowercase"
|
||||
| "uppercase"
|
||||
| "email"
|
||||
| "url"
|
||||
| "alphanumeric"
|
||||
| "numeric"
|
||||
| "alpha"
|
||||
| "phone"
|
||||
| "slug"
|
||||
| "username"
|
||||
| "password";
|
||||
|
||||
export type FieldParserType =
|
||||
| "snake_case"
|
||||
| "camel_case"
|
||||
| "pascal_case"
|
||||
| "kebab_case"
|
||||
| "lowercase"
|
||||
| "uppercase";
|
||||
|
||||
export type TableOptionsTypeAPI = {
|
||||
block_add?: boolean;
|
||||
block_delete?: boolean;
|
||||
block_edit?: boolean;
|
||||
block_sort?: boolean;
|
||||
block_filter?: boolean;
|
||||
block_hide?: boolean | string[];
|
||||
block_select?: boolean;
|
||||
hide_options?: boolean;
|
||||
field_validators?: Array<
|
||||
FieldValidatorType | { [key: string]: FieldValidatorType }
|
||||
>;
|
||||
field_parsers?: Array<FieldParserType | { [key: string]: FieldParserType }>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { FieldParserType, FieldValidatorType } from "../api";
|
||||
|
||||
export type getCodesObjProps = {
|
||||
runCurlCode: string;
|
||||
webhookCurlCode: string;
|
||||
|
|
@ -23,5 +25,7 @@ export interface ColumnField {
|
|||
filterable: boolean;
|
||||
formatter?: FormatterType;
|
||||
description?: string;
|
||||
disable_edit?: boolean;
|
||||
default?: any; // Add this line
|
||||
edit_mode?: "modal" | "inline";
|
||||
}
|
||||
|
|
|
|||
74
src/frontend/src/utils/stringManipulation.ts
Normal file
74
src/frontend/src/utils/stringManipulation.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { FieldParserType } from "../types/api";
|
||||
|
||||
function toSnakeCase(str: string): string {
|
||||
return str.trim().replace(/\s+/g, "_");
|
||||
}
|
||||
|
||||
function toCamelCase(str: string): string {
|
||||
return str
|
||||
.replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ""))
|
||||
.replace(/^[A-Z]/, (c) => c.toLowerCase());
|
||||
}
|
||||
|
||||
function toPascalCase(str: string): string {
|
||||
return str
|
||||
.replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ""))
|
||||
.replace(/^[a-z]/, (c) => c.toUpperCase());
|
||||
}
|
||||
|
||||
function toKebabCase(str: string): string {
|
||||
return str
|
||||
.replace(/([A-Z])/g, " $1")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/[_]+/g, "-");
|
||||
}
|
||||
|
||||
function toLowerCase(str: string): string {
|
||||
return str.toLowerCase();
|
||||
}
|
||||
|
||||
function toUpperCase(str: string): string {
|
||||
return str.toUpperCase();
|
||||
}
|
||||
|
||||
export function parseString(
|
||||
str: string,
|
||||
parsers: FieldParserType[] | FieldParserType,
|
||||
): string {
|
||||
let result = str;
|
||||
|
||||
let parsersArray: FieldParserType[] = [];
|
||||
|
||||
if (typeof parsers === "string") {
|
||||
parsersArray = [parsers];
|
||||
} else {
|
||||
parsersArray = parsers;
|
||||
}
|
||||
|
||||
for (const parser of parsersArray) {
|
||||
switch (parser) {
|
||||
case "snake_case":
|
||||
result = toSnakeCase(result);
|
||||
break;
|
||||
case "camel_case":
|
||||
result = toCamelCase(result);
|
||||
break;
|
||||
case "pascal_case":
|
||||
result = toPascalCase(result);
|
||||
break;
|
||||
case "kebab_case":
|
||||
result = toKebabCase(result);
|
||||
break;
|
||||
case "lowercase":
|
||||
result = toLowerCase(result);
|
||||
break;
|
||||
case "uppercase":
|
||||
result = toUpperCase(result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import TableAutoCellRender from "@/components/core/parameterRenderComponent/components/tableComponent/components/tableAutoCellRender";
|
||||
import { ColumnField, FormatterType } from "@/types/utils/functions";
|
||||
import { ColDef, ColGroupDef } from "ag-grid-community";
|
||||
import { ColDef, ColGroupDef, ValueParserParams } from "ag-grid-community";
|
||||
import clsx, { ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import {
|
||||
|
|
@ -9,7 +9,12 @@ import {
|
|||
MODAL_CLASSES,
|
||||
SHORTCUT_KEYS,
|
||||
} from "../constants/constants";
|
||||
import { APIDataType, InputFieldType, VertexDataTypeAPI } from "../types/api";
|
||||
import {
|
||||
APIDataType,
|
||||
InputFieldType,
|
||||
TableOptionsTypeAPI,
|
||||
VertexDataTypeAPI,
|
||||
} from "../types/api";
|
||||
import {
|
||||
groupedObjType,
|
||||
nodeGroupedObjType,
|
||||
|
|
@ -18,6 +23,7 @@ import {
|
|||
import { NodeDataType, NodeType } from "../types/flow";
|
||||
import { FlowState } from "../types/tabs";
|
||||
import { isErrorLog } from "../types/utils/typeCheckingUtils";
|
||||
import { parseString } from "./stringManipulation";
|
||||
|
||||
export function classNames(...classes: Array<string>): string {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
|
|
@ -515,6 +521,20 @@ export function FormatColumns(columns: ColumnField[]): ColDef<any>[] {
|
|||
field: col.name,
|
||||
sortable: col.sortable,
|
||||
filter: col.filterable,
|
||||
editable: !col.disable_edit,
|
||||
valueParser: (params: ValueParserParams) => {
|
||||
const { context, newValue, colDef } = params;
|
||||
if (
|
||||
context.field_parsers &&
|
||||
context.field_parsers[colDef.field ?? ""]
|
||||
) {
|
||||
return parseString(
|
||||
newValue,
|
||||
context.field_parsers[colDef.field ?? ""],
|
||||
);
|
||||
}
|
||||
return newValue;
|
||||
},
|
||||
};
|
||||
if (!col.formatter) {
|
||||
col.formatter = FormatterType.text;
|
||||
|
|
@ -525,7 +545,17 @@ export function FormatColumns(columns: ColumnField[]): ColDef<any>[] {
|
|||
newCol.cellRendererParams = {
|
||||
formatter: col.formatter,
|
||||
};
|
||||
newCol.cellRenderer = TableAutoCellRender;
|
||||
if (col.formatter !== FormatterType.text || col.edit_mode !== "inline") {
|
||||
newCol.cellRenderer = TableAutoCellRender;
|
||||
} else {
|
||||
newCol.wrapText = true;
|
||||
newCol.autoHeight = true;
|
||||
newCol.cellEditor = "agLargeTextCellEditor";
|
||||
newCol.cellEditorPopup = true;
|
||||
newCol.cellEditorParams = {
|
||||
maxLength: 100000000,
|
||||
};
|
||||
}
|
||||
}
|
||||
return newCol;
|
||||
});
|
||||
|
|
@ -533,14 +563,17 @@ export function FormatColumns(columns: ColumnField[]): ColDef<any>[] {
|
|||
return colDefs;
|
||||
}
|
||||
|
||||
export function generateBackendColumnsFromValue(rows: Object[]): ColumnField[] {
|
||||
export function generateBackendColumnsFromValue(
|
||||
rows: Object[],
|
||||
tableOptions?: TableOptionsTypeAPI,
|
||||
): ColumnField[] {
|
||||
const columns = extractColumnsFromRows(rows, "union");
|
||||
return columns.map((column) => {
|
||||
const newColumn: ColumnField = {
|
||||
name: column.field ?? "",
|
||||
display_name: column.headerName ?? "",
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
sortable: !tableOptions?.block_sort,
|
||||
filterable: !tableOptions?.block_filter,
|
||||
default: null, // Initialize default to null or appropriate value
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -83,11 +83,11 @@ class CustomComponent(Component):
|
|||
|
||||
await page.getByText("Check & Save").last().click();
|
||||
|
||||
await page.waitForSelector('text="Open Table"', {
|
||||
await page.waitForSelector('text="Open table"', {
|
||||
timeout: 3000,
|
||||
});
|
||||
|
||||
await page.getByText("Open Table").click();
|
||||
await page.getByText("Open table").click();
|
||||
|
||||
await page.waitForSelector(".ag-cell-value", {
|
||||
timeout: 3000,
|
||||
|
|
@ -166,11 +166,11 @@ class CustomComponent(Component):
|
|||
|
||||
await page.getByText("Close").last().click();
|
||||
|
||||
await page.waitForSelector("text=Open Table", {
|
||||
await page.waitForSelector("text=Open table", {
|
||||
timeout: 3000,
|
||||
});
|
||||
|
||||
await page.getByText("Open Table").click();
|
||||
await page.getByText("Open table").click();
|
||||
|
||||
await page.waitForSelector(".ag-cell-value", {
|
||||
timeout: 3000,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue