Skip to content

Commit b49e5ac

Browse files
committed
added utilities based on bsv-2 logic
1 parent bef53c1 commit b49e5ac

File tree

2 files changed

+116
-4
lines changed

2 files changed

+116
-4
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
declare var WebKitMutationObserver: any
2+
declare var MozMutationObserver: any
3+
4+
export const HAS_WINDOW_SUPPORT = typeof window !== 'undefined'
5+
export const HAS_DOCUMENT_SUPPORT = typeof document !== 'undefined'
6+
export const HAS_NAVIGATOR_SUPPORT = typeof navigator !== 'undefined'
7+
export const HAS_PROMISE_SUPPORT = typeof Promise !== 'undefined'
8+
9+
/* istanbul ignore next: JSDOM always returns false */
10+
export const HAS_MUTATION_OBSERVER_SUPPORT =
11+
typeof MutationObserver !== 'undefined' ||
12+
typeof WebKitMutationObserver !== 'undefined' ||
13+
typeof MozMutationObserver !== 'undefined'
14+
15+
export const IS_BROWSER = HAS_WINDOW_SUPPORT && HAS_DOCUMENT_SUPPORT && HAS_NAVIGATOR_SUPPORT
16+
17+
export const WINDOW = HAS_WINDOW_SUPPORT ? window : ({} as Record<string, any>)
18+
export const DOCUMENT = HAS_DOCUMENT_SUPPORT ? document : ({} as Record<string, any>)
19+
export const NAVIGATOR = HAS_NAVIGATOR_SUPPORT ? navigator : ({} as Record<string, any>)
20+
export const USER_AGENT = (NAVIGATOR.userAgent || '').toLowerCase()
21+
22+
export const IS_JSDOM = USER_AGENT.indexOf('jsdom') > 0
23+
export const IS_IE = /msie|trident/.test(USER_AGENT)
24+
25+
// Determine if the browser supports the option passive for events
26+
export const HAS_PASSIVE_EVENT_SUPPORT = (() => {
27+
let passiveEventSupported = false
28+
if (IS_BROWSER) {
29+
try {
30+
const options = {
31+
// This function will be called when the browser
32+
// attempts to access the passive property
33+
get passive() {
34+
/* istanbul ignore next: will never be called in JSDOM */
35+
passiveEventSupported = true
36+
return passiveEventSupported
37+
},
38+
}
39+
WINDOW.addEventListener('test', options, options)
40+
WINDOW.removeEventListener('test', options, options)
41+
} catch {
42+
/* istanbul ignore next: will never be called in JSDOM */
43+
passiveEventSupported = false
44+
}
45+
}
46+
return passiveEventSupported
47+
})()
48+
49+
export const HAS_TOUCH_SUPPORT =
50+
IS_BROWSER && ('ontouchstart' in DOCUMENT.documentElement || NAVIGATOR.maxTouchPoints > 0)
51+
52+
export const HAS_POINTER_EVENT_SUPPORT =
53+
IS_BROWSER && Boolean(WINDOW.PointerEvent || WINDOW.MSPointerEvent)
54+
55+
/* istanbul ignore next: JSDOM only checks for 'IntersectionObserver' */
56+
export const HAS_INTERACTION_OBSERVER_SUPPORT =
57+
IS_BROWSER &&
58+
'IntersectionObserver' in WINDOW &&
59+
'IntersectionObserverEntry' in WINDOW &&
60+
// Edge 15 and UC Browser lack support for `isIntersecting`
61+
// but we an use `intersectionRatio > 0` instead
62+
// 'isIntersecting' in window.IntersectionObserverEntry.prototype &&
63+
'intersectionRatio' in WINDOW.IntersectionObserverEntry.prototype

packages/bootstrap-vue-3/src/utils/dom.ts

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
1-
import {AnimationFrame, DOCUMENT} from '../types/safeTypes'
1+
import {Comment, Slot, VNode} from 'vue'
2+
import {DOCUMENT} from '../constants/env'
3+
import {AnimationFrame} from '../types/safeTypes'
24
import {HAS_WINDOW_SUPPORT} from './env'
35
import {toString} from './stringUtils'
4-
import {Comment, Slot, VNode} from 'vue'
6+
7+
const ELEMENT_PROTO = Element.prototype
8+
9+
// See: https://developer.mozilla.org/en-US/docs/Web/API/Element/matches#Polyfill
10+
/* istanbul ignore next */
11+
export const matchesEl =
12+
ELEMENT_PROTO.matches ||
13+
(ELEMENT_PROTO as any).msMatchesSelector ||
14+
ELEMENT_PROTO.webkitMatchesSelector
515

616
/**
717
* @param el
818
* @returns
919
*/
10-
export const isElement = (el: HTMLElement): boolean => !!(el && el.nodeType === Node.ELEMENT_NODE)
20+
export const isElement = (el: HTMLElement | Element): boolean =>
21+
!!(el && el.nodeType === Node.ELEMENT_NODE)
1122

1223
/**
1324
* @param el
@@ -148,9 +159,13 @@ export const selectAll = (selector: any, root: any) =>
148159
* @param attr
149160
* @returns
150161
*/
151-
export const getAttr = (el: HTMLElement, attr: string): string | null =>
162+
export const getAttr = (el: HTMLElement | Element, attr: string): string | null =>
152163
attr && isElement(el) ? el.getAttribute(attr) : null
153164

165+
// Get an element given an ID
166+
export const getById = (id: string) =>
167+
DOCUMENT.getElementById(/^#/.test(id) ? id.slice(1) : id) || null
168+
154169
/**
155170
* @param el
156171
* @param attr
@@ -192,3 +207,37 @@ export const requestAF: AnimationFrame = HAS_WINDOW_SUPPORT
192207
// Only needed for Opera Mini
193208
((cb) => setTimeout(cb, 16))
194209
: (cb) => setTimeout(cb, 0)
210+
211+
// Determine if an element matches a selector
212+
export const matches = (el: Element, selector: string) =>
213+
isElement(el) ? matchesEl.call(el, selector) : false
214+
215+
// See: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
216+
/* istanbul ignore next */
217+
export const closestEl =
218+
ELEMENT_PROTO.closest ||
219+
function (this: Element, sel: string) {
220+
let el = this
221+
if (!el) return null
222+
do {
223+
// Use our "patched" matches function
224+
if (matches(el, sel)) {
225+
return el
226+
}
227+
el = el.parentElement || (el.parentNode as any)
228+
} while (el !== null && el.nodeType === Node.ELEMENT_NODE)
229+
return null
230+
}
231+
232+
// Finds closest element matching selector. Returns `null` if not found
233+
export const closest = (selector: string, root: Element, includeRoot = false) => {
234+
if (!isElement(root)) {
235+
return null
236+
}
237+
const el = closestEl.call(root, selector)
238+
239+
// Native closest behaviour when `includeRoot` is truthy,
240+
// else emulate jQuery closest and return `null` if match is
241+
// the passed in root element when `includeRoot` is falsey
242+
return includeRoot ? el : el === root ? null : el
243+
}

0 commit comments

Comments
 (0)