HEX
Server: Apache
System: Linux efa57bbe-abb1-400d-2985-3b056fbc2701.secureserver.net 6.1.147-1.el9.elrepo.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Jul 24 12:33:32 EDT 2025 x86_64
User: root (0)
PHP: 8.0.30.4
Disabled: NONE
Upload Files
File: //var/chroot/var/www/wp-content/mu-plugins/vendor/wpex/expert-banner/src/index.js
/**
 * External dependencies
 */
import { get } from 'lodash';

/**
 * WordPress dependencies
 */
import { createInterpolateElement, createPortal, render, useEffect, useState, useRef, forwardRef } from '@wordpress/element';
import { withSelect } from '@wordpress/data';
import { ifCondition, compose, pure } from '@wordpress/compose';
import { Popover, Button } from '@wordpress/components';
import { Icon, info } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import { logInteractionEvent } from './instrumentation';
const EID_PREFIX = 'wp.editor';

/**
 * Local dependencies
 */
import { HIRE_EXPERT_SETTINGS_ID } from './settings';

import './index.scss';

function clickSettingsButton() {
  document.querySelector('.interface-more-menu-dropdown > button')?.click?.();
}

/**
 * This functional component renders null and instead uses a 'hacky' approach
 * to modify the editor toolbar to include Expert Link.
 *
 * @param {Object} props Contains the props such as 'editorToolbar'
 * @returns null
 */
function ExpertBanner(props) {
  const { editorToolbar } = props;
  const [ target, setTarget ] = useState(null);
  const [ tooltip, setTooltip ] = useState(false);
  const [ settingsPanelOpen, setSettingsPanel ] = useState(false);
  const [ settingsAnchor, setSettingsAnchor ] = useState(null);
  const iconRef = useRef(null);
  const settingsBtnId = 'wpex-eb-open-settings';
  const popoverId = 'wpex-eb-tooltip-popover';

  useEffect(() => {
    const div = document.createElement('div');
    div.id = 'wpex-eb-container';
    editorToolbar.parentNode.insertBefore(div, editorToolbar);
    setTarget(div);
  }, [editorToolbar]);

  useEffect(function settingsPanel() {
    if (!settingsPanelOpen) {
      return;
    }

    clickSettingsButton();

    setTimeout(() => {
      const settingButton = document.getElementById(HIRE_EXPERT_SETTINGS_ID);

      if (!settingButton) {
        return;
      }

      settingButton.focus();

      const settingButtonContainer = settingButton.getBoundingClientRect();

      setSettingsAnchor(new DOMRect(
        settingButtonContainer.x + 20,
        settingButtonContainer.y,
        settingButtonContainer.width,
        settingButtonContainer.height
      ));
    }, 200);

    setSettingsPanel(false);
  }, [settingsPanelOpen, setSettingsAnchor]);

  const handleClickOutside = ( e ) => {
    e.stopPropagation();
    e.preventDefault();
  };

  useEffect( () => {
    if (!settingsAnchor){
      return;
    }

    document.addEventListener( 'mousedown', handleClickOutside );
    document.addEventListener( 'click', handleClickOutside );

    return () => {
      document.removeEventListener( 'mousedown', handleClickOutside );
      document.removeEventListener( 'click', handleClickOutside );
    };
  }, [settingsAnchor] );

  if (!target) {
    return null;
  }

  const iconRect = iconRef.current?.getBoundingClientRect();
  const targetRect = new DOMRect(
    iconRect?.x,
    iconRect?.y + 10,
    iconRect?.width,
    iconRect?.height
  )

  return createPortal(
    <>
      {
        settingsAnchor && (
            <Popover
              anchorRect={ settingsAnchor }
              className="wpex-eb-tooltip"
              focusOnMount={ false }
              noArrow={ false }
              offset={ 50 }
              position="middle right"
            >
              <p className="wpex-eb-tooltip__title">
                { __( 'Just so you know…', 'gd-system-plugin' ) }
              </p>
              <p className='wpex-eb-tooltip__description'>
                { __( 'Here you can enable or disable "Hire an expert"', 'gd-system-plugin' ) }
              </p>
              <button
                className="wpex-eb-tooltip__button"
                onClick={ (e) => setSettingsAnchor(null) }
              >
                { __( 'Got it', 'gd-system-plugin' ) }
              </button>
            </Popover>
        )
      }

      <span ref={ iconRef }>
        <Icon
          icon={ info }
          onClick={ () => setTooltip((v) => !v) }
        />
      </span>

      {
        tooltip && (
          <Popover
            className="wpex-eb-tooltip wpex-eb-tooltip--black"
            anchorRect={ targetRect }
            noArrow={ false }
            focusOnMount={ false }
            position="middle top"
          >
            <div>
            {
              createInterpolateElement(
                __( 'To disable this option, go to the <button>Tools</button> menu.', 'gd-system-plugin' ),
                {
                  button: (
                    <Button
                      variant='link'
                      id={ settingsBtnId }
                      onClick={ () => {
                          setTooltip(false);
                          setSettingsPanel(true);
                        }
                      }
                    />
                  )
                }
              )
            }
            </div>
          </Popover>
        )
      }

      <a
        id='wpex-expert-banner'
        target='_blank'
        href='https://www.godaddy.com/websites/web-design'
        rel='noopener noreferrer'
        onClick={
          () => logInteractionEvent({
            eid: `${EID_PREFIX}.expert/wpex_expert/expert.link`,
          })
        }
      >
        { __('Hire an expert', 'gd-system-plugin') }
      </a>
      <div id='wpex-eb-divider' />
    </>,
    target
  );
}

