src/utils.js
// @flow
/**
* Create a custom event
*
* @private
*/
export const createCustomEvent = (() => {
if (typeof window !== 'undefined' && typeof window.CustomEvent === "function") {
return function(
type: string,
options: ?{ detail?: Object, cancelable?: boolean },
): CustomEvent {
return new document.defaultView.CustomEvent(type, {
cancelable: (options && options.cancelable) || false,
detail: (options && options.detail) || undefined,
})
}
} else {
// Custom event polyfill from
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#polyfill
return function(
type: string,
options: ?{ detail?: Object, cancelable?: boolean },
): CustomEvent {
const event = document.createEvent("CustomEvent")
event.initCustomEvent(
type,
/* bubbles */ false,
(options && options.cancelable) || false,
(options && options.detail) || undefined,
)
return event
}
}
})()
/**
* Get the current coordinates of the `el` relative to the document.
*
* @private
*/
export function calculateElementOffset(
el: HTMLElement,
): { top: number, left: number } {
const rect = el.getBoundingClientRect()
const { defaultView, documentElement } = el.ownerDocument
const offset = {
top: rect.top + defaultView.pageYOffset,
left: rect.left + defaultView.pageXOffset,
}
if (documentElement) {
offset.top -= documentElement.clientTop
offset.left -= documentElement.clientLeft
}
return offset
}
const CHAR_CODE_ZERO = "0".charCodeAt(0)
const CHAR_CODE_NINE = "9".charCodeAt(0)
function isDigit(charCode: number): boolean {
return charCode >= CHAR_CODE_ZERO && charCode <= CHAR_CODE_NINE
}
/**
* Returns the line-height of the given node in pixels.
*
* @private
*/
export function getLineHeightPx(node: HTMLElement): number {
const computedStyle = window.getComputedStyle(node)
// If the char code starts with a digit, it is either a value in pixels,
// or unitless, as per:
// https://drafts.csswg.org/css2/visudet.html#propdef-line-height
// https://drafts.csswg.org/css2/cascade.html#computed-value
if (isDigit(computedStyle.lineHeight.charCodeAt(0))) {
// In real browsers the value is *always* in pixels, even for unit-less
// line-heights. However, we still check as per the spec.
if (
isDigit(
computedStyle.lineHeight.charCodeAt(
computedStyle.lineHeight.length - 1,
),
)
) {
return (
parseFloat(computedStyle.lineHeight) *
parseFloat(computedStyle.fontSize)
)
} else {
return parseFloat(computedStyle.lineHeight)
}
}
// Otherwise, the value is "normal".
// If the line-height is "normal", calculate by font-size
return calculateLineHeightPx(node.nodeName, computedStyle)
}
/**
* Returns calculated line-height of the given node in pixels.
*
* @private
*/
export function calculateLineHeightPx(
nodeName: string,
computedStyle: CSSStyleDeclaration,
): number {
const body = document.body
if (!body) {
return 0
}
const tempNode = document.createElement(nodeName)
tempNode.innerHTML = " "
tempNode.style.fontSize = computedStyle.fontSize
tempNode.style.fontFamily = computedStyle.fontFamily
tempNode.style.padding = "0"
body.appendChild(tempNode)
// Make sure textarea has only 1 row
if (tempNode instanceof HTMLTextAreaElement) {
;(tempNode: HTMLTextAreaElement).rows = 1
}
// Assume the height of the element is the line-height
const height = tempNode.offsetHeight
body.removeChild(tempNode)
return height
}