Skip to content

Commit

Permalink
Work in progress:
Browse files Browse the repository at this point in the history
* added JSDoc for all files
* added strong TypeScript definitions
* Fixed some issue with `Offcanvas` not removing the overlay
* fixed an issue with `Dropdown`, focusing in after hiding the dropdown menu
* added an `Component.getInstance` static method to all components
* added components data like the original, requires doc updates
* added `defaults`, `name` and `version` methods to all components
* reworked the `initCallback` and `removeDataApi`
* all components expose an initialization method
* code cleanup

@fmasa please take a minute and check the TypeScript definitions whenever you get some time, report back anything wrong, this commit is dedicated to YOU!
  • Loading branch information
thednp committed Dec 12, 2021
1 parent 14e9aab commit 273a861
Show file tree
Hide file tree
Showing 202 changed files with 15,673 additions and 2,965 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ package-lock.json
.npmignore
.vscode/
node_modules/
local/
demo.html
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.vscode/
assets/
node_modules/
local/
CONTRIBUTING.md
package-lock.json
index.html
Expand Down
4 changes: 2 additions & 2 deletions assets/js/scripts-v4.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ var offCanvasCollapse = document.getElementsByClassName('offcanvas-collapse')[0]
// scrollTarget = /(EDGE|Mac)/i.test(navigator.userAgent) ? document.body : document.documentElement;
scrollTarget = document.documentElement;

