go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/projects/nodes/static/_nextjs/src/components/editNode.tsx (about)

     1  /**
     2   * Copyright (c) 2024 - Present. Will Charczuk. All rights reserved.
     3   * Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository.
     4   */
     5  import { FormGroup, InputGroup } from "@blueprintjs/core";
     6  import { ReactElement, useEffect, useState } from "react";
     7  import { nodeTypes, valueScalarNumberTypes, valueTypes } from "../refdata/nodeTypes";
     8  import * as api from "../api/nodes";
     9  import { SelectStrings } from "./selectStrings";
    10  import { EditValue } from "./editValue";
    11  import Editor from "@monaco-editor/react";
    12  import * as monaco from "@monaco-editor/react";
    13  
    14  monaco.loader.config({
    15    paths: {
    16      vs: '/public/vs',
    17    },
    18  })
    19  
    20  export interface EditNodeProps {
    21    node: api.Node;
    22    value?: any;
    23    isCreate?: boolean;
    24    autoFocus?: boolean;
    25    onNodeChanged: (n: api.Node) => void;
    26    onValueChanged: (v: api.NativeValueType) => void;
    27  }
    28  
    29  const numbers = new Set(valueScalarNumberTypes)
    30  
    31  export function EditNode(props: EditNodeProps) {
    32    const [label, setLabel] = useState<string>(props.node.label)
    33    const [inputType, setInputType] = useState<string | undefined>(props.node.metadata.input_type)
    34    const [outputType, setOutputType] = useState<string | undefined>(props.node.metadata.output_type)
    35    const [expression, setExpression] = useState<string | undefined>(props.node.metadata.expression)
    36    const [value, setValue] = useState<any | undefined>(props.value);
    37  
    38    useEffect(() => { setLabel(props.node.label) }, [props.node.label])
    39    useEffect(() => { setValue(props.value) }, [props.value])
    40    useEffect(() => { setInputType(props.node.metadata.input_type) }, [props.node.metadata.input_type])
    41    useEffect(() => { setOutputType(props.node.metadata.output_type) }, [props.node.metadata.output_type])
    42    useEffect(() => { setExpression(props.node.metadata.expression) }, [props.node.metadata.expression])
    43  
    44    const nodeTypeName = props.node.metadata.node_type;
    45    const nodeType = nodeTypes[nodeTypeName];
    46  
    47    const onLabelChange = (e: any) => {
    48      setLabel(e.target.value);
    49      props.onNodeChanged({
    50        ...props.node,
    51        label: e.target.value,
    52      })
    53    }
    54    const onInputTypeChange = (inputType: string) => {
    55      setInputType(inputType);
    56      if (nodeType.outputTypeSameAsInput) {
    57        setOutputType(inputType);
    58      }
    59  
    60      props.onNodeChanged({
    61        ...props.node,
    62        metadata: {
    63          ...props.node.metadata,
    64          input_type: inputType,
    65          output_type: nodeType.outputTypeSameAsInput ? inputType : outputType,
    66        },
    67      })
    68    }
    69    const onOutputTypeChange = (outputType: string) => {
    70      setOutputType(outputType);
    71      if (numbers.has(outputType)) {
    72        setValue(0)
    73        props.onValueChanged(0)
    74      }
    75      props.onNodeChanged({
    76        ...props.node,
    77        metadata: {
    78          ...props.node.metadata,
    79          output_type: outputType,
    80        },
    81      })
    82    }
    83    const onExpressionChange = (e: string) => {
    84      setExpression(e);
    85      props.onNodeChanged({
    86        ...props.node,
    87        metadata: {
    88          ...props.node.metadata,
    89          expression: e,
    90        }
    91      })
    92    }
    93    const onValueChanged = (v) => {
    94      setValue(v);
    95      props.onValueChanged(v);
    96    }
    97  
    98    let valueControl: ReactElement | null = null;
    99    if (nodeType.showValue && nodeType.canSetValue) {
   100      valueControl = (<EditValue tabIndex={0} value={value} valueType={outputType} onValueChanged={onValueChanged} />)
   101    }
   102  
   103    return (
   104      <div className="edit-node">
   105        <FormGroup label={"Label"} inline={true} labelFor={"label-value"}>
   106          <InputGroup autoFocus={props.autoFocus} tabIndex={0} id="label-value" name={"label"} value={label} onChange={onLabelChange} fill={true} />
   107        </FormGroup>
   108        {nodeType.canSetInputType && (
   109          <FormGroup label={"Input Type"} inline={true} labelFor={"input-type-value"}>
   110            <SelectStrings tabIndex={0} id="input-type-value" items={nodeType.inputTypes || valueTypes} value={inputType} onItemSelect={onInputTypeChange} disabled={props.isCreate !== true} fill={true} />
   111          </FormGroup>
   112        )}
   113        {nodeType.canSetOutputType && (
   114          <FormGroup label={"Output Type"} inline={true} labelFor={"output-type-value"}>
   115            <SelectStrings tabIndex={0} id="output-type-value" items={nodeType.outputTypes || valueTypes} value={outputType} onItemSelect={onOutputTypeChange} disabled={props.isCreate !== true} fill={true} />
   116          </FormGroup>
   117        )}
   118        {valueControl}
   119        {nodeType.showExpression && nodeType.canSetExpression && (
   120          <FormGroup label={"Expression"} labelInfo={nodeType.expressionRequired ? "(required)" : ""} helperText={nodeType.expressionHelpText}>
   121            <Editor
   122              height="200px"
   123              width="720px"
   124              language={nodeType.expressionLanguage || "python"}
   125              theme="bp5"
   126              onChange={onExpressionChange}
   127              value={expression || nodeType.expressionDefault}
   128              options={{
   129                formatOnType: true,
   130                autoIndent: 'full',
   131                renderLineHighlight: 'none',
   132                minimap: {
   133                  enabled: false,
   134                },
   135              }}
   136            />
   137          </FormGroup>
   138        )}
   139      </div>
   140    )
   141  }