'use strict';

const codeBlock = require('./code-block-e0af90fc.js');
const dtoRendererManager = require('./dto-renderer-manager-ae5fcbba.js');
const contentState = require('./content-state-09ffae92.js');
const typeGuard = require('./type-guard-8fd7909d.js');
const baseRenderer = require('./base-renderer-0a3b62b7.js');
require('./global-slimmer-registry-7bbdb1f2.js');
const domNodeSubjectObserverFactory = require('./dom-node-subject-observer-factory-26bc0450.js');
const vegaEventId = require('./vega-event-id-c08ce3d3.js');
const internalIconManager = require('./internal-icon-manager-97635798.js');
const vegaNonceManager = require('./vega-nonce-manager-55854929.js');

/**
 * Manager for storing and retrieving the extension context associated with each rich text editor host element.
 *
 * @todo Currently, there are three types of contexts in RTE: toolbar render context, extension context, and DTO render context. The second and third types are bound to the extension context via host props. We are considering optimizing these three types of contexts to make the program more flexible and easier to maintain.
 */
class RTEExtensionContextManager {
  constructor() {
    this.extensionContextMap = new WeakMap();
  }
  /**
   * Store the extension context for the given rich text editor host element.
   *
   * @param {RTEExtensionContext} context - The extension context to store.
   */
  setExtensionContext(context) {
    if (context && context.host) {
      this.extensionContextMap.set(context.host, context);
    }
  }
  /**
   * Get the extension context for the given rich text editor host element.
   *
   * @typedef HTMLVegaRichTextEditorElement - The type definition for the rich text editor host element.
   * @param {Nullable<HTMLVegaRichTextEditorElement>} el - The host rich text editor element.
   * @returns {Nullable<RTEExtensionContext>} - The extension context associated with the host.
   */
  getExtensionContext(el) {
    return el ? this.extensionContextMap.get(el) : null;
  }
}
const RTEExtensionContextManager$1 = new RTEExtensionContextManager();

/**
 * Base renderer for Vega Rich Text Editor.
 */
class VegaRTEExtensionRenderer extends baseRenderer.VegaRTEBaseRenderer {
  /**
   * Returns the styles to be injected into the RTE scope.
   * Override this method to provide custom styles for the custom blocks or nodes.
   *
   * @returns {string} - The styles as a string.
   */
  injectRTEScopeStyles() {
    return '';
  }
  /**
   * Renders the given data as a DOM element.
   *
   * @param {RTEBlock | RTENode} renderData - The data to render, which can be a block or a node.
   * @param {RTERenderContext} rteContext - The rte editor context.
   * @param {VegaRTECreateElementFunction} createElement - The function to create a DOM element.
   * @returns {VegaRTERenderResult} - The rendered DOM element.
   */
  render(renderData, rteContext, createElement) {
    var _a;
    const element = this.doRender(createElement, renderData, rteContext);
    this.fallbackRegisterRef(element, renderData);
    this.appendChildren(((_a = element.childrenContainerGetter) === null || _a === void 0 ? void 0 : _a.call(element)) || element, renderData, rteContext, createElement);
    return element;
  }
  /**
   * Register the reference of the element in the rendering registry.
   *
   * @param {RTEBlock | RTENode} renderData - The data to register with the element.
   * @returns {VegaRTEUseRef} - The function to register the element.
   */
  useRef(renderData) {
    renderData[VegaRTEExtensionRenderer.REGISTER_REF_FLAG] = true;
    return function (element) {
      if (element) {
        codeBlock.stateEntityRenderingRegistry.register(element, renderData);
      }
    };
  }
  /**
   * Get a registered renderer by its key.
   *
   * @param {string} key - The key of the renderer.
   * @returns {Nullable<BaseRenderer>} - The registered renderer or null if not found.
   */
  getDTORenderer(key) {
    return dtoRendererManager.RTEDTORendererManager.getRTERenderer(key);
  }
  /**
   * Get the extension context for the given rich text editor host element.
   *
   * @typedef HTMLVegaRichTextEditorElement - The type definition for the rich text editor host element.
   * @param {HTMLVegaRichTextEditorElement} el - The host rich text editor element.
   * @returns {Nullable<RTEExtensionContext>} - The extension context associated with the host.
   */
  getExtensionContext(el) {
    return RTEExtensionContextManager$1.getExtensionContext(el);
  }
  /**
   * Auto render the block children if the block element is not exist
   *
   * @param {VegaRTERenderResult} parentElement - The parent element to append children to.
   * @param {RTEBlock | RTENode} renderData - The data to render children for.
   * @param {RTERenderContext} rteContext - The render context.
   * @param {VegaRTECreateElementFunction} createElement - The function to create a DOM element.
   */
  appendChildren(parentElement, renderData, rteContext, createElement) {
    if (Array.isArray(renderData['children']) &&
      renderData['children'].length &&
      !parentElement['$children$']) {
      parentElement['$children$'] = renderData['children']
        .map((child) => {
        return this.renderChildren(child, rteContext, createElement);
      })
        .filter(typeGuard.isNonNullable);
    }
  }
  /**
   * Render the children node.
   *
   * @param {RTENode | RTEBlock} renderData - The node to render children for.
   * @param {RTERenderContext} rteContext - The render context.
   * @param {VegaRTECreateElementFunction} createElement - The function to create a DOM element.
   * @returns {Nullable<VegaRTERenderResult>} - The rendered DOM element.
   */
  renderChildren(renderData, rteContext, createElement) {
    const renderer = this.getDTORenderer(renderData.type);
    if (renderer) {
      return renderer.render(renderData, rteContext, createElement);
    }
  }
  /**
   * Fallback register reference for the element.
   *
   * @param {VegaRTERenderResult} element - The rendered DOM element.
   * @param {RTEBlock | RTENode} renderData - The data to register with the element.
   */
  fallbackRegisterRef(element, renderData) {
    if (!renderData[VegaRTEExtensionRenderer.REGISTER_REF_FLAG] &&
      element['$attrs$'] &&
      !element['$attrs$']['ref']) {
      // eslint-disable-next-line jsdoc/require-jsdoc
      element['$attrs$']['ref'] = this.useRef(renderData);
    }
  }
}
VegaRTEExtensionRenderer.REGISTER_REF_FLAG = '__registeredRef__';

