hover functionality for sc categories widget in serviceportal

Appu
Tera Guru

HI Developers,

I am new to service portal,

I want to edit the existing the widget SC Categories widget this widget is already modified. now further modification is required the requirement is instead of clicking on the catalogs then it displays the list of categories under that catalog.
I need dropdown without a click which lists the catalogs, then when I hover over the list catalog it should show the categories of that particular catalog like mentioned in the below Image.

Appu_0-1710492576952.png

I place the cursor on the Dropdown it lists all the link1,2, and 3then when i hover on link 3 its category is Link C
similar thing I need with the catalog and categories.
Let me Provide you the HTML,CSS,Server script and  Client script:

HTML:

<section ng-if="data.catalog_id != '8797c663ff123100158bffffffffff86'" aria-label="${Service Catalog categories}">
  <div title="${Please click on the dropdown to view more catalogs.}" ng-if="c.data.catalogSelectorArr.length > 2" ng-class="{'hidden-xs' : hideCategoryWidgetInXS}" class="catalog_block category-widget no-border">
    <h4 class="widget_title">{{::data.messages.catalogTitle}}</h4>
  <ul class="list-group category-list" role="list" aria-label="{{::data.messages.catalogTitle}}">
    <li role="listitem" class="list-group-item text-overflow-ellipsis" ng-include="'catalog-template.html'"/>
  </ul>
</div>
<script type="text/ng-template" id="catalog-template.html">
  <select class="sc-basic-select" aria-label="{{::data.messages.catalogTitle}}" ng-change="changeCatalog(selectedCatalog)" sn-focus="true" ng-model="selectedCatalog" ng-options="catalog.displayValue for catalog in c.data.catalogSelectorArr track by catalog.value"/>
</script>

<div ng-if="data.catalog_id!='95b37d361b8d8dd07502a934604bcb9b'" ng-class="{'hidden-xs' : hideCategoryWidgetInXS}" class="category_block category-widget no-border">
    <h4 class="widget_title">
      <span class="pull-right visible-xs" >
      	<i class="fa fa-times-circle" ng-click="hideBrowseCategory()" aria-label="${Show Items}" tabindex="0"/>
      </span>
      <span ng-if="::options.glyph">
        <fa name="{{::options.glyph}}" />
      </span>${Categories}</h4>
  <ul class="list-group category-list" role="tablist" aria-label="${Categories}">
    <li role="presentation"
        class="list-group-item text-overflow-ellipsis" 
        ng-if="category.totalCount > 0" ng-include="'category-template.html'"
        ng-repeat="category in data.categoriesList | limitTo: data.limit track by category.sys_id">
    </li>
  </ul>
  <div style="margin-top: 10px; margin-bottom: 20px" class="panel-footer" ng-if="data.showMore">
    <a href="javascript&colon;void(0)" role="button" ng-click="loadMore()" >{{::data.showMoreMsg}}</a>
    <div style="padding-top: 10px" class="text-muted">
      {{data.more_msg}}
    </div>
  </div>
</div>
<script type="text/ng-template" id="category-template.html">
	<div ng-click="categorySelected(category)"
  	ng-keydown="handleKeyPressOnCategory($event, category)"
		sn-focus="category.sys_id == data.categoryId"
		ng-enabled="category.totalCount > 0"
    id="tab_{{::category.sys_id}}"
    role="tab"
    aria-selected ="{{category.sys_id == data.categoryId}}"
    aria-controls="tabpanel_{{::category.sys_id}}"
    ng-class="{true: 'text-active', false: ''}[category.sys_id == data.categoryId]"
    tabindex="0" class="group-item group-item-primary pointer">
      <i ng-if="::category.subcategories.length > 0 && category.totalCount > category.count" 
          class="l-h-1_6x pull-left fa collapse-icon" 
          ng-class="{true: 'fa-minus-square-o', false: 'fa-plus-square-o'}[category.showChildren]" 
          aria-label="{{category.showChildren? '${collapse category} ' + category.title : '${expand category} ' + category.title}}"
          ng-click="displayChildren($event, category)"/></i>
      <!--<span class="badge pull-right pointer" ng-if="options.omit_badges != 'true' && data.largeDataSet != 'true'">{{::category.totalCount}}</span>-->
      <span class="block text-overflow-ellipsis category" 
				  id="{{::category.sys_id}}"
          uib-tooltip="{{::category.title}} {{::category.catalog_tooltip}}"
          tooltip-placement="top"
          tooltip-enable="!isTouchDevice()"
          tooltip-append-to-body="true">
          {{::category.title}}
          <span class="sr-only">${items}</span>
      </span>
    </div>
  	<ul class="list-group sub-category-list list-bg-primary" role="tablist" aria-label="{{category.title}} ${subcategories}" ng-if="category.showChildren && category.subcategories.length > 0" ng-class="{true: 'no-indent', false: ''}[category.level > 2]">
      <li role="presentation"
      		class="list-group-item text-overflow-ellipsis" 
          ng-include="'category-template.html'" 
          ng-repeat="category in category.subcategories track by category.sys_id" 
          ng-if="category.totalCount > 0">
      </li>
  	</ul>
  </script>
