"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _i18n = require("@wordpress/i18n");
var _element = require("@wordpress/element");
var _components = require("@wordpress/components");
var _blocks = require("@wordpress/blocks");
var _data = require("@wordpress/data");
var _components2 = require("../components");
var _store = require("../store");
var _jsxRuntime = require("react/jsx-runtime");
/**
 * WordPress dependencies
 */

/**
 * Internal dependencies
 */

const EMPTY_OBJECT = {};
function BlockHooksControlPure({
  name,
  clientId,
  metadata: {
    ignoredHookedBlocks = []
  } = {}
}) {
  const blockTypes = (0, _data.useSelect)(select => select(_blocks.store).getBlockTypes(), []);

  // A hooked block added via a filter will not be exposed through a block
  // type's `blockHooks` property; however, if the containing layout has been
  // modified, it will be present in the anchor block's `ignoredHookedBlocks`
  // metadata.
  const hookedBlocksForCurrentBlock = (0, _element.useMemo)(() => blockTypes?.filter(({
    name: blockName,
    blockHooks
  }) => blockHooks && name in blockHooks || ignoredHookedBlocks.includes(blockName)), [blockTypes, name, ignoredHookedBlocks]);
  const hookedBlockClientIds = (0, _data.useSelect)(select => {
    const {
      getBlocks,
      getBlockRootClientId,
      getGlobalBlockCount
    } = select(_store.store);
    const rootClientId = getBlockRootClientId(clientId);
    const _hookedBlockClientIds = hookedBlocksForCurrentBlock.reduce((clientIds, block) => {
      // If the block doesn't exist anywhere in the block tree,
      // we know that we have to set the toggle to disabled.
      if (getGlobalBlockCount(block.name) === 0) {
        return clientIds;
      }
      const relativePosition = block?.blockHooks?.[name];
      let candidates;
      switch (relativePosition) {
        case 'before':
        case 'after':
          // Any of the current block's siblings (with the right block type) qualifies
          // as a hooked block (inserted `before` or `after` the current one), as the block
          // might've been automatically inserted and then moved around a bit by the user.
          candidates = getBlocks(rootClientId);
          break;
        case 'first_child':
        case 'last_child':
          // Any of the current block's child blocks (with the right block type) qualifies
          // as a hooked first or last child block, as the block might've been automatically
          // inserted and then moved around a bit by the user.
          candidates = getBlocks(clientId);
          break;
        case undefined:
          // If we haven't found a blockHooks field with a relative position for the hooked
          // block, it means that it was added by a filter. In this case, we look for the block
          // both among the current block's siblings and its children.
          candidates = [...getBlocks(rootClientId), ...getBlocks(clientId)];
          break;
      }
      const hookedBlock = candidates?.find(candidate => candidate.name === block.name);

      // If the block exists in the designated location, we consider it hooked
      // and show the toggle as enabled.
      if (hookedBlock) {
        return {
          ...clientIds,
          [block.name]: hookedBlock.clientId
        };
      }

      // If no hooked block was found in any of its designated locations,
      // we set the toggle to disabled.
      return clientIds;
    }, {});
    if (Object.values(_hookedBlockClientIds).length > 0) {
      return _hookedBlockClientIds;
    }
    return EMPTY_OBJECT;
  }, [hookedBlocksForCurrentBlock, name, clientId]);
  const {
    getBlockIndex,
    getBlockCount,
    getBlockRootClientId
  } = (0, _data.useSelect)(_store.store);
  const {
    insertBlock,
    removeBlock
  } = (0, _data.useDispatch)(_store.store);
  if (!hookedBlocksForCurrentBlock.length) {
    return null;
  }

  // Group by block namespace (i.e. prefix before the slash).
  const groupedHookedBlocks = hookedBlocksForCurrentBlock.reduce((groups, block) => {
    const [namespace] = block.name.split('/');
    if (!groups[namespace]) {
      groups[namespace] = [];
    }
    groups[namespace].push(block);
    return groups;
  }, {});
  const insertBlockIntoDesignatedLocation = (block, relativePosition) => {
    const blockIndex = getBlockIndex(clientId);
    const innerBlocksLength = getBlockCount(clientId);
    const rootClientId = getBlockRootClientId(clientId);
    switch (relativePosition) {
      case 'before':
      case 'after':
        insertBlock(block, relativePosition === 'after' ? blockIndex + 1 : blockIndex, rootClientId,
        // Insert as a child of the current block's parent
        false);
        break;
      case 'first_child':
      case 'last_child':
        insertBlock(block,
        // TODO: It'd be great if insertBlock() would accept negative indices for insertion.
        relativePosition === 'first_child' ? 0 : innerBlocksLength, clientId,
        // Insert as a child of the current block.
        false);
        break;
      case undefined:
        // If we do not know the relative position, it is because the block was
        // added via a filter. In this case, we default to inserting it after the
        // current block.
        insertBlock(block, blockIndex + 1, rootClientId,
        // Insert as a child of the current block's parent
        false);
        break;
    }
  };
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components2.InspectorControls, {
    children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.PanelBody, {
      className: "block-editor-hooks__block-hooks",
      title: (0, _i18n.__)('Plugins'),
      initialOpen: true,
      children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("p", {
        className: "block-editor-hooks__block-hooks-helptext",
        children: (0, _i18n.__)('Manage the inclusion of blocks added automatically by plugins.')
      }), Object.keys(groupedHookedBlocks).map(vendor => {
        return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_element.Fragment, {
          children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("h3", {
            children: vendor
          }), groupedHookedBlocks[vendor].map(block => {
            const checked = (block.name in hookedBlockClientIds);
            return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.ToggleControl, {
              checked: checked,
              label: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.__experimentalHStack, {
                justify: "flex-start",
                children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components2.BlockIcon, {
                  icon: block.icon
                }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
                  children: block.title
                })]
              }),
              onChange: () => {
                if (!checked) {
                  // Create and insert block.
                  const relativePosition = block.blockHooks[name];
                  insertBlockIntoDesignatedLocation((0, _blocks.createBlock)(block.name), relativePosition);
                  return;
                }

                // Remove block.
                removeBlock(hookedBlockClientIds[block.name], false);
              }
            }, block.title);
          })]
        }, vendor);
      })]
    })
  });
}
var _default = exports.default = {
  edit: BlockHooksControlPure,
  attributeKeys: ['metadata'],
  hasSupport() {
    return true;
  }
};
//# sourceMappingURL=block-hooks.js.map