/**
 * Abstract class for Vega Rich Text Editor extensions.
 */
class VegaRTEExtension {
  constructor() {
    /**
     * The context of the RTE extension, which may include references to the host editor and other relevant information.
     */
    this.extensionContext = null;
    this.hasCustomStylesRenderers = new Set();
  }
  /**
   * Registers an action handle strategy for a specific node or block class.
   *
   * @param {string} nodeOrBlockClassName - The name of the node or block class.
   * @param {string} actionType - The type of the action.
   * @param {ActionHandleStrategy<ModifyContentAction | AnnotationAction, RTENode | RTEBlock | VegaRTEContent>} strategy - The strategy to register.
   */
  static registerActionHandleStrategy(nodeOrBlockClassName, actionType, strategy) {
    codeBlock.ActionHandleStrategyRegistry.register(actionType, nodeOrBlockClassName, strategy);
  }
  /**
   * Registers an action handler interceptor for a specific node or block class.
   *
   * @param {string} nodeOrBlockClassName - The name of the node or block class.
   * @param {string} actionType - The type of the action.
   * @param {ActionHandlerInterceptor<ModifyContentAction, RTENode | RTEBlock | VegaRTEContent>} strategy - The interceptor to register.
   */
  static registerActionHandlerInterceptor(nodeOrBlockClassName, actionType, strategy) {
    codeBlock.ActionHandlerInterceptorRegistry.register(actionType, nodeOrBlockClassName, strategy);
  }
  /**
   * Registers a filter styles strategy for a specific block or node class.
   *
   * @param {string} blockOrNodeClassName - The name of the block or node class.
   * @param {RTEFilterStylesStrategy} strategy - The filter styles strategy to register.
   */
  static registerFilterStylesStrategy(blockOrNodeClassName, strategy) {
    codeBlock.RTEFilterStylesStrategyRegistry.register(blockOrNodeClassName, strategy);
  }
  /**
   * Initializes the RTE extension with the provided context.
   *
   * @param {RTEExtensionContext} context - The context of the RTE extension.
   */
  initialExtension(context) {
    this.extensionContext = context;
    RTEExtensionContextManager$1.setExtensionContext(context);
  }
  /**
   * Callback method invoked when the extension is destroyed.
   * Override this method to perform any necessary cleanup.
   */
  destroyExtension() {
  }
  /**
   * Collect custom styles for the extension renderers.
   * If custom nodes have special styles, override this method to provide the styles.
   *
   * @returns {string} - The custom styles as a string.
   */
  collectExtensionRendererStyles() {
    let styles = '';
    if (this.hasCustomStylesRenderers.size > 0) {
      this.hasCustomStylesRenderers.forEach((renderer) => {
        styles += `${renderer.injectRTEScopeStyles()}\n`;
      });
    }
    return styles;
  }
  /**
   * Register a node type with the extension.
   *
   * @param {string} type - The type of the node or block. The type should be a string that uniquely identifies the node or block.
   * @param {ClassType<RTEBlock | RTENode>} DTOClass - The DTO class to register.
   */
  registerNode(type, DTOClass) {
    codeBlock.RTEDTOClassManager.registerRTEDTOClass(type, DTOClass);
  }
  /**
   * Register a renderer for a node type with the extension.
   *
   * @param {string} type - The type of the node or block. The type should be a string that uniquely identifies the node or block.
   * @param {BaseRenderer} renderer - The renderer to register.
   */
  registerRenderer(type, renderer) {
    dtoRendererManager.RTEDTORendererManager.registerRTEDTORenderer(type, renderer);
    if (renderer instanceof VegaRTEExtensionRenderer && renderer.injectRTEScopeStyles()) {
      this.hasCustomStylesRenderers.add(renderer);
    }
  }
  /**
   * Register a toolbar button renderer for the extension.
   *
   * @param {string} type - The type of the toolbar button.
   * @param {VegaRTEToolbarButtonRenderer} renderer - The renderer to register.
   */
  registerToolbarButtonRenderer(type, renderer) {
    dtoRendererManager.RTEDTORendererManager.registerRTEToolbarButtonRenderer(type, renderer);
  }
  /**
   * This is a helper method that utilizes RTEUtils to register common strategies.
   * Call the method with the class name of the block to make it support basic strategies like:
   * - Delete children
   * - Append the children
   * - Delete the block children content
   *
   * @param {string} blockClassName - The class name of the block to register strategies for.
   */
  registerBlockBasicStrategies(blockClassName) {
    contentState.RTEDTOActionStrategyManager.registerBlockBasicStrategies(blockClassName);
  }
  /**
   * Unregister a strategy for a specific action type and block class if not need.
   *
   * @param {string} actionType - The special action type or the action class name.
   * @param {string} blockClassName - The class name of the block to unregister the strategy for.
   */
  unRegisterStrategy(actionType, blockClassName) {
    codeBlock.ActionHandleStrategyRegistry.unregister(actionType, blockClassName);
  }
  /**
   * Unregister a strategy for a specific action type and block class if not need.
   *
   * @param {string} actionType - The special action type or the action class name.
   * @param {string} blockClassName - The class name of the block to unregister the strategy for.
   * @param {ActionHandlerInterceptor<ModifyContentAction, RTENode | RTEBlock | VegaRTEContent>} strategy - The interceptor to unregister.
   */
  unRegisterInterceptor(actionType, blockClassName, strategy) {
    codeBlock.ActionHandlerInterceptorRegistry.unregister(actionType, blockClassName, strategy);
  }
  /**
   * Register an element to block DTO class strategy.
   *
   * @param {ElementToDTOStrategy} strategy - The strategy to register.
   */
  registerElementToBlockDTOClassStrategy(strategy) {
    contentState.ElementToDTOClassStrategyManager.registerElementToBlockStrategy(strategy);
  }
  /**
   * Register an element to node DTO class strategy.
   *
   * @param {ElementToDTOStrategy} strategy - The strategy to register.
   */
  registerElementToNodeDTOClassStrategy(strategy) {
    contentState.ElementToDTOClassStrategyManager.registerElementToNodeStrategy(strategy);
  }
  /**
   * Get the registered element to node DTO class strategies.
   *
   * @returns {ElementToDTOStrategy[]} - The registered element to node DTO class strategies.
   */
  getElementToNodeDTOClassStrategies() {
    return contentState.ElementToDTOClassStrategyManager.getElementToNodeStrategies();
  }
  /**
   * Register a custom annotation handler for HTML to DTO conversion.
   *
   * @param {AnnotationGeneratorStrategyAbstract} handler - The annotation handler to register.
   */
  registerAnnotationHandler(handler) {
    contentState.htmlElementToAnnotationGenerator.registerHandler(handler);
  }
}