</section>

CSS:

section {
 color: #000000;
}

.widget_title {
	font-family: HelveticaNowTextBold;
  color: #46535e !important;
  margin-left: 10px !important;
	margin-top: 20px !important;
  margin-bottom: 30px !important;
}
.catalog_block, .category_block {
 		background-color: White;
    border: 1px solid;
    border-color: #cdcdcd;
  	margin-bottom: 15px;
}

.catalog_block .category-list .list-group-item {
  border-bottom :0px !important;
  border: 0px;
  padding: 0 10px !important;
}

.catalog_block .category-list .list-group-item .select2-container .select2-choice {
  border-radius: 0 !important;
}

.catalog_block li {
 border-bottom: 0px;
}

.all_tiles {
 margin-top: 10px;
}

.category-widget {
  .category-list {
    .fa {
      margin-right: 5px;
    }
    .l-h-1_6x {
      line-height: 1.6em;
    }
    a {
		width: 100%;
      	vertical-align: middle;
	    display: inline-block;
    }
    .list-group {
      margin-bottom: 0;
    }
    .list-group-item {
      padding: 0;
    	border-left: 0px;
    	border-right: 0px;
    	border-bottom: 1px solid #ddd;
    }
    .group-item {
      padding: 10px 0px;
      border-left: 3px solid transparent !important;
      margin-left: 10px;
    }
    .group-item-default {
      &:hover, &:focus {
        background-color: darken($panel-default-heading-bg, 2%);
      }
    }
    .group-item-primary {
      &:hover, &:focus {
        border-left: 3px solid #012774 !important;
      }
    }
    .group-item-success {
      &:hover {
        background-color: lighten($panel-success-heading-bg, 3%)
      }
    }
    .group-item-info {
      &:hover, &:focus {
        background-color: lighten($panel-info-heading-bg, 3%)
      }
    }
    .group-item-warning {
      &:hover, &:focus {
        background-color: $panel-warning-heading-bg
      }
    }
    .group-item-danger {
      &:hover, &:focus {
        background-color: lighten($panel-danger-heading-bg, 3%)
      }
    }
    
    .list-bg-default {
      background-color: $panel-default-heading-bg;
    }
    .list-bg-primary {
      background-color: lighten($panel-info-heading-bg, 6%);
    }
    .list-bg-success {
      background-color: lighten($panel-success-heading-bg, 6%);
    }
    .list-bg-info {
      background-color: lighten($panel-info-heading-bg, 6%);
    }
    .list-bg-warning {
      background-color: lighten($panel-warning-heading-bg, 3%);
    }
    .list-bg-danger {
      background-color: lighten($panel-danger-heading-bg, 6%);
    }
  }
}
.panel-default .badge {
  //background-color: $panel-default-text;
  background-color: #012774;
  color: white
}
.panel-success .badge {
  background-color:  $panel-success-text;
  color: white;
}
.panel-info .badge {
  background-color: $panel-info-text;
  color: white;
}
.panel-warning .badge {
  background-color: $panel-warning-text;
  color: white;
}
.panel-danger .badge {
  background-color: $panel-danger-text;
  color: white;
}
.category-list {
	font-family: HelveticaNowText;
}
.category-list .badge {
  color: #007585;
  background-color: #fff;
}
.text-active {
   color: $link-color;
   border-left: 3px solid $primary
}
.text-default {
   color: $text-muted;
}
.panel-footer {
	font-family: HelveticaNowText;
  background-color: #fff;
}

