jonshaffer/angular-data-table

View on GitHub
src/components/popover/PopoverDirective.js

Summary

Maintainability
D
2 days
Test Coverage
import POSITION from './Popover.constants';
 
/**
* Popover Directive
* @param {function} $animate
* @param {function} $compile
* @param {function} $document
* @param {function} $http
* @param {object} $q
* @param {function} $templateCache
* @param {function} $timeout
* @param {function} PopoverRegistry
* @param {function} PositionHelper
*/
 
Function `PopoverDirective` has 176 lines of code (exceeds 25 allowed). Consider refactoring.
Function `PopoverDirective` has a Cognitive Complexity of 31 (exceeds 5 allowed). Consider refactoring.
Function `PopoverDirective` has 9 arguments (exceeds 4 allowed). Consider refactoring.
export default function PopoverDirective($animate, $compile, $document, $http,
$q, $templateCache, $timeout, PopoverRegistry, PositionHelper) {
/**
* Loads a template from the template cache
* @param {string} template
* @param {boolean} plain
* @return {object} html template
*/
function loadTemplate(template, plain) {
if (!template) {
return '';
}
 
if (angular.isString(template) && plain) {
return template;
}
 
return $templateCache.get(template) || $http.get(template, { cache: true });
}
 
/**
* Determines a boolean given a value
* @param {object} value
* @return {boolean}
*/
function toBoolean(value) {
if (value && value.length !== 0) {
const v = value.toString().toLowerCase();
value = (v === 'true');
} else {
value = false;
}
 
return value;
}
 
return {
restrict: 'A',
scope: true,
replace: false,
Function `link` has 151 lines of code (exceeds 25 allowed). Consider refactoring.
link($scope, $element, $attributes) {
$scope.popover = null;
 
$scope.options = {
alignment: $attributes.popoverAlignment || 'middle',
placement: $attributes.popoverPlacement || 'right',
plain: toBoolean($attributes.popoverPlain || false),
popoverId: $attributes.popoverId,
showCaret: toBoolean($attributes.popoverPlain || false),
spacing: parseInt($attributes.popoverSpacing, 10) || 0,
template: $attributes.popoverTemplate,
text: $attributes.popoverText,
};
 
$scope.$on('$destroy', () => {
$element.off();
});
 
// attach mouse events to element
$element.on('mouseenter', display);
$element.on('mouseleave', beginTimeout);
$element.on('mousemove', cancelTimeout);
 
/**
* Begin a timeout of 500ms before hiding popover
*/
function beginTimeout() {
$scope.exitTimeout = $timeout(remove, 500);
}
 
/**
* Cancel the timeout to keep popover visible
*/
function cancelTimeout() {
$timeout.cancel($scope.exitTimeout);
}
 
/**
* Displays the popover on the page
*/
function display() {
// Cancel exit timeout
cancelTimeout();
 
const elm = $document[0].getElementById($scope.options.popoverId);
if ($scope.popover && elm) return;
 
if ($scope.options.text && !$scope.options.template) {
displayTextPopover();
} else {
displayTemplatePopover();
}
}
 
/**
* When using template, load and compile the template prior to appending popover
*/
function displayTemplatePopover() {
$q.when(loadTemplate($scope.options.template, $scope.options.plain)).then((template) => {
if (!angular.isString(template)) {
if (template.data && angular.isString(template.data)) {
template = template.data;
} else {
template = '';
}
}
 
buildElement('template');
 
$scope.popover.html(template);
$compile($scope.popover)($scope);
angular.element($document.body).append($scope.popover);
positionPopover($element, $scope.popover, $scope.options);
 
managePopover();
});
}
 
/**
* With text only, simply build up the popover and append it to body
*/
function displayTextPopover() {
buildElement('text');
 
$scope.popover.html($scope.options.text);
angular.element($document[0].body).append($scope.popover);
 
managePopover();
}
 
function buildElement(type) {
$scope.popover = angular.element(`<div
class="dt-popover popover${$scope.options.placement}"
id="${$scope.options.popoverId}"></div>`);
 
if (type === 'text') {
$scope.popover.addClass('popover-text');
}
}
 
/**
* Position popover, bind handlers, and register popover
*/
function managePopover() {
positionPopover($element, $scope.popover, $scope.options);
 
// attach mouse events to popover
$scope.popover.on('mouseleave', beginTimeout);
$scope.popover.on('mousemove', cancelTimeout);
 
PopoverRegistry.add($scope.options.popoverId, {
element: $element,
popover: $scope.popover,
});
}
 
/**
* Removes the template from the registry and page
*/
function remove() {
if ($scope.popover) {
$scope.popover.off();
$scope.popover.remove();
}
 
$scope.popover = null;
PopoverRegistry.remove($scope.options.popoverId);
}
 
/**
* Positions the popover
* @param {object} triggerElement
* @param {object} popover
* @param {object} options
*/
Function `positionPopover` has 36 lines of code (exceeds 25 allowed). Consider refactoring.
function positionPopover(triggerElement, popover, options) {
$timeout(() => {
const elDimensions = triggerElement[0].getBoundingClientRect();
const popoverDimensions = popover[0].getBoundingClientRect();
 
let top;
let left;
 
if (options.placement === POSITION.RIGHT) {
left = elDimensions.left + elDimensions.width + options.spacing;
top = calculateVerticalAlignment();
} else if (options.placement === POSITION.LEFT) {
left = elDimensions.left - popoverDimensions.width - options.spacing;
top = calculateVerticalAlignment();
} else if (options.placement === POSITION.TOP) {
top = elDimensions.top - popoverDimensions.height - options.spacing;
left = calculateHorizontalAlignment();
} else if (options.placement === POSITION.BOTTOM) {
top = elDimensions.top + elDimensions.height + options.spacing;
left = calculateHorizontalAlignment();
}
 
function calculateVerticalAlignment() {
return PositionHelper.calculateVerticalAlignment(elDimensions,
popoverDimensions, options.alignment);
}
 
function calculateHorizontalAlignment() {
return PositionHelper.calculateHorizontalAlignment(elDimensions,
popoverDimensions, options.alignment);
}
 
popover.css({
top: `${top}px`,
left: `${left}px`,
height: '300px',
});
 
if ($scope.options.showCaret) {
addCaret($scope.popover, elDimensions, popoverDimensions);
}
 
$animate.addClass($scope.popover, 'popover-animation');
}, 50);
}
 
/**
* Adds a caret and positions it relatively to the popover
* @param {object} popoverEl
* @param {object} elDimensions
* @param {object} popoverDimensions
*/
Function `addCaret` has 30 lines of code (exceeds 25 allowed). Consider refactoring.
function addCaret(popoverEl, elDimensions, popoverDimensions) {
const caret = angular.element(`<span class="popover-caret caret-${$scope.options.placement}"></span>`);
popoverEl.append(caret);
const caretDimensions = caret[0].getBoundingClientRect();
 
let left;
let top;
 
if ($scope.options.placement === POSITION.RIGHT) {
left = -6;
top = calculateVerticalCaret();
} else if ($scope.options.placement === POSITION.LEFT) {
left = popoverDimensions.width - 2;
top = calculateVerticalCaret();
} else if ($scope.options.placement === POSITION.TOP) {
top = popoverDimensions.height - 5;
left = calculateHorizontalCaret();
} else if ($scope.options.placement === POSITION.BOTTOM) {
top = -8;
left = calculateHorizontalCaret();
}
 
function calculateVerticalCaret() {
return PositionHelper.calculateVerticalCaret(elDimensions,
popoverDimensions, caretDimensions, $scope.options.alignment);
}
 
function calculateHorizontalCaret() {
return PositionHelper.calculateHorizontalCaret(elDimensions,
popoverDimensions, caretDimensions, $scope.options.alignment);
}
 
caret.css({
top: `${top}px`,
left: `${left}px`,
});
}
},
};
}