"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.StyleBookPreview = exports.StyleBookBody = void 0;
exports.getExamplesForSinglePageUse = getExamplesForSinglePageUse;
var _clsx = _interopRequireDefault(require("clsx"));
var _components = require("@wordpress/components");
var _i18n = require("@wordpress/i18n");
var _blockEditor = require("@wordpress/block-editor");
var _editor = require("@wordpress/editor");
var _data = require("@wordpress/data");
var _element = require("@wordpress/element");
var _keycodes = require("@wordpress/keycodes");
var _mediaUtils = require("@wordpress/media-utils");
var _coreData = require("@wordpress/core-data");
var _lockUnlock = require("../../lock-unlock");
var _editorCanvasContainer = _interopRequireDefault(require("../editor-canvas-container"));
var _constants = require("./constants");
var _categories = require("./categories");
var _examples = require("./examples");
var _store = require("../../store");
var _sidebarGlobalStylesWrapper = require("../sidebar-global-styles-wrapper");
var _globalStylesRenderer = require("../global-styles-renderer");
var _constants2 = require("../style-book/constants");
var _jsxRuntime = require("react/jsx-runtime");
/**
 * External dependencies
 */

/**
 * WordPress dependencies
 */

/**
 * Internal dependencies
 */

const {
  ExperimentalBlockEditorProvider,
  useGlobalStyle,
  GlobalStylesContext,
  useGlobalStylesOutputWithConfig
} = (0, _lockUnlock.unlock)(_blockEditor.privateApis);
const {
  mergeBaseAndUserConfigs
} = (0, _lockUnlock.unlock)(_editor.privateApis);
const {
  Tabs
} = (0, _lockUnlock.unlock)(_components.privateApis);
function isObjectEmpty(object) {
  return !object || Object.keys(object).length === 0;
}

/**
 * Scrolls to a section within an iframe.
 *
 * @param {string}            anchorId The id of the element to scroll to.
 * @param {HTMLIFrameElement} iframe   The target iframe.
 */
const scrollToSection = (anchorId, iframe) => {
  if (!anchorId || !iframe || !iframe?.contentDocument) {
    return;
  }
  const element = anchorId === 'top' ? iframe.contentDocument.body : iframe.contentDocument.getElementById(anchorId);
  if (element) {
    element.scrollIntoView({
      behavior: 'smooth'
    });
  }
};

/**
 * Parses a Block Editor navigation path to build a style book navigation path.
 * The object can be extended to include a category, representing a style book tab/section.
 *
 * @param {string} path An internal Block Editor navigation path.
 * @return {null|{block: string}} An object containing the example to navigate to.
 */
const getStyleBookNavigationFromPath = path => {
  if (path && typeof path === 'string') {
    if (path === '/' || path.startsWith('/typography') || path.startsWith('/colors') || path.startsWith('/blocks')) {
      return {
        top: true
      };
    }
  }
  return null;
};

/**
 * Retrieves colors, gradients, and duotone filters from Global Styles.
 * The inclusion of default (Core) palettes is controlled by the relevant
 * theme.json property e.g. defaultPalette, defaultGradients, defaultDuotone.
 *
 * @return {Object} Object containing properties for each type of palette.
 */
function useMultiOriginPalettes() {
  const {
    colors,
    gradients
  } = (0, _blockEditor.__experimentalUseMultipleOriginColorsAndGradients)();

  // Add duotone filters to the palettes data.
  const [shouldDisplayDefaultDuotones, customDuotones, themeDuotones, defaultDuotones] = (0, _blockEditor.useSettings)('color.defaultDuotone', 'color.duotone.custom', 'color.duotone.theme', 'color.duotone.default');
  const palettes = (0, _element.useMemo)(() => {
    const result = {
      colors,
      gradients,
      duotones: []
    };
    if (themeDuotones && themeDuotones.length) {
      result.duotones.push({
        name: (0, _i18n._x)('Theme', 'Indicates these duotone filters come from the theme.'),
        slug: 'theme',
        duotones: themeDuotones
      });
    }
    if (shouldDisplayDefaultDuotones && defaultDuotones && defaultDuotones.length) {
      result.duotones.push({
        name: (0, _i18n._x)('Default', 'Indicates these duotone filters come from WordPress.'),
        slug: 'default',
        duotones: defaultDuotones
      });
    }
    if (customDuotones && customDuotones.length) {
      result.duotones.push({
        name: (0, _i18n._x)('Custom', 'Indicates these doutone filters are created by the user.'),
        slug: 'custom',
        duotones: customDuotones
      });
    }
    return result;
  }, [colors, gradients, customDuotones, themeDuotones, defaultDuotones, shouldDisplayDefaultDuotones]);
  return palettes;
}

