Skip to content

Commit f886707

Browse files
committed
feat: Added bootsrap utils full files - array and dom
1 parent a5f5e36 commit f886707

File tree

2 files changed

+371
-1
lines changed

2 files changed

+371
-1
lines changed

src/utils/array.js

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,140 @@
1+
// Production steps of ECMA-262, Edition 6, 22.1.2.1
2+
// es6-ified by @alexsasharegan
3+
if (!Array.from) {
4+
Array.from = (function () {
5+
const toStr = Object.prototype.toString
6+
const isCallable = fn => typeof fn === 'function' || toStr.call(fn) === '[object Function]'
7+
const toInteger = value => {
8+
const number = Number(value)
9+
if (isNaN(number)) {
10+
return 0
11+
}
12+
if (number === 0 || !isFinite(number)) {
13+
return number
14+
}
15+
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number))
16+
}
17+
const maxSafeInteger = Math.pow(2, 53) - 1
18+
const toLength = value => Math.min(Math.max(toInteger(value), 0), maxSafeInteger)
19+
20+
// The length property of the from method is 1.
21+
return function from (arrayLike /*, mapFn, thisArg */) {
22+
// 1. Let C be the this value.
23+
const C = this
24+
25+
// 2. Let items be ToObject(arrayLike).
26+
const items = Object(arrayLike)
27+
28+
// 3. ReturnIfAbrupt(items).
29+
if (arrayLike == null) {
30+
throw new TypeError('Array.from requires an array-like object - not null or undefined')
31+
}
32+
33+
// 4. If mapfn is undefined, then let mapping be false.
34+
const mapFn = arguments.length > 1 ? arguments[1] : void undefined
35+
let T
36+
37+
if (typeof mapFn !== 'undefined') {
38+
// 5. else
39+
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
40+
if (!isCallable(mapFn)) {
41+
throw new TypeError('Array.from: when provided, the second argument must be a function')
42+
}
43+
44+
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
45+
if (arguments.length > 2) {
46+
T = arguments[2]
47+
}
48+
}
49+
50+
// 10. Let lenValue be Get(items, "length").
51+
// 11. Let len be ToLength(lenValue).
52+
const len = toLength(items.length)
53+
54+
// 13. If IsConstructor(C) is true, then
55+
// 13. a. Let A be the result of calling the [[Construct]] internal method
56+
// of C with an argument list containing the single item len.
57+
// 14. a. Else, Let A be ArrayCreate(len).
58+
let A = isCallable(C) ? Object(new C(len)) : new Array(len)
59+
60+
// 16. Let k be 0.
61+
let k = 0
62+
// 17. Repeat, while k < len… (also steps a - h)
63+
let kValue
64+
while (k < len) {
65+
kValue = items[k]
66+
if (mapFn) {
67+
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k)
68+
} else {
69+
A[k] = kValue
70+
}
71+
k += 1
72+
}
73+
// 18. Let putStatus be Put(A, "length", len, true).
74+
A.length = len
75+
// 20. Return A.
76+
return A
77+
}
78+
})()
79+
}
80+
81+
// https://tc39.github.io/ecma262/#sec-array.prototype.find
82+
// Needed for IE support
83+
if (!Array.prototype.find) {
84+
// eslint-disable-next-line no-extend-native
85+
Object.defineProperty(Array.prototype, 'find', {
86+
value: function (predicate) {
87+
// 1. Let O be ? ToObject(this value).
88+
if (this == null) {
89+
throw new TypeError('"this" is null or not defined')
90+
}
91+
92+
const o = Object(this)
93+
94+
// 2. Let len be ? ToLength(? Get(O, "length")).
95+
const len = o.length >>> 0
96+
97+
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
98+
if (typeof predicate !== 'function') {
99+
throw new TypeError('predicate must be a function')
100+
}
101+
102+
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
103+
const thisArg = arguments[1]
104+
105+
// 5. Let k be 0.
106+
let k = 0
107+
108+
// 6. Repeat, while k < len
109+
while (k < len) {
110+
// a. Let Pk be ! ToString(k).
111+
// b. Let kValue be ? Get(O, Pk).
112+
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
113+
// d. If testResult is true, return kValue.
114+
const kValue = o[k]
115+
if (predicate.call(thisArg, kValue, k, o)) {
116+
return kValue
117+
}
118+
// e. Increase k by 1.
119+
k++
120+
}
121+
122+
// 7. Return undefined.
123+
return undefined
124+
}
125+
})
126+
}
127+
128+
if (!Array.isArray) {
129+
Array.isArray = arg => Object.prototype.toString.call(arg) === '[object Array]'
130+
}
131+
132+
// Static
133+
export const from = Array.from
134+
export const isArray = Array.isArray
135+
136+
// Instance
1137
export const arrayIncludes = (array, value) => array.indexOf(value) !== -1
2138
export function concat () {
3139
return Array.prototype.concat.apply([], arguments)
4140
}
5-
export const isArray = Array.isArray