/**
 * Logic based on AMP approach: https://github.com/ampproject/amp-wp/blob/81b9df742d9f78fb5ee3657622d21eb8c7926f64/assets/src/block-editor/plugins/wrapped-amp-preview-button.js#L76-L92
 *
 * Core WordPress refers to this example for how to manipulate the toolbar.
 * https://github.com/WordPress/gutenberg/issues/16988
 */
const ExpertBannerWithHOC = pure(
  compose([
    withSelect((select) => {
      const { getPostType, getEntityRecord } = select('core');
      const { getEditedPostAttribute } = select('core/editor');

      const postType = getPostType(getEditedPostAttribute('type'));
      const hireExpertHidden = getEntityRecord('root', 'site')?.[wpexExpertBanner.optionHidden] ?? false;

      return {
        hireExpertHidden,
        isViewable: get(postType, ['viewable'], false),
        editorToolbar: document.querySelector('.edit-post-header__settings'),
      };
    }),
    // This HOC creator renders the component only when the condition is true. At that point the 'Post' preview
    // button should have already been rendered (since it also relies on the same condition for rendering).
    // If Post preview is rendered then editor toolbar is also rendered and is safe to query.
    ifCondition(({ hireExpertHidden }) => !hireExpertHidden),
    ifCondition(({ isViewable }) => isViewable),
    ifCondition(({ editorToolbar }) => !!editorToolbar),
  ])(ExpertBanner),
);

/**
 * Setup container div and attach it to the element with editor id.
 * This give us a point to mount the functional react component so we can
 * use React hooks.
 */
const attachExpertBannerToDOM = () => {
  // Create a new container element
  const container = document.createElement('div');

  // Find the existing DOM node with the ID 'editor'
  const editor = document.getElementById('editor');

  // Attach the new container element to the editor node
  editor.appendChild(container);

  render(
    <ExpertBannerWithHOC />,
    container
  );
}

/**
 * 'DOMContentLoaded' Browser hook is not sufficient to have the toolbar accessible for query.
 * Because of that we use a HOC to hook into the core data-stores.
 * Once we have confirmation via data-store we can query and manipulate the toolbar.
 */
document.addEventListener('DOMContentLoaded', attachExpertBannerToDOM);