/**
 * WordPress dependencies
 */
import { useContext, useMemo, useRef, useState, useLayoutEffect } from '@wordpress/element';
import { useRefEffect } from '@wordpress/compose';

/**
 * Internal dependencies
 */
import { BlockRefs } from '../../provider/block-refs-provider';

/** @typedef {import('@wordpress/element').RefCallback} RefCallback */
/** @typedef {import('@wordpress/element').RefObject} RefObject */

/**
 * Provides a ref to the BlockRefs context.
 *
 * @param {string} clientId The client ID of the element ref.
 *
 * @return {RefCallback} Ref callback.
 */
export function useBlockRefProvider(clientId) {
  const {
    refsMap
  } = useContext(BlockRefs);
  return useRefEffect(element => {
    refsMap.set(clientId, element);
    return () => refsMap.delete(clientId);
  }, [clientId]);
}

/**
 * Gets a ref pointing to the current block element. Continues to return the same
 * stable ref object even if the `clientId` argument changes. This hook is not
 * reactive, i.e., it won't trigger a rerender of the calling component if the
 * ref value changes. For reactive use cases there is the `useBlockElement` hook.
 *
 * @param {string} clientId The client ID to get a ref for.
 *
 * @return {RefObject} A ref containing the element.
 */
function useBlockRef(clientId) {
  const {
    refsMap
  } = useContext(BlockRefs);
  const latestClientId = useRef();
  latestClientId.current = clientId;

  // Always return an object, even if no ref exists for a given client ID, so
  // that `current` works at a later point.
  return useMemo(() => ({
    get current() {
      var _refsMap$get;
      return (_refsMap$get = refsMap.get(latestClientId.current)) !== null && _refsMap$get !== void 0 ? _refsMap$get : null;
    }
  }), [refsMap]);
}

/**
 * Return the element for a given client ID. Updates whenever the element
 * changes, becomes available, or disappears.
 *
 * @param {string} clientId The client ID to an element for.
 *
 * @return {Element|null} The block's wrapper element.
 */
function useBlockElement(clientId) {
  const {
    refsMap
  } = useContext(BlockRefs);
  const [blockElement, setBlockElement] = useState(null);
  // Delay setting the resulting `blockElement` until an effect. If the block element
  // changes (i.e., the block is unmounted and re-mounted), this allows enough time
  // for the ref callbacks to clean up the old element and set the new one.
  useLayoutEffect(() => {
    setBlockElement(refsMap.get(clientId));
    return refsMap.subscribe(clientId, () => setBlockElement(refsMap.get(clientId)));
  }, [refsMap, clientId]);
  return blockElement;
}
export { useBlockRef as __unstableUseBlockRef };
export { useBlockElement as __unstableUseBlockElement };
//# sourceMappingURL=use-block-refs.js.map