popper.js
Advanced tools
Comparing version 2.0.0-next.2 to 2.0.0-next.3
@@ -111,2 +111,12 @@ 'use strict'; | ||
function getWindowScroll(element) { | ||
const win = getWindow(element); | ||
const scrollLeft = win.pageXOffset; | ||
const scrollTop = win.pageYOffset; | ||
return { | ||
scrollLeft, | ||
scrollTop | ||
}; | ||
} | ||
var unwrapJqueryElement = (element => element && element.jquery ? element[0] : element); | ||
@@ -166,3 +176,5 @@ | ||
const basePlacements = [top, bottom, right, left]; | ||
const placements = basePlacements.reduce((acc, placement) => acc.concat([placement, `${placement}-start`, `${placement}-end`]), []); // modifiers that need to read the DOM | ||
const start = 'start'; | ||
const end = 'end'; | ||
const placements = basePlacements.reduce((acc, placement) => acc.concat([placement, `${placement}-${start}`, `${placement}-${end}`]), []); // modifiers that need to read the DOM | ||
@@ -179,5 +191,10 @@ const read = 'read'; // pure-logic modifiers | ||
strategy, | ||
placement | ||
placement, | ||
windowScroll | ||
}) => { | ||
const basePlacement = getBasePlacement(placement); | ||
const { | ||
scrollTop, | ||
scrollLeft | ||
} = windowScroll; | ||
@@ -187,4 +204,4 @@ switch (basePlacement) { | ||
return { | ||
x: reference.x + reference.width / 2 - popper.width / 2, | ||
y: reference.y - popper.height | ||
x: scrollLeft + reference.x + reference.width / 2 - popper.width / 2, | ||
y: scrollTop + reference.y - popper.height | ||
}; | ||
@@ -194,4 +211,4 @@ | ||
return { | ||
x: reference.x + reference.width / 2 - popper.width / 2, | ||
y: reference.y + reference.height | ||
x: scrollLeft + reference.x + reference.width / 2 - popper.width / 2, | ||
y: scrollTop + reference.y + reference.height | ||
}; | ||
@@ -201,4 +218,4 @@ | ||
return { | ||
x: reference.x + reference.width, | ||
y: reference.y + reference.height / 2 - popper.height / 2 | ||
x: scrollLeft + reference.x + reference.width, | ||
y: scrollTop + reference.y + reference.height / 2 - popper.height / 2 | ||
}; | ||
@@ -208,4 +225,4 @@ | ||
return { | ||
x: reference.x - popper.width, | ||
y: reference.y + reference.height / 2 - popper.height / 2 | ||
x: scrollLeft + reference.x - popper.width, | ||
y: scrollTop + reference.y + reference.height / 2 - popper.height / 2 | ||
}; | ||
@@ -292,2 +309,105 @@ | ||
// This modifier takes the Popper.js state and prepares some StyleSheet properties | ||
// that can be applied to the popper element to make it render in the expected position. | ||
const mapStrategyToPosition = strategy => { | ||
switch (strategy) { | ||
case 'fixed': | ||
return 'fixed'; | ||
case 'absolute': | ||
default: | ||
return 'absolute'; | ||
} | ||
}; | ||
const computePopperStyles = ({ | ||
offsets, | ||
strategy, | ||
gpuAcceleration | ||
}) => { | ||
// by default it is active, disable it only if explicitly set to false | ||
if (gpuAcceleration === false) { | ||
return { | ||
top: `${offsets.y}px`, | ||
left: `${offsets.x}px`, | ||
position: mapStrategyToPosition(strategy) | ||
}; | ||
} else { | ||
return { | ||
transform: `translate3d(${offsets.x}px, ${offsets.y}px, 0)`, | ||
position: mapStrategyToPosition(strategy) | ||
}; | ||
} | ||
}; | ||
const computeArrowStyles = ({ | ||
offsets, | ||
gpuAcceleration | ||
}) => { | ||
if (gpuAcceleration) { | ||
return { | ||
top: `${offsets.y}px`, | ||
left: `${offsets.x}px`, | ||
position: 'absolute' | ||
}; | ||
} else { | ||
return { | ||
transform: `translate3d(${offsets.x}px, ${offsets.y}px, 0)`, | ||
position: 'absolute' | ||
}; | ||
} | ||
}; | ||
function computeStyles(state, options) { | ||
const gpuAcceleration = options && options.gpuAcceleration != null ? options.gpuAcceleration : true; | ||
state.styles = {}; // popper offsets are always available | ||
state.styles.popper = computePopperStyles({ | ||
offsets: state.offsets.popper, | ||
strategy: state.options.strategy, | ||
gpuAcceleration | ||
}); // arrow offsets may not be available | ||
if (state.offsets.arrow != null) { | ||
state.styles.arrow = computeArrowStyles({ | ||
offsets: state.offsets.arrow, | ||
gpuAcceleration | ||
}); | ||
} | ||
return state; | ||
} | ||
var computeStyles$1 = { | ||
name: 'computeStyles', | ||
enabled: true, | ||
phase: 'main', | ||
fn: computeStyles | ||
}; | ||
// This modifier takes the styles prepared by the `computeStyles` modifier | ||
// and applies them to the HTMLElements such as popper and arrow | ||
function applyStyles(state) { | ||
Object.keys(state.elements).forEach(name => { | ||
const style = state.styles.hasOwnProperty(name) ? state.styles[name] : null; // Flow doesn't support to extend this property, but it's the most | ||
// effective way to apply styles to an HTMLElemen | ||
// $FlowIgnore | ||
Object.assign(state.elements[name].style, style); | ||
}); | ||
return state; | ||
} | ||
var applyStyles$1 = { | ||
name: 'applyStyles', | ||
enabled: true, | ||
phase: 'write', | ||
fn: applyStyles, | ||
requires: ['computeStyles'] | ||
}; | ||
var modifiers = /*#__PURE__*/Object.freeze({ | ||
computeStyles: computeStyles$1, | ||
applyStyles: applyStyles$1 | ||
}); | ||
const defaultModifiers = Object.values(modifiers); | ||
const areValidElements = (a, b) => a instanceof Element && b instanceof Element; | ||
@@ -301,3 +421,4 @@ | ||
}, | ||
modifiers: [] | ||
modifiers: [], | ||
strategy: 'absolute' | ||
}; | ||
@@ -307,8 +428,4 @@ class Popper { | ||
_defineProperty(this, "state", { | ||
reference: undefined, | ||
popper: undefined, | ||
placement: 'bottom', | ||
orderedModifiers: [], | ||
measures: {}, | ||
offsets: {}, | ||
scrollParents: {}, | ||
options: defaultOptions | ||
@@ -321,11 +438,16 @@ }); | ||
this.state.reference = unwrapJqueryElement(reference); | ||
this.state.popper = unwrapJqueryElement(popper); | ||
this.state.elements = { | ||
reference: unwrapJqueryElement(reference), | ||
popper: unwrapJqueryElement(popper) | ||
}; | ||
const { | ||
reference: referenceElement, | ||
popper: popperElement | ||
} = this.state; // Store options into state | ||
} = this.state.elements; // Store options into state | ||
this.state.options = _objectSpread({}, defaultOptions, options); // Don't proceed if `reference` or `popper` are invalid elements | ||
this.state.options = _objectSpread({}, defaultOptions, options); // Cache the placement in cache to make it available to the modifiers | ||
// modifiers will modify this one (rather than the one in options) | ||
this.state.placement = options.placement; // Don't proceed if `reference` or `popper` are invalid elements | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
@@ -341,3 +463,4 @@ return; | ||
this.state.orderedModifiers = orderModifiers(this.state.options.modifiers); // Validate the provided modifiers so that the consumer will get warned | ||
this.state.orderedModifiers = orderModifiers([...defaultModifiers, ...this.state.options.modifiers]); | ||
console.log(this.state.orderedModifiers); // Validate the provided modifiers so that the consumer will get warned | ||
// of one of the custom modifiers is invalid for any reason | ||
@@ -380,15 +503,23 @@ | ||
popper: popperElement | ||
} = this.state; // Don't proceed if `reference` or `popper` are not valid elements anymore | ||
} = this.state.elements; // Don't proceed if `reference` or `popper` are not valid elements anymore | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
return; | ||
} // Get initial measurements | ||
} // Get scrollTop and scrollLeft of the active window | ||
// this will be used in the `computeOffsets` function to properly | ||
// position the popper taking in account the scroll position | ||
const windowScroll = getWindowScroll(popperElement); // Get initial measurements | ||
// these are going to be used to compute the initial popper offsets | ||
// and as cache for any modifier that needs them later | ||
this.state.measures = { | ||
reference: getElementClientRect(referenceElement), | ||
popper: getElementClientRect(popperElement) | ||
}; | ||
}; // Offsets are the actual position the popper needs to have to be | ||
// properly positioned near its reference element | ||
// This is the most basic placement, and will be adjusted by | ||
// the modifiers in the next step | ||
this.state.offsets = { | ||
@@ -399,9 +530,19 @@ popper: computeOffsets({ | ||
strategy: 'absolute', | ||
placement: this.state.options.placement | ||
placement: this.state.options.placement, | ||
windowScroll | ||
}) | ||
}; | ||
this.state.orderedModifiers.forEach(({ | ||
}; // Modifiers have the ability to read the current Popper.js state, included | ||
// the popper offsets, and modify it to address specifc cases | ||
this.state = this.state.orderedModifiers.reduce((acc, { | ||
fn, | ||
enabled | ||
}) => enabled && fn && (this.state = fn(this.state))); | ||
enabled, | ||
options | ||
}) => { | ||
if (enabled && typeof fn === 'function') { | ||
acc = fn(this.state, options); | ||
} | ||
return acc; | ||
}, {}); | ||
} | ||
@@ -413,3 +554,3 @@ | ||
popper: popperElement | ||
} = this.state; | ||
} = this.state.elements; | ||
const { | ||
@@ -431,4 +572,4 @@ scroll, | ||
if (resize && this.state.popper) { | ||
const win = getWindow(this.state.popper); | ||
if (resize && this.state.elements.popper) { | ||
const win = getWindow(this.state.elements.popper); | ||
win && win.addEventListener('resize', this.update, { | ||
@@ -435,0 +576,0 @@ passive: true |
@@ -109,2 +109,12 @@ function _defineProperty(obj, key, value) { | ||
function getWindowScroll(element) { | ||
const win = getWindow(element); | ||
const scrollLeft = win.pageXOffset; | ||
const scrollTop = win.pageYOffset; | ||
return { | ||
scrollLeft, | ||
scrollTop | ||
}; | ||
} | ||
var unwrapJqueryElement = (element => element && element.jquery ? element[0] : element); | ||
@@ -164,3 +174,5 @@ | ||
const basePlacements = [top, bottom, right, left]; | ||
const placements = basePlacements.reduce((acc, placement) => acc.concat([placement, `${placement}-start`, `${placement}-end`]), []); // modifiers that need to read the DOM | ||
const start = 'start'; | ||
const end = 'end'; | ||
const placements = basePlacements.reduce((acc, placement) => acc.concat([placement, `${placement}-${start}`, `${placement}-${end}`]), []); // modifiers that need to read the DOM | ||
@@ -177,5 +189,10 @@ const read = 'read'; // pure-logic modifiers | ||
strategy, | ||
placement | ||
placement, | ||
windowScroll | ||
}) => { | ||
const basePlacement = getBasePlacement(placement); | ||
const { | ||
scrollTop, | ||
scrollLeft | ||
} = windowScroll; | ||
@@ -185,4 +202,4 @@ switch (basePlacement) { | ||
return { | ||
x: reference.x + reference.width / 2 - popper.width / 2, | ||
y: reference.y - popper.height | ||
x: scrollLeft + reference.x + reference.width / 2 - popper.width / 2, | ||
y: scrollTop + reference.y - popper.height | ||
}; | ||
@@ -192,4 +209,4 @@ | ||
return { | ||
x: reference.x + reference.width / 2 - popper.width / 2, | ||
y: reference.y + reference.height | ||
x: scrollLeft + reference.x + reference.width / 2 - popper.width / 2, | ||
y: scrollTop + reference.y + reference.height | ||
}; | ||
@@ -199,4 +216,4 @@ | ||
return { | ||
x: reference.x + reference.width, | ||
y: reference.y + reference.height / 2 - popper.height / 2 | ||
x: scrollLeft + reference.x + reference.width, | ||
y: scrollTop + reference.y + reference.height / 2 - popper.height / 2 | ||
}; | ||
@@ -206,4 +223,4 @@ | ||
return { | ||
x: reference.x - popper.width, | ||
y: reference.y + reference.height / 2 - popper.height / 2 | ||
x: scrollLeft + reference.x - popper.width, | ||
y: scrollTop + reference.y + reference.height / 2 - popper.height / 2 | ||
}; | ||
@@ -290,2 +307,105 @@ | ||
// This modifier takes the Popper.js state and prepares some StyleSheet properties | ||
// that can be applied to the popper element to make it render in the expected position. | ||
const mapStrategyToPosition = strategy => { | ||
switch (strategy) { | ||
case 'fixed': | ||
return 'fixed'; | ||
case 'absolute': | ||
default: | ||
return 'absolute'; | ||
} | ||
}; | ||
const computePopperStyles = ({ | ||
offsets, | ||
strategy, | ||
gpuAcceleration | ||
}) => { | ||
// by default it is active, disable it only if explicitly set to false | ||
if (gpuAcceleration === false) { | ||
return { | ||
top: `${offsets.y}px`, | ||
left: `${offsets.x}px`, | ||
position: mapStrategyToPosition(strategy) | ||
}; | ||
} else { | ||
return { | ||
transform: `translate3d(${offsets.x}px, ${offsets.y}px, 0)`, | ||
position: mapStrategyToPosition(strategy) | ||
}; | ||
} | ||
}; | ||
const computeArrowStyles = ({ | ||
offsets, | ||
gpuAcceleration | ||
}) => { | ||
if (gpuAcceleration) { | ||
return { | ||
top: `${offsets.y}px`, | ||
left: `${offsets.x}px`, | ||
position: 'absolute' | ||
}; | ||
} else { | ||
return { | ||
transform: `translate3d(${offsets.x}px, ${offsets.y}px, 0)`, | ||
position: 'absolute' | ||
}; | ||
} | ||
}; | ||
function computeStyles(state, options) { | ||
const gpuAcceleration = options && options.gpuAcceleration != null ? options.gpuAcceleration : true; | ||
state.styles = {}; // popper offsets are always available | ||
state.styles.popper = computePopperStyles({ | ||
offsets: state.offsets.popper, | ||
strategy: state.options.strategy, | ||
gpuAcceleration | ||
}); // arrow offsets may not be available | ||
if (state.offsets.arrow != null) { | ||
state.styles.arrow = computeArrowStyles({ | ||
offsets: state.offsets.arrow, | ||
gpuAcceleration | ||
}); | ||
} | ||
return state; | ||
} | ||
var computeStyles$1 = { | ||
name: 'computeStyles', | ||
enabled: true, | ||
phase: 'main', | ||
fn: computeStyles | ||
}; | ||
// This modifier takes the styles prepared by the `computeStyles` modifier | ||
// and applies them to the HTMLElements such as popper and arrow | ||
function applyStyles(state) { | ||
Object.keys(state.elements).forEach(name => { | ||
const style = state.styles.hasOwnProperty(name) ? state.styles[name] : null; // Flow doesn't support to extend this property, but it's the most | ||
// effective way to apply styles to an HTMLElemen | ||
// $FlowIgnore | ||
Object.assign(state.elements[name].style, style); | ||
}); | ||
return state; | ||
} | ||
var applyStyles$1 = { | ||
name: 'applyStyles', | ||
enabled: true, | ||
phase: 'write', | ||
fn: applyStyles, | ||
requires: ['computeStyles'] | ||
}; | ||
var modifiers = /*#__PURE__*/Object.freeze({ | ||
computeStyles: computeStyles$1, | ||
applyStyles: applyStyles$1 | ||
}); | ||
const defaultModifiers = Object.values(modifiers); | ||
const areValidElements = (a, b) => a instanceof Element && b instanceof Element; | ||
@@ -299,3 +419,4 @@ | ||
}, | ||
modifiers: [] | ||
modifiers: [], | ||
strategy: 'absolute' | ||
}; | ||
@@ -305,8 +426,4 @@ class Popper { | ||
_defineProperty(this, "state", { | ||
reference: undefined, | ||
popper: undefined, | ||
placement: 'bottom', | ||
orderedModifiers: [], | ||
measures: {}, | ||
offsets: {}, | ||
scrollParents: {}, | ||
options: defaultOptions | ||
@@ -319,11 +436,16 @@ }); | ||
this.state.reference = unwrapJqueryElement(reference); | ||
this.state.popper = unwrapJqueryElement(popper); | ||
this.state.elements = { | ||
reference: unwrapJqueryElement(reference), | ||
popper: unwrapJqueryElement(popper) | ||
}; | ||
const { | ||
reference: referenceElement, | ||
popper: popperElement | ||
} = this.state; // Store options into state | ||
} = this.state.elements; // Store options into state | ||
this.state.options = _objectSpread({}, defaultOptions, options); // Don't proceed if `reference` or `popper` are invalid elements | ||
this.state.options = _objectSpread({}, defaultOptions, options); // Cache the placement in cache to make it available to the modifiers | ||
// modifiers will modify this one (rather than the one in options) | ||
this.state.placement = options.placement; // Don't proceed if `reference` or `popper` are invalid elements | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
@@ -339,3 +461,4 @@ return; | ||
this.state.orderedModifiers = orderModifiers(this.state.options.modifiers); // Validate the provided modifiers so that the consumer will get warned | ||
this.state.orderedModifiers = orderModifiers([...defaultModifiers, ...this.state.options.modifiers]); | ||
console.log(this.state.orderedModifiers); // Validate the provided modifiers so that the consumer will get warned | ||
// of one of the custom modifiers is invalid for any reason | ||
@@ -378,15 +501,23 @@ | ||
popper: popperElement | ||
} = this.state; // Don't proceed if `reference` or `popper` are not valid elements anymore | ||
} = this.state.elements; // Don't proceed if `reference` or `popper` are not valid elements anymore | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
return; | ||
} // Get initial measurements | ||
} // Get scrollTop and scrollLeft of the active window | ||
// this will be used in the `computeOffsets` function to properly | ||
// position the popper taking in account the scroll position | ||
const windowScroll = getWindowScroll(popperElement); // Get initial measurements | ||
// these are going to be used to compute the initial popper offsets | ||
// and as cache for any modifier that needs them later | ||
this.state.measures = { | ||
reference: getElementClientRect(referenceElement), | ||
popper: getElementClientRect(popperElement) | ||
}; | ||
}; // Offsets are the actual position the popper needs to have to be | ||
// properly positioned near its reference element | ||
// This is the most basic placement, and will be adjusted by | ||
// the modifiers in the next step | ||
this.state.offsets = { | ||
@@ -397,9 +528,19 @@ popper: computeOffsets({ | ||
strategy: 'absolute', | ||
placement: this.state.options.placement | ||
placement: this.state.options.placement, | ||
windowScroll | ||
}) | ||
}; | ||
this.state.orderedModifiers.forEach(({ | ||
}; // Modifiers have the ability to read the current Popper.js state, included | ||
// the popper offsets, and modify it to address specifc cases | ||
this.state = this.state.orderedModifiers.reduce((acc, { | ||
fn, | ||
enabled | ||
}) => enabled && fn && (this.state = fn(this.state))); | ||
enabled, | ||
options | ||
}) => { | ||
if (enabled && typeof fn === 'function') { | ||
acc = fn(this.state, options); | ||
} | ||
return acc; | ||
}, {}); | ||
} | ||
@@ -411,3 +552,3 @@ | ||
popper: popperElement | ||
} = this.state; | ||
} = this.state.elements; | ||
const { | ||
@@ -429,4 +570,4 @@ scroll, | ||
if (resize && this.state.popper) { | ||
const win = getWindow(this.state.popper); | ||
if (resize && this.state.elements.popper) { | ||
const win = getWindow(this.state.elements.popper); | ||
win && win.addEventListener('resize', this.update, { | ||
@@ -433,0 +574,0 @@ passive: true |
@@ -1,425 +0,574 @@ | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
(global.Popper = factory()); | ||
}(this, (function () { 'use strict'; | ||
function _defineProperty(obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
} | ||
return obj; | ||
} | ||
function _objectSpread(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i] != null ? arguments[i] : {}; | ||
var ownKeys = Object.keys(source); | ||
function _objectSpread(target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i] != null ? arguments[i] : {}; | ||
var ownKeys = Object.keys(source); | ||
if (typeof Object.getOwnPropertySymbols === 'function') { | ||
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { | ||
return Object.getOwnPropertyDescriptor(source, sym).enumerable; | ||
})); | ||
} | ||
if (typeof Object.getOwnPropertySymbols === 'function') { | ||
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { | ||
return Object.getOwnPropertyDescriptor(source, sym).enumerable; | ||
})); | ||
ownKeys.forEach(function (key) { | ||
_defineProperty(target, key, source[key]); | ||
}); | ||
} | ||
ownKeys.forEach(function (key) { | ||
_defineProperty(target, key, source[key]); | ||
}); | ||
return target; | ||
} | ||
return target; | ||
} | ||
// Returns the width, height and offsets of the provided element | ||
var getElementClientRect = (element => { | ||
const { | ||
width, | ||
height, | ||
top, | ||
left | ||
} = element.getBoundingClientRect(); | ||
return { | ||
width, | ||
height, | ||
y: top, | ||
x: left | ||
}; | ||
}); | ||
// Returns the width, height and offsets of the provided element | ||
var getElementClientRect = (element => { | ||
const { | ||
width, | ||
height, | ||
top, | ||
left | ||
} = element.getBoundingClientRect(); | ||
return { | ||
width, | ||
height, | ||
y: top, | ||
x: left | ||
}; | ||
}); | ||
var getParentNode = (element => { | ||
if (element.nodeName === 'HTML') { | ||
// DocumentElement detectedF | ||
return element; | ||
} | ||
var getParentNode = (element => { | ||
if (element.nodeName === 'HTML') { | ||
// DocumentElement detectedF | ||
return element; | ||
} | ||
return element.parentNode || // DOM Element detected | ||
// $FlowFixMe: need a better way to handle this... | ||
element.host || // ShadowRoot detected | ||
document.ownerDocument || // Fallback to ownerDocument if available | ||
document.documentElement // Or to documentElement if everything else fails | ||
; | ||
}); | ||
return element.parentNode || // DOM Element detected | ||
// $FlowFixMe: need a better way to handle this... | ||
element.host || // ShadowRoot detected | ||
document.ownerDocument || // Fallback to ownerDocument if available | ||
document.documentElement // Or to documentElement if everything else fails | ||
; | ||
}); | ||
function getScrollParent(node) { | ||
if (!node) { | ||
return document.body; | ||
} | ||
function getScrollParent(node) { | ||
if (!node) { | ||
return document.body; | ||
switch (node.nodeName) { | ||
case 'HTML': | ||
case 'BODY': | ||
return node.ownerDocument.body; | ||
case '#document': | ||
// Flow doesn't understand nodeName type refinement unfortunately | ||
return node.body; | ||
} | ||
if (node instanceof Element) { | ||
// Firefox want us to check `-x` and `-y` variations as well | ||
const { | ||
overflow, | ||
overflowX, | ||
overflowY | ||
} = getComputedStyle(node); | ||
if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) { | ||
return node; | ||
} | ||
} | ||
return getScrollParent(getParentNode(node)); | ||
} | ||
switch (node.nodeName) { | ||
case 'HTML': | ||
case 'BODY': | ||
return node.ownerDocument.body; | ||
function listScrollParents(element, list = []) { | ||
const scrollParent = getScrollParent(element); | ||
const isBody = scrollParent.nodeName === 'BODY'; | ||
const target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent; | ||
const updatedList = list.concat(target); | ||
return isBody ? updatedList : updatedList.concat(listScrollParents(getParentNode(target))); | ||
} | ||
case '#document': | ||
// Flow doesn't understand nodeName type refinement unfortunately | ||
return node.body; | ||
function getWindow(element) { | ||
const ownerDocument = element.ownerDocument; | ||
return ownerDocument ? ownerDocument.defaultView : window; | ||
} | ||
if (node instanceof Element) { | ||
// Firefox want us to check `-x` and `-y` variations as well | ||
const { | ||
overflow, | ||
overflowX, | ||
overflowY | ||
} = getComputedStyle(node); | ||
function getWindowScroll(element) { | ||
const win = getWindow(element); | ||
const scrollLeft = win.pageXOffset; | ||
const scrollTop = win.pageYOffset; | ||
return { | ||
scrollLeft, | ||
scrollTop | ||
}; | ||
} | ||
if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) { | ||
return node; | ||
var unwrapJqueryElement = (element => element && element.jquery ? element[0] : element); | ||
const order = modifiers => { | ||
let sortedModifiers = [...modifiers]; | ||
let i = 0, | ||
j, | ||
temp; | ||
while (i < sortedModifiers.length) { | ||
temp = sortedModifiers.slice(0, i); | ||
for (j = i; j < sortedModifiers.length; j++) { | ||
if ((sortedModifiers[j].requires || []).every(n => temp.some(({ | ||
name | ||
}) => n === name))) { | ||
sortedModifiers.splice(i++, 0, sortedModifiers.splice(j, 1)[0]); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
return getScrollParent(getParentNode(node)); | ||
} | ||
return sortedModifiers; | ||
}; | ||
function listScrollParents(element, list = []) { | ||
const scrollParent = getScrollParent(element); | ||
const isBody = scrollParent.nodeName === 'BODY'; | ||
const target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent; | ||
const updatedList = list.concat(target); | ||
return isBody ? updatedList : updatedList.concat(listScrollParents(getParentNode(target))); | ||
} | ||
var orderModifiers = (modifiers => [...order(modifiers.filter(({ | ||
phase | ||
}) => phase === 'read')), ...order(modifiers.filter(({ | ||
phase | ||
}) => phase === 'main')), ...order(modifiers.filter(({ | ||
phase | ||
}) => phase === 'write'))]); | ||
function getWindow(element) { | ||
const ownerDocument = element.ownerDocument; | ||
return ownerDocument ? ownerDocument.defaultView : window; | ||
} | ||
// Expands the eventListeners value to an object containing the | ||
// `scroll` and `resize` booleans | ||
// | ||
// true => true, true | ||
// false => false, false | ||
// true, false => true, false | ||
// false, false => false, false | ||
var expandEventListeners = (eventListeners => { | ||
const fallbackValue = typeof eventListeners === 'boolean' ? eventListeners : false; | ||
return { | ||
scroll: typeof eventListeners.scroll === 'boolean' ? eventListeners.scroll : fallbackValue, | ||
resize: typeof eventListeners.resize === 'boolean' ? eventListeners.resize : fallbackValue | ||
}; | ||
}); | ||
var unwrapJqueryElement = (element => element && element.jquery ? element[0] : element); | ||
var getBasePlacement = (placement => placement.split('-')[0]); | ||
const order = modifiers => { | ||
let sortedModifiers = [...modifiers]; | ||
let i = 0, | ||
j, | ||
temp; | ||
const top = 'top'; | ||
const bottom = 'bottom'; | ||
const right = 'right'; | ||
const left = 'left'; | ||
const basePlacements = [top, bottom, right, left]; | ||
const start = 'start'; | ||
const end = 'end'; | ||
const placements = basePlacements.reduce((acc, placement) => acc.concat([placement, `${placement}-${start}`, `${placement}-${end}`]), []); // modifiers that need to read the DOM | ||
while (i < sortedModifiers.length) { | ||
temp = sortedModifiers.slice(0, i); | ||
const read = 'read'; // pure-logic modifiers | ||
for (j = i; j < sortedModifiers.length; j++) { | ||
if ((sortedModifiers[j].requires || []).every(n => temp.some(({ | ||
name | ||
}) => n === name))) { | ||
sortedModifiers.splice(i++, 0, sortedModifiers.splice(j, 1)[0]); | ||
break; | ||
const main = 'main'; // modifier with the purpose to write to the DOM (or write into a framework state) | ||
const write = 'write'; | ||
var computeOffsets = (({ | ||
reference, | ||
popper, | ||
strategy, | ||
placement, | ||
windowScroll | ||
}) => { | ||
const basePlacement = getBasePlacement(placement); | ||
const { | ||
scrollTop, | ||
scrollLeft | ||
} = windowScroll; | ||
switch (basePlacement) { | ||
case top: | ||
return { | ||
x: scrollLeft + reference.x + reference.width / 2 - popper.width / 2, | ||
y: scrollTop + reference.y - popper.height | ||
}; | ||
case bottom: | ||
return { | ||
x: scrollLeft + reference.x + reference.width / 2 - popper.width / 2, | ||
y: scrollTop + reference.y + reference.height | ||
}; | ||
case right: | ||
return { | ||
x: scrollLeft + reference.x + reference.width, | ||
y: scrollTop + reference.y + reference.height / 2 - popper.height / 2 | ||
}; | ||
case left: | ||
return { | ||
x: scrollLeft + reference.x - popper.width, | ||
y: scrollTop + reference.y + reference.height / 2 - popper.height / 2 | ||
}; | ||
default: | ||
// $FlowFixMe: This will actually never match; github.com/facebook/flow/issues/2395 | ||
return undefined; | ||
} | ||
}); | ||
var format = ((str, ...args) => [...args].reduce((p, c) => p.replace(/%s/, c), str)); | ||
function microtaskDebounce(fn) { | ||
let called = false; | ||
return () => new Promise(resolve => { | ||
if (called) { | ||
return resolve(); | ||
} | ||
} | ||
called = true; | ||
Promise.resolve().then(() => { | ||
called = false; | ||
resolve(fn()); | ||
}); | ||
}); | ||
} | ||
return sortedModifiers; | ||
}; | ||
const ERROR_MESSAGE = 'PopperJS: modifier "%s" provided an invalid %s property, expected %s but got %s'; | ||
const VALID_PROPERTIES = ['name', 'enabled', 'phase', 'fn', 'onLoad', 'requires', 'options']; | ||
var validateModifiers = (modifiers => { | ||
modifiers.forEach(modifier => { | ||
Object.keys(modifier).forEach(key => { | ||
switch (key) { | ||
case 'name': | ||
if (typeof modifier.name !== 'string') { | ||
console.error(format(ERROR_MESSAGE, String(modifier.name), '"name"', '"string"', `"${String(modifier.name)}"`)); | ||
} | ||
var orderModifiers = (modifiers => [...order(modifiers.filter(({ | ||
phase | ||
}) => phase === 'read')), ...order(modifiers.filter(({ | ||
phase | ||
}) => phase === 'main')), ...order(modifiers.filter(({ | ||
phase | ||
}) => phase === 'write'))]); | ||
break; | ||
// Expands the eventListeners value to an object containing the | ||
// `scroll` and `resize` booleans | ||
// | ||
// true => true, true | ||
// false => false, false | ||
// true, false => true, false | ||
// false, false => false, false | ||
var expandEventListeners = (eventListeners => { | ||
const fallbackValue = typeof eventListeners === 'boolean' ? eventListeners : false; | ||
return { | ||
scroll: typeof eventListeners.scroll === 'boolean' ? eventListeners.scroll : fallbackValue, | ||
resize: typeof eventListeners.resize === 'boolean' ? eventListeners.resize : fallbackValue | ||
}; | ||
}); | ||
case 'enabled': | ||
if (typeof modifier.enabled !== 'boolean') { | ||
console.error(format(ERROR_MESSAGE, modifier.name, '"enabled"', '"boolean"', `"${String(modifier.enabled)}"`)); | ||
} | ||
var getBasePlacement = (placement => placement.split('-')[0]); | ||
case 'phase': | ||
if (![read, main, write].includes(modifier.phase)) { | ||
console.error(format(ERROR_MESSAGE, modifier.name, '"phase"', 'either "read", "main" or "write"', `"${String(modifier.phase)}"`)); | ||
} | ||
const top = 'top'; | ||
const bottom = 'bottom'; | ||
const right = 'right'; | ||
const left = 'left'; | ||
const basePlacements = [top, bottom, right, left]; | ||
const placements = basePlacements.reduce((acc, placement) => acc.concat([placement, `${placement}-start`, `${placement}-end`]), []); // modifiers that need to read the DOM | ||
break; | ||
const read = 'read'; // pure-logic modifiers | ||
case 'fn': | ||
if (typeof modifier.fn !== 'function') { | ||
console.error(format(ERROR_MESSAGE, modifier.name, '"fn"', '"function"', `"${String(modifier.fn)}"`)); | ||
} | ||
const main = 'main'; // modifier with the purpose to write to the DOM (or write into a framework state) | ||
break; | ||
const write = 'write'; | ||
case 'onLoad': | ||
if (typeof modifier.onLoad !== 'function') { | ||
console.error(format(ERROR_MESSAGE, modifier.name, '"onLoad"', '"function"', `"${String(modifier.fn)}"`)); | ||
} | ||
var computeOffsets = (({ | ||
reference, | ||
popper, | ||
strategy, | ||
placement | ||
}) => { | ||
const basePlacement = getBasePlacement(placement); | ||
break; | ||
switch (basePlacement) { | ||
case top: | ||
case 'requires': | ||
if (!Array.isArray(modifier.requires)) { | ||
console.error(format(ERROR_MESSAGE, modifier.name, '"requires"', '"array"', `"${String(modifier.requires)}"`)); | ||
} | ||
break; | ||
case 'object': | ||
break; | ||
default: | ||
console.error(`PopperJS: an invalid property has been provided to the "${modifier.name}" modifier, valid properties are ${VALID_PROPERTIES.map(s => `"${s}"`).join(', ')}; but "${key}" was provided.`); | ||
} | ||
}); | ||
}); | ||
}); | ||
// This modifier takes the Popper.js state and prepares some StyleSheet properties | ||
// that can be applied to the popper element to make it render in the expected position. | ||
const mapStrategyToPosition = strategy => { | ||
switch (strategy) { | ||
case 'fixed': | ||
return 'fixed'; | ||
case 'absolute': | ||
default: | ||
return 'absolute'; | ||
} | ||
}; | ||
const computePopperStyles = ({ | ||
offsets, | ||
strategy, | ||
gpuAcceleration | ||
}) => { | ||
// by default it is active, disable it only if explicitly set to false | ||
if (gpuAcceleration === false) { | ||
return { | ||
x: reference.x + reference.width / 2 - popper.width / 2, | ||
y: reference.y - popper.height | ||
top: `${offsets.y}px`, | ||
left: `${offsets.x}px`, | ||
position: mapStrategyToPosition(strategy) | ||
}; | ||
case bottom: | ||
} else { | ||
return { | ||
x: reference.x + reference.width / 2 - popper.width / 2, | ||
y: reference.y + reference.height | ||
transform: `translate3d(${offsets.x}px, ${offsets.y}px, 0)`, | ||
position: mapStrategyToPosition(strategy) | ||
}; | ||
case right: | ||
} | ||
}; | ||
const computeArrowStyles = ({ | ||
offsets, | ||
gpuAcceleration | ||
}) => { | ||
if (gpuAcceleration) { | ||
return { | ||
x: reference.x + reference.width, | ||
y: reference.y + reference.height / 2 - popper.height / 2 | ||
top: `${offsets.y}px`, | ||
left: `${offsets.x}px`, | ||
position: 'absolute' | ||
}; | ||
case left: | ||
} else { | ||
return { | ||
x: reference.x - popper.width, | ||
y: reference.y + reference.height / 2 - popper.height / 2 | ||
transform: `translate3d(${offsets.x}px, ${offsets.y}px, 0)`, | ||
position: 'absolute' | ||
}; | ||
} | ||
}; | ||
function computeStyles(state, options) { | ||
const gpuAcceleration = options && options.gpuAcceleration != null ? options.gpuAcceleration : true; | ||
state.styles = {}; // popper offsets are always available | ||
default: | ||
// $FlowFixMe: This will actually never match; github.com/facebook/flow/issues/2395 | ||
return undefined; | ||
} | ||
}); | ||
state.styles.popper = computePopperStyles({ | ||
offsets: state.offsets.popper, | ||
strategy: state.options.strategy, | ||
gpuAcceleration | ||
}); // arrow offsets may not be available | ||
var format = ((str, ...args) => [...args].reduce((p, c) => p.replace(/%s/, c), str)); | ||
function microtaskDebounce(fn) { | ||
let called = false; | ||
return () => new Promise(resolve => { | ||
if (called) { | ||
return resolve(); | ||
if (state.offsets.arrow != null) { | ||
state.styles.arrow = computeArrowStyles({ | ||
offsets: state.offsets.arrow, | ||
gpuAcceleration | ||
}); | ||
} | ||
called = true; | ||
Promise.resolve().then(() => { | ||
called = false; | ||
resolve(fn()); | ||
}); | ||
}); | ||
} | ||
return state; | ||
} | ||
var computeStyles$1 = { | ||
name: 'computeStyles', | ||
enabled: true, | ||
phase: 'main', | ||
fn: computeStyles | ||
}; | ||
const ERROR_MESSAGE = 'PopperJS: modifier "%s" provided an invalid %s property, expected %s but got %s'; | ||
const VALID_PROPERTIES = ['name', 'enabled', 'phase', 'fn', 'onLoad', 'requires', 'options']; | ||
var validateModifiers = (modifiers => { | ||
modifiers.forEach(modifier => { | ||
Object.keys(modifier).forEach(key => { | ||
switch (key) { | ||
case 'name': | ||
if (typeof modifier.name !== 'string') { | ||
console.error(format(ERROR_MESSAGE, String(modifier.name), '"name"', '"string"', `"${String(modifier.name)}"`)); | ||
} | ||
// This modifier takes the styles prepared by the `computeStyles` modifier | ||
// and applies them to the HTMLElements such as popper and arrow | ||
function applyStyles(state) { | ||
Object.keys(state.elements).forEach(name => { | ||
const style = state.styles.hasOwnProperty(name) ? state.styles[name] : null; // Flow doesn't support to extend this property, but it's the most | ||
// effective way to apply styles to an HTMLElemen | ||
// $FlowIgnore | ||
break; | ||
Object.assign(state.elements[name].style, style); | ||
}); | ||
return state; | ||
} | ||
var applyStyles$1 = { | ||
name: 'applyStyles', | ||
enabled: true, | ||
phase: 'write', | ||
fn: applyStyles, | ||
requires: ['computeStyles'] | ||
}; | ||
case 'enabled': | ||
if (typeof modifier.enabled !== 'boolean') { | ||
console.error(format(ERROR_MESSAGE, modifier.name, '"enabled"', '"boolean"', `"${String(modifier.enabled)}"`)); | ||
} | ||
case 'phase': | ||
if (![read, main, write].includes(modifier.phase)) { | ||
console.error(format(ERROR_MESSAGE, modifier.name, '"phase"', 'either "read", "main" or "write"', `"${String(modifier.phase)}"`)); | ||
} | ||
break; | ||
var modifiers = /*#__PURE__*/Object.freeze({ | ||
computeStyles: computeStyles$1, | ||
applyStyles: applyStyles$1 | ||
}); | ||
case 'fn': | ||
if (typeof modifier.fn !== 'function') { | ||
console.error(format(ERROR_MESSAGE, modifier.name, '"fn"', '"function"', `"${String(modifier.fn)}"`)); | ||
} | ||
const defaultModifiers = Object.values(modifiers); | ||
break; | ||
const areValidElements = (a, b) => a instanceof Element && b instanceof Element; | ||
case 'onLoad': | ||
if (typeof modifier.onLoad !== 'function') { | ||
console.error(format(ERROR_MESSAGE, modifier.name, '"onLoad"', '"function"', `"${String(modifier.fn)}"`)); | ||
} | ||
const defaultOptions = { | ||
placement: 'bottom', | ||
eventListeners: { | ||
scroll: true, | ||
resize: true | ||
}, | ||
modifiers: [], | ||
strategy: 'absolute' | ||
}; | ||
class Popper { | ||
constructor(reference, popper, options = defaultOptions) { | ||
_defineProperty(this, "state", { | ||
placement: 'bottom', | ||
orderedModifiers: [], | ||
options: defaultOptions | ||
}); | ||
break; | ||
// make update() debounced, so that it only runs at most once-per-tick | ||
this.update = microtaskDebounce(this.update.bind(this)); // Unwrap `reference` and `popper` elements in case they are | ||
// wrapped by jQuery, otherwise consume them as is | ||
case 'requires': | ||
if (!Array.isArray(modifier.requires)) { | ||
console.error(format(ERROR_MESSAGE, modifier.name, '"requires"', '"array"', `"${String(modifier.requires)}"`)); | ||
} | ||
this.state.elements = { | ||
reference: unwrapJqueryElement(reference), | ||
popper: unwrapJqueryElement(popper) | ||
}; | ||
const { | ||
reference: referenceElement, | ||
popper: popperElement | ||
} = this.state.elements; // Store options into state | ||
break; | ||
this.state.options = _objectSpread({}, defaultOptions, options); // Cache the placement in cache to make it available to the modifiers | ||
// modifiers will modify this one (rather than the one in options) | ||
case 'object': | ||
break; | ||
this.state.placement = options.placement; // Don't proceed if `reference` or `popper` are invalid elements | ||
default: | ||
console.error(`PopperJS: an invalid property has been provided to the "${modifier.name}" modifier, valid properties are ${VALID_PROPERTIES.map(s => `"${s}"`).join(', ')}; but "${key}" was provided.`); | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
return; | ||
} | ||
}); | ||
}); | ||
}); | ||
const areValidElements = (a, b) => a instanceof Element && b instanceof Element; | ||
this.state.scrollParents = { | ||
reference: listScrollParents(referenceElement), | ||
popper: listScrollParents(popperElement) | ||
}; // Order `options.modifiers` so that the dependencies are fulfilled | ||
// once the modifiers are executed | ||
const defaultOptions = { | ||
placement: 'bottom', | ||
eventListeners: { | ||
scroll: true, | ||
resize: true | ||
}, | ||
modifiers: [] | ||
}; | ||
class Popper { | ||
constructor(reference, popper, options = defaultOptions) { | ||
_defineProperty(this, "state", { | ||
reference: undefined, | ||
popper: undefined, | ||
orderedModifiers: [], | ||
measures: {}, | ||
offsets: {}, | ||
scrollParents: {}, | ||
options: defaultOptions | ||
}); | ||
this.state.orderedModifiers = orderModifiers([...defaultModifiers, ...this.state.options.modifiers]); | ||
console.log(this.state.orderedModifiers); // Validate the provided modifiers so that the consumer will get warned | ||
// of one of the custom modifiers is invalid for any reason | ||
// make update() debounced, so that it only runs at most once-per-tick | ||
this.update = microtaskDebounce(this.update.bind(this)); // Unwrap `reference` and `popper` elements in case they are | ||
// wrapped by jQuery, otherwise consume them as is | ||
{ | ||
validateModifiers(this.state.options.modifiers); | ||
} // Modifiers have the opportunity to execute some arbitrary code before | ||
// the first update cycle is ran, the order of execution will be the same | ||
// defined by the modifier dependencies directive. | ||
// The `onLoad` function may add or alter the options of themselves | ||
this.state.reference = unwrapJqueryElement(reference); | ||
this.state.popper = unwrapJqueryElement(popper); | ||
const { | ||
reference: referenceElement, | ||
popper: popperElement | ||
} = this.state; // Store options into state | ||
this.state.options = _objectSpread({}, defaultOptions, options); // Don't proceed if `reference` or `popper` are invalid elements | ||
this.state.orderedModifiers.forEach(({ | ||
onLoad, | ||
enabled | ||
}) => enabled && onLoad && onLoad(this.state)); | ||
this.update().then(() => { | ||
// After the first update completed, enable the event listeners | ||
this.enableEventListeners(this.state.options.eventListeners); | ||
}); | ||
} // Async and optimistically optimized update | ||
// it will not be executed if not necessary | ||
// check Popper#constructor to see how it gets debounced | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
return; | ||
} | ||
this.state.scrollParents = { | ||
reference: listScrollParents(referenceElement), | ||
popper: listScrollParents(popperElement) | ||
}; // Order `options.modifiers` so that the dependencies are fulfilled | ||
// once the modifiers are executed | ||
update() { | ||
return new Promise((success, reject) => { | ||
this.forceUpdate(); | ||
success(this.state); | ||
}); | ||
} // Syncronous and forcefully executed update | ||
// it will always be executed even if not necessary, usually NOT needed | ||
// use Popper#update instead | ||
this.state.orderedModifiers = orderModifiers(this.state.options.modifiers); // Validate the provided modifiers so that the consumer will get warned | ||
// of one of the custom modifiers is invalid for any reason | ||
{ | ||
validateModifiers(this.state.options.modifiers); | ||
} // Modifiers have the opportunity to execute some arbitrary code before | ||
// the first update cycle is ran, the order of execution will be the same | ||
// defined by the modifier dependencies directive. | ||
// The `onLoad` function may add or alter the options of themselves | ||
forceUpdate() { | ||
const { | ||
reference: referenceElement, | ||
popper: popperElement | ||
} = this.state.elements; // Don't proceed if `reference` or `popper` are not valid elements anymore | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
return; | ||
} // Get scrollTop and scrollLeft of the active window | ||
// this will be used in the `computeOffsets` function to properly | ||
// position the popper taking in account the scroll position | ||
this.state.orderedModifiers.forEach(({ | ||
onLoad, | ||
enabled | ||
}) => enabled && onLoad && onLoad(this.state)); | ||
this.update().then(() => { | ||
// After the first update completed, enable the event listeners | ||
this.enableEventListeners(this.state.options.eventListeners); | ||
}); | ||
} // Async and optimistically optimized update | ||
// it will not be executed if not necessary | ||
// check Popper#constructor to see how it gets debounced | ||
const windowScroll = getWindowScroll(popperElement); // Get initial measurements | ||
// these are going to be used to compute the initial popper offsets | ||
// and as cache for any modifier that needs them later | ||
update() { | ||
return new Promise((success, reject) => { | ||
this.forceUpdate(); | ||
success(this.state); | ||
}); | ||
} // Syncronous and forcefully executed update | ||
// it will always be executed even if not necessary, usually NOT needed | ||
// use Popper#update instead | ||
this.state.measures = { | ||
reference: getElementClientRect(referenceElement), | ||
popper: getElementClientRect(popperElement) | ||
}; // Offsets are the actual position the popper needs to have to be | ||
// properly positioned near its reference element | ||
// This is the most basic placement, and will be adjusted by | ||
// the modifiers in the next step | ||
this.state.offsets = { | ||
popper: computeOffsets({ | ||
reference: this.state.measures.reference, | ||
popper: this.state.measures.popper, | ||
strategy: 'absolute', | ||
placement: this.state.options.placement, | ||
windowScroll | ||
}) | ||
}; // Modifiers have the ability to read the current Popper.js state, included | ||
// the popper offsets, and modify it to address specifc cases | ||
forceUpdate() { | ||
const { | ||
reference: referenceElement, | ||
popper: popperElement | ||
} = this.state; // Don't proceed if `reference` or `popper` are not valid elements anymore | ||
this.state = this.state.orderedModifiers.reduce((acc, { | ||
fn, | ||
enabled, | ||
options | ||
}) => { | ||
if (enabled && typeof fn === 'function') { | ||
acc = fn(this.state, options); | ||
} | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
return; | ||
} // Get initial measurements | ||
// these are going to be used to compute the initial popper offsets | ||
// and as cache for any modifier that needs them later | ||
return acc; | ||
}, {}); | ||
} | ||
enableEventListeners(eventListeners) { | ||
const { | ||
reference: referenceElement, | ||
popper: popperElement | ||
} = this.state.elements; | ||
const { | ||
scroll, | ||
resize | ||
} = expandEventListeners(eventListeners); // Don't proceed if `reference` or `popper` are not valid elements anymore | ||
this.state.measures = { | ||
reference: getElementClientRect(referenceElement), | ||
popper: getElementClientRect(popperElement) | ||
}; | ||
this.state.offsets = { | ||
popper: computeOffsets({ | ||
reference: this.state.measures.reference, | ||
popper: this.state.measures.popper, | ||
strategy: 'absolute', | ||
placement: this.state.options.placement | ||
}) | ||
}; | ||
this.state.orderedModifiers.forEach(({ | ||
fn, | ||
enabled | ||
}) => enabled && fn && (this.state = fn(this.state))); | ||
} | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
return; | ||
} | ||
enableEventListeners(eventListeners) { | ||
const { | ||
reference: referenceElement, | ||
popper: popperElement | ||
} = this.state; | ||
const { | ||
scroll, | ||
resize | ||
} = expandEventListeners(eventListeners); // Don't proceed if `reference` or `popper` are not valid elements anymore | ||
if (scroll) { | ||
const scrollParents = [...listScrollParents(referenceElement), ...listScrollParents(popperElement)]; | ||
scrollParents.length > 0 && scrollParents.forEach(scrollParent => scrollParent.addEventListener('scroll', this.update, { | ||
passive: true | ||
})); | ||
} | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
return; | ||
if (resize && this.state.elements.popper) { | ||
const win = getWindow(this.state.elements.popper); | ||
win && win.addEventListener('resize', this.update, { | ||
passive: true | ||
}); | ||
} | ||
} | ||
if (scroll) { | ||
const scrollParents = [...listScrollParents(referenceElement), ...listScrollParents(popperElement)]; | ||
scrollParents.length > 0 && scrollParents.forEach(scrollParent => scrollParent.addEventListener('scroll', this.update, { | ||
passive: true | ||
})); | ||
} | ||
if (resize && this.state.popper) { | ||
const win = getWindow(this.state.popper); | ||
win && win.addEventListener('resize', this.update, { | ||
passive: true | ||
}); | ||
} | ||
} | ||
} | ||
return Popper; | ||
export default Popper; | ||
}))); | ||
//# sourceMappingURL=index.js.map |
@@ -7,3 +7,5 @@ export const top = 'top'; | ||
export const basePlacements = [top, bottom, right, left]; | ||
export const placements = basePlacements.reduce((acc, placement) => acc.concat([placement, `${placement}-start`, `${placement}-end`]), []); // modifiers that need to read the DOM | ||
export const start = 'start'; | ||
export const end = 'end'; | ||
export const placements = basePlacements.reduce((acc, placement) => acc.concat([placement, `${placement}-${start}`, `${placement}-${end}`]), []); // modifiers that need to read the DOM | ||
@@ -10,0 +12,0 @@ export const read = 'read'; // pure-logic modifiers |
@@ -8,3 +8,4 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } | ||
import listScrollParents from './dom-utils/listScrollParents'; | ||
import getWindow from './dom-utils/getWindow'; // Pure Utils | ||
import getWindow from './dom-utils/getWindow'; | ||
import getWindowScroll from './dom-utils/getWindowScroll'; // Pure Utils | ||
@@ -17,3 +18,6 @@ import unwrapJqueryElement from './utils/unwrapJqueryElement'; | ||
import debounce from './utils/debounce'; | ||
import validateModifiers from './utils/validateModifiers'; | ||
import validateModifiers from './utils/validateModifiers'; // Default modifiers | ||
import * as modifiers from './modifiers/index'; | ||
const defaultModifiers = Object.values(modifiers); | ||
const INVALID_ELEMENT_ERROR = 'Invalid `%s` argument provided to Popper.js, it must be either a valid DOM element or a jQuery-wrapped DOM element, you provided `%s`'; | ||
@@ -29,3 +33,4 @@ | ||
}, | ||
modifiers: [] | ||
modifiers: [], | ||
strategy: 'absolute' | ||
}; | ||
@@ -35,8 +40,4 @@ export default class Popper { | ||
_defineProperty(this, "state", { | ||
reference: undefined, | ||
popper: undefined, | ||
placement: 'bottom', | ||
orderedModifiers: [], | ||
measures: {}, | ||
offsets: {}, | ||
scrollParents: {}, | ||
options: defaultOptions | ||
@@ -49,11 +50,16 @@ }); | ||
this.state.reference = unwrapJqueryElement(reference); | ||
this.state.popper = unwrapJqueryElement(popper); | ||
this.state.elements = { | ||
reference: unwrapJqueryElement(reference), | ||
popper: unwrapJqueryElement(popper) | ||
}; | ||
const { | ||
reference: referenceElement, | ||
popper: popperElement | ||
} = this.state; // Store options into state | ||
} = this.state.elements; // Store options into state | ||
this.state.options = _objectSpread({}, defaultOptions, options); // Don't proceed if `reference` or `popper` are invalid elements | ||
this.state.options = _objectSpread({}, defaultOptions, options); // Cache the placement in cache to make it available to the modifiers | ||
// modifiers will modify this one (rather than the one in options) | ||
this.state.placement = options.placement; // Don't proceed if `reference` or `popper` are invalid elements | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
@@ -69,3 +75,4 @@ return; | ||
this.state.orderedModifiers = orderModifiers(this.state.options.modifiers); // Validate the provided modifiers so that the consumer will get warned | ||
this.state.orderedModifiers = orderModifiers([...defaultModifiers, ...this.state.options.modifiers]); | ||
console.log(this.state.orderedModifiers); // Validate the provided modifiers so that the consumer will get warned | ||
// of one of the custom modifiers is invalid for any reason | ||
@@ -108,15 +115,23 @@ | ||
popper: popperElement | ||
} = this.state; // Don't proceed if `reference` or `popper` are not valid elements anymore | ||
} = this.state.elements; // Don't proceed if `reference` or `popper` are not valid elements anymore | ||
if (!areValidElements(referenceElement, popperElement)) { | ||
return; | ||
} // Get initial measurements | ||
} // Get scrollTop and scrollLeft of the active window | ||
// this will be used in the `computeOffsets` function to properly | ||
// position the popper taking in account the scroll position | ||
const windowScroll = getWindowScroll(popperElement); // Get initial measurements | ||
// these are going to be used to compute the initial popper offsets | ||
// and as cache for any modifier that needs them later | ||
this.state.measures = { | ||
reference: getElementClientRect(referenceElement), | ||
popper: getElementClientRect(popperElement) | ||
}; | ||
}; // Offsets are the actual position the popper needs to have to be | ||
// properly positioned near its reference element | ||
// This is the most basic placement, and will be adjusted by | ||
// the modifiers in the next step | ||
this.state.offsets = { | ||
@@ -127,9 +142,19 @@ popper: computeOffsets({ | ||
strategy: 'absolute', | ||
placement: this.state.options.placement | ||
placement: this.state.options.placement, | ||
windowScroll | ||
}) | ||
}; | ||
this.state.orderedModifiers.forEach(({ | ||
}; // Modifiers have the ability to read the current Popper.js state, included | ||
// the popper offsets, and modify it to address specifc cases | ||
this.state = this.state.orderedModifiers.reduce((acc, { | ||
fn, | ||
enabled | ||
}) => enabled && fn && (this.state = fn(this.state))); | ||
enabled, | ||
options | ||
}) => { | ||
if (enabled && typeof fn === 'function') { | ||
acc = fn(this.state, options); | ||
} | ||
return acc; | ||
}, {}); | ||
} | ||
@@ -141,3 +166,3 @@ | ||
popper: popperElement | ||
} = this.state; | ||
} = this.state.elements; | ||
const { | ||
@@ -159,4 +184,4 @@ scroll, | ||
if (resize && this.state.popper) { | ||
const win = getWindow(this.state.popper); | ||
if (resize && this.state.elements.popper) { | ||
const win = getWindow(this.state.elements.popper); | ||
win && win.addEventListener('resize', this.update, { | ||
@@ -163,0 +188,0 @@ passive: true |
@@ -9,5 +9,10 @@ import getBasePlacement from './getBasePlacement'; | ||
strategy, | ||
placement | ||
placement, | ||
windowScroll | ||
}) => { | ||
const basePlacement = getBasePlacement(placement); | ||
const { | ||
scrollTop, | ||
scrollLeft | ||
} = windowScroll; | ||
@@ -17,4 +22,4 @@ switch (basePlacement) { | ||
return { | ||
x: reference.x + reference.width / 2 - popper.width / 2, | ||
y: reference.y - popper.height | ||
x: scrollLeft + reference.x + reference.width / 2 - popper.width / 2, | ||
y: scrollTop + reference.y - popper.height | ||
}; | ||
@@ -24,4 +29,4 @@ | ||
return { | ||
x: reference.x + reference.width / 2 - popper.width / 2, | ||
y: reference.y + reference.height | ||
x: scrollLeft + reference.x + reference.width / 2 - popper.width / 2, | ||
y: scrollTop + reference.y + reference.height | ||
}; | ||
@@ -31,4 +36,4 @@ | ||
return { | ||
x: reference.x + reference.width, | ||
y: reference.y + reference.height / 2 - popper.height / 2 | ||
x: scrollLeft + reference.x + reference.width, | ||
y: scrollTop + reference.y + reference.height / 2 - popper.height / 2 | ||
}; | ||
@@ -38,4 +43,4 @@ | ||
return { | ||
x: reference.x - popper.width, | ||
y: reference.y + reference.height / 2 - popper.height / 2 | ||
x: scrollLeft + reference.x - popper.width, | ||
y: scrollTop + reference.y + reference.height / 2 - popper.height / 2 | ||
}; | ||
@@ -42,0 +47,0 @@ |
{ | ||
"name": "popper.js", | ||
"version": "2.0.0-next.2", | ||
"version": "2.0.0-next.3", | ||
"main": "dist/cjs/index.js", | ||
@@ -15,3 +15,3 @@ "main:umd": "dist/umd/index.js", | ||
"dev:serve": "serve tests/visual", | ||
"dev:bundles": "cross-env NODE_ENV=dev rollup -c rollup.config.js", | ||
"dev:bundles": "cross-env NODE_ENV=development rollup -c rollup.config.js", | ||
"build": "yarn clean && yarn build:es && yarn build:bundles && yarn build:flow", | ||
@@ -71,5 +71,7 @@ "build:es": "babel src -d lib --ignore '**/*.test.js','**/__mocks__'", | ||
"rollup-plugin-babel": "^4.0.3", | ||
"rollup-plugin-bundle-size": "^1.0.2", | ||
"rollup-plugin-replace": "^2.1.0", | ||
"rollup-plugin-terser": "^3.0.0", | ||
"serve": "^10.0.2" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
321111
73
1978
23