sideLinks.map((x,i) => x.addEventListener('click', (e) => {
sideLinks.map(function(x,i) { x.addEventListener('click', function(e) {
var target = document.getElementById(x.getAttribute('href').replace('#', ''));
e.preventDefault();
scrollTarget.scrollTop = target.getBoundingClientRect().top + (window.pageYOffset || document.documentElement.scrollTop) - 70;
topNav.contains(x) && offCanvasCollapse.classList.toggle('open')
}))
})})
// offcanvas
document.querySelector('[data-toggle="offcanvas"]').addEventListener('click', function () {
offCanvasCollapse.classList.toggle('open')
Expand Down
39 changes: 29 additions & 10 deletions assets/js/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ function setOffset() {
mobileNavOffcanvas.style.top = '';
}
}
mobileNavOffcanvas.addEventListener('show.bs.offcanvas', () => {
mobileNavOffcanvas.addEventListener('show.bs.offcanvas', function(){
setOffset();
window.addEventListener('scroll', setOffset);
});
mobileNavOffcanvas.addEventListener('hidden.bs.offcanvas', () => {
mobileNavOffcanvas.addEventListener('hidden.bs.offcanvas', function(){
mobileNavOffcanvas.style.top = '';
window.removeEventListener('scroll', setOffset)

Expand All @@ -31,7 +31,7 @@ sideLinks.forEach( function(x,i) {x.addEventListener('click', function(e){
const offset = document.body.offsetWidth <= 768 ? 70 : 0;
e.preventDefault();
scrollTarget.scrollTop = target.getBoundingClientRect().top + (window.pageYOffset || document.documentElement.scrollTop) - offset;
if (mobileNav.contains(e.target)) mobileNavOffcanvas.Offcanvas.hide();
if (mobileNav.contains(e.target)) BSN.Offcanvas.getInstance(mobileNavOffcanvas).hide();
})})

// COMPONENTS
Expand Down Expand Up @@ -83,13 +83,13 @@ myModal.addEventListener('hidden.bs.modal', function (e) {

// Modal initialized with JavaScript
// wrap
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('DOMContentLoaded', function(){
var btnModal = document.getElementById('openModalViaJS');
var myModalJS = document.getElementById('myModalJS');
var modalInitJS = new BSN.Modal(myModalJS, {
backdrop: 'static'
});
btnModal.addEventListener('click', () => {
btnModal.addEventListener('click', function(){
modalInitJS.show();
}, false);
}, {once: true})
Expand Down Expand Up @@ -166,15 +166,17 @@ toastElement.addEventListener('hidden.bs.toast',function(e){
},false)

showToastBTN.addEventListener('click',function(){
toastElement.Toast ? toastElement.Toast.show() : console.log( 'DISPOSED!' )
},false)
const inst = BSN.Toast.getInstance(toastElement);
inst ? inst.show() : console.log( 'DISPOSED!' )
}, false)

// ScrollSpy
function toggleScrollSpy(){
var disposableSpy = document.getElementById('disposableSpy')
var disposableSpy = document.getElementById('disposableSpy');
var spyInstance = BSN.ScrollSpy.getInstance(disposableSpy);

if ( disposableSpy.ScrollSpy ){
disposableSpy.ScrollSpy.dispose()
if ( spyInstance ){
spyInstance.dispose()
this.innerHTML = 'Init'
this.classList.remove( 'btn-outline-danger' )
this.classList.add( 'btn-outline-primary' )
Expand Down Expand Up @@ -207,4 +209,21 @@ offcanvasExample.addEventListener('hidden.bs.offcanvas', function(e){
var related = e.relatedTarget;
var relatedTarget = '\nevent.relatedTarget is: ' + (related ? related.tagName + '.' + related.className.replace(/\s/,'.') : 'null');
console.log('The hidden.bs.offcanvas event fired for #' + offcanvasExample.id + relatedTarget);
}, false);

// carousel
const carouselGenericExample = document.getElementById('carouselGenericExample');
carouselGenericExample.addEventListener('slide.bs.carousel', function(e) {
var related = `\n> relatedTarget <div class="${Array.from(e.relatedTarget.classList).join(' ')}">\n`;
var from = `\n> from index ${e.from}`;
var to = `\n> to index ${e.to}`;
var direction = `\n> with direction ${e.direction}`;
console.log('The "slide.bs.carousel" event fired for <div id=' + carouselGenericExample.id + '"> ' + direction + from + to + related);
}, false);
carouselGenericExample.addEventListener('slid.bs.carousel', function(e) {
var related = `\n> relatedTarget <div class="${Array.from(e.relatedTarget.classList).join(' ')}">\n`;
var from = `\n> from index ${e.from}`;
var to = `\n> to index ${e.to}`;
var direction = `\n> with direction ${e.direction}`;
console.log('The "slid.bs.carousel" event fired for <div id=' + carouselGenericExample.id + '"> ' + direction + from + to + related);
}, false);
139 changes: 130 additions & 9 deletions dist/bootstrap-native-v4-esm.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,63 @@
/*!
* Native JavaScript for Bootstrap v4.0.8 (https://thednp.github.io/bootstrap.native/)
* Native JavaScript for Bootstrap v4.1.0 (https://thednp.github.io/bootstrap.native/)
* Copyright 2015-2021 © dnp_theme
* Licensed under MIT (https://github.com/thednp/bootstrap.native/blob/master/LICENSE)
*/
/**
* A global namespace for 'transitionend' string.
* @type {string}
*/
var transitionEndEvent = 'webkitTransition' in document.head.style ? 'webkitTransitionEnd' : 'transitionend';

/**
* A global namespace for CSS3 transition support.
* @type {boolean}
*/
var supportTransition = 'webkitTransition' in document.head.style || 'transition' in document.head.style;

var transitionDuration = 'webkitTransition' in document.head.style ? 'webkitTransitionDuration' : 'transitionDuration';
/**
* A global namespace for 'transitionDelay' string.
* @type {string}
*/
var transitionDelay = 'webkitTransition' in document.head.style ? 'webkitTransitionDelay' : 'transitionDelay';

/**
* A global namespace for 'transitionProperty' string.
* @type {string}
*/
var transitionProperty = 'webkitTransition' in document.head.style ? 'webkitTransitionProperty' : 'transitionProperty';

/**
* Utility to get the computed transitionDelay
* from Element in miliseconds.
*
* @param {Element} element target
* @return {number} the value in miliseconds
*/
function getElementTransitionDelay(element) {
var computedStyle = getComputedStyle(element);
var propertyValue = computedStyle[transitionProperty];
var delayValue = computedStyle[transitionDelay];
var delayScale = delayValue.includes('ms') ? 1 : 1000;
var duration = supportTransition && propertyValue && propertyValue !== 'none'
? parseFloat(delayValue) * delayScale : 0;

return !Number.isNaN(duration) ? duration : 0;
}

/**
* A global namespace for 'transitionDuration' string.
* @type {string}
*/
var transitionDuration = 'webkitTransition' in document.head.style ? 'webkitTransitionDuration' : 'transitionDuration';

/**
* Utility to get the computed transitionDuration
* from Element in miliseconds.
*
* @param {Element} element target
* @return {number} the value in miliseconds
*/
function getElementTransitionDuration(element) {
var computedStyle = getComputedStyle(element);
var propertyValue = computedStyle[transitionProperty];
Expand All @@ -22,32 +69,55 @@ function getElementTransitionDuration(element) {
return !Number.isNaN(duration) ? duration : 0;
}

/**
* Utility to make sure callbacks are consistently
* called when transition ends.
*
* @param {Element} element target
* @param {function} handler `transitionend` callback
*/
function emulateTransitionEnd(element, handler) {
var called = 0;
var endEvent = new Event(transitionEndEvent);
var duration = getElementTransitionDuration(element);
var delay = getElementTransitionDelay(element);

if (duration) {
element.addEventListener(transitionEndEvent, function transitionEndWrapper(e) {
/**
* Wrap the handler in on -> off callback
* @param {Event} e Event object
* @callback
*/
var transitionEndWrapper = function (e) {
if (e.target === element) {
handler.apply(element, [e]);
element.removeEventListener(transitionEndEvent, transitionEndWrapper);
called = 1;
}
});
};
element.addEventListener(transitionEndEvent, transitionEndWrapper);
setTimeout(function () {
if (!called) { element.dispatchEvent(endEvent); }
}, duration + 17);
}, duration + delay + 17);
} else {
handler.apply(element, [endEvent]);
}
}

/**
* Utility to check if target is typeof Element
* or find one that matches a selector.
*
* @param {Element | string} selector the input selector or target element
* @param {Element | null} parent optional Element to look into
* @return {Element | null} the Element or result of the querySelector
*/
function queryElement(selector, parent) {
var lookUp = parent && parent instanceof Element ? parent : document;
return selector instanceof Element ? selector : lookUp.querySelector(selector);
}

/** BSN v4 custom event */
function bootstrapCustomEvent(eventType, componentName, eventProperties) {
var OriginalCustomEvent = new CustomEvent((eventType + ".bs." + componentName), { cancelable: true });

Expand All @@ -61,6 +131,10 @@ function bootstrapCustomEvent(eventType, componentName, eventProperties) {
return OriginalCustomEvent;
}

/**
* A quick shortcut for `dispatchEvent` v4.
* @param {CustomEvent} customEvent the event object
*/
function dispatchCustomEvent(customEvent) {
if (this) { this.dispatchEvent(customEvent); }
}
Expand Down Expand Up @@ -288,12 +362,28 @@ function Button(elem) {
});
}

/**
* A global namespace for mouse hover events.
* @type {[string, string]}
*/
var mouseHoverEvents = ('onmouseleave' in document) ? ['mouseenter', 'mouseleave'] : ['mouseover', 'mouseout'];

/**
* A global namespace for 'addEventListener' string.
* @type {string}
*/
var addEventListener = 'addEventListener';

/**
* A global namespace for 'removeEventListener' string.
* @type {string}
*/
var removeEventListener = 'removeEventListener';

/**
* A global namespace for passive events support.
* @type {boolean}
*/
var supportPassive = (function () {
var result = false;
try {
Expand All @@ -315,15 +405,32 @@ var supportPassive = (function () {

// general event options

/**
* A global namespace for most scroll event listeners.
*/
var passiveHandler = supportPassive ? { passive: true } : false;

/**
* Utility to determine if an `Element`
* is partially visible in viewport.
*
* @param {Element} element target
* @return {boolean} Boolean
*/
function isElementInScrollRange(element) {
var bcr = element.getBoundingClientRect();
var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
return bcr.top <= viewportHeight && bcr.bottom >= 0; // bottom && top
}

/**
* Utility to force re-paint of an Element
*
* @param {Element | HTMLElement} element is the target
* @return {number} the Element.offsetHeight value
*/
function reflow(element) {
// @ts-ignore
return element.offsetHeight;
}

Expand Down Expand Up @@ -824,6 +931,10 @@ function Collapse(elem, opsInput) {
element.Collapse = self;
}

/**
* Points the focus to a specific element.
* @param {Element} element target
*/
function setFocus(element) {
element.focus();
}
Expand Down Expand Up @@ -1292,9 +1403,18 @@ function Modal(elem, opsInput) { // element can be the modal/triggering button
}
}

/**
* A global namespace for mouse click events.
* @type {{down: string, up: string}}
*/
var mouseClickEvents = { down: 'mousedown', up: 'mouseup' };

// Popover, Tooltip & ScrollSpy
/**
* Returns the `Window` / `HTML` scroll position.
* Popover, Tooltip & ScrollSpy need it.
*
* @returns {{x: number, y: number}} the scroll `{x,y}` values
*/
function getScroll() {
return {
y: window.pageYOffset || document.documentElement.scrollTop,
Expand Down Expand Up @@ -2344,9 +2464,10 @@ function Tooltip(elem, opsInput) {
element.Tooltip = self;
}

/** BSN v4 componentsInit */
var componentsInit = {};

/* Native JavaScript for Bootstrap | Initialize Data API
/* Native JavaScript for Bootstrap v4 | Initialize Data API
-------------------------------------------------------- */
function initializeDataAPI(Constructor, collection) {
Array.from(collection).map(function (x) { return new Constructor(x); });
Expand Down Expand Up @@ -2380,7 +2501,7 @@ else {
}, false);
}

/* Native JavaScript for Bootstrap | Remove Data API
/* Native JavaScript for Bootstrap v4 | Remove Data API
---------------------------------------------------- */
function removeElementDataAPI(ConstructorName, collection) {
Array.from(collection).map(function (x) { return x[ConstructorName].dispose(); });
Expand All @@ -2392,7 +2513,7 @@ function removeDataAPI(context) {
});
}

var version = "4.0.8";
var version = "4.1.0";

var Version = version;

Expand Down
4 changes: 2 additions & 2 deletions dist/bootstrap-native-v4-esm.min.js

Large diffs are not rendered by default.

Loading

3 comments on commit 273a861

@fmasa
Copy link
Contributor

@fmasa fmasa commented on 273a861 Dec 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thednp The types are great now 😮 Thank you very much!

The only thing that could be improved is types of some boolean arguments, such as 273a861#diff-961ded9e3d59514d6afb993f49c7fd2152561ceb2cabfaf3e5cec6a13bda6bfbR394

We can probably just use boolean instead of boolean | number to be a bit more strict. I was honestly confused as to what does providing numbers meant there as I expected some special behavior. I can submit PR to fix these few occurrences, if you want. 🙂

@thednp
Copy link
Owner Author

@thednp thednp commented on 273a861 Dec 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm considering that as well.

@thednp
Copy link
Owner Author

@thednp thednp commented on 273a861 Dec 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My work right now is focused on the bootstrapCustomEvent utility, I don't know how to make it output a specific component. I don't know much TypeScript to do the magic it needs.

Please sign in to comment.