Client Script:

function($scope, $location, $timeout, $window, $document, $rootScope, spUtil,spAriaUtil) {
	var c = this;
	$scope.selectedCatalog = c.data.catalogSelectorArr[c.data.selectedCatalogIndex];
	$scope.changeCatalog = function (selectedCatalog) {
		window.GlideWebAnalytics.trackEvent("Service Catalog", "Catalog Browse", "Catalog Filter Changed");
		$timeout(function() {
			if (selectedCatalog && selectedCatalog.hasOwnProperty("value")) {
				$location.search('catalog_id', selectedCatalog.value ? selectedCatalog.value : -1);
				$location.search('sys_id', null);
			}
		});
	}
	$scope.handleKeyPressOnCategory = function($event, category) {
		$event.stopPropagation();
		var currentElement = $($event.currentTarget);
		var allCategories = currentElement.closest('ul.category-list').find('.group-item');
		var index = -1;
		if(allCategories.length > 0) {
			index = allCategories.index(currentElement);
		}
		switch($event.which) {
			case 40: // bottom key
				if(index+1 < allCategories.length) {
					allCategories.get(index+1).focus();
				}
				break;
			case 38: // top key
				if(index > 0) {
					allCategories.get(index-1).focus();
				}
				break;
			case 37: // Left key
				if(category.showChildren) {
					$scope.displayChildren($event, category);
				}
				break;
			case 39: //right key
				if(!category.showChildren) {
					$scope.displayChildren($event, category);
				}
				break;
		}
		
	}
	$scope.hideCategoryWidgetInXS = (c.options.hide_xs == 'true');
	if (c.data.categoryId) {
		if (!$scope.hideCategoryWidgetInXS)
			$scope.hideCategoryWidgetInXS = true;
	} else {
		if ($scope.hideCategoryWidgetInXS)
			$scope.hideCategoryWidgetInXS = false;
	}
	
	spUtil.getPreference('glide.ui.accessibility', function(value) {
		if (value == "true")
			$scope.tabindex = 0;
		else
			$scope.tabindex = -1;
	});
	$scope.displayChildren = function($event, category) {
		$event.stopPropagation();
		if(!category || !category.subcategories || category.subcategories.length === 0) {
			return;
		}
		category.showChildren = !category.showChildren;
		if (category.showChildren) {
			spAriaUtil.sendLiveMessage(category.title + ' '+ c.data.messages.expanded);
		} 
		else {
			spAriaUtil.sendLiveMessage(category.title + ' ' +c.data.messages.collapsed);
		}
	};
	
	$scope.toggleSubCategories =  function(val) {
		toggleSubCategories(c.data.categoriesList, val);
	};
	
	function toggleSubCategories(categories, val) {
		categories.forEach(function(category) {
			category.showChildren = val;
			if (category.subcategories && category.subcategories.length > 0) {
				toggleSubCategories(category.subcategories, val);
			}
		});
	}
	
	$scope.loadMore =  function() {
		c.data.new_limit = c.data.limit + (c.options.number_of_categories_to_load || 15);
		c.data.more_msg = c.data.pleaseWait;
		c.server.update();
	};
	
	$scope.categorySelected = function(category) {
			$timeout(function() {
				$location.search('sys_id', category.sys_id);
				$location.search('catalog_id', c.data.catalog_id);
				$location.search('id', c.data.page ||'sc_category');
			});
	};
	
	var listenerForBrowseCategories = $scope.$on("$sp.service_catelog.show.categories_widget", function() {
		$scope.hideCategoryWidgetInXS = false;
	});
	
	$scope.hideBrowseCategory = function () {
		$scope.hideCategoryWidgetInXS = true;
		$rootScope.$broadcast("$sp.service_catelog.show.items_widget");
	}
	
	$scope.$on("$destroy", function() {
		listenerForBrowseCategories();
	});
	
	$scope.isTouchDevice = function() {
		return ('ontouchstart' in $window);
	}
	
	$scope.getCategoryPadding = function(categoryLevel) {
		if (categoryLevel >= 4)
			categoryLevel = 4;
		return 15*categoryLevel + "px";
	}
}

