nonChromiumPlatformUtils-95812e02.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*!
  2. * All material copyright ESRI, All Rights Reserved, unless otherwise specified.
  3. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details.
  4. * v1.0.0-beta.97
  5. */
  6. 'use strict';
  7. const floatingUi = require('./floating-ui-b48c8256.js');
  8. require('./dom-2ec8c9ed.js');
  9. require('./resources-b5a5f8a7.js');
  10. require('./guid-f4f03a7a.js');
  11. require('./debounce-2c8b61fb.js');
  12. /**
  13. * This module provides utils to fix positioning across shadow DOM in non-Chromium browsers
  14. *
  15. * It is based on floating-ui's distributable
  16. */
  17. /**
  18. * 👇 the following are needed to fix shadow DOM positioning 👇️
  19. *
  20. * @param element
  21. */
  22. function getTrueOffsetParent(element) {
  23. if (!isHTMLElement(element) || getComputedStyle(element).position === "fixed") {
  24. return null;
  25. }
  26. return composedOffsetParent(element);
  27. }
  28. /**
  29. * Polyfills the old offsetParent behavior from before the spec was changed:
  30. * https://github.com/w3c/csswg-drafts/issues/159
  31. *
  32. * @param element
  33. */
  34. function composedOffsetParent(element) {
  35. let { offsetParent } = element;
  36. let ancestor = element;
  37. let foundInsideSlot = false;
  38. while (ancestor && ancestor !== offsetParent) {
  39. const { assignedSlot } = ancestor;
  40. if (assignedSlot) {
  41. let newOffsetParent = assignedSlot.offsetParent;
  42. if (getComputedStyle(assignedSlot).display === "contents") {
  43. const hadStyleAttribute = assignedSlot.hasAttribute("style");
  44. const oldDisplay = assignedSlot.style.display;
  45. assignedSlot.style.display = getComputedStyle(ancestor).display;
  46. newOffsetParent = assignedSlot.offsetParent;
  47. assignedSlot.style.display = oldDisplay;
  48. if (!hadStyleAttribute) {
  49. assignedSlot.removeAttribute("style");
  50. }
  51. }
  52. ancestor = assignedSlot;
  53. if (offsetParent !== newOffsetParent) {
  54. offsetParent = newOffsetParent;
  55. foundInsideSlot = true;
  56. }
  57. }
  58. else if (isShadowRoot(ancestor) && ancestor.host && foundInsideSlot) {
  59. break;
  60. }
  61. ancestor = (isShadowRoot(ancestor) && ancestor.host) || ancestor.parentNode;
  62. }
  63. return offsetParent;
  64. }
  65. function getElementRects(_ref) {
  66. const { reference, floating, strategy } = _ref;
  67. return {
  68. reference: getRectRelativeToOffsetParent(reference, getOffsetParent(floating), strategy),
  69. floating: { ...getDimensions(floating), x: 0, y: 0 }
  70. };
  71. }
  72. /**
  73. * ☝️ the following are needed to fix shadow DOM positioning ☝️
  74. */
  75. /**
  76. * 👇 the following are taken directly from floating-ui's ESM distributable to support the exports above 👇️
  77. *
  78. * **Notes**:
  79. * unused functions are removed
  80. * ESLint is disabled
  81. * TS-warnings are suppressed
  82. */
  83. /* eslint-disable */
  84. function isWindow(value) {
  85. return value && value.document && value.location && value.alert && value.setInterval;
  86. }
  87. function getWindow(node) {
  88. if (node == null) {
  89. return window;
  90. }
  91. if (!isWindow(node)) {
  92. const ownerDocument = node.ownerDocument;
  93. return ownerDocument ? ownerDocument.defaultView || window : window;
  94. }
  95. return node;
  96. }
  97. function getComputedStyle(element) {
  98. return getWindow(element).getComputedStyle(element);
  99. }
  100. function getNodeName(node) {
  101. return isWindow(node) ? "" : node ? (node.nodeName || "").toLowerCase() : "";
  102. }
  103. function getUAString() {
  104. // @ts-ignore
  105. const uaData = navigator.userAgentData;
  106. if (uaData != null && uaData.brands) {
  107. return uaData.brands.map((item) => item.brand + "/" + item.version).join(" ");
  108. }
  109. return navigator.userAgent;
  110. }
  111. function isHTMLElement(value) {
  112. return value instanceof getWindow(value).HTMLElement;
  113. }
  114. function isElement(value) {
  115. return value instanceof getWindow(value).Element;
  116. }
  117. function isNode(value) {
  118. return value instanceof getWindow(value).Node;
  119. }
  120. function isShadowRoot(node) {
  121. // Browsers without `ShadowRoot` support
  122. if (typeof ShadowRoot === "undefined") {
  123. return false;
  124. }
  125. const OwnElement = getWindow(node).ShadowRoot;
  126. return node instanceof OwnElement || node instanceof ShadowRoot;
  127. }
  128. function isOverflowElement(element) {
  129. // Firefox wants us to check `-x` and `-y` variations as well
  130. const { overflow, overflowX, overflowY, display } = getComputedStyle(element);
  131. return (/auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX) && !["inline", "contents"].includes(display));
  132. }
  133. function isTableElement(element) {
  134. return ["table", "td", "th"].includes(getNodeName(element));
  135. }
  136. function isContainingBlock(element) {
  137. // TODO: Try and use feature detection here instead
  138. const isFirefox = /firefox/i.test(getUAString());
  139. const css = getComputedStyle(element); // This is non-exhaustive but covers the most common CSS properties that
  140. // create a containing block.
  141. // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
  142. return (css.transform !== "none" ||
  143. css.perspective !== "none" ||
  144. (isFirefox && css.willChange === "filter") ||
  145. (isFirefox && (css.filter ? css.filter !== "none" : false)) ||
  146. ["transform", "perspective"].some((value) => css.willChange.includes(value)) ||
  147. ["paint", "layout", "strict", "content"].some(
  148. // TS 4.1 compat
  149. (value) => {
  150. const contain = css.contain;
  151. return contain != null ? contain.includes(value) : false;
  152. }));
  153. }
  154. function isLayoutViewport() {
  155. // Not Safari
  156. return !/^((?!chrome|android).)*safari/i.test(getUAString()); // Feature detection for this fails in various ways
  157. // • Always-visible scrollbar or not
  158. // • Width of <html>, etc.
  159. // const vV = win.visualViewport;
  160. // return vV ? Math.abs(win.innerWidth / vV.scale - vV.width) < 0.5 : true;
  161. }
  162. function isLastTraversableNode(node) {
  163. return ["html", "body", "#document"].includes(getNodeName(node));
  164. }
  165. const min = Math.min;
  166. const max = Math.max;
  167. const round = Math.round;
  168. function getBoundingClientRect(element, includeScale, isFixedStrategy) {
  169. var _win$visualViewport$o, _win$visualViewport, _win$visualViewport$o2, _win$visualViewport2;
  170. if (includeScale === void 0) {
  171. includeScale = false;
  172. }
  173. if (isFixedStrategy === void 0) {
  174. isFixedStrategy = false;
  175. }
  176. const clientRect = element.getBoundingClientRect();
  177. let scaleX = 1;
  178. let scaleY = 1;
  179. if (includeScale && isHTMLElement(element)) {
  180. scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;
  181. scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;
  182. }
  183. const win = isElement(element) ? getWindow(element) : window;
  184. const addVisualOffsets = !isLayoutViewport() && isFixedStrategy;
  185. const x = (clientRect.left +
  186. (addVisualOffsets
  187. ? (_win$visualViewport$o =
  188. (_win$visualViewport = win.visualViewport) == null ? void 0 : _win$visualViewport.offsetLeft) != null
  189. ? _win$visualViewport$o
  190. : 0
  191. : 0)) /
  192. scaleX;
  193. const y = (clientRect.top +
  194. (addVisualOffsets
  195. ? (_win$visualViewport$o2 =
  196. (_win$visualViewport2 = win.visualViewport) == null ? void 0 : _win$visualViewport2.offsetTop) != null
  197. ? _win$visualViewport$o2
  198. : 0
  199. : 0)) /
  200. scaleY;
  201. const width = clientRect.width / scaleX;
  202. const height = clientRect.height / scaleY;
  203. return {
  204. width,
  205. height,
  206. top: y,
  207. right: x + width,
  208. bottom: y + height,
  209. left: x,
  210. x,
  211. y
  212. };
  213. }
  214. function getDocumentElement(node) {
  215. return ((isNode(node) ? node.ownerDocument : node.document) || window.document).documentElement;
  216. }
  217. function getNodeScroll(element) {
  218. if (isElement(element)) {
  219. return {
  220. scrollLeft: element.scrollLeft,
  221. scrollTop: element.scrollTop
  222. };
  223. }
  224. return {
  225. scrollLeft: element.pageXOffset,
  226. scrollTop: element.pageYOffset
  227. };
  228. }
  229. function getWindowScrollBarX(element) {
  230. // If <html> has a CSS width greater than the viewport, then this will be
  231. // incorrect for RTL.
  232. // @ts-ignore
  233. return getBoundingClientRect(getDocumentElement(element)).left + getNodeScroll(element).scrollLeft;
  234. }
  235. function isScaled(element) {
  236. // @ts-ignore
  237. const rect = getBoundingClientRect(element);
  238. return round(rect.width) !== element.offsetWidth || round(rect.height) !== element.offsetHeight;
  239. }
  240. function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
  241. const isOffsetParentAnElement = isHTMLElement(offsetParent);
  242. const documentElement = getDocumentElement(offsetParent);
  243. const rect = getBoundingClientRect(element, // @ts-ignore - checked above (TS 4.1 compat)
  244. isOffsetParentAnElement && isScaled(offsetParent), strategy === "fixed");
  245. let scroll = {
  246. scrollLeft: 0,
  247. scrollTop: 0
  248. };
  249. const offsets = {
  250. x: 0,
  251. y: 0
  252. };
  253. if (isOffsetParentAnElement || (!isOffsetParentAnElement && strategy !== "fixed")) {
  254. if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) {
  255. scroll = getNodeScroll(offsetParent);
  256. }
  257. if (isHTMLElement(offsetParent)) {
  258. // @ts-ignore
  259. const offsetRect = getBoundingClientRect(offsetParent, true);
  260. offsets.x = offsetRect.x + offsetParent.clientLeft;
  261. offsets.y = offsetRect.y + offsetParent.clientTop;
  262. }
  263. else if (documentElement) {
  264. offsets.x = getWindowScrollBarX(documentElement);
  265. }
  266. }
  267. return {
  268. x: rect.left + scroll.scrollLeft - offsets.x,
  269. y: rect.top + scroll.scrollTop - offsets.y,
  270. width: rect.width,
  271. height: rect.height
  272. };
  273. }
  274. function getParentNode(node) {
  275. if (getNodeName(node) === "html") {
  276. return node;
  277. }
  278. return (
  279. // this is a quicker (but less type safe) way to save quite some bytes from the bundle
  280. // @ts-ignore
  281. node.assignedSlot || // step into the shadow DOM of the parent of a slotted node
  282. node.parentNode || // DOM Element detected
  283. (isShadowRoot(node) ? node.host : null) || // ShadowRoot detected
  284. getDocumentElement(node) // fallback
  285. );
  286. }
  287. function getContainingBlock(element) {
  288. let currentNode = getParentNode(element);
  289. if (isShadowRoot(currentNode)) {
  290. currentNode = currentNode.host;
  291. }
  292. while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
  293. if (isContainingBlock(currentNode)) {
  294. return currentNode;
  295. }
  296. else {
  297. const parent = currentNode.parentNode;
  298. currentNode = isShadowRoot(parent) ? parent.host : parent;
  299. }
  300. }
  301. return null;
  302. } // Gets the closest ancestor positioned element. Handles some edge cases,
  303. // such as table ancestors and cross browser bugs.
  304. function getOffsetParent(element) {
  305. const window = getWindow(element);
  306. let offsetParent = getTrueOffsetParent(element);
  307. while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === "static") {
  308. offsetParent = getTrueOffsetParent(offsetParent);
  309. }
  310. if (offsetParent &&
  311. (getNodeName(offsetParent) === "html" ||
  312. (getNodeName(offsetParent) === "body" &&
  313. getComputedStyle(offsetParent).position === "static" &&
  314. !isContainingBlock(offsetParent)))) {
  315. return window;
  316. }
  317. return offsetParent || getContainingBlock(element) || window;
  318. }
  319. function getDimensions(element) {
  320. if (isHTMLElement(element)) {
  321. return {
  322. width: element.offsetWidth,
  323. height: element.offsetHeight
  324. };
  325. }
  326. // @ts-ignore
  327. const rect = getBoundingClientRect(element);
  328. return {
  329. width: rect.width,
  330. height: rect.height
  331. };
  332. }
  333. function getViewportRect(element, strategy) {
  334. const win = getWindow(element);
  335. const html = getDocumentElement(element);
  336. const visualViewport = win.visualViewport;
  337. let width = html.clientWidth;
  338. let height = html.clientHeight;
  339. let x = 0;
  340. let y = 0;
  341. if (visualViewport) {
  342. width = visualViewport.width;
  343. height = visualViewport.height;
  344. const layoutViewport = isLayoutViewport();
  345. if (layoutViewport || (!layoutViewport && strategy === "fixed")) {
  346. x = visualViewport.offsetLeft;
  347. y = visualViewport.offsetTop;
  348. }
  349. }
  350. return {
  351. width,
  352. height,
  353. x,
  354. y
  355. };
  356. }
  357. // of the `<html>` and `<body>` rect bounds if horizontally scrollable
  358. function getDocumentRect(element) {
  359. var _element$ownerDocumen;
  360. const html = getDocumentElement(element);
  361. const scroll = getNodeScroll(element);
  362. const body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;
  363. const width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);
  364. const height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);
  365. let x = -scroll.scrollLeft + getWindowScrollBarX(element);
  366. const y = -scroll.scrollTop;
  367. if (getComputedStyle(body || html).direction === "rtl") {
  368. x += max(html.clientWidth, body ? body.clientWidth : 0) - width;
  369. }
  370. return {
  371. width,
  372. height,
  373. x,
  374. y
  375. };
  376. }
  377. function getNearestOverflowAncestor(node) {
  378. const parentNode = getParentNode(node);
  379. if (isLastTraversableNode(parentNode)) {
  380. // @ts-ignore assume body is always available
  381. return node.ownerDocument.body;
  382. }
  383. if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
  384. return parentNode;
  385. }
  386. return getNearestOverflowAncestor(parentNode);
  387. }
  388. function getOverflowAncestors(node, list) {
  389. var _node$ownerDocument;
  390. if (list === void 0) {
  391. list = [];
  392. }
  393. const scrollableAncestor = getNearestOverflowAncestor(node);
  394. const isBody = scrollableAncestor === ((_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.body);
  395. const win = getWindow(scrollableAncestor);
  396. const target = isBody
  397. ? [win].concat(win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [])
  398. : scrollableAncestor;
  399. const updatedList = list.concat(target);
  400. return isBody
  401. ? updatedList // @ts-ignore: isBody tells us target will be an HTMLElement here
  402. : updatedList.concat(getOverflowAncestors(target));
  403. }
  404. function contains(parent, child) {
  405. const rootNode = child.getRootNode == null ? void 0 : child.getRootNode(); // First, attempt with faster native method
  406. if (parent.contains(child)) {
  407. return true;
  408. } // then fallback to custom implementation with Shadow DOM support
  409. else if (rootNode && isShadowRoot(rootNode)) {
  410. let next = child;
  411. do {
  412. // use `===` replace node.isSameNode()
  413. if (next && parent === next) {
  414. return true;
  415. } // @ts-ignore: need a better way to handle this...
  416. next = next.parentNode || next.host;
  417. } while (next);
  418. }
  419. return false;
  420. }
  421. function getNearestParentCapableOfEscapingClipping(element, clippingAncestors) {
  422. let currentNode = element;
  423. while (currentNode && !isLastTraversableNode(currentNode) && !clippingAncestors.includes(currentNode)) {
  424. if (isElement(currentNode) && ["absolute", "fixed"].includes(getComputedStyle(currentNode).position)) {
  425. break;
  426. }
  427. const parentNode = getParentNode(currentNode);
  428. currentNode = isShadowRoot(parentNode) ? parentNode.host : parentNode;
  429. }
  430. return currentNode;
  431. }
  432. function getInnerBoundingClientRect(element, strategy) {
  433. const clientRect = getBoundingClientRect(element, false, strategy === "fixed");
  434. const top = clientRect.top + element.clientTop;
  435. const left = clientRect.left + element.clientLeft;
  436. return {
  437. top,
  438. left,
  439. x: left,
  440. y: top,
  441. right: left + element.clientWidth,
  442. bottom: top + element.clientHeight,
  443. width: element.clientWidth,
  444. height: element.clientHeight
  445. };
  446. }
  447. function getClientRectFromClippingAncestor(element, clippingParent, strategy) {
  448. if (clippingParent === "viewport") {
  449. return floatingUi.rectToClientRect(getViewportRect(element, strategy));
  450. }
  451. if (isElement(clippingParent)) {
  452. return getInnerBoundingClientRect(clippingParent, strategy);
  453. }
  454. return floatingUi.rectToClientRect(getDocumentRect(getDocumentElement(element)));
  455. } // A "clipping ancestor" is an overflowable container with the characteristic of
  456. // clipping (or hiding) overflowing elements with a position different from
  457. // `initial`
  458. function getClippingAncestors(element) {
  459. // @ts-ignore
  460. const clippingAncestors = getOverflowAncestors(element);
  461. const nearestEscapableParent = getNearestParentCapableOfEscapingClipping(element, clippingAncestors);
  462. let clipperElement = null;
  463. if (nearestEscapableParent && isHTMLElement(nearestEscapableParent)) {
  464. const offsetParent = getOffsetParent(nearestEscapableParent);
  465. if (isOverflowElement(nearestEscapableParent)) {
  466. clipperElement = nearestEscapableParent;
  467. }
  468. else if (isHTMLElement(offsetParent)) {
  469. clipperElement = offsetParent;
  470. }
  471. }
  472. if (!isElement(clipperElement)) {
  473. return [];
  474. } // @ts-ignore isElement check ensures we return Array<Element>
  475. return clippingAncestors.filter((clippingAncestors) => clipperElement &&
  476. isElement(clippingAncestors) &&
  477. contains(clippingAncestors, clipperElement) &&
  478. getNodeName(clippingAncestors) !== "body");
  479. } // Gets the maximum area that the element is visible in due to any number of
  480. // clipping ancestors
  481. function getClippingRect(_ref) {
  482. let { element, boundary, rootBoundary, strategy } = _ref;
  483. const mainClippingAncestors = boundary === "clippingAncestors" ? getClippingAncestors(element) : [].concat(boundary);
  484. const clippingAncestors = [...mainClippingAncestors, rootBoundary];
  485. const firstClippingAncestor = clippingAncestors[0];
  486. const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {
  487. const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy);
  488. accRect.top = max(rect.top, accRect.top);
  489. accRect.right = min(rect.right, accRect.right);
  490. accRect.bottom = min(rect.bottom, accRect.bottom);
  491. accRect.left = max(rect.left, accRect.left);
  492. return accRect;
  493. }, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy));
  494. return {
  495. width: clippingRect.right - clippingRect.left,
  496. height: clippingRect.bottom - clippingRect.top,
  497. x: clippingRect.left,
  498. y: clippingRect.top
  499. };
  500. }
  501. exports.getClippingRect = getClippingRect;
  502. exports.getElementRects = getElementRects;
  503. exports.getOffsetParent = getOffsetParent;