<commit_before>import debounce from 'lodash.debounce';
import { useCallback, useEffect, useRef, useState } from 'react';
import { PositionEnum } from '../../const';
import { useSelector } from '../../reduxConnect';

import { findNodeInState } from '../../selector/editable';
import { Cell, isRow, Node, Row } from '../../types/node';
import deepEquals from '../../utils/deepEquals';
import { getCellData } from '../../utils/getCellData';
import { getDropLevels } from '../../utils/getDropLevels';
import { useUpdateCellData } from './nodeActions';
import { useAllCellPlugins, useLang } from './options';

type NodeSelector<T> = (node: Node, ancestors: Node[]) => T;

/**
 * Use this function to get derived properties of a node. It prevents unnessesary rerenders when only the nessesary properties are returned by the selector
 * @param nodeId an id of a node (cell or row)
 * @param selector receives the node object or null (if no node with this id exists) and returns T
 * @returns the selection T
 */
export const useNodeProps = <T>(
  nodeId: string,
  selector: NodeSelector<T>
): T => {
  const node = useSelector((state) => {
    const result = findNodeInState(state, nodeId);

    if (!result) {
      return null;
    }
    return selector(result.node, result.ancestors);
  }, deepEquals);

  return node;
};

type CellSelector<T> = (node: Cell, ancestors: Node[]) => T;

/**
 * This is the same as @see useNodeProps, but only for cells. selector will receive null if the given nodeId is not a cell
 * @param nodeId an id of a cell
 * @param selector receives the cell or null (if no cell with this id exists) object and returns T
 * @returns the selection T
 */
export const useCellProps = <T>(
  nodeId: string,
  selector: CellSelector<T>
): T => {
  return useNodeProps(nodeId, (node, ancestors) =>
    !isRow(node) ? selector(node, ancestors) : null
  );
};

/**
 * better use useCellProps, unless you really need the full cell object
 * @param nodeId an id of a cell
 * @returns full Cell object
 */
export const useCell = (nodeId: string) => {
  return useNodeProps(nodeId, (node, ancestors) =>
    !isRow(node) ? node : null
  );
};

type RowSelector<T> = (node: Row, ancestors: Node[]) => T;
/**
 * This is the same as @see useNodeProps, but only for rows.
 * @param nodeId an id of a row
 * @param selector receives the row or null (if no row with this id exists) object and returns T
 * @returns the selection T
 */
export const useRowProps = <T>(nodeId: string, selector: RowSelector<T>): T => {
  return useNodeProps(nodeId, (node, ancestors) =>
    isRow(node) ? selector(node, ancestors) : null
  );
};

/**
 *
 * @param nodeId id of a node
 * @returns the relative hover position over the given node, or null if this node is not hovered over
 */
export const useNodeHoverPosition = (nodeId: string): PositionEnum => {
  return useSelector((state) =>
    state.reactPage.hover?.nodeId === nodeId
      ? state.reactPage.hover.position
      : null
  );
};

/**
 *
 * @param nodeId id of a node
 * @returns an array of ids that are ancestors of the given node
 */
export const useNodeAncestorIds = (nodeId: string) => {
  return useNodeProps(nodeId, (node, ancestors) => ancestors.map((a) => a.id));
};

/**
 *
 * @param nodeId the id of a row or cell
 * @returns the nearest ancestor cell of the cell or row
 */
export const useParentCellId = (nodeId: string) => {
  return useNodeProps(nodeId, (node, ancestors) =>
    node && ancestors ? ancestors.find((node) => !isRow(node))?.id : null
  );
};

/**
 *
 * @deprecated currently not used
 */
