react-lightning-design-system
Version:
Salesforce Lightning Design System components built with React
378 lines (376 loc) • 17.6 kB
JavaScript
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _regeneratorRuntime from "@babel/runtime/regenerator";
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import classnames from 'classnames';
import RelativePortal from 'react-relative-portal';
import { ComponentSettingsContext } from './ComponentSettings';
import { useControlledValue, useEventCallback } from './hooks';
function delay(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
function getViewportRect() {
var _ref = window || {},
_ref$innerHeight = _ref.innerHeight,
height = _ref$innerHeight === void 0 ? Infinity : _ref$innerHeight,
_ref$innerWidth = _ref.innerWidth,
width = _ref$innerWidth === void 0 ? Infinity : _ref$innerWidth;
return {
top: 0,
left: 0,
width: width,
height: height
};
}
function calcAlignmentRect(target, rect, alignment) {
var _alignment = _slicedToArray(alignment, 2),
firstAlign = _alignment[0],
secondAlign = _alignment[1];
return _objectSpread(_objectSpread({}, rect), {}, {
top: firstAlign === 'top-absolute' || secondAlign === 'top-absolute' ? 0 : firstAlign === 'bottom-absolute' || secondAlign === 'bottom-absolute' ? getViewportRect().height - rect.height : firstAlign === 'top' ? target.top + target.height : firstAlign === 'bottom' ? target.top - rect.height : secondAlign === 'top' ? target.top : secondAlign === 'bottom' ? target.top + target.height - rect.height :
// valign middle
target.top + (target.height - rect.height) * 0.5,
left: firstAlign === 'left-absolute' || secondAlign === 'left-absolute' ? 0 : firstAlign === 'right-absolute' || secondAlign === 'right-absolute' ? getViewportRect().width - rect.width : firstAlign === 'left' ? target.left + target.width : firstAlign === 'right' ? target.left - rect.width : secondAlign === 'left' ? target.left : secondAlign === 'right' ? target.left + target.width - rect.width :
// halign center
target.left + (target.width - rect.width) * 0.5
});
}
function hasViewportIntersection(_ref2) {
var top = _ref2.top,
left = _ref2.left,
width = _ref2.width,
height = _ref2.height;
var _getViewportRect = getViewportRect(),
viewportWidth = _getViewportRect.width,
viewportHeight = _getViewportRect.height;
return top < 0 || top + height > viewportHeight || left < 0 || left + width > viewportWidth;
}
function isEqualRect(aRect, bRect) {
if (aRect === bRect) {
return true;
}
if (!aRect || !bRect) {
return false;
}
return aRect.top === bRect.top && aRect.left === bRect.left && aRect.width === bRect.width && aRect.height === bRect.height;
}
function getCenterPoint(rect) {
return {
x: rect.left + 0.5 * rect.width,
y: rect.top + 0.5 * rect.height
};
}
function getPreferAlignment(rect) {
if (!rect) {
return {
v: 'top',
h: 'left'
};
}
var _getCenterPoint = getCenterPoint(rect),
rx = _getCenterPoint.x,
ry = _getCenterPoint.y;
var _getCenterPoint2 = getCenterPoint(getViewportRect()),
vx = _getCenterPoint2.x,
vy = _getCenterPoint2.y;
return {
h: rx < vx ? 'left' : 'right',
v: ry < vy ? 'top' : 'bottom'
};
}
function throttle(func, ms) {
var last = 0;
return function () {
var now = Date.now();
if (last + ms < now) {
func.apply(void 0, arguments);
last = now;
}
};
}
function ignoreFirstCall(func) {
var called = false;
return function () {
if (called) {
func.apply(void 0, arguments);
}
called = true;
};
}
function removeAbsoluteAlign(alignment) {
return alignment.map(function (a) {
return a.replace(/-absolute$/, '');
});
}
function getPossibleAlignments(alignmentStyle, align, target) {
var _getPreferAlignment = getPreferAlignment(target),
h = _getPreferAlignment.h,
v = _getPreferAlignment.v;
var alignments = [['top', 'left'], ['top', 'right'], ['top'], ['bottom', 'left'], ['bottom', 'right'], ['bottom'], ['left', 'top'], ['left', 'bottom'], ['left'], ['right', 'top'], ['right', 'bottom'], ['right'], ['top', "".concat(h, "-absolute")], ['bottom', "".concat(h, "-absolute")], ['left', "".concat(v, "-absolute")], ['right', "".concat(v, "-absolute")], ["".concat(v, "-absolute"), 'left'], ["".concat(v, "-absolute"), 'right'], ["".concat(h, "-absolute"), 'top'], ["".concat(h, "-absolute"), 'bottom'], ["".concat(v, "-absolute")], ["".concat(h, "-absolute")], ["".concat(v, "-absolute"), "".concat(h, "-absolute")]];
return alignments.filter(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
firstAlign = _ref4[0],
secondAlign = _ref4[1];
return !align || firstAlign === align || secondAlign === align;
}).filter(function (_ref5) {
var _ref6 = _slicedToArray(_ref5, 2),
firstAlign = _ref6[0],
secondAlign = _ref6[1];
return alignmentStyle === 'menu' ? secondAlign && /^(top|bottom)/.test(firstAlign) : true;
});
}
var EMPTY_RECT = {
top: 0,
left: 0,
width: 0,
height: 0
};
/**
*
*/
function useAutoAlign(props) {
var _rootNodeRect$top, _rootNodeRect$left;
var triggerSelector = props.triggerSelector,
alignmentStyle = props.alignmentStyle,
align = props.align,
alignment_ = props.alignment;
var pidRef = useRef(null);
var elRef = useRef(null);
var autoAlignContentRef = useRef(null);
var _useControlledValue = useControlledValue(alignment_, getPossibleAlignments(alignmentStyle, align)[0]),
_useControlledValue2 = _slicedToArray(_useControlledValue, 2),
alignment = _useControlledValue2[0],
setAlignment = _useControlledValue2[1];
var _useState = useState(),
_useState2 = _slicedToArray(_useState, 2),
rootNodeRect = _useState2[0],
setRootNodeRect = _useState2[1];
var _useState3 = useState(),
_useState4 = _slicedToArray(_useState3, 2),
triggerNodeRect = _useState4[0],
setTriggerNodeRect = _useState4[1];
var updateAlignment = useEventCallback(function () {
var _elRef$current$getBou, _elRef$current, _autoAlignContentRef$, _autoAlignContentRef$2;
var newTriggerNodeRect = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : EMPTY_RECT;
var newRootNodeRect = (_elRef$current$getBou = (_elRef$current = elRef.current) === null || _elRef$current === void 0 ? void 0 : _elRef$current.getBoundingClientRect()) !== null && _elRef$current$getBou !== void 0 ? _elRef$current$getBou : EMPTY_RECT;
var _ref7 = (_autoAlignContentRef$ = (_autoAlignContentRef$2 = autoAlignContentRef.current) === null || _autoAlignContentRef$2 === void 0 ? void 0 : _autoAlignContentRef$2.getBoundingClientRect()) !== null && _autoAlignContentRef$ !== void 0 ? _autoAlignContentRef$ : EMPTY_RECT,
contentRectWidth = _ref7.width,
contentRectHeight = _ref7.height;
var newAlignment = null;
var possibleAlignments = getPossibleAlignments(alignmentStyle, align, newTriggerNodeRect);
var _iterator = _createForOfIteratorHelper(possibleAlignments),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var possibleAlignment = _step.value;
var aRect = calcAlignmentRect(newTriggerNodeRect, {
width: contentRectWidth,
height: contentRectHeight
}, possibleAlignment);
if (!hasViewportIntersection(aRect)) {
newAlignment = possibleAlignment;
break;
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
if (!newAlignment) {
newAlignment = possibleAlignments[possibleAlignments.length - 1];
}
if (newAlignment[0] !== alignment[0] || newAlignment[1] !== alignment[1]) {
setAlignment(newAlignment);
setTriggerNodeRect(newTriggerNodeRect);
setRootNodeRect(newRootNodeRect);
} else if (!triggerNodeRect || newTriggerNodeRect.width !== triggerNodeRect.width || newTriggerNodeRect.height !== triggerNodeRect.height || /absolute$/.test(alignment[0]) || /absolute$/.test(alignment[1] || '')) {
setTriggerNodeRect(newTriggerNodeRect);
setRootNodeRect(newRootNodeRect);
}
});
var recalcAlignment = useEventCallback(function () {
var el = elRef.current;
if (el) {
var _ref8, _el$matches;
var matches = // eslint-disable-next-line @typescript-eslint/unbound-method
(_ref8 = (_el$matches = el.matches) !== null && _el$matches !== void 0 ? _el$matches : el.matchesSelector) !== null && _ref8 !== void 0 ? _ref8 : el.msMatchesSelector;
var triggerEl = el;
try {
while (triggerEl) {
if (matches.call(triggerEl, triggerSelector)) {
break;
}
triggerEl = triggerEl.parentElement;
}
} catch (e) {
triggerEl = null;
}
if (triggerEl) {
var _triggerEl$getBoundin = triggerEl.getBoundingClientRect(),
_top = _triggerEl$getBoundin.top,
_left = _triggerEl$getBoundin.left,
width = _triggerEl$getBoundin.width,
height = _triggerEl$getBoundin.height;
if (!isEqualRect(triggerNodeRect, {
top: _top,
left: _left,
width: width,
height: height
})) {
updateAlignment({
top: _top,
left: _left,
width: width,
height: height
});
} else {
updateAlignment(triggerNodeRect);
}
} else {
updateAlignment(triggerNodeRect);
}
}
});
var requestRecalcAlignment = useMemo(function () {
return throttle( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
var _pidRef$current;
var pid, _i, _arr, ms;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
pid = ((_pidRef$current = pidRef.current) !== null && _pidRef$current !== void 0 ? _pidRef$current : 0) + 1;
pidRef.current = pid;
_i = 0, _arr = [0, 300, 400, 300, 200];
case 3:
if (!(_i < _arr.length)) {
_context.next = 13;
break;
}
ms = _arr[_i];
_context.next = 7;
return delay(ms);
case 7:
if (!(pidRef.current !== pid)) {
_context.next = 9;
break;
}
return _context.abrupt("return");
case 9:
recalcAlignment();
case 10:
_i++;
_context.next = 3;
break;
case 13:
pidRef.current = 0;
case 14:
case "end":
return _context.stop();
}
}, _callee);
})), 100);
}, [recalcAlignment]);
var onScroll = useMemo(function () {
return ignoreFirstCall(requestRecalcAlignment);
}, [requestRecalcAlignment]);
var elRefCallback = useCallback(function (el) {
if (el) {
elRef.current = el;
requestRecalcAlignment();
}
}, [requestRecalcAlignment]);
useEffect(function () {
return function () {
pidRef.current = null;
};
}, []);
var _calcAlignmentRect = calcAlignmentRect(triggerNodeRect !== null && triggerNodeRect !== void 0 ? triggerNodeRect : EMPTY_RECT, rootNodeRect !== null && rootNodeRect !== void 0 ? rootNodeRect : EMPTY_RECT, alignment),
top = _calcAlignmentRect.top,
left_ = _calcAlignmentRect.left;
var left = left_;
if ((alignment[0] === 'top' || alignment[0] === 'bottom') && !alignment[1]) {
var _triggerNodeRect$left, _triggerNodeRect$widt;
left = ((_triggerNodeRect$left = triggerNodeRect === null || triggerNodeRect === void 0 ? void 0 : triggerNodeRect.left) !== null && _triggerNodeRect$left !== void 0 ? _triggerNodeRect$left : 0) + ((_triggerNodeRect$widt = triggerNodeRect === null || triggerNodeRect === void 0 ? void 0 : triggerNodeRect.width) !== null && _triggerNodeRect$widt !== void 0 ? _triggerNodeRect$widt : 0) * 0.5;
}
var offsetTop = top - ((_rootNodeRect$top = rootNodeRect === null || rootNodeRect === void 0 ? void 0 : rootNodeRect.top) !== null && _rootNodeRect$top !== void 0 ? _rootNodeRect$top : 0);
var offsetLeft = left - ((_rootNodeRect$left = rootNodeRect === null || rootNodeRect === void 0 ? void 0 : rootNodeRect.left) !== null && _rootNodeRect$left !== void 0 ? _rootNodeRect$left : 0);
var returnAlignment = useMemo(function () {
return removeAbsoluteAlign(alignment);
}, [alignment]);
return {
initialized: triggerNodeRect != null,
alignment: returnAlignment,
offsetTop: offsetTop,
offsetLeft: offsetLeft,
onScroll: onScroll,
elRef: elRefCallback,
autoAlignContentRef: autoAlignContentRef
};
}
/**
*
*/
export var AutoAlign = function AutoAlign(props) {
var preventPortalize = props.preventPortalize,
additionalPortalClassName = props.portalClassName,
_props$portalStyle = props.portalStyle,
additionalPortalStyle = _props$portalStyle === void 0 ? {} : _props$portalStyle,
_props$offsetX = props.offsetX,
offsetX = _props$offsetX === void 0 ? 0 : _props$offsetX,
_props$offsetY = props.offsetY,
offsetY = _props$offsetY === void 0 ? 0 : _props$offsetY,
children = props.children;
var _useAutoAlign = useAutoAlign(props),
initialized = _useAutoAlign.initialized,
alignment = _useAutoAlign.alignment,
offsetLeft = _useAutoAlign.offsetLeft,
offsetTop = _useAutoAlign.offsetTop,
onScroll = _useAutoAlign.onScroll,
elRef = _useAutoAlign.elRef,
autoAlignContentRef = _useAutoAlign.autoAlignContentRef;
var compSettings = useContext(ComponentSettingsContext);
var _compSettings$portalC = compSettings.portalClassName,
portalClassName = _compSettings$portalC === void 0 ? 'slds-scope' : _compSettings$portalC,
_compSettings$portalS = compSettings.portalStyle,
portalStyle = _compSettings$portalS === void 0 ? {
position: 'absolute',
top: 0,
left: 0,
right: 0
} : _compSettings$portalS;
var adjustedOffsetLeft = offsetLeft + offsetX;
var adjustedOffsetTop = offsetTop + offsetY;
if (typeof children !== 'function') {
return /*#__PURE__*/React.isValidElement(children) ? children : /*#__PURE__*/React.createElement(React.Fragment, null, children);
}
var content = children({
alignment: alignment,
autoAlignContentRef: autoAlignContentRef
});
return preventPortalize || process.env.NODE_ENV === 'test' ? content : /*#__PURE__*/React.createElement("div", {
ref: elRef
}, /*#__PURE__*/React.createElement(RelativePortal, {
fullWidth: true,
top: adjustedOffsetTop,
left: adjustedOffsetLeft,
right: -adjustedOffsetLeft,
onScroll: onScroll,
component: "div",
className: classnames(portalClassName, additionalPortalClassName),
style: _objectSpread(_objectSpread({}, portalStyle), additionalPortalStyle)
}, /*#__PURE__*/React.createElement(ComponentSettingsContext.Provider, {
value: compSettings
}, initialized ? content : /*#__PURE__*/React.createElement("div", {
className: "slds-hidden"
}, content))));
};
//# sourceMappingURL=AutoAlign.js.map