diff --git a/.env.local b/.env.local index 704fff2e..d9948fcc 100644 --- a/.env.local +++ b/.env.local @@ -4,7 +4,7 @@ NEXT_PUBLIC_EDITION=ce SAGITTARIUS_GRAPHQL_URL=http://localhost:3010/graphql NEXT_PUBLIC_SCULPTOR_VERSION=0.0.0 -NEXT_PUBLIC_PICTOR_VERSION=0.7.0 +NEXT_PUBLIC_PICTOR_VERSION=0.7.2 NEXT_PUBLIC_ALLOWED_REDIRECT_DOMAINS=*.code0.tech,*.codezero.build NEXT_PUBLIC_OTEL_SERVICE_NAME=#"sculptor-client" diff --git a/package-lock.json b/package-lock.json index 85419a4d..9cce8b59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.7.2", - "@code0-tech/triangulum": "^0.14.3", + "@code0-tech/triangulum": "^0.14.6", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@opentelemetry/api": "^1.9.1", @@ -379,9 +379,9 @@ "peer": true }, "node_modules/@code0-tech/triangulum": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.14.3.tgz", - "integrity": "sha512-BpYH2qHv9s8vQHQvC0P2SAlRXit4e5Krf1ICGwlFxL8nEw+ul9iO6bCqC5pEpS18Az/PI2Bvmw4Xufj4EhEsig==", + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.14.6.tgz", + "integrity": "sha512-1SUZjIJd3SWWAU9Z/YkaEWMLvb3J2VY+NmECeoAwUzPfQfPUzhfYzsb+uc7xc/H2YVl9rOMK7foyHJCoYNbOVQ==", "peerDependencies": { "@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2462825049-b5d5dce99ef9bf2f81df41d896ec36fa5e28559c", "@typescript/vfs": "^1.6.4", diff --git a/package.json b/package.json index 40a1d405..9558a30d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.7.2", - "@code0-tech/triangulum": "^0.14.3", + "@code0-tech/triangulum": "^0.14.6", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@opentelemetry/api": "^1.9.1", diff --git a/src/packages/ce/src/datatype/components/inputs/DataTypeInputComponent.tsx b/src/packages/ce/src/datatype/components/inputs/DataTypeInputComponent.tsx index e37dd634..6b07563c 100644 --- a/src/packages/ce/src/datatype/components/inputs/DataTypeInputComponent.tsx +++ b/src/packages/ce/src/datatype/components/inputs/DataTypeInputComponent.tsx @@ -6,8 +6,13 @@ import {DataTypeBooleanInputComponent} from "@edition/datatype/components/inputs import {DataTypeSelectInputComponent} from "@edition/datatype/components/inputs/select/DataTypeSelectInputComponent"; import {InputWrapperProps} from "@code0-tech/pictor/dist/components/form/InputWrapper"; import {DataTypeNumberInputComponent} from "@edition/datatype/components/inputs/number/DataTypeNumberInputComponent"; +import {DataTypeJSONInputComponent} from "@edition/datatype/components/inputs/json/DataTypeJSONInputComponent"; +import {DataTypeGenericInputComponent} from "@edition/datatype/components/inputs/generic/DataTypeGenericInputComponent"; +import { + DataTypeSubFlowInputComponent +} from "@edition/datatype/components/inputs/sub-flow/DataTypeSubFlowInputComponent"; -export interface DataTypeInputComponentProps extends Omit, "wrapperComponent" | "onChange"> { +export interface DataTypeInputComponentProps extends Omit, "onChange"> { schema: NodeSchema clearable?: boolean onChange?: (value: ReferenceValue | LiteralValue | NodeFunction | undefined) => void @@ -20,8 +25,9 @@ export const DataTypeInputComponent: React.FC = (pr const {schema, ...rest} = props const suggestions = schema?.schema?.suggestions as (NodeFunction | ReferenceValue | LiteralValue)[] + const inputName = schema?.schema?.input === "generic" ? schema.functionSchema.input : schema?.schema?.input - switch (schema?.schema?.input) { + switch (inputName) { case "boolean": return = (pr schema={schema} suggestions={suggestions} {...rest}/> + case "list": + case "data": + return + case "generic": + return + case "sub-flow": + return default: return - diff --git a/src/packages/ce/src/datatype/components/inputs/DataTypeInputValueComponent.tsx b/src/packages/ce/src/datatype/components/inputs/DataTypeInputValueComponent.tsx index aee7618f..8e304c0f 100644 --- a/src/packages/ce/src/datatype/components/inputs/DataTypeInputValueComponent.tsx +++ b/src/packages/ce/src/datatype/components/inputs/DataTypeInputValueComponent.tsx @@ -12,12 +12,12 @@ export interface DataTypeInputValueComponentProps extends Omit = (props) => { - const {children, inside = false, initialValue, suggestions, onChange, formValidation} = props + const {children, inside = false, initialValue, suggestions, onChange, formValidation, ...rest} = props return inside || initialValue?.__typename === "NodeFunction" || initialValue?.__typename === "NodeFunctionIdWrapper" || initialValue?.__typename === "ReferenceValue" ? - } rightType={"action"}> + } rightType={"action"} {...rest}>
{ initialValue?.__typename === "NodeFunction" || initialValue?.__typename === "NodeFunctionIdWrapper" ? ( diff --git a/src/packages/ce/src/datatype/components/inputs/generic/DataTypeGenericInputComponent.tsx b/src/packages/ce/src/datatype/components/inputs/generic/DataTypeGenericInputComponent.tsx new file mode 100644 index 00000000..d89e5c0d --- /dev/null +++ b/src/packages/ce/src/datatype/components/inputs/generic/DataTypeGenericInputComponent.tsx @@ -0,0 +1,41 @@ +import {DataTypeInputComponentProps} from "@edition/datatype/components/inputs/DataTypeInputComponent"; +import React from "react"; +import {ButtonGroup} from "@code0-tech/pictor/dist/components/button-group/ButtonGroup"; +import {Button, InputDescription, InputLabel} from "@code0-tech/pictor"; +import {useDebouncedCallback} from "use-debounce"; +import {LiteralValue, NodeFunction, ReferenceValue} from "@code0-tech/sagittarius-graphql-types"; + +export type DataTypeGenericInputComponentProps = DataTypeInputComponentProps + +export const DataTypeGenericInputComponent: React.FC = (props) => { + + const {title, description, onChange, formValidation} = props + + const onChangeDebounced = useDebouncedCallback((value: LiteralValue | NodeFunction | ReferenceValue | undefined) => { + formValidation?.setValue?.(value) + onChange?.(value) + }, 200) + + return <> + {title} + {description} + + + + + + + + + +} \ No newline at end of file diff --git a/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputComponent.tsx b/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputComponent.tsx index 17e49239..f3257050 100644 --- a/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputComponent.tsx +++ b/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputComponent.tsx @@ -1,29 +1,15 @@ import React from "react" -import {IconAlignLeft, IconEdit, IconX} from "@tabler/icons-react" import "../type/DataTypeTypeInputComponent.style.scss" import {DataTypeJSONInputTreeComponent} from "./DataTypeJSONInputTreeComponent"; import {DataTypeInputComponentProps} from "../DataTypeInputComponent"; -import {LiteralValue, NodeFunction, NodeParameterValue} from "@code0-tech/sagittarius-graphql-types"; -import {NodeBadgeComponent} from "../../badges/NodeBadgeComponent"; -import {ReferenceBadgeComponent} from "../../badges/ReferenceBadgeComponent"; -import { - Button, - Card, - Flex, - InputDescription, - InputLabel, - InputMessage, - Text, - useService, - useStore -} from "@code0-tech/pictor"; -import {ButtonGroup} from "@code0-tech/pictor/dist/components/button-group/ButtonGroup"; -import {FunctionSuggestionMenuComponent} from "@edition/function/components/suggestion/FunctionSuggestionMenuComponent"; +import {LiteralValue, NodeFunction, ReferenceValue} from "@code0-tech/sagittarius-graphql-types"; +import {InputDescription, InputLabel, Spacing} from "@code0-tech/pictor"; import { DataTypeJSONInputEditDialogComponent } from "@edition/datatype/components/inputs/json/DataTypeJSONInputEditDialogComponent"; -import {FlowService} from "@edition/flow/services/Flow.service"; -import {useValue} from "@edition/datatype/hooks/DataType.value.hook"; +import {DataTypeInputValueComponent} from "@edition/datatype/components/inputs/DataTypeInputValueComponent"; +import {useDebouncedCallback} from "use-debounce"; +import {DataInput} from "@code0-tech/triangulum/dist/util/schema.util"; export interface EditableJSONEntry { key: string @@ -33,21 +19,27 @@ export interface EditableJSONEntry { export type DataTypeJSONInputComponentProps = DataTypeInputComponentProps +//TODO render fallback value if undefined based on schema export const DataTypeJSONInputComponent: React.FC = (props) => { + const {schema, title, description, suggestions, formValidation, initialValue, onChange} = props - const {schema, title, description, formValidation, onChange} = props - - const flowService = useService(FlowService) - const flowStore = useStore(FlowService) - - const suggestions = [] const [editDialogOpen, setEditDialogOpen] = React.useState(false) - const [editEntry, setEditEntry] = React.useState(null) + const [editEntry, setEditEntry] = React.useState(undefined) const [collapsedState, setCollapsedStateRaw] = React.useState>({}) + const onChangeDebounced = useDebouncedCallback((value: LiteralValue | NodeFunction | ReferenceValue | undefined) => { + formValidation?.setValue?.(value) + onChange?.(value) + }, 200) - const [value, setValue] = React.useState(null) + const value = React.useMemo( + () => initialValue ?? (schema.functionSchema.input === "list" ? { + __typename: "LiteralValue", + value: [] + } as LiteralValue : generateDefaultDataValue(schema.functionSchema as DataInput)), + [initialValue, schema] + ) const setCollapsedState = (path: string[], collapsed: boolean) => { setCollapsedStateRaw(prev => ({...prev, [path.join(".")]: collapsed})) @@ -65,65 +57,47 @@ export const DataTypeJSONInputComponent: React.FC setEditDialogOpen(open)} - onObjectChange={v => { - formValidation?.setValue?.(v) - setValue(v ?? null) - // @ts-ignore - onChange?.() - }} + onObjectChange={onChangeDebounced} /> )} {title} {description} - - - - {"Object"} - - - { - formValidation?.setValue?.(suggestion.value) - setValue(suggestion.value) - // @ts-ignore - onChange?.() - }} - triggerContent={}/> - - - - - - {value?.__typename === "NodeFunction" || value?.__typename === "NodeFunctionIdWrapper" ? ( - - ) : value?.__typename === "ReferenceValue" ? ( - - ) : ( - - )} - - - {!formValidation?.valid && formValidation?.notValidMessage && ( - {formValidation.notValidMessage} - )} + + + + ) } + + +//TODO: only if required +const generateDefaultDataValue = (schema: DataInput): LiteralValue => { + return { + __typename: "LiteralValue", + value: { + ...(Object.entries(schema.properties ?? {})?.map(([key, propSchema]) => { + if (!Array.isArray(propSchema)) { + if (propSchema.input === "data") { + return {[key]: generateDefaultDataValue(propSchema).value} + } + if (propSchema.input === "list") { + return {[key]: []} + } + return {[key]: null} + } + })) + } + } +} diff --git a/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputEditDialogComponent.tsx b/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputEditDialogComponent.tsx index 441040e5..a2527c84 100644 --- a/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputEditDialogComponent.tsx +++ b/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputEditDialogComponent.tsx @@ -22,13 +22,13 @@ import { export interface DataTypeJSONInputEditDialogComponentProps { open: boolean - entry: EditableJSONEntry | null - value: LiteralValue | null + entry: EditableJSONEntry | undefined + value: LiteralValue | undefined onOpenChange?: (open: boolean) => void - onObjectChange?: (object: LiteralValue | null) => void + onObjectChange?: (object: LiteralValue | undefined) => void } -function getValueAtPath(obj: LiteralValue | null, path: string[]): unknown { +function getValueAtPath(obj: LiteralValue | undefined, path: string[]): unknown { if (!obj || !Array.isArray(path) || path.length === 0) return obj?.value // Traverse .value recursively if nested let current: any = obj.value @@ -42,8 +42,8 @@ function getValueAtPath(obj: LiteralValue | null, path: string[]): unknown { return current } -function setValueAtPath(obj: LiteralValue | null, path: string[], value: unknown): LiteralValue | null { - if (!obj) return null +function setValueAtPath(obj: LiteralValue | undefined, path: string[], value: unknown): LiteralValue | undefined { + if (!obj) return undefined if (path.length === 0) return { ...obj, value } const [key, ...rest] = path if (Array.isArray(obj.value)) { @@ -81,22 +81,24 @@ export const DataTypeJSONInputEditDialogComponent: React.FC>({}) const [activePath, setActivePath] = React.useState(entry?.path ?? []) - const [editedObject, setEditedObject] = React.useState(value) + const [editedObject, setEditedObject] = React.useState(value) const [editorValue, setEditorValue] = React.useState(getValueAtPath(value, entry?.path ?? [])) const clickTimeout = React.useRef(null) - React.useEffect(() => { - setEditorValue(getValueAtPath(editedObject, activePath)) - }, [activePath]) + React.useEffect( + () => setEditorValue(getValueAtPath(editedObject, activePath)), + [activePath] + ) React.useEffect(() => { setActivePath(entry?.path ?? []) setEditedObject(value) }, [entry]) - React.useEffect(() => { - setEditOpen(open) - }, [open]) + React.useEffect( + () => setEditOpen(open), + [open] + ) const setCollapsedState = (path: string[], collapsed: boolean) => { setCollapsedStateRaw(prev => ({...prev, [path.join(".")]: collapsed})) @@ -120,9 +122,6 @@ export const DataTypeJSONInputEditDialogComponent: React.FC null - const tokenHighlights = {} - return ( onOpenChange?.(open)}> @@ -170,9 +169,9 @@ export const DataTypeJSONInputEditDialogComponent: React.FC diff --git a/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputTreeComponent.tsx b/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputTreeComponent.tsx index 1367f2aa..fcef4edb 100644 --- a/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputTreeComponent.tsx +++ b/src/packages/ce/src/datatype/components/inputs/json/DataTypeJSONInputTreeComponent.tsx @@ -89,7 +89,7 @@ export const DataTypeJSONInputTreeComponent: React.FC{Array.isArray(value) ? "is a list of" : "is a nested object"} - {!isCollapsed &&
    {renderNodes}
} + {!isCollapsed && (renderNodes?.length ?? 0) > 0 &&
    {renderNodes}
}
) } @@ -120,7 +120,7 @@ export const DataTypeJSONInputTreeComponent: React.FC{key} has value - + {String((val as any))} @@ -161,6 +161,6 @@ export const DataTypeJSONInputTreeComponent: React.FC{validNodes} } \ No newline at end of file diff --git a/src/packages/ce/src/datatype/components/inputs/sub-flow/DataTypeSubFlowInputComponent.tsx b/src/packages/ce/src/datatype/components/inputs/sub-flow/DataTypeSubFlowInputComponent.tsx new file mode 100644 index 00000000..a9320bd9 --- /dev/null +++ b/src/packages/ce/src/datatype/components/inputs/sub-flow/DataTypeSubFlowInputComponent.tsx @@ -0,0 +1,53 @@ +import {DataTypeInputComponentProps} from "@edition/datatype/components/inputs/DataTypeInputComponent"; +import React from "react"; +import {useDebouncedCallback} from "use-debounce"; +import {InputDescription, InputLabel, Text} from "@code0-tech/pictor"; +import lodash from "lodash" +import {useFunctionSuggestions} from "@edition/function/hooks/Function.suggestion.hook"; +import {DataTypeInputValueComponent} from "@edition/datatype/components/inputs/DataTypeInputValueComponent"; +import {SuggestionDialogComponent} from "@edition/function/components/suggestion/SuggestionDialogComponent"; +import {LiteralValue, NodeFunction, ReferenceValue} from "@code0-tech/sagittarius-graphql-types"; + + +export type DataTypeSubFlowInputComponentProps = DataTypeInputComponentProps + + +export const DataTypeSubFlowInputComponent: React.FC = (props) => { + + const {formValidation, title, initialValue, description, suggestions, onChange} = props + + const defaultValue: number = React.useMemo(() => suggestions?.findIndex(suggest => { + return initialValue && lodash.isMatch(initialValue, suggest) + }), [suggestions])! + + const [suggestionDialogOpen, setSuggestionDialogOpen] = React.useState(false) + const result = useFunctionSuggestions() + + const onChangeDebounced = useDebouncedCallback((value: LiteralValue | ReferenceValue | NodeFunction | undefined) => { + formValidation?.setValue?.(value ?? undefined) + onChange?.(value ?? undefined) + }, 200) + + return React.useMemo(() => <> + { + onChangeDebounced(suggestion.value as NodeFunction) + }} + onOpenChange={setSuggestionDialogOpen}/> + {title} + {description} + { + setSuggestionDialogOpen(true) + } + }} + initialValue={initialValue} + onChange={onChangeDebounced} + suggestions={suggestions} + formValidation={formValidation}> + Select next node + + , [formValidation, defaultValue, suggestionDialogOpen]) +} \ No newline at end of file diff --git a/src/packages/ce/src/datatype/components/inputs/type/DataTypeTypeInputComponent.style.scss b/src/packages/ce/src/datatype/components/inputs/type/DataTypeTypeInputComponent.style.scss index 358ee6ae..65f252f9 100644 --- a/src/packages/ce/src/datatype/components/inputs/type/DataTypeTypeInputComponent.style.scss +++ b/src/packages/ce/src/datatype/components/inputs/type/DataTypeTypeInputComponent.style.scss @@ -29,8 +29,8 @@ li { height: 20px; top: 5px; transform: translateY(-50%); - border-left: 2px solid helpers.backgroundColor(variables.$tertiary); - border-bottom: 2px solid helpers.backgroundColor(variables.$tertiary); + border-left: 2px solid helpers.backgroundColor(variables.$white, 2); + border-bottom: 2px solid helpers.backgroundColor(variables.$white, 2); border-bottom-left-radius: 0.5rem; } @@ -39,7 +39,7 @@ li { position: absolute; width: 2px; height: 100%; - background: helpers.backgroundColor(variables.$tertiary); + background: helpers.backgroundColor(variables.$white, 2); top: 0; left: -0.35rem; } @@ -53,12 +53,11 @@ li { & { @include helpers.borderRadius(); - @include box.box(variables.$primary); - @include box.boxHover(variables.$secondary); + @include box.boxHover(variables.$tertiary); @include box.boxActive(variables.$secondary); @include helpers.fontStyle(); box-shadow: none; - //border-top: 1px solid helpers.borderColor(); + background: transparent; cursor: pointer; } } \ No newline at end of file diff --git a/src/packages/ce/src/function/components/files/FunctionFilesComponent.tsx b/src/packages/ce/src/function/components/files/FunctionFilesComponent.tsx index 6193d98d..faad1e40 100644 --- a/src/packages/ce/src/function/components/files/FunctionFilesComponent.tsx +++ b/src/packages/ce/src/function/components/files/FunctionFilesComponent.tsx @@ -170,12 +170,14 @@ export const FunctionFilesComponent: React.FC = (pr <> { nodes?.map(node => { return