helpers.mjs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); }
  2. function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
  3. function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; }
  4. function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
  5. function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
  6. // Utilities
  7. import { capitalize, Comment, computed, Fragment, isVNode, reactive, readonly, shallowRef, toRefs, unref, watchEffect } from 'vue';
  8. import { IN_BROWSER } from "./globals.mjs"; // Types
  9. export function getNestedValue(obj, path, fallback) {
  10. const last = path.length - 1;
  11. if (last < 0) return obj === undefined ? fallback : obj;
  12. for (let i = 0; i < last; i++) {
  13. if (obj == null) {
  14. return fallback;
  15. }
  16. obj = obj[path[i]];
  17. }
  18. if (obj == null) return fallback;
  19. return obj[path[last]] === undefined ? fallback : obj[path[last]];
  20. }
  21. export function deepEqual(a, b) {
  22. if (a === b) return true;
  23. if (a instanceof Date && b instanceof Date && a.getTime() !== b.getTime()) {
  24. // If the values are Date, compare them as timestamps
  25. return false;
  26. }
  27. if (a !== Object(a) || b !== Object(b)) {
  28. // If the values aren't objects, they were already checked for equality
  29. return false;
  30. }
  31. const props = Object.keys(a);
  32. if (props.length !== Object.keys(b).length) {
  33. // Different number of props, don't bother to check
  34. return false;
  35. }
  36. return props.every(p => deepEqual(a[p], b[p]));
  37. }
  38. export function getObjectValueByPath(obj, path, fallback) {
  39. // credit: http://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-with-string-key#comment55278413_6491621
  40. if (obj == null || !path || typeof path !== 'string') return fallback;
  41. if (obj[path] !== undefined) return obj[path];
  42. path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
  43. path = path.replace(/^\./, ''); // strip a leading dot
  44. return getNestedValue(obj, path.split('.'), fallback);
  45. }
  46. export function getPropertyFromItem(item, property, fallback) {
  47. if (property === true) return item === undefined ? fallback : item;
  48. if (property == null || typeof property === 'boolean') return fallback;
  49. if (item !== Object(item)) {
  50. if (typeof property !== 'function') return fallback;
  51. const value = property(item, fallback);
  52. return typeof value === 'undefined' ? fallback : value;
  53. }
  54. if (typeof property === 'string') return getObjectValueByPath(item, property, fallback);
  55. if (Array.isArray(property)) return getNestedValue(item, property, fallback);
  56. if (typeof property !== 'function') return fallback;
  57. const value = property(item, fallback);
  58. return typeof value === 'undefined' ? fallback : value;
  59. }
  60. export function createRange(length) {
  61. let start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  62. return Array.from({
  63. length
  64. }, (v, k) => start + k);
  65. }
  66. export function getZIndex(el) {
  67. if (!el || el.nodeType !== Node.ELEMENT_NODE) return 0;
  68. const index = +window.getComputedStyle(el).getPropertyValue('z-index');
  69. if (!index) return getZIndex(el.parentNode);
  70. return index;
  71. }
  72. export function convertToUnit(str) {
  73. let unit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'px';
  74. if (str == null || str === '') {
  75. return undefined;
  76. } else if (isNaN(+str)) {
  77. return String(str);
  78. } else if (!isFinite(+str)) {
  79. return undefined;
  80. } else {
  81. return `${Number(str)}${unit}`;
  82. }
  83. }
  84. export function isObject(obj) {
  85. return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
  86. }
  87. export function isPlainObject(obj) {
  88. let proto;
  89. return obj !== null && typeof obj === 'object' && ((proto = Object.getPrototypeOf(obj)) === Object.prototype || proto === null);
  90. }
  91. export function refElement(obj) {
  92. if (obj && '$el' in obj) {
  93. const el = obj.$el;
  94. if (el?.nodeType === Node.TEXT_NODE) {
  95. // Multi-root component, use the first element
  96. return el.nextElementSibling;
  97. }
  98. return el;
  99. }
  100. return obj;
  101. }
  102. // KeyboardEvent.keyCode aliases
  103. export const keyCodes = Object.freeze({
  104. enter: 13,
  105. tab: 9,
  106. delete: 46,
  107. esc: 27,
  108. space: 32,
  109. up: 38,
  110. down: 40,
  111. left: 37,
  112. right: 39,
  113. end: 35,
  114. home: 36,
  115. del: 46,
  116. backspace: 8,
  117. insert: 45,
  118. pageup: 33,
  119. pagedown: 34,
  120. shift: 16
  121. });
  122. export const keyValues = Object.freeze({
  123. enter: 'Enter',
  124. tab: 'Tab',
  125. delete: 'Delete',
  126. esc: 'Escape',
  127. space: 'Space',
  128. up: 'ArrowUp',
  129. down: 'ArrowDown',
  130. left: 'ArrowLeft',
  131. right: 'ArrowRight',
  132. end: 'End',
  133. home: 'Home',
  134. del: 'Delete',
  135. backspace: 'Backspace',
  136. insert: 'Insert',
  137. pageup: 'PageUp',
  138. pagedown: 'PageDown',
  139. shift: 'Shift'
  140. });
  141. export function keys(o) {
  142. return Object.keys(o);
  143. }
  144. export function has(obj, key) {
  145. return key.every(k => obj.hasOwnProperty(k));
  146. }
  147. // Array of keys
  148. export function pick(obj, paths) {
  149. const found = {};
  150. const keys = new Set(Object.keys(obj));
  151. for (const path of paths) {
  152. if (keys.has(path)) {
  153. found[path] = obj[path];
  154. }
  155. }
  156. return found;
  157. }
  158. // Array of keys
  159. // Array of keys or RegExp to test keys against
  160. export function pickWithRest(obj, paths, exclude) {
  161. const found = Object.create(null);
  162. const rest = Object.create(null);
  163. for (const key in obj) {
  164. if (paths.some(path => path instanceof RegExp ? path.test(key) : path === key) && !exclude?.some(path => path === key)) {
  165. found[key] = obj[key];
  166. } else {
  167. rest[key] = obj[key];
  168. }
  169. }
  170. return [found, rest];
  171. }
  172. export function omit(obj, exclude) {
  173. const clone = {
  174. ...obj
  175. };
  176. exclude.forEach(prop => delete clone[prop]);
  177. return clone;
  178. }
  179. export function only(obj, include) {
  180. const clone = {};
  181. include.forEach(prop => clone[prop] = obj[prop]);
  182. return clone;
  183. }
  184. const onRE = /^on[^a-z]/;
  185. export const isOn = key => onRE.test(key);
  186. const bubblingEvents = ['onAfterscriptexecute', 'onAnimationcancel', 'onAnimationend', 'onAnimationiteration', 'onAnimationstart', 'onAuxclick', 'onBeforeinput', 'onBeforescriptexecute', 'onChange', 'onClick', 'onCompositionend', 'onCompositionstart', 'onCompositionupdate', 'onContextmenu', 'onCopy', 'onCut', 'onDblclick', 'onFocusin', 'onFocusout', 'onFullscreenchange', 'onFullscreenerror', 'onGesturechange', 'onGestureend', 'onGesturestart', 'onGotpointercapture', 'onInput', 'onKeydown', 'onKeypress', 'onKeyup', 'onLostpointercapture', 'onMousedown', 'onMousemove', 'onMouseout', 'onMouseover', 'onMouseup', 'onMousewheel', 'onPaste', 'onPointercancel', 'onPointerdown', 'onPointerenter', 'onPointerleave', 'onPointermove', 'onPointerout', 'onPointerover', 'onPointerup', 'onReset', 'onSelect', 'onSubmit', 'onTouchcancel', 'onTouchend', 'onTouchmove', 'onTouchstart', 'onTransitioncancel', 'onTransitionend', 'onTransitionrun', 'onTransitionstart', 'onWheel'];
  187. const compositionIgnoreKeys = ['ArrowUp', 'ArrowDown', 'ArrowRight', 'ArrowLeft', 'Enter', 'Escape', 'Tab', ' '];
  188. export function isComposingIgnoreKey(e) {
  189. return e.isComposing && compositionIgnoreKeys.includes(e.key);
  190. }
  191. /**
  192. * Filter attributes that should be applied to
  193. * the root element of an input component. Remaining
  194. * attributes should be passed to the <input> element inside.
  195. */
  196. export function filterInputAttrs(attrs) {
  197. const [events, props] = pickWithRest(attrs, [onRE]);
  198. const inputEvents = omit(events, bubblingEvents);
  199. const [rootAttrs, inputAttrs] = pickWithRest(props, ['class', 'style', 'id', /^data-/]);
  200. Object.assign(rootAttrs, events);
  201. Object.assign(inputAttrs, inputEvents);
  202. return [rootAttrs, inputAttrs];
  203. }
  204. /**
  205. * Returns the set difference of B and A, i.e. the set of elements in B but not in A
  206. */
  207. export function arrayDiff(a, b) {
  208. const diff = [];
  209. for (let i = 0; i < b.length; i++) {
  210. if (!a.includes(b[i])) diff.push(b[i]);
  211. }
  212. return diff;
  213. }
  214. export function wrapInArray(v) {
  215. return v == null ? [] : Array.isArray(v) ? v : [v];
  216. }
  217. export function defaultFilter(value, search, item) {
  218. return value != null && search != null && typeof value !== 'boolean' && value.toString().toLocaleLowerCase().indexOf(search.toLocaleLowerCase()) !== -1;
  219. }
  220. export function debounce(fn, delay) {
  221. let timeoutId = 0;
  222. const wrap = function () {
  223. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  224. args[_key] = arguments[_key];
  225. }
  226. clearTimeout(timeoutId);
  227. timeoutId = setTimeout(() => fn(...args), unref(delay));
  228. };
  229. wrap.clear = () => {
  230. clearTimeout(timeoutId);
  231. };
  232. wrap.immediate = fn;
  233. return wrap;
  234. }
  235. export function throttle(fn, limit) {
  236. let throttling = false;
  237. return function () {
  238. if (!throttling) {
  239. throttling = true;
  240. setTimeout(() => throttling = false, limit);
  241. return fn(...arguments);
  242. }
  243. };
  244. }
  245. export function clamp(value) {
  246. let min = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  247. let max = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
  248. return Math.max(min, Math.min(max, value));
  249. }
  250. export function getDecimals(value) {
  251. const trimmedStr = value.toString().trim();
  252. return trimmedStr.includes('.') ? trimmedStr.length - trimmedStr.indexOf('.') - 1 : 0;
  253. }
  254. export function padEnd(str, length) {
  255. let char = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '0';
  256. return str + char.repeat(Math.max(0, length - str.length));
  257. }
  258. export function padStart(str, length) {
  259. let char = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '0';
  260. return char.repeat(Math.max(0, length - str.length)) + str;
  261. }
  262. export function chunk(str) {
  263. let size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
  264. const chunked = [];
  265. let index = 0;
  266. while (index < str.length) {
  267. chunked.push(str.substr(index, size));
  268. index += size;
  269. }
  270. return chunked;
  271. }
  272. export function chunkArray(array) {
  273. let size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
  274. return Array.from({
  275. length: Math.ceil(array.length / size)
  276. }, (v, i) => array.slice(i * size, i * size + size));
  277. }
  278. export function humanReadableFileSize(bytes) {
  279. let base = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1000;
  280. if (bytes < base) {
  281. return `${bytes} B`;
  282. }
  283. const prefix = base === 1024 ? ['Ki', 'Mi', 'Gi'] : ['k', 'M', 'G'];
  284. let unit = -1;
  285. while (Math.abs(bytes) >= base && unit < prefix.length - 1) {
  286. bytes /= base;
  287. ++unit;
  288. }
  289. return `${bytes.toFixed(1)} ${prefix[unit]}B`;
  290. }
  291. export function mergeDeep() {
  292. let source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  293. let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  294. let arrayFn = arguments.length > 2 ? arguments[2] : undefined;
  295. const out = {};
  296. for (const key in source) {
  297. out[key] = source[key];
  298. }
  299. for (const key in target) {
  300. const sourceProperty = source[key];
  301. const targetProperty = target[key];
  302. // Only continue deep merging if
  303. // both properties are plain objects
  304. if (isPlainObject(sourceProperty) && isPlainObject(targetProperty)) {
  305. out[key] = mergeDeep(sourceProperty, targetProperty, arrayFn);
  306. continue;
  307. }
  308. if (arrayFn && Array.isArray(sourceProperty) && Array.isArray(targetProperty)) {
  309. out[key] = arrayFn(sourceProperty, targetProperty);
  310. continue;
  311. }
  312. out[key] = targetProperty;
  313. }
  314. return out;
  315. }
  316. export function flattenFragments(nodes) {
  317. return nodes.map(node => {
  318. if (node.type === Fragment) {
  319. return flattenFragments(node.children);
  320. } else {
  321. return node;
  322. }
  323. }).flat();
  324. }
  325. export function toKebabCase() {
  326. let str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  327. if (toKebabCase.cache.has(str)) return toKebabCase.cache.get(str);
  328. const kebab = str.replace(/[^a-z]/gi, '-').replace(/\B([A-Z])/g, '-$1').toLowerCase();
  329. toKebabCase.cache.set(str, kebab);
  330. return kebab;
  331. }
  332. toKebabCase.cache = new Map();
  333. export function findChildrenWithProvide(key, vnode) {
  334. if (!vnode || typeof vnode !== 'object') return [];
  335. if (Array.isArray(vnode)) {
  336. return vnode.map(child => findChildrenWithProvide(key, child)).flat(1);
  337. } else if (vnode.suspense) {
  338. return findChildrenWithProvide(key, vnode.ssContent);
  339. } else if (Array.isArray(vnode.children)) {
  340. return vnode.children.map(child => findChildrenWithProvide(key, child)).flat(1);
  341. } else if (vnode.component) {
  342. if (Object.getOwnPropertySymbols(vnode.component.provides).includes(key)) {
  343. return [vnode.component];
  344. } else if (vnode.component.subTree) {
  345. return findChildrenWithProvide(key, vnode.component.subTree).flat(1);
  346. }
  347. }
  348. return [];
  349. }
  350. var _arr = /*#__PURE__*/new WeakMap();
  351. var _pointer = /*#__PURE__*/new WeakMap();
  352. export class CircularBuffer {
  353. constructor(size) {
  354. _classPrivateFieldInitSpec(this, _arr, []);
  355. _classPrivateFieldInitSpec(this, _pointer, 0);
  356. this.size = size;
  357. }
  358. push(val) {
  359. _classPrivateFieldGet(_arr, this)[_classPrivateFieldGet(_pointer, this)] = val;
  360. _classPrivateFieldSet(_pointer, this, (_classPrivateFieldGet(_pointer, this) + 1) % this.size);
  361. }
  362. values() {
  363. return _classPrivateFieldGet(_arr, this).slice(_classPrivateFieldGet(_pointer, this)).concat(_classPrivateFieldGet(_arr, this).slice(0, _classPrivateFieldGet(_pointer, this)));
  364. }
  365. }
  366. export function getEventCoordinates(e) {
  367. if ('touches' in e) {
  368. return {
  369. clientX: e.touches[0].clientX,
  370. clientY: e.touches[0].clientY
  371. };
  372. }
  373. return {
  374. clientX: e.clientX,
  375. clientY: e.clientY
  376. };
  377. }
  378. // Only allow a single return type
  379. /**
  380. * Convert a computed ref to a record of refs.
  381. * The getter function must always return an object with the same keys.
  382. */
  383. export function destructComputed(getter) {
  384. const refs = reactive({});
  385. const base = computed(getter);
  386. watchEffect(() => {
  387. for (const key in base.value) {
  388. refs[key] = base.value[key];
  389. }
  390. }, {
  391. flush: 'sync'
  392. });
  393. return toRefs(refs);
  394. }
  395. /** Array.includes but value can be any type */
  396. export function includes(arr, val) {
  397. return arr.includes(val);
  398. }
  399. export function eventName(propName) {
  400. return propName[2].toLowerCase() + propName.slice(3);
  401. }
  402. export const EventProp = () => [Function, Array];
  403. export function hasEvent(props, name) {
  404. name = 'on' + capitalize(name);
  405. return !!(props[name] || props[`${name}Once`] || props[`${name}Capture`] || props[`${name}OnceCapture`] || props[`${name}CaptureOnce`]);
  406. }
  407. export function callEvent(handler) {
  408. for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
  409. args[_key2 - 1] = arguments[_key2];
  410. }
  411. if (Array.isArray(handler)) {
  412. for (const h of handler) {
  413. h(...args);
  414. }
  415. } else if (typeof handler === 'function') {
  416. handler(...args);
  417. }
  418. }
  419. export function focusableChildren(el) {
  420. let filterByTabIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
  421. const targets = ['button', '[href]', 'input:not([type="hidden"])', 'select', 'textarea', '[tabindex]'].map(s => `${s}${filterByTabIndex ? ':not([tabindex="-1"])' : ''}:not([disabled])`).join(', ');
  422. return [...el.querySelectorAll(targets)];
  423. }
  424. export function getNextElement(elements, location, condition) {
  425. let _el;
  426. let idx = elements.indexOf(document.activeElement);
  427. const inc = location === 'next' ? 1 : -1;
  428. do {
  429. idx += inc;
  430. _el = elements[idx];
  431. } while ((!_el || _el.offsetParent == null || !(condition?.(_el) ?? true)) && idx < elements.length && idx >= 0);
  432. return _el;
  433. }
  434. export function focusChild(el, location) {
  435. const focusable = focusableChildren(el);
  436. if (!location) {
  437. if (el === document.activeElement || !el.contains(document.activeElement)) {
  438. focusable[0]?.focus();
  439. }
  440. } else if (location === 'first') {
  441. focusable[0]?.focus();
  442. } else if (location === 'last') {
  443. focusable.at(-1)?.focus();
  444. } else if (typeof location === 'number') {
  445. focusable[location]?.focus();
  446. } else {
  447. const _el = getNextElement(focusable, location);
  448. if (_el) _el.focus();else focusChild(el, location === 'next' ? 'first' : 'last');
  449. }
  450. }
  451. export function isEmpty(val) {
  452. return val === null || val === undefined || typeof val === 'string' && val.trim() === '';
  453. }
  454. export function noop() {}
  455. /** Returns null if the selector is not supported or we can't check */
  456. export function matchesSelector(el, selector) {
  457. const supportsSelector = IN_BROWSER && typeof CSS !== 'undefined' && typeof CSS.supports !== 'undefined' && CSS.supports(`selector(${selector})`);
  458. if (!supportsSelector) return null;
  459. try {
  460. return !!el && el.matches(selector);
  461. } catch (err) {
  462. return null;
  463. }
  464. }
  465. export function ensureValidVNode(vnodes) {
  466. return vnodes.some(child => {
  467. if (!isVNode(child)) return true;
  468. if (child.type === Comment) return false;
  469. return child.type !== Fragment || ensureValidVNode(child.children);
  470. }) ? vnodes : null;
  471. }
  472. export function defer(timeout, cb) {
  473. if (!IN_BROWSER || timeout === 0) {
  474. cb();
  475. return () => {};
  476. }
  477. const timeoutId = window.setTimeout(cb, timeout);
  478. return () => window.clearTimeout(timeoutId);
  479. }
  480. export function eagerComputed(fn, options) {
  481. const result = shallowRef();
  482. watchEffect(() => {
  483. result.value = fn();
  484. }, {
  485. flush: 'sync',
  486. ...options
  487. });
  488. return readonly(result);
  489. }
  490. export function isClickInsideElement(event, targetDiv) {
  491. const mouseX = event.clientX;
  492. const mouseY = event.clientY;
  493. const divRect = targetDiv.getBoundingClientRect();
  494. const divLeft = divRect.left;
  495. const divTop = divRect.top;
  496. const divRight = divRect.right;
  497. const divBottom = divRect.bottom;
  498. return mouseX >= divLeft && mouseX <= divRight && mouseY >= divTop && mouseY <= divBottom;
  499. }
  500. export function templateRef() {
  501. const el = shallowRef();
  502. const fn = target => {
  503. el.value = target;
  504. };
  505. Object.defineProperty(fn, 'value', {
  506. enumerable: true,
  507. get: () => el.value,
  508. set: val => el.value = val
  509. });
  510. Object.defineProperty(fn, 'el', {
  511. enumerable: true,
  512. get: () => refElement(el.value)
  513. });
  514. return fn;
  515. }
  516. export function checkPrintable(e) {
  517. const isPrintableChar = e.key.length === 1;
  518. const noModifier = !e.ctrlKey && !e.metaKey && !e.altKey;
  519. return isPrintableChar && noModifier;
  520. }
  521. //# sourceMappingURL=helpers.mjs.map