/**
 * Base class for rendering extension toolbar button in the rich text editor.
 * The renderer provides common functionalities for rendering toolbar buttons, including:
 * - Rendering a button with an icon, label, and optional arrow icon.
 * - Rendering a dropdown with a list of items.
 * - Rendering dropdown items.
 * - Rendering template for dropdown list.
 * - Determining if the button should be disabled based on the editor context.
 * - Handling button and dropdown click events.
 * Override the methods to customize the rendering and behavior of the button.
 */
class VegaRTEToolbarButtonRenderer {
  /**
   * Checks if the button should be hidden.
   *
   * @param {VegaRTEToolbarRenderContext} editorContext The render context for the editor.
   * @returns {boolean} True if the button is hidden, false otherwise.
   */
  isHidden(editorContext) {
    return editorContext.sourceEditMode;
  }
  /**
   * Get the extension context for the given rich text editor host element.
   *
   * @typedef HTMLVegaRichTextEditorElement - The type definition for the rich text editor host element.
   * @param {HTMLVegaRichTextEditorElement} el - The host rich text editor element.
   * @returns {Nullable<RTEExtensionContext>} - The extension context associated with the host.
   */
  getExtensionContext(el) {
    return RTEExtensionContextManager$1.getExtensionContext(el);
  }
  /**
   * Renders a dropdown component with the specified properties and children.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {VegaRTEToolbarRenderContext} editorContext - The render context for the editor.
   * @param {RTEToolbarDropdownProps} dropdownProps - The properties for the dropdown.
   * @param {VegaRTERenderResult[]} children - The children elements for the dropdown.
   * @returns {VegaRTERenderResult} - The render result for the dropdown.
   */
  renderDropdown(h, editorContext, dropdownProps, children) {
    return h('vega-dropdown', Object.assign({ trigger: 'click', searchable: false, maxWidth: '200', itemDisplayRule: 'ellipsis', 
      // eslint-disable-next-line jsdoc/require-jsdoc
      ref: (el) => {
        domNodeSubjectObserverFactory.DomNodeSubjectObserverFactory.addUniqueObserverToNode(el, vegaEventId.VegaDropdownClick, (input) => {
          const payload = input;
          this.handleDropdownClick(payload.detail, editorContext);
        });
      } }, dropdownProps), children);
  }
  /**
   * Renders the dropdown list template.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {(RTEDropdownListItem | RTEDropdownListGroup)[]} itemList - The list of dropdown items.
   * @returns {VegaRTERenderResult} - The render result for the dropdown list.
   */
  renderDropdownListTemplate(h, itemList) {
    return h('div', {
      slot: 'items',
      onMouseDown: this.preventDefault,
      class: 'rte-dropdown-list',
    }, [
      h('style', { nonce: vegaNonceManager.vegaNonceManager.nonce }, '.rte-dropdown-list vega-dropdown-group vega-dropdown-group { padding-left: 4px }'),
      ...itemList.map((item) => {
        return 'children' in item
          ? this.renderDropdownGroup(h, item)
          : this.renderDropdownItem(h, item);
      }),
    ]);
  }
  /**
   * Renders a dropdown item for the toolbar button.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {RTEDropdownListItem} item - The dropdown item.
   * @returns {VegaRTERenderResult} - The render result for the dropdown item.
   */
  renderDropdownItem(h, item) {
    return h('vega-dropdown-item', {
      key: item.key,
      itemKey: item.key,
      label: item.label,
      prefixIcon: item.icon,
    });
  }
  /**
   * Renders a dropdown item for the toolbar button.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {RTEDropdownListGroup} item - The dropdown group.
   * @returns {VegaRTERenderResult} - The render result for the dropdown item.
   */
  renderDropdownGroup(h, item) {
    return h('vega-dropdown-group', {
      key: item.key,
      label: item.label,
      divide: false,
    }, item.children.map((child) => {
      return 'children' in child
        ? this.renderDropdownGroup(h, child)
        : this.renderDropdownItem(h, child);
    }));
  }
  /**
   * Renders a common toolbar button with optional icon, label, and arrow icon.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {VegaRTEToolbarRenderContext} editorContext - The render context for the editor.
   * @param {RTEToolbarButtonProps} buttonProps - The properties for the button.
   * @returns {VegaRTERenderResult} - The render result for the button.
   */
  renderButton(h, editorContext, buttonProps) {
    const { icon, label, showArrowIcon } = buttonProps;
    const isDisabled = this.isDisabled(editorContext);
    return h('vega-rich-text-editor-toolbar-button', {
      disabled: isDisabled,
      icon: icon,
      hidden: this.isHidden(editorContext),
      selected: this.isSelected(editorContext),
      // eslint-disable-next-line jsdoc/require-jsdoc
      ref: (el) => {
        domNodeSubjectObserverFactory.DomNodeSubjectObserverFactory.addUniqueObserverToNode(el, vegaEventId.VegaClick, () => this.handleButtonClick(editorContext));
      },
      onMouseDown: this.preventDefault,
    }, [
      this.renderButtonLabel(h, editorContext, label),
      showArrowIcon
        ? h('vega-icon', {
          size: 'size-8',
          color: isDisabled ? 'text-disabled' : 'text-secondary',
          style: { marginLeft: '8px' },
          icon: internalIconManager.VegaInternalIconManager.getIconKey('chevron-down'),
        })
        : null,
    ].filter(typeGuard.isNonNullable));
  }
  /**
   * Renders the button label.
   *
   * @param {VegaRTECreateElementFunction} h - Create element function.
   * @param {VegaRTEToolbarRenderContext} editorContext - The render context for the editor.
   * @param {Nullable<string>} label - The button label if exist.
   * @returns {Nullable<VegaRTERenderResult>} - The render result for the button.
   */
  renderButtonLabel(h, editorContext, label) {
    if (label) {
      const isDisabled = this.isDisabled(editorContext);
      return h('vega-font', {
        variant: 'font-field-label-sm',
        color: isDisabled ? 'text-disabled' : 'text-primary',
      }, label);
    }
    return null;
  }
  /**
   * Determines if the button should be disabled based on the editor context.
   *
   * @param {VegaRTEToolbarRenderContext} editorContext - The render context for the editor.
   * @returns {boolean} - True if the button should be disabled, false otherwise.
   */
  isDisabled(editorContext) {
    return editorContext.host.disabled;
  }
  /**
   * Checks if the button should be selected.
   *
   * @param {VegaRTEToolbarRenderContext} _editorContext The render context for the editor.
   * @returns {boolean} True if the button is selected, false otherwise.
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  isSelected(_editorContext) {
    return false;
  }
  /**
   * Prevents the default action of the click event. This is to avoid losing focus from the editor when the button is clicked.
   *
   * @param {MouseEvent} e The mouse event to prevent default action for.
   */
  preventDefault(e) {
    e.preventDefault();
  }
  /**
   * Recursively searches for a dropdown list item by its key.
   *
   * @param {(RTEDropdownListItem | RTEDropdownListGroup)[]} itemList The list of dropdown items or groups to search through.
   * @param {string} key The key of the item to find.
   * @returns {Nullable<RTEDropdownListItem>} The dropdown list item if found, otherwise null.
   */
  getListItemByKey(itemList, key) {
    for (const item of itemList) {
      if ('children' in item) {
        const foundItem = this.getListItemByKey(item.children, key);
        if (foundItem) {
          return foundItem;
        }
      }
      else if (item.key === key) {
        return item;
      }
    }
    return null;
  }
}

exports.RTEExtensionContextManager = RTEExtensionContextManager$1;
exports.VegaRTEExtension = VegaRTEExtension;
exports.VegaRTEExtensionRenderer = VegaRTEExtensionRenderer;
exports.VegaRTEToolbarButtonRenderer = VegaRTEToolbarButtonRenderer;