export const useNodeDropLevels = (nodeId: string) => {
  return useNodeProps(nodeId, (node, ancestors) =>
    getDropLevels(node, ancestors)
  );<commit_msg>fix: don't select parent cells that have no plugin<commit_after>import debounce from 'lodash.debounce';
import { useCallback, useEffect, useRef, useState } from 'react';
import { PositionEnum } from '../../const';
import { useSelector } from '../../reduxConnect';

import { findNodeInState } from '../../selector/editable';
import { Cell, isRow, Node, Row } from '../../types/node';
import deepEquals from '../../utils/deepEquals';
import { getCellData } from '../../utils/getCellData';
import { getDropLevels } from '../../utils/getDropLevels';
import { useUpdateCellData } from './nodeActions';
import { useAllCellPlugins, useLang } from './options';

type NodeSelector<T> = (node: Node, ancestors: Node[]) => T;

/**
 * Use this function to get derived properties of a node. It prevents unnessesary rerenders when only the nessesary properties are returned by the selector
 * @param nodeId an id of a node (cell or row)
 * @param selector receives the node object or null (if no node with this id exists) and returns T
 * @returns the selection T
 */
export const useNodeProps = <T>(
  nodeId: string,
  selector: NodeSelector<T>
): T => {
  const node = useSelector((state) => {
    const result = findNodeInState(state, nodeId);

    if (!result) {
      return null;
    }
    return selector(result.node, result.ancestors);
  }, deepEquals);

  return node;
};

type CellSelector<T> = (node: Cell, ancestors: Node[]) => T;

/**
 * This is the same as @see useNodeProps, but only for cells. selector will receive null if the given nodeId is not a cell
 * @param nodeId an id of a cell
 * @param selector receives the cell or null (if no cell with this id exists) object and returns T
 * @returns the selection T
 */
export const useCellProps = <T>(
  nodeId: string,
  selector: CellSelector<T>
): T => {
  return useNodeProps(nodeId, (node, ancestors) =>
    !isRow(node) ? selector(node, ancestors) : null
  );
};

/**
 * better use useCellProps, unless you really need the full cell object
 * @param nodeId an id of a cell
 * @returns full Cell object
 */
export const useCell = (nodeId: string) => {
  return useNodeProps(nodeId, (node, ancestors) =>
    !isRow(node) ? node : null
  );
};

type RowSelector<T> = (node: Row, ancestors: Node[]) => T;
/**
 * This is the same as @see useNodeProps, but only for rows.
 * @param nodeId an id of a row
 * @param selector receives the row or null (if no row with this id exists) object and returns T
 * @returns the selection T
 */
export const useRowProps = <T>(nodeId: string, selector: RowSelector<T>): T => {
  return useNodeProps(nodeId, (node, ancestors) =>
    isRow(node) ? selector(node, ancestors) : null
  );
};

/**
 *
 * @param nodeId id of a node
 * @returns the relative hover position over the given node, or null if this node is not hovered over
 */
export const useNodeHoverPosition = (nodeId: string): PositionEnum => {
  return useSelector((state) =>
    state.reactPage.hover?.nodeId === nodeId
      ? state.reactPage.hover.position
      : null
  );
};

/**
 *
 * @param nodeId id of a node
 * @returns an array of ids that are ancestors of the given node
 */
export const useNodeAncestorIds = (nodeId: string) => {
  return useNodeProps(nodeId, (node, ancestors) => ancestors.map((a) => a.id));
};

/**
 *
 * @param nodeId the id of a row or cell
 * @returns the nearest ancestor cell of the cell or row that has a plugin
 */
export const useParentCellId = (nodeId: string) => {
  return useNodeProps(nodeId, (node, ancestors) =>
    node && ancestors
      ? ancestors.find((node) => !isRow(node) && node.plugin)?.id
      : null
  );
};

/**
 *
 * @deprecated currently not used
 */
export const useNodeDropLevels = (nodeId: string) => {
  return useNodeProps(nodeId, (node, ancestors) =>
    getDropLevels(node, ancestors)
  );
{'fraction_non_alphanumeric': 0.13180249535439342, 'fraction_numerical': 0.0, 'mean_word_length': 3.709375, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4885649', 'n_tokens_mistral': 2261, 'n_tokens_neox': 2257, 'n_words': 865}