function ArgumentNavigation()

in src/web/src/views/workspace/WSEditorCommandArgumentsContent.tsx [212:431]


function ArgumentNavigation(props: {
  commandUrl: string;
  args: CMDArg[];
  clsArgDefineMap: ClsArgDefinitionMap;
  onEdit: (arg: CMDArg, argIdxStack: ArgIdx[]) => void;
  onFlatten: (arg: CMDArg, argIdxStack: ArgIdx[]) => void;
  onUnwrap: (arg: CMDArg, argIdxStack: ArgIdx[]) => void;
  onAddSubcommand: (arg: CMDArg, argIdxStack: ArgIdx[]) => void;
}) {
  const [argIdxStack, setArgIdxStack] = useState<ArgIdx[]>([]);

  const getArgProps = (
    selectedArgBase: CMDArgBase,
  ): { title: string; props: CMDArg[]; flattenArgVar: string | undefined } | undefined => {
    if (selectedArgBase.type.startsWith("@")) {
      const clsArgDefine = props.clsArgDefineMap[(selectedArgBase as CMDClsArgBase).clsName];
      const clsArgProps = getArgProps(clsArgDefine);
      if (clsArgProps !== undefined && clsArgDefine.type === "object") {
        clsArgProps!.flattenArgVar = (selectedArgBase as CMDClsArg).var;
      }
      return clsArgProps;
    }
    if (selectedArgBase.type === "object") {
      return {
        title: "Props",
        props: (selectedArgBase as CMDObjectArgBase).args,
        flattenArgVar: (selectedArgBase as CMDObjectArg).var,
      };
    } else if (selectedArgBase.type.startsWith("dict<")) {
      const item = (selectedArgBase as CMDDictArgBase).item;
      const itemProps = item ? getArgProps(item) : undefined;
      if (!itemProps) {
        return undefined;
      }
      return {
        title: "Dict Element Props",
        props: itemProps.props,
        flattenArgVar: undefined,
      };
    } else if (selectedArgBase.type.startsWith("array<")) {
      const itemProps = getArgProps((selectedArgBase as CMDArrayArgBase).item);
      if (!itemProps) {
        return undefined;
      }
      return {
        title: "Array Element Props",
        props: itemProps.props,
        flattenArgVar: undefined,
      };
    } else {
      return undefined;
    }
  };

  const getSelectedArg = (stack: ArgIdx[]): CMDArg | undefined => {
    if (stack.length === 0) {
      return undefined;
    } else {
      let args: CMDArg[] = [...props.args];
      let selectedArg: CMDArg | undefined = undefined;
      for (const i in stack) {
        const argVar = stack[i].var;
        selectedArg = args.find((arg) => arg.var === argVar);
        if (!selectedArg) {
          break;
        }
        args = getArgProps(selectedArg)?.props ?? [];
      }
      return selectedArg;
    }
  };

  useEffect(() => {
    setArgIdxStack([]);
  }, [props.commandUrl]);

  useEffect(() => {
    // update argument idx stack
    const stack = [...argIdxStack];
    while (stack.length > 0 && !getSelectedArg(stack)) {
      stack.pop();
    }
    setArgIdxStack(stack);
  }, [props.args, props.clsArgDefineMap]);

  const handleSelectSubArg = (subArgVar: string) => {
    let subArg;
    if (argIdxStack.length > 0) {
      const arg = getSelectedArg(argIdxStack);
      if (!arg) {
        return;
      }
      subArg = getArgProps(arg)?.props.find((a) => a.var === subArgVar);
    } else {
      subArg = props.args.find((a) => a.var === subArgVar);
    }

    if (!subArg) {
      return;
    }
    const argIdx: ArgIdx = {
      var: subArg.var,
      displayKey: subArg.options[0],
    };
    if (argIdxStack.length === 0) {
      if (argIdx.displayKey.length === 1) {
        argIdx.displayKey = `-${argIdx.displayKey}`;
      } else {
        argIdx.displayKey = `--${argIdx.displayKey}`;
      }
    }

    let argType = subArg.type;
    if (argType.startsWith("@")) {
      argType = props.clsArgDefineMap[(subArg as CMDClsArg).clsName].type;
    }
    if (argType.startsWith("dict<")) {
      argIdx.displayKey += "{}";
    } else if (argType.startsWith("array<")) {
      argIdx.displayKey += "[]";
    }

    setArgIdxStack([...argIdxStack, argIdx]);
  };

  const handleChangeArgIdStack = (end: number) => {
    setArgIdxStack(argIdxStack.slice(0, end));
  };

  const buildArgumentReviewer = () => {
    const selectedArg = getSelectedArg(argIdxStack);
    if (!selectedArg) {
      return <></>;
    }

    const stage = selectedArg.stage;

    return (
      <React.Fragment>
        <Box
          sx={{
            display: "flex",
            flexDirection: "row",
            alignItems: "flex-start",
            justifyContent: "flex-start",
          }}
        >
          <ArgNavBar argIdxStack={argIdxStack} onChangeArgIdStack={handleChangeArgIdStack} />
          {stage === "Stable" && <StableTypography sx={{ flexShrink: 0 }}>{stage}</StableTypography>}
          {stage === "Preview" && <PreviewTypography sx={{ flexShrink: 0 }}>{stage}</PreviewTypography>}
          {stage === "Experimental" && <ExperimentalTypography sx={{ flexShrink: 0 }}>{stage}</ExperimentalTypography>}
        </Box>
        <ArgumentReviewer
          arg={selectedArg}
          depth={argIdxStack.length}
          onEdit={() => {
            props.onEdit(selectedArg, argIdxStack);
          }}
          onUnwrap={() => {
            props.onUnwrap(selectedArg, argIdxStack);
          }}
        />
      </React.Fragment>
    );
  };

  const buildArgumentPropsReviewer = () => {
    if (argIdxStack.length === 0) {
      if (props.args.length === 0) {
        return <></>;
      }
      return (
        <ArgumentPropsReviewer
          title={"Argument Groups"}
          args={props.args}
          onFlatten={undefined}
          onAddSubcommand={undefined}
          depth={argIdxStack.length}
          onSelectSubArg={handleSelectSubArg}
        />
      );
    } else {
      const selectedArg = getSelectedArg(argIdxStack);
      if (!selectedArg) {
        return <></>;
      }
      const argProps = getArgProps(selectedArg);
      if (!argProps) {
        return <></>;
      }
      const canFlatten = argProps.flattenArgVar !== undefined;
      return (
        <ArgumentPropsReviewer
          title={argProps.title}
          args={argProps.props}
          depth={argIdxStack.length}
          selectedArg={selectedArg!}
          onFlatten={
            canFlatten
              ? () => {
                  props.onFlatten(selectedArg!, argIdxStack);
                }
              : undefined
          }
          onAddSubcommand={() => {
            props.onAddSubcommand(selectedArg!, argIdxStack);
          }}
          onSelectSubArg={handleSelectSubArg}
        />
      );
    }
  };

  return (
    <React.Fragment>
      {argIdxStack.length > 0 && <React.Fragment>{buildArgumentReviewer()}</React.Fragment>}
      <React.Fragment>{buildArgumentPropsReviewer()}</React.Fragment>
    </React.Fragment>
  );
}