Source: segments-experiment-web/src/main/resources/META-INF/resources/js/components/ClickGoalPicker/utils.es.js

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

import PropTypes from 'prop-types';

const TARGET_OFFSET = 10;

export const GeometryType = PropTypes.shape({
	height: PropTypes.number.isRequired,
	left: PropTypes.number.isRequired,
	right: PropTypes.number.isRequired,
	top: PropTypes.number.isRequired,
	width: PropTypes.number.isRequired
});

/**
 * Given a DOM Event object or a React synthetic event, stops all propagation.
 */
export function stopImmediatePropagation(event) {
	if (event.nativeEvent) {
		// This is a React synthetic event; must access nativeEvent instead.
		event.nativeEvent.stopImmediatePropagation();
	} else {
		event.stopImmediatePropagation();
	}
}

/**
 * Returns all targetable elements within `element`
 *
 * Currently, that means all visible "a" and "button" elements which
 * have an "id".
 */
export function getTargetableElements(element) {
	const elements = element.querySelectorAll('a, button');

	// As first cut, only deal with items that have an id.
	return Array.from(elements).filter(element => {
		return element.id && _isVisible(element);
	});
}

/**
 * Checks `element` for visibility
 *
 * Useful, for example, to exclude elements that are concealed inside dropdowns.
 */
function _isVisible(element) {
	const {display, opacity, visibility} = getComputedStyle(element);

	const hidden =
		display === 'none' || opacity === 0 || visibility !== 'visible';

	return !hidden;
}

/**
 * Get dimensional information about `element`.
 *
 * Used here to get measurements for the "root" ("#content") element.
 */
export function getRootElementGeometry(rootElement) {
	const {
		height,
		left,
		right,
		top,
		width
	} = rootElement.getBoundingClientRect();

	return {
		height: height + TARGET_OFFSET,
		left: left + TARGET_OFFSET / 2,
		right: right - TARGET_OFFSET / 2,
		top: top + TARGET_OFFSET / 2,
		width: width + TARGET_OFFSET
	};
}

/**
 * Get dimensional information about `element`.
 *
 * Used here to get measurements for the "target" element.
 */
export function getElementGeometry(element) {
	const {
		bottom,
		height,
		left,
		right,
		top,
		width
	} = element.getBoundingClientRect();

	return {
		bottom,
		height: height + TARGET_OFFSET,
		left,
		right,
		top,
		width: width + TARGET_OFFSET
	};
}