solids
Version:
CSS-only Material Design Primitives
151 lines (132 loc) • 4.92 kB
JavaScript
/**
* @license
*
* Copyright 2019 Stijn de Witt. Some rights reserved.
* Licensed under the MIT Open Source license.
* https://opensource.org/licenses/MIT
* See LICENSE for details.
*
* Based on code copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0.
* http://www.apache.org/licenses/LICENSE-2.0
* See LICENSE-MDC for details.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under these licenses is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the licenses for the specific language governing permissions and
* limitations under these licenses.
*
*/
/**
* Stores result from supportsCssVariables to avoid redundant processing to detect CSS custom variable support.
* @private {boolean|undefined}
*/
let supportsCssVariables_;
/**
* Stores result from applyPassive to avoid redundant processing to detect passive event listener support.
* @private {boolean|undefined}
*/
let supportsPassive_;
/**
* @param {!Window} windowObj
* @return {boolean}
*/
function detectEdgePseudoVarBug(windowObj) {
// Detect versions of Edge with buggy var() support
// See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11495448/
const document = windowObj.document;
const node = document.createElement('div');
node.className = 'mdc-ripple-surface--test-edge-var-bug';
document.body.appendChild(node);
// The bug exists if ::before style ends up propagating to the parent element.
// Additionally, getComputedStyle returns null in iframes with display: "none" in Firefox,
// but Firefox is known to support CSS custom properties correctly.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=548397
const computedStyle = windowObj.getComputedStyle(node);
const hasPseudoVarBug = computedStyle !== null && computedStyle.borderTopStyle === 'solid';
node.remove();
return hasPseudoVarBug;
}
/**
* @param {!Window} windowObj
* @param {boolean=} forceRefresh
* @return {boolean|undefined}
*/
function supportsCssVariables(windowObj, forceRefresh = false) {
let supportsCssVariables = supportsCssVariables_;
if (typeof supportsCssVariables_ === 'boolean' && !forceRefresh) {
return supportsCssVariables;
}
const supportsFunctionPresent = windowObj.CSS && typeof windowObj.CSS.supports === 'function';
if (!supportsFunctionPresent) {
return;
}
const explicitlySupportsCssVars = windowObj.CSS.supports('--css-vars', 'yes');
// See: https://bugs.webkit.org/show_bug.cgi?id=154669
// See: README section on Safari
const weAreFeatureDetectingSafari10plus = (
windowObj.CSS.supports('(--css-vars: yes)') &&
windowObj.CSS.supports('color', '#00000000')
);
if (explicitlySupportsCssVars || weAreFeatureDetectingSafari10plus) {
supportsCssVariables = !detectEdgePseudoVarBug(windowObj);
} else {
supportsCssVariables = false;
}
if (!forceRefresh) {
supportsCssVariables_ = supportsCssVariables;
}
return supportsCssVariables;
}
//
/**
* Determine whether the current browser supports passive event listeners, and if so, use them.
* @param {!Window=} globalObj
* @param {boolean=} forceRefresh
* @return {boolean|{passive: boolean}}
*/
function applyPassive(globalObj = window, forceRefresh = false) {
if (supportsPassive_ === undefined || forceRefresh) {
let isSupported = false;
try {
globalObj.document.addEventListener('test', null, {get passive() {
isSupported = true;
}});
} catch (e) { }
supportsPassive_ = isSupported;
}
return supportsPassive_ ? {passive: true} : false;
}
/**
* @param {!Object} HTMLElementPrototype
* @return {!Array<string>}
*/
function getMatchesProperty(HTMLElementPrototype) {
return [
'webkitMatchesSelector', 'msMatchesSelector', 'matches',
].filter((p) => p in HTMLElementPrototype).pop();
}
/**
* @param {!Event} ev
* @param {{x: number, y: number}} pageOffset
* @param {!ClientRect} clientRect
* @return {{x: number, y: number}}
*/
function getNormalizedEventCoords(ev, pageOffset, clientRect) {
const {x, y} = pageOffset;
const documentX = x + clientRect.left;
const documentY = y + clientRect.top;
let normalizedX;
let normalizedY;
// Determine touch point relative to the ripple container.
if (ev.type === 'touchstart') {
normalizedX = ev.changedTouches[0].pageX - documentX;
normalizedY = ev.changedTouches[0].pageY - documentY;
} else {
normalizedX = ev.pageX - documentX;
normalizedY = ev.pageY - documentY;
}
return {x: normalizedX, y: normalizedY};
}
export {supportsCssVariables, applyPassive, getMatchesProperty, getNormalizedEventCoords};