dom.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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. import { g as guid } from './guid.js';
  7. const autoTheme = "calcite-theme-auto";
  8. const darkTheme = "calcite-theme-dark";
  9. const lightTheme = "calcite-theme-light";
  10. const CSS_UTILITY = {
  11. autoTheme,
  12. darkTheme,
  13. lightTheme,
  14. rtl: "calcite--rtl"
  15. };
  16. const TEXT = {
  17. loading: "Loading"
  18. };
  19. /**
  20. * This helper will guarantee an ID on the provided element.
  21. *
  22. * If it already has an ID, it will be preserved, otherwise a unique one will be generated and assigned.
  23. *
  24. * @param el
  25. * @returns {string} The element's ID.
  26. */
  27. function ensureId(el) {
  28. if (!el) {
  29. return "";
  30. }
  31. return (el.id = el.id || `${el.tagName.toLowerCase()}-${guid()}`);
  32. }
  33. function nodeListToArray(nodeList) {
  34. return Array.isArray(nodeList) ? nodeList : Array.from(nodeList);
  35. }
  36. function getThemeName(el) {
  37. const closestElWithTheme = closestElementCrossShadowBoundary(el, `.${CSS_UTILITY.darkTheme}, .${CSS_UTILITY.lightTheme}`);
  38. return (closestElWithTheme === null || closestElWithTheme === void 0 ? void 0 : closestElWithTheme.classList.contains("calcite-theme-dark")) ? "dark" : "light";
  39. }
  40. function getElementDir(el) {
  41. const prop = "dir";
  42. const selector = `[${prop}]`;
  43. const closest = closestElementCrossShadowBoundary(el, selector);
  44. return closest ? closest.getAttribute(prop) : "ltr";
  45. }
  46. function getElementProp(el, prop, fallbackValue) {
  47. const selector = `[${prop}]`;
  48. const closest = el.closest(selector);
  49. return closest ? closest.getAttribute(prop) : fallbackValue;
  50. }
  51. function getRootNode(el) {
  52. return el.getRootNode();
  53. }
  54. function getHost(root) {
  55. return root.host || null;
  56. }
  57. /**
  58. * This helper queries an element's rootNode and any ancestor rootNodes.
  59. *
  60. * If both an 'id' and 'selector' are supplied, 'id' will take precedence over 'selector'.
  61. *
  62. * @param element
  63. * @param root0
  64. * @param root0.selector
  65. * @param root0.id
  66. * @returns {Element} The element.
  67. */
  68. function queryElementRoots(element, { selector, id }) {
  69. // Gets the rootNode and any ancestor rootNodes (shadowRoot or document) of an element and queries them for a selector.
  70. // Based on: https://stackoverflow.com/q/54520554/194216
  71. function queryFrom(el) {
  72. if (!el) {
  73. return null;
  74. }
  75. if (el.assignedSlot) {
  76. el = el.assignedSlot;
  77. }
  78. const rootNode = getRootNode(el);
  79. const found = id
  80. ? "getElementById" in rootNode
  81. ? /*
  82. Check to make sure 'getElementById' exists in cases where element is no longer connected to the DOM and getRootNode() returns the element.
  83. https://github.com/Esri/calcite-components/pull/4280
  84. */
  85. rootNode.getElementById(id)
  86. : null
  87. : selector
  88. ? rootNode.querySelector(selector)
  89. : null;
  90. const host = getHost(rootNode);
  91. return found ? found : host ? queryFrom(host) : null;
  92. }
  93. return queryFrom(element);
  94. }
  95. function closestElementCrossShadowBoundary(element, selector) {
  96. // based on https://stackoverflow.com/q/54520554/194216
  97. function closestFrom(el) {
  98. return el ? el.closest(selector) || closestFrom(getHost(getRootNode(el))) : null;
  99. }
  100. return closestFrom(element);
  101. }
  102. /**
  103. * This utility helps invoke a callback as it traverses a node and its ancestors until reaching the root document.
  104. *
  105. * Returning early or undefined in `onVisit` will continue traversing up the DOM tree. Otherwise, traversal will halt with the returned value as the result of the function
  106. *
  107. * @param element
  108. * @param onVisit
  109. */
  110. function walkUpAncestry(element, onVisit) {
  111. return visit(element, onVisit);
  112. }
  113. function visit(node, onVisit) {
  114. if (!node) {
  115. return;
  116. }
  117. const result = onVisit(node);
  118. if (result !== undefined) {
  119. return result;
  120. }
  121. const { parentNode } = node;
  122. return visit(parentNode instanceof ShadowRoot ? parentNode.host : parentNode, onVisit);
  123. }
  124. function containsCrossShadowBoundary(element, maybeDescendant) {
  125. return !!walkUpAncestry(maybeDescendant, (node) => (node === element ? true : undefined));
  126. }
  127. function isCalciteFocusable(el) {
  128. return typeof (el === null || el === void 0 ? void 0 : el.setFocus) === "function";
  129. }
  130. async function focusElement(el) {
  131. if (!el) {
  132. return;
  133. }
  134. return isCalciteFocusable(el) ? el.setFocus() : el.focus();
  135. }
  136. const defaultSlotSelector = ":not([slot])";
  137. function getSlotted(element, slotName, options) {
  138. if (slotName && !Array.isArray(slotName) && typeof slotName !== "string") {
  139. options = slotName;
  140. slotName = null;
  141. }
  142. const slotSelector = slotName
  143. ? Array.isArray(slotName)
  144. ? slotName.map((name) => `[slot="${name}"]`).join(",")
  145. : `[slot="${slotName}"]`
  146. : defaultSlotSelector;
  147. if (options === null || options === void 0 ? void 0 : options.all) {
  148. return queryMultiple(element, slotSelector, options);
  149. }
  150. return querySingle(element, slotSelector, options);
  151. }
  152. function getDirectChildren(el, selector) {
  153. return el ? Array.from(el.children || []).filter((child) => child === null || child === void 0 ? void 0 : child.matches(selector)) : [];
  154. }
  155. function queryMultiple(element, slotSelector, options) {
  156. let matches = slotSelector === defaultSlotSelector
  157. ? getDirectChildren(element, defaultSlotSelector)
  158. : Array.from(element.querySelectorAll(slotSelector));
  159. matches = options && options.direct === false ? matches : matches.filter((el) => el.parentElement === element);
  160. matches = (options === null || options === void 0 ? void 0 : options.matches) ? matches.filter((el) => el === null || el === void 0 ? void 0 : el.matches(options.matches)) : matches;
  161. const selector = options === null || options === void 0 ? void 0 : options.selector;
  162. return selector
  163. ? matches
  164. .map((item) => Array.from(item.querySelectorAll(selector)))
  165. .reduce((previousValue, currentValue) => [...previousValue, ...currentValue], [])
  166. .filter((match) => !!match)
  167. : matches;
  168. }
  169. function querySingle(element, slotSelector, options) {
  170. let match = slotSelector === defaultSlotSelector
  171. ? getDirectChildren(element, defaultSlotSelector)[0] || null
  172. : element.querySelector(slotSelector);
  173. match = options && options.direct === false ? match : (match === null || match === void 0 ? void 0 : match.parentElement) === element ? match : null;
  174. match = (options === null || options === void 0 ? void 0 : options.matches) ? ((match === null || match === void 0 ? void 0 : match.matches(options.matches)) ? match : null) : match;
  175. const selector = options === null || options === void 0 ? void 0 : options.selector;
  176. return selector ? match === null || match === void 0 ? void 0 : match.querySelector(selector) : match;
  177. }
  178. function filterDirectChildren(el, selector) {
  179. return Array.from(el.children).filter((child) => child.matches(selector));
  180. }
  181. // set a default icon from a defined set or allow an override with an icon name string
  182. function setRequestedIcon(iconObject, iconValue, matchedValue) {
  183. if (typeof iconValue === "string" && iconValue !== "") {
  184. return iconValue;
  185. }
  186. else if (iconValue === "") {
  187. return iconObject[matchedValue];
  188. }
  189. }
  190. function intersects(rect1, rect2) {
  191. return !(rect2.left > rect1.right ||
  192. rect2.right < rect1.left ||
  193. rect2.top > rect1.bottom ||
  194. rect2.bottom < rect1.top);
  195. }
  196. /**
  197. * This helper makes sure that boolean aria attributes are properly converted to a string.
  198. *
  199. * It should only be used for aria attributes that require a string value of "true" or "false".
  200. *
  201. * @param value
  202. * @returns {string} The string conversion of a boolean value ("true" | "false").
  203. */
  204. function toAriaBoolean(value) {
  205. return Boolean(value).toString();
  206. }
  207. /**
  208. * This helper returns true if the pointer event fired from the primary button of the device.
  209. *
  210. * See https://www.w3.org/TR/pointerevents/#the-button-property.
  211. *
  212. * @param event
  213. * @returns {boolean}
  214. */
  215. function isPrimaryPointerButton(event) {
  216. return !!(event.isPrimary && event.button === 0);
  217. }
  218. export { CSS_UTILITY as C, TEXT as T, autoTheme as a, getSlotted as b, getElementDir as c, darkTheme as d, getThemeName as e, focusElement as f, getElementProp as g, ensureId as h, isPrimaryPointerButton as i, isCalciteFocusable as j, intersects as k, getRootNode as l, filterDirectChildren as m, nodeListToArray as n, closestElementCrossShadowBoundary as o, containsCrossShadowBoundary as p, queryElementRoots as q, setRequestedIcon as s, toAriaBoolean as t };