Source: src/js/views/maps/LayerCategoryItemView.js


'use strict';

define(
  [
    'jquery',
    'underscore',
    'backbone',
    'text!templates/maps/layer-category-item.html',
    'models/maps/AssetCategory',
    'common/IconUtilities',
    // Sub-views
    'views/maps/LayerListView',
  ],
  function (
    $,
    _,
    Backbone,
    Template,
    AssetCategory,
    IconUtilities,
    // Sub-views
    LayerListView,
  ) {
    const BASE_CLASS = 'layer-category-item';
    const CLASS_NAMES = {
      metadata: `${BASE_CLASS}__metadata`,
      icon: `${BASE_CLASS}__icon`,
      expanded: `${BASE_CLASS}__expanded`,
      collapsed: `${BASE_CLASS}__collapsed`,
      layers: `${BASE_CLASS}__layers`,
    };

    /**
    * @class LayerCategoryItemView
    * @classdesc One item in a Category List: shows some basic information about the
    * layer category, including label and icon. Also has a button that expands the
    * nested layers list.
    * @classcategory Views/Maps
    * @name LayerCategoryItemView
    * @extends Backbone.View
    * @screenshot views/maps/LayerCategoryItemView.png
    * @since 2.28.0
    * @constructs
    */
    const LayerCategoryItemView = Backbone.View.extend(
      /** @lends LayerCategoryItemView.prototype */{

        /**
        * The type of View this is
        * @type {string}
        */
        type: 'LayerCategoryItemView',

        /**
        * The HTML classes to use for this view's element
        * @type {string}
        */
        className: BASE_CLASS,

        /**
        * The model that this view uses
        * @type {AssetCategory}
        */
        model: undefined,

        /**
         * The primary HTML template for this view
         * @type {Underscore.template}
         */
        template: _.template(Template),

        /** @inheritdoc */
        events() {
          return {[`click .${CLASS_NAMES.metadata}`]: 'toggleExpanded'};
        },

        /**
        * Executed when a new LayerCategoryItemView is created
        * @param {Object} options - A literal object with options to pass to the view
        */
        initialize(options) {
          if (options?.model instanceof AssetCategory) {
            this.model = options.model;
          }
        },

        /**
        * Renders this view
        * @return {LayerCategoryItemView} Returns the rendered view element
        */
        render() {
          if (!this.model) {
            return;
          }

          // Insert the template into the view
          this.$el.html(this.template({
            classNames: CLASS_NAMES,
            label: this.model.get('label'),
          }));

          // Insert the icon on the left
          this.insertIcon();

          this.layerListView = new LayerListView({
            collection: this.model.get('mapAssets'),
            isCategorized: true,
          });
          this.layerListView.render();
          this.$(`.${CLASS_NAMES.layers}`).append(this.layerListView.el);

          // Show the category as expanded or collapsed depending on the model
          // properties.
          this.updateLayerList();
          this.listenTo(this.model, 'change:expanded', this.updateLayerList);

          return this;
        },

        /**
         * Inserts the icon before the label.
         */
        insertIcon() {
          const icon = this.model.get('icon');
          if (icon && typeof icon === 'string' && IconUtilities.isSVG(icon)) {
            this.$(`.${CLASS_NAMES.icon}`).html(icon);
          }
        },

        /**
         * Sets the model's 'expanded' status attribute to true if it's false, and
         * to false if it's true. Executed when a user clicks on this CategoryItem in a
         * CategoryListView.
         */
        toggleExpanded() {
          this.model.set('expanded', !this.model.get('expanded'));
        },

        /**
         * Show or hide the layer list based on the category's expand status.
         */
        updateLayerList() {
          const expanded = this.$(`.${CLASS_NAMES.expanded}`);
          const collapsed = this.$(`.${CLASS_NAMES.collapsed}`);
          const layers = this.$(`.${CLASS_NAMES.layers}`);
          if (this.model.get('expanded')) {
            expanded.show();
            collapsed.hide();
            layers.addClass('open');
          } else {
            expanded.hide();
            collapsed.show();
            layers.removeClass('open');
          }
        },

        /**
         * Searches and only displays self if layers match the text.
         * @param {string} [text] - The search text from user input.
         * @returns {boolean} - True if a layer item matches the text
         */
        search(text) {
          const matched = this.layerListView.search(text);
          if (matched) {
            this.$el.show();
            this.model.set('expanded', text !== '');
          } else {
            this.$el.hide();
            this.model.set('expanded', false);
          }
          return matched;
        },
      }
    );

    return LayerCategoryItemView;
  }
);