src/utils/dom.js

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,246 @@
1+
import { from as arrayFrom } from './array'
2+
3+
// Determine if an element is an HTML Element
4+
export const isElement = el => {
5+
return el && el.nodeType === Node.ELEMENT_NODE
6+
}
7+
8+
// Determine if an HTML element is visible - Faster than CSS check
9+
export const isVisible = el => {
10+
/* istanbul ignore next: getBoundingClientRect not avaiable in JSDOM */
11+
return isElement(el) &&
12+
document.body.contains(el) &&
13+
el.getBoundingClientRect().height > 0 &&
14+
el.getBoundingClientRect().width > 0
15+
}
16+
17+
// Determine if an element is disabled
18+
export const isDisabled = el => {
19+
return !isElement(el) ||
20+
el.disabled ||
21+
el.classList.contains('disabled') ||
22+
Boolean(el.getAttribute('disabled'))
23+
}
24+
25+
// Cause/wait-for an element to reflow it's content (adjusting it's height/width)
26+
export const reflow = el => {
27+
// requsting an elements offsetHight will trigger a reflow of the element content
28+
/* istanbul ignore next: reflow doesnt happen in JSDOM */
29+
return isElement(el) && el.offsetHeight
30+
}
31+
32+
// Select all elements matching selector. Returns [] if none found
33+
export const selectAll = (selector, root) => {
34+
if (!isElement(root)) {
35+
root = document
36+
}
37+
return arrayFrom(root.querySelectorAll(selector))
38+
}
39+
40+
// Select a single element, returns null if not found
41+
export const select = (selector, root) => {
42+
if (!isElement(root)) {
43+
root = document
44+
}
45+
return root.querySelector(selector) || null
46+
}
47+
48+
// Determine if an element matches a selector
49+
export const matches = (el, selector) => {
50+
if (!isElement(el)) {
51+
return false
52+
}
53+
54+
// https://developer.mozilla.org/en-US/docs/Web/API/Element/matches#Polyfill
55+
// Prefer native implementations over polyfill function
56+
const proto = Element.prototype
57+
/* istanbul ignore next */
58+
const Matches = proto.matches ||
59+
proto.matchesSelector ||
60+
proto.mozMatchesSelector ||
61+
proto.msMatchesSelector ||
62+
proto.oMatchesSelector ||
63+
proto.webkitMatchesSelector ||
64+
function (sel) /* istanbul ignore next */ {
65+
const element = this
66+
const m = selectAll(sel, element.document || element.ownerDocument)
67+
let i = m.length
68+
// eslint-disable-next-line no-empty
69+
while (--i >= 0 && m.item(i) !== element) {}
70+
return i > -1
71+
}
72+
73+
return Matches.call(el, selector)
74+
}
75+
76+
// Finds closest element matching selector. Returns null if not found
77+
export const closest = (selector, root) => {
78+
if (!isElement(root)) {
79+
return null
80+
}
81+
82+
// https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
83+
// Since we dont support IE < 10, we can use the "Matches" version of the polyfill for speed
84+
// Prefer native implementation over polyfill function
85+
/* istanbul ignore next */
86+
const Closest = Element.prototype.closest ||
87+
function (sel) {
88+
let element = this
89+
if (!document.documentElement.contains(element)) {
90+
return null
91+
}
92+
do {
93+
// Use our "patched" matches function
94+
if (matches(element, sel)) {
95+
return element
96+
}
97+
element = element.parentElement
98+
} while (element !== null)
99+
return null
100+
}
101+
102+
const el = Closest.call(root, selector)
103+
// Emulate jQuery closest and return null if match is the passed in element (root)
104+
return el === root ? null : el
105+
}
106+
107+
// Returns true if the parent element contains the child element
108+
export const contains = (parent, child) => {
109+
if (!parent || typeof parent.contains !== 'function') {
110+
return false
111+
}
112+
return parent.contains(child)
113+
}
114+
115+
// Get an element given an ID
116+
export const getById = id => {
117+
return document.getElementById(/^#/.test(id) ? id.slice(1) : id) || null
118+
}
119+
120+
// Add a class to an element
1121
export const addClass = (el, className) => {
2122
if (className && isElement(el)) {
3123
el.classList.add(className)
4124
}
5125
}
6126

127+
// Remove a class from an element
7128
export const removeClass = (el, className) => {
8129
if (className && isElement(el)) {
9130
el.classList.remove(className)
10131
}
11132
}
133+
134+
// Test if an element has a class
135+
export const hasClass = (el, className) => {
136+
if (className && isElement(el)) {
137+
return el.classList.contains(className)
138+
}
139+
return false
140+
}
141+
142+
// Set an attribute on an element
143+
export const setAttr = (el, attr, value) => {
144+
if (attr && isElement(el)) {
145+
el.setAttribute(attr, value)
146+
}
147+
}
148+
149+
// Remove an attribute from an element
150+
export const removeAttr = (el, attr) => {
151+
if (attr && isElement(el)) {
152+
el.removeAttribute(attr)
153+
}
154+
}
155+
156+
// Get an attribute value from an element (returns null if not found)
157+
export const getAttr = (el, attr) => {
158+
if (attr && isElement(el)) {
159+
return el.getAttribute(attr)
160+
}
161+
return null
162+
}
163+
164+
// Determine if an attribute exists on an element (returns true or false, or null if element not found)
165+
export const hasAttr = (el, attr) => {
166+
if (attr && isElement(el)) {
167+
return el.hasAttribute(attr)
168+
}
169+
return null
170+
}
171+
172+
// Return the Bounding Client Rec of an element. Retruns null if not an element
173+
/* istanbul ignore next: getBoundingClientRect() doesnt work in JSDOM */
174+
export const getBCR = el => {
175+
return isElement(el) ? el.getBoundingClientRect() : null
176+
}
177+
178+
// Get computed style object for an element
179+
/* istanbul ignore next: getComputedStyle() doesnt work in JSDOM */
180+
export const getCS = el => {
181+
return isElement(el) ? window.getComputedStyle(el) : {}
182+
}
183+
184+
// Return an element's offset wrt document element
185+
// https://j11y.io/jquery/#v=git&fn=jQuery.fn.offset
186+
/* istanbul ignore next: getBoundingClientRect(), getClientRects() doesnt work in JSDOM */
187+
export const offset = el => {
188+
if (isElement(el)) {
189+
if (!el.getClientRects().length) {
190+
return { top: 0, left: 0 }
191+
}
192+
const bcr = getBCR(el)
193+
const win = el.ownerDocument.defaultView
194+
return {
195+
top: bcr.top + win.pageYOffset,
196+
left: bcr.left + win.pageXOffset
197+
}
198+
}
199+
}
200+
201+
// Return an element's offset wrt to it's offsetParent
202+
// https://j11y.io/jquery/#v=git&fn=jQuery.fn.position
203+
/* istanbul ignore next: getBoundingClientRect(), getClientRects() doesnt work in JSDOM */
204+
export const position = el => {
205+
if (!isElement(el)) {
206+
return
207+
}
208+
let parentOffset = { top: 0, left: 0 }
209+
let offsetSelf
210+
let offsetParent
211+
if (getCS(el).position === 'fixed') {
212+
offsetSelf = getBCR(el)
213+
} else {
214+
offsetSelf = offset(el)
215+
const doc = el.ownerDocument
216+
offsetParent = el.offsetParent || doc.documentElement
217+
while (offsetParent &&
218+
(offsetParent === doc.body || offsetParent === doc.documentElement) &&
219+
getCS(offsetParent).position === 'static') {
220+
offsetParent = offsetParent.parentNode
221+
}
222+
if (offsetParent && offsetParent !== el && offsetParent.nodeType === Node.ELEMENT_NODE) {
223+
parentOffset = offset(offsetParent)
224+
parentOffset.top += parseFloat(getCS(offsetParent).borderTopWidth)
225+
parentOffset.left += parseFloat(getCS(offsetParent).borderLeftWidth)
226+
}
227+
}
228+
return {
229+
top: offsetSelf.top - parentOffset.top - parseFloat(getCS(el).marginTop),
230+
left: offsetSelf.left - parentOffset.left - parseFloat(getCS(el).marginLeft)
231+
}
232+
}
233+
234+
// Attach an event listener to an element
235+
export const eventOn = (el, evtName, handler, options) => {
236+
if (el && el.addEventListener) {
237+
el.addEventListener(evtName, handler, options)
238+
}
239+
}
240+
241+
// Remove an event listener from an element
242+
export const eventOff = (el, evtName, handler, options) => {
243+
if (el && el.removeEventListener) {
244+
el.removeEventListener(evtName, handler, options)
245+
}
246+
}

0 commit comments

Comments
 (0)