function FlattenDialog()

in src/web/src/views/workspace/WSEditorCommandArgumentsContent.tsx [1387:1653]


function FlattenDialog(props: {
  commandUrl: string;
  arg: CMDArg;
  clsArgDefineMap: ClsArgDefinitionMap;
  open: boolean;
  onClose: (flattened: boolean) => Promise<void>;
}) {
  const [updating, setUpdating] = useState<boolean>(false);
  const [invalidText, setInvalidText] = useState<string | undefined>(undefined);
  const [subArgOptions, setSubArgOptions] = useState<{ var: string; options: string }[]>([]);
  const [argSimilarTree, setArgSimilarTree] = useState<ArgSimilarTree | undefined>(undefined);
  const [argSimilarTreeExpandedIds, setArgSimilarTreeExpandedIds] = useState<string[]>([]);
  const [argSimilarTreeArgIdsUpdated, setArgSimilarTreeArgIdsUpdated] = useState<string[]>([]);

  useEffect(() => {
    const { arg, clsArgDefineMap } = props;
    let subArgs;
    if (arg.type.startsWith("@")) {
      const clsName = (arg as CMDClsArg).clsName;
      subArgs = (clsArgDefineMap[clsName] as CMDObjectArgBase).args;
    } else {
      subArgs = (arg as CMDObjectArg).args;
    }
    const subArgOptions = subArgs.map((value) => {
      return {
        var: value.var,
        options: value.options.join(" "),
      };
    });

    setSubArgOptions(subArgOptions);
    setArgSimilarTree(undefined);
    setArgSimilarTreeExpandedIds([]);
  }, [props.arg]);

  const handleClose = () => {
    setInvalidText(undefined);
    props.onClose(false);
  };

  const verifyFlatten = () => {
    setInvalidText(undefined);
    const argOptions: { [argVar: string]: string[] } = {};
    let invalidText: string | undefined = undefined;

    subArgOptions.forEach((arg, idx) => {
      const names = arg.options.split(" ").filter((n) => n.length > 0);
      if (names.length < 1) {
        invalidText = `Prop ${idx + 1} option name is required.`;
        return undefined;
      }

      for (const idx in names) {
        const piece = names[idx];
        if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(piece)) {
          invalidText = `Invalid 'Prop ${idx + 1} option name': '${piece}'. Supported regular expression is: [a-z0-9]+(-[a-z0-9]+)* `;
          return undefined;
        }
      }
      argOptions[arg.var] = names;
    });
    if (invalidText !== undefined) {
      setInvalidText(invalidText);
      return undefined;
    }

    return {
      subArgsOptions: argOptions,
    };
  };

  const handleFlatten = async () => {
    const data = verifyFlatten();
    if (data === undefined) {
      return;
    }

    setUpdating(true);

    const flattenUrl = `${props.commandUrl}/Arguments/${props.arg.var}/Flatten`;

    try {
      await axios.post(flattenUrl, {
        ...data,
      });
      setUpdating(false);
      await props.onClose(true);
    } catch (err: any) {
      console.error(err);
      if (err.response?.data?.message) {
        const data = err.response!.data!;
        setInvalidText(`ResponseError: ${data.message!}: ${JSON.stringify(data.details)}`);
      }
      setUpdating(false);
    }
  };

  const handleDisplaySimilar = () => {
    if (verifyFlatten() === undefined) {
      return;
    }

    setUpdating(true);

    const similarUrl = `${props.commandUrl}/Arguments/${props.arg.var}/FindSimilar`;
    axios
      .post(similarUrl)
      .then((res) => {
        setUpdating(false);
        const { tree, expandedIds } = BuildArgSimilarTree(res);
        setArgSimilarTree(tree);
        setArgSimilarTreeExpandedIds(expandedIds);
        setArgSimilarTreeArgIdsUpdated([]);
      })
      .catch((err) => {
        console.error(err);
        if (err.response?.data?.message) {
          const data = err.response!.data!;
          setInvalidText(`ResponseError: ${data.message!}: ${JSON.stringify(data.details)}`);
        }
        setUpdating(false);
      });
  };

  const handleDisableSimilar = () => {
    setArgSimilarTree(undefined);
    setArgSimilarTreeExpandedIds([]);
  };

  const onSimilarTreeUpdated = (newTree: ArgSimilarTree) => {
    setArgSimilarTree(newTree);
  };

  const onSimilarTreeExpandedIdsUpdated = (expandedIds: string[]) => {
    setArgSimilarTreeExpandedIds(expandedIds);
  };

  const handleFlattenSimilar = async () => {
    const data = verifyFlatten();
    if (data === undefined) {
      return;
    }
    setUpdating(true);
    let invalidText = "";
    const updatedIds: string[] = [...argSimilarTreeArgIdsUpdated];
    for (const idx in argSimilarTree!.selectedArgIds) {
      const argId = argSimilarTree!.selectedArgIds[idx];
      if (updatedIds.indexOf(argId) === -1) {
        const flattenUrl = `${argId}/Flatten`;
        try {
          await axios.post(flattenUrl, {
            ...data,
          });
          updatedIds.push(argId);
          setArgSimilarTreeArgIdsUpdated([...updatedIds]);
        } catch (err: any) {
          console.error(err);
          if (err.response?.data?.message) {
            const data = err.response!.data!;
            invalidText += `ResponseError: ${data.message!}: ${JSON.stringify(data.details)}`;
          }
        }
      }
    }

    if (invalidText.length > 0) {
      setInvalidText(invalidText);
      setUpdating(false);
    } else {
      setUpdating(false);
      await props.onClose(true);
    }
  };

  const buildSubArgText = (arg: { var: string; options: string }, idx: number) => {
    return (
      <TextField
        id={`subArg-${arg.var}`}
        key={arg.var}
        label={`Prop ${idx + 1}`}
        helperText={idx === 0 ? "You can input multiple names separated by a space character" : undefined}
        type="text"
        fullWidth
        variant="standard"
        value={arg.options}
        onChange={(event: any) => {
          const options = subArgOptions.map((value) => {
            if (value.var === arg.var) {
              return {
                ...value,
                options: event.target.value,
              };
            } else {
              return value;
            }
          });
          setSubArgOptions(options);
        }}
        margin="normal"
        required
      />
    );
  };

  return (
    <Dialog disableEscapeKeyDown open={props.open} sx={{ "& .MuiDialog-paper": { width: "80%" } }}>
      {!argSimilarTree && (
        <>
          <DialogTitle>Flatten Props</DialogTitle>
          <DialogContent dividers={true}>
            {invalidText && (
              <Alert variant="filled" severity="error">
                {" "}
                {invalidText}{" "}
              </Alert>
            )}
            {subArgOptions.map(buildSubArgText)}
          </DialogContent>
        </>
      )}

      {argSimilarTree && (
        <>
          <DialogTitle>Flatten Similar Argument Props</DialogTitle>
          <DialogContent dividers={true}>
            {invalidText && (
              <Alert variant="filled" severity="error">
                {" "}
                {invalidText}{" "}
              </Alert>
            )}
            <WSECArgumentSimilarPicker
              tree={argSimilarTree}
              expandedIds={argSimilarTreeExpandedIds}
              updatedIds={argSimilarTreeArgIdsUpdated}
              onTreeUpdated={onSimilarTreeUpdated}
              onToggle={onSimilarTreeExpandedIdsUpdated}
            />
          </DialogContent>
        </>
      )}
      <DialogActions>
        {updating && (
          <Box sx={{ width: "100%" }}>
            <LinearProgress color="secondary" />
          </Box>
        )}
        {!updating && !argSimilarTree && (
          <>
            <Button onClick={handleClose}>Cancel</Button>
            {!props.arg.type.startsWith("@") && <Button onClick={handleFlatten}>Flatten</Button>}
            {props.arg.type.startsWith("@") && <Button onClick={handleFlatten}>Unwrap & Flatten</Button>}
            <Button onClick={handleDisplaySimilar}>Flatten Similar</Button>
          </>
        )}
        {!updating && argSimilarTree && (
          <>
            <Button onClick={handleDisableSimilar}>Back</Button>
            <Button onClick={handleFlattenSimilar} disabled={argSimilarTree.selectedArgIds.length === 0}>
              Update
            </Button>
          </>
        )}
      </DialogActions>
    </Dialog>
  );
}