/**
 * Get deduped examples for single page stylebook.
 * @param {Array} examples Array of examples.
 * @return {Array} Deduped examples.
 */
function getExamplesForSinglePageUse(examples) {
  const examplesForSinglePageUse = [];
  const overviewCategoryExamples = (0, _categories.getExamplesByCategory)({
    slug: 'overview'
  }, examples);
  examplesForSinglePageUse.push(...overviewCategoryExamples.examples);
  const otherExamples = examples.filter(example => {
    return example.category !== 'overview' && !overviewCategoryExamples.examples.find(overviewExample => overviewExample.name === example.name);
  });
  examplesForSinglePageUse.push(...otherExamples);
  return examplesForSinglePageUse;
}
function StyleBook({
  enableResizing = true,
  isSelected,
  onClick,
  onSelect,
  showCloseButton = true,
  onClose,
  showTabs = true,
  userConfig = {},
  path = ''
}) {
  const [textColor] = useGlobalStyle('color.text');
  const [backgroundColor] = useGlobalStyle('color.background');
  const colors = useMultiOriginPalettes();
  const examples = (0, _element.useMemo)(() => (0, _examples.getExamples)(colors), [colors]);
  const tabs = (0, _element.useMemo)(() => (0, _categories.getTopLevelStyleBookCategories)().filter(category => examples.some(example => example.category === category.slug)), [examples]);
  const examplesForSinglePageUse = getExamplesForSinglePageUse(examples);
  const {
    base: baseConfig
  } = (0, _element.useContext)(GlobalStylesContext);
  const goTo = getStyleBookNavigationFromPath(path);
  const mergedConfig = (0, _element.useMemo)(() => {
    if (!isObjectEmpty(userConfig) && !isObjectEmpty(baseConfig)) {
      return mergeBaseAndUserConfigs(baseConfig, userConfig);
    }
    return {};
  }, [baseConfig, userConfig]);

  // Copied from packages/edit-site/src/components/revisions/index.js
  // could we create a shared hook?
  const originalSettings = (0, _data.useSelect)(select => select(_blockEditor.store).getSettings(), []);
  const [globalStyles] = useGlobalStylesOutputWithConfig(mergedConfig);
  const settings = (0, _element.useMemo)(() => ({
    ...originalSettings,
    styles: !isObjectEmpty(globalStyles) && !isObjectEmpty(userConfig) ? globalStyles : originalSettings.styles,
    isPreviewMode: true
  }), [globalStyles, originalSettings, userConfig]);
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_editorCanvasContainer.default, {
    onClose: onClose,
    enableResizing: enableResizing,
    closeButtonLabel: showCloseButton ? (0, _i18n.__)('Close') : null,
    children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
      className: (0, _clsx.default)('edit-site-style-book', {
        'is-button': !!onClick
      }),
      style: {
        color: textColor,
        background: backgroundColor
      },
      children: showTabs ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(Tabs, {
        children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
          className: "edit-site-style-book__tablist-container",
          children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Tabs.TabList, {
            children: tabs.map(tab => /*#__PURE__*/(0, _jsxRuntime.jsx)(Tabs.Tab, {
              tabId: tab.slug,
              children: tab.title
            }, tab.slug))
          })
        }), tabs.map(tab => {
          const categoryDefinition = tab.slug ? (0, _categories.getTopLevelStyleBookCategories)().find(_category => _category.slug === tab.slug) : null;
          const filteredExamples = categoryDefinition ? (0, _categories.getExamplesByCategory)(categoryDefinition, examples) : {
            examples
          };
          return /*#__PURE__*/(0, _jsxRuntime.jsx)(Tabs.TabPanel, {
            tabId: tab.slug,
            focusable: false,
            className: "edit-site-style-book__tabpanel",
            children: /*#__PURE__*/(0, _jsxRuntime.jsx)(StyleBookBody, {
              category: tab.slug,
              examples: filteredExamples,
              isSelected: isSelected,
              onSelect: onSelect,
              settings: settings,
              title: tab.title,
              goTo: goTo
            })
          }, tab.slug);
        })]
      }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(StyleBookBody, {
        examples: {
          examples: examplesForSinglePageUse
        },
        isSelected: isSelected,
        onClick: onClick,
        onSelect: onSelect,
        settings: settings,
        goTo: goTo
      })
    })
  });
}

