dom.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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.82
  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. * @returns {string} The element's ID.
  25. */
  26. function ensureId(el) {
  27. if (!el) {
  28. return "";
  29. }
  30. return (el.id = el.id || `${el.tagName.toLowerCase()}-${guid()}`);
  31. }
  32. function nodeListToArray(nodeList) {
  33. return Array.isArray(nodeList) ? nodeList : Array.from(nodeList);
  34. }
  35. function getThemeName(el) {
  36. const closestElWithTheme = closestElementCrossShadowBoundary(el, `.${CSS_UTILITY.darkTheme}, .${CSS_UTILITY.lightTheme}`);
  37. return (closestElWithTheme === null || closestElWithTheme === void 0 ? void 0 : closestElWithTheme.classList.contains("calcite-theme-dark")) ? "dark" : "light";
  38. }
  39. function getElementDir(el) {
  40. const prop = "dir";
  41. const selector = `[${prop}]`;
  42. const closest = closestElementCrossShadowBoundary(el, selector);
  43. return closest ? closest.getAttribute(prop) : "ltr";
  44. }
  45. function getElementProp(el, prop, fallbackValue) {
  46. const selector = `[${prop}]`;
  47. const closest = el.closest(selector);
  48. return closest ? closest.getAttribute(prop) : fallbackValue;
  49. }
  50. function getRootNode(el) {
  51. return el.getRootNode();
  52. }
  53. function getHost(root) {
  54. return root.host || null;
  55. }
  56. /**
  57. * This helper queries an element's rootNodes and any ancestor rootNodes.
  58. *
  59. * @returns {Element[]} The elements.
  60. */
  61. function queryElementsRoots(element, selector) {
  62. // Gets the rootNode and any ancestor rootNodes (shadowRoot or document) of an element and queries them for a selector.
  63. // Based on: https://stackoverflow.com/q/54520554/194216
  64. function queryFromAll(el, allResults) {
  65. if (!el) {
  66. return allResults;
  67. }
  68. if (el.assignedSlot) {
  69. el = el.assignedSlot;
  70. }
  71. const rootNode = getRootNode(el);
  72. const results = Array.from(rootNode.querySelectorAll(selector));
  73. const uniqueResults = results.filter((result) => !allResults.includes(result));
  74. allResults = [...allResults, ...uniqueResults];
  75. const host = getHost(rootNode);
  76. return host ? queryFromAll(host, allResults) : allResults;
  77. }
  78. return queryFromAll(element, []);
  79. }
  80. /**
  81. * This helper queries an element's rootNode and any ancestor rootNodes.
  82. *
  83. * If both an 'id' and 'selector' are supplied, 'id' will take precedence over 'selector'.
  84. *
  85. * @returns {Element} The element.
  86. */
  87. function queryElementRoots(element, { selector, id }) {
  88. // Gets the rootNode and any ancestor rootNodes (shadowRoot or document) of an element and queries them for a selector.
  89. // Based on: https://stackoverflow.com/q/54520554/194216
  90. function queryFrom(el) {
  91. if (!el) {
  92. return null;
  93. }
  94. if (el.assignedSlot) {
  95. el = el.assignedSlot;
  96. }
  97. const rootNode = getRootNode(el);
  98. const found = id
  99. ? "getElementById" in rootNode
  100. ? /*
  101. Check to make sure 'getElementById' exists in cases where element is no longer connected to the DOM and getRootNode() returns the element.
  102. https://github.com/Esri/calcite-components/pull/4280
  103. */
  104. rootNode.getElementById(id)
  105. : null
  106. : selector
  107. ? rootNode.querySelector(selector)
  108. : null;
  109. const host = getHost(rootNode);
  110. return found ? found : host ? queryFrom(host) : null;
  111. }
  112. return queryFrom(element);
  113. }
  114. function closestElementCrossShadowBoundary(element, selector) {
  115. // based on https://stackoverflow.com/q/54520554/194216
  116. function closestFrom(el) {
  117. return el ? el.closest(selector) || closestFrom(getHost(getRootNode(el))) : null;
  118. }
  119. return closestFrom(element);
  120. }
  121. function isCalciteFocusable(el) {
  122. return typeof (el === null || el === void 0 ? void 0 : el.setFocus) === "function";
  123. }
  124. async function focusElement(el) {
  125. if (!el) {
  126. return;
  127. }
  128. return isCalciteFocusable(el) ? el.setFocus() : el.focus();
  129. }
  130. const defaultSlotSelector = ":not([slot])";
  131. function getSlotted(element, slotName, options) {
  132. if (slotName && !Array.isArray(slotName) && typeof slotName !== "string") {
  133. options = slotName;
  134. slotName = null;
  135. }
  136. const slotSelector = slotName
  137. ? Array.isArray(slotName)
  138. ? slotName.map((name) => `[slot="${name}"]`).join(",")
  139. : `[slot="${slotName}"]`
  140. : defaultSlotSelector;
  141. if (options === null || options === void 0 ? void 0 : options.all) {
  142. return queryMultiple(element, slotSelector, options);
  143. }
  144. return querySingle(element, slotSelector, options);
  145. }
  146. function getDirectChildren(el, selector) {
  147. return el ? Array.from(el.children || []).filter((child) => child === null || child === void 0 ? void 0 : child.matches(selector)) : [];
  148. }
  149. function queryMultiple(element, slotSelector, options) {
  150. let matches = slotSelector === defaultSlotSelector
  151. ? getDirectChildren(element, defaultSlotSelector)
  152. : Array.from(element.querySelectorAll(slotSelector));
  153. matches = options && options.direct === false ? matches : matches.filter((el) => el.parentElement === element);
  154. 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;
  155. const selector = options === null || options === void 0 ? void 0 : options.selector;
  156. return selector
  157. ? matches
  158. .map((item) => Array.from(item.querySelectorAll(selector)))
  159. .reduce((previousValue, currentValue) => [...previousValue, ...currentValue], [])
  160. .filter((match) => !!match)
  161. : matches;
  162. }
  163. function querySingle(element, slotSelector, options) {
  164. let match = slotSelector === defaultSlotSelector
  165. ? getDirectChildren(element, defaultSlotSelector)[0] || null
  166. : element.querySelector(slotSelector);
  167. match = options && options.direct === false ? match : (match === null || match === void 0 ? void 0 : match.parentElement) === element ? match : null;
  168. match = (options === null || options === void 0 ? void 0 : options.matches) ? ((match === null || match === void 0 ? void 0 : match.matches(options.matches)) ? match : null) : match;
  169. const selector = options === null || options === void 0 ? void 0 : options.selector;
  170. return selector ? match === null || match === void 0 ? void 0 : match.querySelector(selector) : match;
  171. }
  172. function filterDirectChildren(el, selector) {
  173. return Array.from(el.children).filter((child) => child.matches(selector));
  174. }
  175. // set a default icon from a defined set or allow an override with an icon name string
  176. function setRequestedIcon(iconObject, iconValue, matchedValue) {
  177. if (typeof iconValue === "string" && iconValue !== "") {
  178. return iconValue;
  179. }
  180. else if (iconValue === "") {
  181. return iconObject[matchedValue];
  182. }
  183. }
  184. function intersects(rect1, rect2) {
  185. return !(rect2.left > rect1.right ||
  186. rect2.right < rect1.left ||
  187. rect2.top > rect1.bottom ||
  188. rect2.bottom < rect1.top);
  189. }
  190. /**
  191. * This helper makes sure that boolean aria attributes are properly converted to a string.
  192. *
  193. * It should only be used for aria attributes that require a string value of "true" or "false".
  194. *
  195. * @returns {string} The string conversion of a boolean value ("true" | "false").
  196. */
  197. function toAriaBoolean(value) {
  198. return (!!value).toString();
  199. }
  200. export { CSS_UTILITY as C, TEXT as T, getElementDir as a, getSlotted as b, queryElementRoots as c, getThemeName as d, closestElementCrossShadowBoundary as e, focusElement as f, getElementProp as g, filterDirectChildren as h, ensureId as i, isCalciteFocusable as j, intersects as k, nodeListToArray as n, queryElementsRoots as q, setRequestedIcon as s, toAriaBoolean as t };