text: import Collection from 'ol/Collection';
  import Control from 'ol/control/Control';
  
  import { CLASS_CONTROL, CLASS_UNSELECTABLE } from 'ol/css';
  
  import el from './el';
  import SidePanelPane from './SidePanelPane';
  import SidePanelTab from './SidePanelTab';
  
  import './SidePanel.css';
  
  /**
   * Top-level {@link external:Control|Control} for ol-side-panel - provides a responsive tabbed left side panel for OpenLayers.
   * @observableProperty {SidePanelPane} activePane - The currently active pane - or null if no pane is active.
   * @extends external:Control
   * @example
   * import { SidePanel } from 'ol-side-panel';
   *
   * const sidePanel = new SidePanel();
   *
   * map.addControl(sidePanel);
   *
   * const layersPane = sidePanel.definePane({
   *   paneId: 'layers',
   *   name: "Layers",
   *   icon: '<i class="bi bi-layers-half"></i>'
   * });
   *
   * const layersGreeting = document.createElement('p');
   * layersGreeting.innerHTML = "Hi there layers!";
   *
   * layersPane.addWidgetElement(layersGreeting);
   */
  class SidePanel extends Control {
  
    constructor() {
  
      super({
        element: document.createElement('div'),
        activePane: null,
      });
  
      const { element } = this;
      element.className = `ol-side-panel ${CLASS_UNSELECTABLE} ${CLASS_CONTROL}`;
  
      window.nextOlSidePanelPaneUniqId = window.nextOlSidePanelPaneUniqId || 0;
  
      /**
       * @type {external:Collection<SidePanelPane>}
       * @private
       */
      this.panes = new Collection();
  
      element.appendChild(el('div', { class: 'ol-side-panel-tabs', role: 'tablist' }, (tabList) => {
        this.panes.on('add', (evt) => {
          const pane = evt.element;
  
          tabList.appendChild(SidePanelTab.fromPane(pane).element_);
  
          pane.on('change:active', () => {
            if (!pane.get('active') && pane === this.get('activePane')) {
              this.updateActivePane_(null);
            } else if (pane.get('active')) {
              this.updateActivePane_(pane);
            }
          });
  
        });
        this.panes.on('remove', (evt) => {
          tabList.removeChild(evt.element.tabRef_.element_);
        });
  
        tabList.appendChild(el('div', { class: 'ol-side-panel-tabs-separator' }));
  
      }));
  
      element.appendChild(el('div', { class: 'ol-side-panel-content' }, (sidePanelContent) => {
        this.panes.on('add', evt => sidePanelContent.appendChild(evt.element.element_));
        this.panes.on('remove', (evt) => {
          sidePanelContent.removeChild(evt.element.element_);
          if (evt.element === this.get('activePane')) {
            this.updateActivePane_(null);
          }
        });
      }));
  
      this.on('change:activePane', () => {
        if (this.get('activePane')) {
          this.getMap().getTargetElement().className = this.getMap().getTargetElement().className.replace(/\bol-side-panel-collapsed\b/g, '');
        } else if (this.getMap().getTargetElement().className.search(/\bol-side-panel-collapsed\b/) === -1) {
          this.getMap().getTargetElement().className += ' ol-side-panel-collapsed';
        }
      });
  
    }
  
    /**
     * Defines a new pane.
     * @param {Object} args - pane args
     * @param {String} args.paneId - Locally unique identifier for the pane - used to refer to the pane within an
     *  instance of the ol-side-panel control.
     * @param {String|external:Element} args.name - Name of the pane - User-facing name of this pane.
     * @param {String|external:Element} args.icon - Icon of the pane - User-facing icon for this pane.
     * @param {Number} args.weight - Weight of the pane - controls the order of
     *  the pane tabs with lower numbers appearing closer to the top and higher numbers appearing farther
     *  down. Panes with a weight >= 100 will have their icons appear at the bottom of the tab bar.
     * @returns {SidePanelPane} A reference to the newly defined pane.
     */
    definePane({
      paneId, name, icon, weight,
    }) {
      /* eslint-disable-next-line no-plusplus */
      const paneUniqId = window.nextOlSidePanelPaneUniqId++;
      const pane = new SidePanelPane({
        paneId, paneUniqId, name, icon, weight,
      });
      this.panes.push(pane);
      return pane;
    }
  
    /**
     * Get the pane with the specified identifier.
     * @param {String} paneId - The identifier of the pane to get - this is the same identifier that was
     *  passed into {@link SidePanel#definePane|definePane}.
     * @returns {SidePanelPane} A reference to the pane.
     */
    getPaneById(paneId) {
      return this.panes.getArray().find(p => p.paneId_ === paneId);
    }
  
    /**
     * Remove the pane with the specified identifier.
     * @param {String} paneId - The identifier of the pane to remove - this is the same identifier that was
     *  passed into {@link SidePanel#definePane|definePane}.
     */
    removePaneById(paneId) {
      this.removePane_(this.getPaneById(paneId));
    }
  
    /**
     * Remove the pane with the specified identifier.
     * @param {String} paneId - The identifier of the pane to remove - this is the same identifier that was
     *  passed into {@link SidePanel#definePane|definePane}.
     * @private
     */
    removePane_(pane) {
      this.panes.remove(pane);
    }
  
    /**
     * Add an HTML element to the named pane as a new {@link SidePanelWidget}.
     * @param {String} paneId The identifier of the pane to add the widget to.
     * @param {external:Element} widgetElement
     * @returns {SidePanelWidget} A reference to the newly added widget.
     */
    addWidgetElement(paneId, widgetElement) {
      return this.getPane(paneId).addWidgetElement(widgetElement);
    }
  
    updateActivePane_(pane) {
      this.set('activePane', pane);
      this.panes.getArray().filter(p => p !== pane).forEach(p => p.set('active', false));
    }
  
    /**
     * @param {external:Map} map - The OpenLayers map instance this control is being added to.
     */
    setMap(map) {
      const oldMap = this.getMap();
      if (map === oldMap) {
        return;
      }
  
      const removeClassFromOldMapTarget = (oldMapTarget) => {
        if (oldMapTarget) {
          oldMapTarget.className.replace(/\bmap-with-ol-side-panel\b/g, '');
        }
      };
  
      const addClassToNewMapTarget = (newMapTarget) => {
        if (newMapTarget) {
  
          /* eslint-disable-next-line no-param-reassign */
          newMapTarget.className += ' map-with-ol-side-panel ol-side-panel-collapsed';
        }
      };
  
      if (oldMap) {
        removeClassFromOldMapTarget(oldMap.getTargetElement());
        // Cleanup the old event listener
        if (this.onMapTargetChange) {
          oldMap.un('change:target', this.onMapTargetChange.listener);
          this.onMapTargetChange = null;
        }
      }
      super.setMap(map);
  
      if (map) {
        addClassToNewMapTarget(map.getTargetElement());
  
        const handleMapTargetChange = (evt) => {
          removeClassFromOldMapTarget(evt.oldValue);
          addClassToNewMapTarget(map.getTargetElement());
        };
  
        this.onMapTargetChange = map.getInteractions().on('change:target', handleMapTargetChange);
      }
    }
  
  }
  
  export default SidePanel;
  