/**
 * Style Book Preview component renders the stylebook without the Editor dependency.
 *
 * @param {Object}  props            Component props.
 * @param {Object}  props.userConfig User configuration.
 * @param {boolean} props.isStatic   Whether the stylebook is static or clickable.
 * @return {Object} Style Book Preview component.
 */
const StyleBookPreview = ({
  userConfig = {},
  isStatic = false
}) => {
  const siteEditorSettings = (0, _data.useSelect)(select => select(_store.store).getSettings(), []);
  const canUserUploadMedia = (0, _data.useSelect)(select => select(_coreData.store).canUser('create', {
    kind: 'root',
    name: 'media'
  }), []);

  // Update block editor settings because useMultipleOriginColorsAndGradients fetch colours from there.
  (0, _element.useEffect)(() => {
    (0, _data.dispatch)(_blockEditor.store).updateSettings({
      ...siteEditorSettings,
      mediaUpload: canUserUploadMedia ? _mediaUtils.uploadMedia : undefined
    });
  }, [siteEditorSettings, canUserUploadMedia]);
  const [section, onChangeSection] = (0, _sidebarGlobalStylesWrapper.useSection)();
  const isSelected = blockName => {
    // Match '/blocks/core%2Fbutton' and
    // '/blocks/core%2Fbutton/typography', but not
    // '/blocks/core%2Fbuttons'.
    return section === `/blocks/${encodeURIComponent(blockName)}` || section.startsWith(`/blocks/${encodeURIComponent(blockName)}/`);
  };
  const onSelect = blockName => {
    if (_constants2.STYLE_BOOK_COLOR_GROUPS.find(group => group.slug === blockName)) {
      // Go to color palettes Global Styles.
      onChangeSection('/colors/palette');
      return;
    }
    if (blockName === 'typography') {
      // Go to typography Global Styles.
      onChangeSection('/typography');
      return;
    }

    // Now go to the selected block.
    onChangeSection(`/blocks/${encodeURIComponent(blockName)}`);
  };
  const colors = useMultiOriginPalettes();
  const examples = (0, _examples.getExamples)(colors);
  const examplesForSinglePageUse = getExamplesForSinglePageUse(examples);
  let previewCategory = null;
  if (section.includes('/colors')) {
    previewCategory = 'colors';
  } else if (section.includes('/typography')) {
    previewCategory = 'text';
  } else if (section.includes('/blocks')) {
    previewCategory = 'blocks';
    const blockName = decodeURIComponent(section).split('/blocks/')[1];
    if (blockName && examples.find(example => example.name === blockName)) {
      previewCategory = blockName;
    }
  } else if (!isStatic) {
    previewCategory = 'overview';
  }
  const categoryDefinition = _constants2.STYLE_BOOK_PREVIEW_CATEGORIES.find(category => category.slug === previewCategory);

  // If there's no category definition there may be a single block.
  const filteredExamples = categoryDefinition ? (0, _categories.getExamplesByCategory)(categoryDefinition, examples) : {
    examples: [examples.find(example => example.name === previewCategory)]
  };

  // If there's no preview category, show all examples.
  const displayedExamples = previewCategory ? filteredExamples : {
    examples: examplesForSinglePageUse
  };
  const {
    base: baseConfig
  } = (0, _element.useContext)(GlobalStylesContext);
  const goTo = getStyleBookNavigationFromPath(section);
  const mergedConfig = (0, _element.useMemo)(() => {
    if (!isObjectEmpty(userConfig) && !isObjectEmpty(baseConfig)) {
      return mergeBaseAndUserConfigs(baseConfig, userConfig);
    }
    return {};
  }, [baseConfig, userConfig]);
  const [globalStyles] = useGlobalStylesOutputWithConfig(mergedConfig);
  const settings = (0, _element.useMemo)(() => ({
    ...siteEditorSettings,
    styles: !isObjectEmpty(globalStyles) && !isObjectEmpty(userConfig) ? globalStyles : siteEditorSettings.styles,
    isPreviewMode: true
  }), [globalStyles, siteEditorSettings, userConfig]);
  return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
    className: "edit-site-style-book",
    children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_blockEditor.BlockEditorProvider, {
      settings: settings,
      children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_globalStylesRenderer.GlobalStylesRenderer, {
        disableRootPadding: true
      }), /*#__PURE__*/(0, _jsxRuntime.jsx)(StyleBookBody, {
        examples: displayedExamples,
        settings: settings,
        goTo: goTo,
        isSelected: !isStatic ? isSelected : null,
        onSelect: !isStatic ? onSelect : null
      })]
    })
  });
};
exports.StyleBookPreview = StyleBookPreview;
const StyleBookBody = ({
  examples,
  isSelected,
  onClick,
  onSelect,
  settings,
  title,
  goTo
}) => {
  const [isFocused, setIsFocused] = (0, _element.useState)(false);
  const [hasIframeLoaded, setHasIframeLoaded] = (0, _element.useState)(false);
  const iframeRef = (0, _element.useRef)(null);
  // The presence of an `onClick` prop indicates that the Style Book is being used as a button.
  // In this case, add additional props to the iframe to make it behave like a button.
  const buttonModeProps = {
    role: 'button',
    onFocus: () => setIsFocused(true),
    onBlur: () => setIsFocused(false),
    onKeyDown: event => {
      if (event.defaultPrevented) {
        return;
      }
      const {
        keyCode
      } = event;
      if (onClick && (keyCode === _keycodes.ENTER || keyCode === _keycodes.SPACE)) {
        event.preventDefault();
        onClick(event);
      }
    },
    onClick: event => {
      if (event.defaultPrevented) {
        return;
      }
      if (onClick) {
        event.preventDefault();
        onClick(event);
      }
    },
    readonly: true
  };
  const handleLoad = () => setHasIframeLoaded(true);
  (0, _element.useLayoutEffect)(() => {
    if (hasIframeLoaded && iframeRef?.current) {
      if (goTo?.top) {
        scrollToSection('top', iframeRef?.current);
      }
    }
  }, [iframeRef?.current, goTo, scrollToSection, hasIframeLoaded]);
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_blockEditor.__unstableIframe, {
    onLoad: handleLoad,
    ref: iframeRef,
    className: (0, _clsx.default)('edit-site-style-book__iframe', {
      'is-focused': isFocused && !!onClick,
      'is-button': !!onClick
    }),
    name: "style-book-canvas",
    tabIndex: 0,
    ...(onClick ? buttonModeProps : {}),
    children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.__unstableEditorStyles, {
      styles: settings.styles
    }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("style", {
      children: [_constants.STYLE_BOOK_IFRAME_STYLES, !!onClick && 'body { cursor: pointer; } body * { pointer-events: none; }']
    }), /*#__PURE__*/(0, _jsxRuntime.jsx)(Examples, {
      className: "edit-site-style-book__examples",
      filteredExamples: examples,
      label: title ? (0, _i18n.sprintf)(
      // translators: %s: Category of blocks, e.g. Text.
      (0, _i18n.__)('Examples of blocks in the %s category'), title) : (0, _i18n.__)('Examples of blocks'),
      isSelected: isSelected,
      onSelect: onSelect
    }, title)]
  });
};
exports.StyleBookBody = StyleBookBody;
const Examples = (0, _element.memo)(({
  className,
  filteredExamples,
  label,
  isSelected,
  onSelect
}) => {
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Composite, {
    orientation: "vertical",
    className: className,
    "aria-label": label,
    role: "grid",
    children: [!!filteredExamples?.examples?.length && filteredExamples.examples.map(example => /*#__PURE__*/(0, _jsxRuntime.jsx)(Example, {
      id: `example-${example.name}`,
      title: example.title,
      content: example.content,
      blocks: example.blocks,
      isSelected: isSelected?.(example.name),
      onClick: !!onSelect ? () => onSelect(example.name) : null
    }, example.name)), !!filteredExamples?.subcategories?.length && filteredExamples.subcategories.map(subcategory => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Composite.Group, {
      className: "edit-site-style-book__subcategory",
      children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Composite.GroupLabel, {
        children: /*#__PURE__*/(0, _jsxRuntime.jsx)("h2", {
          className: "edit-site-style-book__subcategory-title",
          children: subcategory.title
        })
      }), /*#__PURE__*/(0, _jsxRuntime.jsx)(Subcategory, {
        examples: subcategory.examples,
        isSelected: isSelected,
        onSelect: onSelect
      })]
    }, `subcategory-${subcategory.slug}`))]
  });
});
const Subcategory = ({
  examples,
  isSelected,
  onSelect
}) => {
  return !!examples?.length && examples.map(example => /*#__PURE__*/(0, _jsxRuntime.jsx)(Example, {
    id: `example-${example.name}`,
    title: example.title,
    content: example.content,
    blocks: example.blocks,
    isSelected: isSelected?.(example.name),
    onClick: !!onSelect ? () => onSelect(example.name) : null
  }, example.name));
};
const disabledExamples = ['example-duotones'];
const Example = ({
  id,
  title,
  blocks,
  isSelected,
  onClick,
  content
}) => {
  const originalSettings = (0, _data.useSelect)(select => select(_blockEditor.store).getSettings(), []);
  const settings = (0, _element.useMemo)(() => ({
    ...originalSettings,
    focusMode: false,
    // Disable "Spotlight mode".
    isPreviewMode: true
  }), [originalSettings]);

  // Cache the list of blocks to avoid additional processing when the component is re-rendered.
  const renderedBlocks = (0, _element.useMemo)(() => Array.isArray(blocks) ? blocks : [blocks], [blocks]);
  const disabledProps = disabledExamples.includes(id) || !onClick ? {
    disabled: true,
    accessibleWhenDisabled: !!onClick
  } : {};
  return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
    role: "row",
    children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
      role: "gridcell",
      children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Composite.Item, {
        className: (0, _clsx.default)('edit-site-style-book__example', {
          'is-selected': isSelected,
          'is-disabled-example': !!disabledProps?.disabled
        }),
        id: id,
        "aria-label": !!onClick ? (0, _i18n.sprintf)(
        // translators: %s: Title of a block, e.g. Heading.
        (0, _i18n.__)('Open %s styles in Styles panel'), title) : undefined,
        render: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {}),
        role: !!onClick ? 'button' : null,
        onClick: onClick,
        ...disabledProps,
        children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
          className: "edit-site-style-book__example-title",
          children: title
        }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
          className: "edit-site-style-book__example-preview",
          "aria-hidden": true,
          children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Disabled, {
            className: "edit-site-style-book__example-preview__content",
            children: content ? content : /*#__PURE__*/(0, _jsxRuntime.jsxs)(ExperimentalBlockEditorProvider, {
              value: renderedBlocks,
              settings: settings,
              children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.__unstableEditorStyles, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.BlockList, {
                renderAppender: false
              })]
            })
          })
        })]
      })
    })
  });
};
var _default = exports.default = StyleBook;
//# sourceMappingURL=index.js.map