function UnitablesBeeTableCell()

in packages/unitables/src/bee/UnitablesBeeTable.tsx [341:829]


function UnitablesBeeTableCell({
  joinedName,
  rowCount,
  columnCount,
}: BeeTableCellProps<ROWTYPE> & {
  joinedName: string;
  rowCount: number;
  columnCount: number;
}) {
  const [{ field, onChange: onFieldChange, name: fieldName }] = useField(joinedName, {});
  const cellRef = useRef<HTMLDivElement | null>(null);
  const [autoFieldKey, forceUpdate] = useReducer((x) => x + 1, 0);
  const { containerCellCoordinates } = useBeeTableCoordinates();
  const { isBeeTableChange } = useUnitablesContext();
  const { submitRow, rowInputs } = useUnitablesRow(containerCellCoordinates?.rowIndex ?? 0);
  const fieldInput = useMemo(() => getObjectValueByPath(rowInputs, fieldName), [rowInputs, fieldName]);
  const [isSelectFieldOpen, setIsSelectFieldOpen] = useState(false);
  const fieldCharacteristics = useMemo(() => {
    if (!field) {
      return;
    }
    return {
      xDmnType: field["x-dmn-type"] as X_DMN_TYPE,
      isEnum: !!field.enum,
      isList: field.type === "array",
    };
  }, [field]);
  const previousFieldInput = useRef(fieldInput);

  // keep previous updated;
  useEffect(() => {
    previousFieldInput.current = fieldInput;
  }, [fieldInput]);

  // FIXME: Decouple from DMN --> https://github.com/apache/incubator-kie-issues/issues/166
  const setValue = useCallback(
    (newValue?: string) => {
      isBeeTableChange.current = true;
      const newValueWithoutSymbols = newValue?.replace(/\r/g, "") ?? "";

      if (field.enum) {
        if (field.enum.findIndex((value: unknown) => value === newValueWithoutSymbols) >= 0) {
          onFieldChange(newValueWithoutSymbols);
        } else {
          onFieldChange(field.placeholder);
        }
        // Changing the values using onChange will not re-render <select> nodes;
        // This ensure a re-render of the SelectField;
        forceUpdate();
      } else if (field.type === "string") {
        if (field.format === "time") {
          if (moment(newValueWithoutSymbols, [moment.HTML5_FMT.TIME, moment.HTML5_FMT.TIME_SECONDS], true).isValid()) {
            onFieldChange(newValueWithoutSymbols);
          } else {
            onFieldChange("");
          }
        } else if (field.format === "date") {
          if (moment(newValueWithoutSymbols, [moment.HTML5_FMT.DATE]).isValid()) {
            onFieldChange(newValueWithoutSymbols);
          } else {
            onFieldChange("");
          }
        } else if (field.format === "date-time") {
          const valueAsNumber = Date.parse(newValueWithoutSymbols);
          if (!isNaN(valueAsNumber)) {
            onFieldChange(newValueWithoutSymbols);
          } else {
            onFieldChange("");
          }
        } else {
          onFieldChange(newValueWithoutSymbols);
        }
      } else if (field.type === "number") {
        const numberValue = parseFloat(newValueWithoutSymbols);
        onFieldChange(isNaN(numberValue) ? undefined : numberValue);
      } else if (field.type === "boolean") {
        onFieldChange(newValueWithoutSymbols === "true");
      } else if (field.type === "array") {
        // FIXME: Support lists --> https://github.com/apache/incubator-kie-issues/issues/167
      } else if (field.type === "object" && typeof newValue !== "object") {
        // objects are flattened in a single row - this case shouldn't happen;
      } else {
        onFieldChange(newValue);
      }
      submitRow();
    },
    [isBeeTableChange, field.enum, field.type, field.placeholder, field.format, submitRow, onFieldChange]
  );

  const { isActive, isEditing } = useBeeTableSelectableCellRef(
    containerCellCoordinates?.rowIndex ?? 0,
    containerCellCoordinates?.columnIndex ?? 0,
    setValue,
    useCallback(() => `${fieldInput ?? ""}`, [fieldInput])
  );
  const { mutateSelection } = useBeeTableSelectionDispatch();

  const navigateVertically = useCallback(
    (args: { isShiftPressed: boolean }) => {
      mutateSelection({
        part: SelectionPart.ActiveCell,
        columnCount: () => columnCount,
        rowCount,
        deltaColumns: 0,
        deltaRows: args.isShiftPressed ? -1 : 1,
        isEditingActiveCell: false,
        keepInsideSelection: true,
      });
    },
    [mutateSelection, rowCount, columnCount]
  );

  const setEditingCell = useCallback(
    (isEditing: boolean) => {
      mutateSelection({
        part: SelectionPart.ActiveCell,
        columnCount: () => columnCount,
        rowCount,
        deltaColumns: 0,
        deltaRows: 0,
        isEditingActiveCell: isEditing,
        keepInsideSelection: true,
      });
    },
    [mutateSelection, rowCount, columnCount]
  );

  const onKeyDown = useCallback(
    (e: React.KeyboardEvent<HTMLDivElement>) => {
      // TAB
      if (e.key.toLowerCase() === "tab") {
        // ListField - START
        if (fieldCharacteristics?.isList) {
          // Get all uniforms components inside the ListField;
          const uniformsComponents = cellRef.current?.querySelectorAll('[id^="uniforms-"]');
          if (uniformsComponents === undefined) {
            setEditingCell(false);
            return;
          }

          const uniformComponentTargetIndex = Array.from(uniformsComponents ?? []).findIndex(
            (component) => component.id === (e.target as HTMLElement).id
          );

          // If it wasn't possible to retrieve the index, it should focus on the first button;
          if (uniformComponentTargetIndex < 0) {
            (uniformsComponents?.item(1) as HTMLElement).parentElement?.focus();
            setEditingCell(true);
            e.stopPropagation();
            return;
          }

          const nextUniformsComponent = e.shiftKey
            ? uniformsComponents[uniformComponentTargetIndex - 1]
            : uniformsComponents[uniformComponentTargetIndex + 1];

          // Should leave ListField if nextUniformsComponent doesn't exist
          if (nextUniformsComponent === undefined) {
            setEditingCell(false);
            return;
          }

          // TextField, BoolField, DateTimeField, NumField, ListAddField, ListDelField
          if (
            nextUniformsComponent.tagName.toLowerCase() === "input" ||
            nextUniformsComponent.tagName.toLowerCase() === "button"
          ) {
            (nextUniformsComponent as HTMLButtonElement | HTMLInputElement).parentElement?.focus();
            setEditingCell(true);
            submitRow();
            e.stopPropagation();
            return;
          }

          // Nested ListFields or SelectField
          if (nextUniformsComponent.tagName.toLowerCase() === "div") {
            (nextUniformsComponent as HTMLElement)?.focus();
            setEditingCell(true);
            submitRow();
            e.stopPropagation();
            return;
          }
        } // ListField - END

        submitRow();
        setEditingCell(false);
        if (fieldCharacteristics?.isEnum) {
          setIsSelectFieldOpen((prev) => {
            if (prev) {
              cellRef.current?.getElementsByTagName("button")?.[0]?.click();
            }
            return false;
          });
        }
        return;
      }

      // ESC
      if (e.key.toLowerCase() === "escape") {
        e.stopPropagation();
        onFieldChange(previousFieldInput.current);
        cellRef.current?.focus();
        setEditingCell(false);
        if (fieldCharacteristics?.isEnum) {
          setIsSelectFieldOpen((prev) => {
            if (prev) {
              cellRef.current?.getElementsByTagName("button")?.[0]?.click();
            }
            return false;
          });
        }
        return;
      }

      // ENTER
      if (e.key.toLowerCase() === "enter") {
        // ListField - START
        if (fieldCharacteristics?.isList) {
          e.stopPropagation();
          e.preventDefault();
          const uniformsComponents = cellRef.current?.querySelectorAll('[id^="uniforms-"]');
          if (!uniformsComponents) {
            return;
          }

          // To search the uniforms components avoiding returning the top ListField
          // we search backwards;
          const reversedUniformsComponents = Array.from(uniformsComponents).reverse();
          const reversedUniformComponentTargetIndex = reversedUniformsComponents.findIndex((component) =>
            component.contains(e.target as HTMLElement)
          );
          const uniformsComponent = reversedUniformsComponents[reversedUniformComponentTargetIndex];

          // If field is selected, and the target is not present
          if (!uniformsComponent) {
            // check if it's a button from a SelectField
            const selectFieldUl = document.querySelectorAll(`ul[name^="${fieldName}."]`)?.item(0);
            if (selectFieldUl && selectFieldUl.contains(e.target as HTMLElement)) {
              setIsSelectFieldOpen(false);
              submitRow();
              (cellRef.current?.querySelector(`[id=${selectFieldUl.id}]`) as HTMLDivElement)
                ?.getElementsByTagName("button")
                ?.item(0)
                ?.focus();
            } else if (uniformsComponents[1].tagName.toLowerCase() === "button") {
              (uniformsComponents[1] as HTMLButtonElement)?.focus();
            }
          } else {
            // A button is the ListAddField or ListDelField
            if (uniformsComponent.tagName.toLowerCase() === "button") {
              (uniformsComponent as HTMLButtonElement)?.click();

              // The ListField ListDelField is the last element
              if (reversedUniformComponentTargetIndex === 0) {
                // focus on ListField parent element;
                if (uniformsComponents[1].tagName.toLowerCase() === "button") {
                  (uniformsComponents[1] as HTMLButtonElement)?.focus();
                }
              }
              submitRow();
            }

            // SelectField
            if (uniformsComponent.tagName.toLowerCase() === "div") {
              setIsSelectFieldOpen(true);
            }
          }
          return;
        } // ListField - END

        e.stopPropagation();
        if (fieldCharacteristics?.isEnum) {
          cellRef.current?.getElementsByTagName("button")?.[0]?.click();
          setIsSelectFieldOpen((prev) => {
            if (prev === true) {
              submitRow();
            }
            return !prev;
          });
          setEditingCell(!isEditing);
          return;
        }

        if (!isEditing) {
          const inputField = cellRef.current?.getElementsByTagName("input");
          if (inputField && inputField.length > 0) {
            inputField?.[0]?.focus();
            setEditingCell(true);
            return;
          }
        }
        submitRow();
        setEditingCell(false);
        navigateVertically({ isShiftPressed: e.shiftKey });
        return;
      }

      // Normal editing;
      if (isEditModeTriggeringKey(e)) {
        e.stopPropagation();
        setEditingCell(true);

        // If the target is an input node it is already editing the cell;
        if (
          !fieldCharacteristics?.isList &&
          !isEditing &&
          (e.target as HTMLInputElement).tagName.toLowerCase() !== "input"
        ) {
          // handle checkbox field;
          const inputField = cellRef.current?.getElementsByTagName("input")?.[0];
          if (e.code.toLowerCase() === "space" && fieldCharacteristics?.xDmnType === X_DMN_TYPE.BOOLEAN) {
            inputField?.click();
            submitRow();
            return;
          }
          inputField?.select();
        }

        if (fieldCharacteristics?.isList) {
          if (
            e.code.toLowerCase() === "space" &&
            (e.target as HTMLElement)?.tagName?.toLowerCase() === "input" &&
            (e.target as HTMLInputElement)?.type === "checkbox"
          ) {
            e.preventDefault();
            (e.target as HTMLInputElement).click();
            submitRow();
            return;
          }

          if (e.code.toLowerCase() === "space" && (e.target as HTMLElement)?.tagName?.toLowerCase() === "button") {
            e.preventDefault();
            if ((e.target as HTMLButtonElement).id.match(/^uniforms-/g)) {
              return;
            }
            setIsSelectFieldOpen(true);
            return;
          }
        }
      }

      if (isEditing) {
        e.stopPropagation();
      }
    },
    [isEditing, submitRow, setEditingCell, fieldCharacteristics, onFieldChange, navigateVertically, fieldName]
  );

  // if it's active should focus on cell;
  useEffect(() => {
    if (!isActive) {
      return;
    }

    if (fieldCharacteristics?.isList) {
      if (isSelectFieldOpen) {
        setTimeout(() => {
          document.querySelectorAll(`ul[name^="${fieldName}."]`)?.[0]?.getElementsByTagName("button")?.item(0)?.focus();
        }, 0);
      }
    } else if (fieldCharacteristics?.isEnum) {
      if (isSelectFieldOpen) {
        // if a SelectField is open, it takes a time to render the select options;
        // After the select options are rendered we focus in the selected option;
        setTimeout(() => {
          const selectOptions = document.getElementsByName(fieldName)?.[0]?.getElementsByTagName("button");
          Array.from(selectOptions ?? [])
            ?.filter((selectOption) => selectOption.innerText === cellRef.current?.innerText)?.[0]
            ?.focus();
        }, 0);
      } else {
        cellRef.current?.focus();
      }
    }

    if (!isEditing) {
      cellRef.current?.focus();
    }
  }, [fieldName, isActive, isEditing, fieldCharacteristics?.isList, fieldCharacteristics?.isEnum, isSelectFieldOpen]);

  const onBlur = useCallback(
    (e: React.FocusEvent<HTMLDivElement>) => {
      if (fieldCharacteristics?.isList) {
        if (
          e.target.tagName.toLowerCase() === "button" &&
          (e.relatedTarget as HTMLElement)?.tagName.toLowerCase() === "button"
        ) {
          // if the select field is open and it blurs to another cell, close it;
          const selectFieldUl = document.querySelectorAll(`ul[name^="${fieldName}."]`).item(0);
          // if relatedTarget aka button is not in the SelectField UL, it should close the SelectField
          if (selectFieldUl && !selectFieldUl?.contains(e.relatedTarget as HTMLButtonElement)) {
            (cellRef.current?.querySelector(`[id="${selectFieldUl?.id}"]`) as HTMLDivElement)?.click();
            setIsSelectFieldOpen(false);
          }
        }
        submitRow();
        return;
      }

      if (e.target.tagName.toLowerCase() === "div") {
        if ((e.target.getElementsByTagName("input")?.length ?? 0) > 0) {
          submitRow();
        }
      }

      if (e.target.tagName.toLowerCase() === "input") {
        submitRow();
      }

      if (
        e.target.tagName.toLowerCase() === "button" ||
        (e.relatedTarget as HTMLElement)?.tagName.toLowerCase() === "button"
      ) {
        // if the select field is open and it blurs to another cell, close it;
        const selectOptions = document.getElementsByName(fieldName)?.[0]?.getElementsByTagName("button");
        if ((selectOptions?.length ?? 0) > 0 && (e.relatedTarget as HTMLElement)?.tagName?.toLowerCase() === "td") {
          e.target.click();
          setIsSelectFieldOpen(false);
        }
        submitRow();
      }
    },
    [fieldName, fieldCharacteristics?.isList, submitRow]
  );

  const onClick = useCallback(
    (e: React.MouseEvent<HTMLDivElement>) => {
      // The "enter" key triggers the onClick if the button is inside a form
      if (e.detail === 0) {
        return;
      }
      // ListField
      if (
        e.isTrusted &&
        fieldCharacteristics?.isList &&
        ((e.target as HTMLElement).tagName.toLowerCase() === "path" ||
          (e.target as HTMLElement).tagName.toLowerCase() === "svg" ||
          (e.target as HTMLElement).tagName.toLowerCase() === "button")
      ) {
        // if the select field is open and it blurs to another cell, close it;
        const selectField = document.querySelectorAll(`ul[name^="${fieldName}."]`).item(0);
        if (selectField?.contains(e.target as HTMLButtonElement)) {
          setIsSelectFieldOpen((prev) => !prev);
        }
        submitRow();
        return;
      }

      // SelectField
      if (e.isTrusted && (e.target as HTMLElement).tagName.toLowerCase() === "button") {
        setIsSelectFieldOpen((prev) => {
          if (prev === true) {
            submitRow();
          }
          return !prev;
        });
        setEditingCell(!isEditing);
      }

      if (!isEditing && e.isTrusted && (e.target as HTMLElement).tagName.toLowerCase() === "input") {
        const inputField = cellRef.current?.getElementsByTagName("input");
        if (inputField && inputField.length > 0) {
          inputField?.[0]?.focus();
          setEditingCell(true);
          return;
        }
      }
    },
    [fieldName, isEditing, fieldCharacteristics?.isList, setEditingCell, submitRow]
  );

  return (
    <div
      style={{ outline: "none" }}
      tabIndex={-1}
      ref={cellRef}
      onKeyDown={onKeyDown}
      onBlur={onBlur}
      onClick={onClick}
    >
      <AutoField
        key={joinedName + autoFieldKey}
        name={joinedName}
        form={`${AUTO_ROW_ID}-${containerCellCoordinates?.rowIndex ?? 0}`}
        style={{ height: "60px" }}
      />
    </div>
  );
}