Server Script:

(function() {
	
	var osCheck= false;

	/*As part of A21 D02 - Manageability Instance healthscan improvement, INRY moved hard-coded sys_id's to system properties.  */
	var mp = gs.getProperty('global.MostPopularItemsCatalog');
	var catalog_id = $sp.getParameter("catalog_id") ? $sp.getParameter("catalog_id") + "" : mp;
	data.catalog_id = catalog_id;
	var catalogSelectorArr = [{value: mp, displayValue: "Most Popular Items"}];
	data.selectedCatalogIndex = catalog_id == mp ? 0 : -1;
	var catalogIDs = $sp.getCatalogs().value + "";
	if(!osCheck)
		catalogIDs = catalogIDs.replace(gs.getProperty('global.AppStore') + ',', '');
	catalogIDs = catalogIDs.replace(mp + ',', '');
	var catalogs = catalogIDs.split(",");
	var isCatalogAccessibleViaPortal = catalog_id == -1 ? true : false;
	catalogs.forEach(function(catalogSysId) {
		if (catalog_id == catalogSysId) {
			isCatalogAccessibleViaPortal = true;
		}
	});
	
	var counter = 1;
	catalogs.forEach(function(catalogSysId) {
			var catalog = new sn_sc.Catalog(catalogSysId);
			var catalogObj = {value: catalogSysId, displayValue: catalog.getTitle()};
			if (catalog.canView()) {
				if (catalog_id === catalogSysId) {
					data.selectedCatalogIndex = counter;
				}
				catalogSelectorArr.push(catalogObj);
				counter++;
				}
	});
	data.catalogSelectorArr = catalogSelectorArr;
	
	data.catalog_id = catalog_id ? catalog_id : "-1";
	data.showMoreMsg = gs.getMessage("Show More");
	data.pleaseWait = gs.getMessage("Please wait... fetching categories");
	var categoryId = JSUtil.nil($sp.getParameter('sys_id')) ? "" : $sp.getParameter('sys_id') + "";
	data.categoryId = categoryId;
	var catalogIDs = (data.catalog_id && data.catalog_id !== "-1") ? data.catalog_id : $sp.getCatalogs().value + "";
	var viewType = '';
	var checkCanView = false;
	var showBadge = false;
	var nestedLayout = (options.category_layout !== "Flat");
	var dynamicCategory = false;
	
	var catalogArr = catalogIDs.split(",");
	var catalogs = [];
	catalogArr.forEach(function(catalogSysId) {
		var catalog = new sn_sc.Catalog(catalogSysId);
			if (catalog.canView()) {
				catalogs.push(catalogSysId);
		}
	});
	
	data.categoryId = categoryId;
	var msg = data.messages = {};
	msg.expanded = gs.getMessage("Expanded");
	msg.collapsed = gs.getMessage("Collapsed");
	msg.catalogTitle = options.catalog_title? gs.getMessage(options.catalog_title) : gs.getMessage('Catalogs');
	
	data.largeDataSet = gs.getProperty("glide.sc.largeSet.optimization.enable", "false");
	if (data.largeDataSet == 'true')
		 nestedLayout = false;
	
	if (options.page) {
		var page = new GlideRecord('sp_page');
		if (page.get(options.page))
			data.page = page.id + '';
	}
	checkCanView = String(options.check_can_view) == 'true';
	showBadge = String(options.omit_badges) == 'false';

	if(!isCatalogAccessibleViaPortal) {
		return;
	}
	data.categoriesList = [];
	var categoriesInPage = options.number_of_categories_to_load || 15;
	data.limit = categoriesInPage;
	
	if (input && input.new_limit)
		data.limit = input.new_limit;
	if (input && input.categoriesList)
		data.categoriesList = input.categoriesList.slice(); //Copy the input array
	
	if (input && input.startWindow) {
		data.endWindow = input.endWindow;
	}
	else {
		data.startWindow = 0;
		data.endWindow = 0;
	}

	while (data.categoriesList.length < data.limit + 1) {
			data.startWindow = data.endWindow;
			data.endWindow = data.endWindow + categoriesInPage;
			var catGr = queryCategory(catalogs, nestedLayout, dynamicCategory, data.startWindow, data.endWindow);
			if (!catGr.hasNext())
				break;
			fetchCategories(catGr, data.categoriesList);
	}
	
	if (data.categoriesList.length > data.limit)
			data.showMore = true;
	else
		data.showMore = false;
	
	data.more_msg = gs.getMessage("Showing {0} categories", data.limit);
	
	
	function fetchCategories(categoriesGr, categories) {
		while (categoriesGr.next()) {
			var categoryJS = new sn_sc.CatCategory(categoriesGr.getUniqueValue() + '');
			if (!categoryJS.canView())
				continue;
		
			var categoryDetails = getCategoryDetails(categoryJS, 0);
			categoryDetails.sys_id = categoriesGr.getUniqueValue();
			categories.push(categoryDetails);
		}
	}
	
	function queryCategory(catalogs, nestedLayout, dynamicCategory, startWindow, endWindow) {
			var categoriesGr = new GlideRecord('sc_category');
			categoriesGr.addQuery("sc_catalog", catalogs);
			if (!dynamicCategory)
				categoriesGr.addQuery("sys_class_name", "sc_category");
			categoriesGr.addActiveQuery();
			categoriesGr.orderBy('order');
			categoriesGr.orderBy('title');
			if (nestedLayout)
				categoriesGr.addNullQuery("parent");
			categoriesGr.chooseWindow(startWindow, endWindow);
			categoriesGr.query();
			return categoriesGr;
	}
	
	function getItemCount(categoryJS) {
		if (showBadge)
			return checkCanView ? categoryJS.getViewableItemsCount(true) : categoryJS.getItemsCount(true);
		else 
			return categoryJS.hasVisibleItem(true, checkCanView) ? 1 : 0;
	}
	
	function getCategoryDetails(categoryJS, level) {
		var categoryDetails = {};
		var showChildren = false;
		if (!categoryJS) {
			return categoryDetails;
		}
		categoryDetails.title = categoryJS.getTitle();
		categoryDetails.level = level;
		categoryDetails.description = categoryJS.getDescription();
		categoryDetails.full_description = categoryJS.getFullDescription();
		categoryDetails.icon = categoryJS.getIconSRC();
		categoryDetails.header_icon = categoryJS.getHeaderIconSRC();
		categoryDetails.homepage_image = categoryJS.getHomepageImageSRC();
		categoryDetails.sys_id = categoryJS.getID();
		categoryDetails.showChildren = (categoryDetails.sys_id === categoryId);
		categoryDetails.catalog = {"sys_id": categoryJS.getCatalog(), "title": new sn_sc.Catalog(categoryJS.getCatalog()).getTitle()}
		categoryDetails.count = categoryDetails.totalCount = ((data.largeDataSet != 'true') ? getItemCount(categoryJS) : 1);
		if (catalog_id == -1 && catalogSelectorArr.length > 2)
			categoryDetails.catalog_tooltip = gs.getMessage("[{0}]", categoryDetails.catalog.title);
		else
			categoryDetails.catalog_tooltip = '';
		
		var subCategoryCounts = 0;
		if (nestedLayout) {
			var subcategories = categoryJS.getViewableSubCategories();
			if (subcategories.length == 0) {
				categoryDetails.subcategories = [];
			} 
			else {
					categoryDetails.subcategories = [];
					subcategories.forEach(function(subCategory) {
						var subCategoryJS = new sn_sc.CatCategory(subCategory.sys_id + '');
						var category = getCategoryDetails(subCategoryJS, level + 1);
						categoryDetails.totalCount = categoryDetails.totalCount + category.totalCount;
						categoryDetails.subcategories.push(category);
						categoryDetails.showChildren = categoryDetails.showChildren || category.showChildren;
					});
				}
		}
		else {
			categoryDetails.totalCount = categoryDetails.count;
		}
		return categoryDetails;
	}
})();

Please Help me with a solution

 

Regards,
Appu

0 REPLIES 0