diff --git a/CHANGELOG.md b/CHANGELOG.md
index e5d439453..c550c5a8e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,14 @@
-## Unreleased
+## 0.3.0
+
+### Improvements
+- Removed ~ 230 LOC from calcite-web.js
### Fixed
- Fixed interpolation of variables in the `keyframes` mixin
+- Fixed modals always closing on any click if open
+- Document modals properly
-### Modifide
+### Modified
- Increase large breakpoint to 1450px
- add `extra-large-leader-n`
- trailer
@@ -13,6 +18,9 @@
- Remove `container-max` and `container-min`
- `extra-large-hide` and `-only`
+### Removed
+- remove carousel pattern (will be its own project)
+
## 0.2.3
### Modified
diff --git a/docs/source/patterns/_carousel.md b/docs/source/patterns/_carousel.md
deleted file mode 100644
index 355dfa7ed..000000000
--- a/docs/source/patterns/_carousel.md
+++ /dev/null
@@ -1,9 +0,0 @@
-## Carousel
-
-Calcite Web comes with a very lightweight carousel (or slider). We urge you to use carousels very sparingly as [several](http://erikrunyon.com/2013/07/carousel-interaction-stats/) [reports](http://www.nngroup.com/articles/auto-forwarding/) [show](http://www.widerfunnel.com/conversion-rate-optimization/rotating-offers-the-scourge-of-home-page-design) that carousels can be detrimental to user experience.
-
-If you decide a carousel *is* the best experience for your use case, the Calcite Web carousel is very straightforward. Simply create a div with the class of `carousel` with any number of elements that have a class of `carousel-slide`. The carousel uses [calcite-web.js](../javascript) for the interactive elements. For this reason you'll also need to add a class of `js-carousel` to add JavaScript functionality.
-
-Navigation for the carousel can be any link inside the carousel wrapper. Simply add a `js-carousel-link` class and a `data-slide` attribute with a number representing the slide number the link should navigate to. Numbering starts at 1.
-
-> Note: it's recommended to set a height on the `carousel-slides` element to make sure it will fit your content vertically.
\ No newline at end of file
diff --git a/docs/source/patterns/_modals.md b/docs/source/patterns/_modals.md
index 931e720b1..361e63784 100644
--- a/docs/source/patterns/_modals.md
+++ b/docs/source/patterns/_modals.md
@@ -1,3 +1,7 @@
## Modals
-Modals are meant to "take over" the screen and focus users attention on a dialog which presents the user with an opportunity to add, modify or create content. A modal should always be centered both vertically and horizontally within the browser window. When a modal is opened, the interface darkens and disables all other user interface elements in order to force a user to take an action required by their workflow.
\ No newline at end of file
+Modals are meant to "take over" the screen and focus users attention on a dialog which presents the user with an opportunity to add, modify or create content. A modal should always be centered both vertically and horizontally within the browser window. When a modal is opened, the interface darkens and disables all other user interface elements in order to force a user to take an action required by their workflow. Two modals can't be open at once.
+
+To create a link or button that opens a modal, you must add a `js-modal-toggle` class to the element, along with a `data-modal` attribute specifying the name of the modal that should open. The modal should also get a `data-modal` attribute with the same name.
+
+Elements with the `js-modal-toggle` that are inside a modal don't need the `data-modal` attribute as they will just close the modal they are in.
diff --git a/docs/source/patterns/sample-code/_carousel.html b/docs/source/patterns/sample-code/_carousel.html
deleted file mode 100644
index da582ade7..000000000
--- a/docs/source/patterns/sample-code/_carousel.html
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
- «
- »
-
-
-
-
-
Slide One
-
-
-
Slide Two
-
-
-
Slide Three
-
-
-
Slide Four
-
-
-
Slide Five
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/docs/source/patterns/sample-code/_modals.html b/docs/source/patterns/sample-code/_modals.html
index 8fd3e56c4..2d8336052 100644
--- a/docs/source/patterns/sample-code/_modals.html
+++ b/docs/source/patterns/sample-code/_modals.html
@@ -9,9 +9,9 @@
Modal!
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
-
-
+
+
-Show Modal
+Show Modal
diff --git a/docs/source/table_of_contents.yml b/docs/source/table_of_contents.yml
index 8df607884..b3c2cf45e 100644
--- a/docs/source/table_of_contents.yml
+++ b/docs/source/table_of_contents.yml
@@ -515,10 +515,6 @@ patterns:
id: '93e95ff8-0ad4-478b-b1c4-d87a34486b05'
link: accordions
modifiers: true
- - title: 'Carousels'
- id: 'c5ffc172-f526-4952-a18f-9f06a7d9724c'
- link: carousel
- modifiers: true
- title: 'Drawers'
id: 'e538b8cd-ce5e-4f86-823a-ebb5ec041039'
link: drawers
diff --git a/lib/js/calcite-web.js b/lib/js/calcite-web.js
index 57563e3c0..53971035f 100644
--- a/lib/js/calcite-web.js
+++ b/lib/js/calcite-web.js
@@ -1,668 +1,438 @@
(function Calcite () {
-var calcite = {
- version: '0.0.9'
-};
-
-// ┌───────────────┐
-// │ DOM Utilities │
-// └───────────────┘
-
-calcite.dom = {};
-
-// ┌──────────────────────┐
-// │ DOM Event Management │
-// └──────────────────────┘
-
-// returns standard interaction event, later will add touch support
-calcite.dom.event = function () {
- return 'click';
-};
-
-// add a callback function to an event on a DOM node
-calcite.dom.addEvent = function (domNode, event, fn) {
- if (domNode.addEventListener) {
- return domNode.addEventListener(event, fn, false);
- }
- if (domNode.attachEvent) {
- return domNode.attachEvent('on' + event, fn);
- }
-};
-
-// remove a specific function binding from a DOM node event
-calcite.dom.removeEvent = function (domNode, event, fn) {
- if (domNode.removeEventListener) {
- return domNode.removeEventListener(event, fn, false);
- }
- if (domNode.detachEvent) {
- return domNode.detachEvent('on' + event, fn);
- }
-};
-
-// get the target element of an event
-calcite.dom.eventTarget = function (event) {
- if (!event.target) {
- return event.srcElement;
- }
- if (event.target) {
- return event.target;
- }
-};
-
-// prevent default behavior of an event
-calcite.dom.preventDefault = function (event) {
- if (event.preventDefault) {
- return event.preventDefault();
- }
- if (event.returnValue) {
- event.returnValue = false;
- }
-};
-
-// stop and event from bubbling up the DOM tree
-calcite.dom.stopPropagation = function (event) {
- event = event || window.event;
- if (event.stopPropagation) {
- return event.stopPropagation();
- }
- if (event.cancelBubble) {
- event.cancelBubble = true;
- }
-};
+ // ┌────────────┐
+ // │ Public API │
+ // └────────────┘
+ // define all public api methods (excluding patterns)
+ var calcite = {
+ version: 'v0.2.2',
+ click: click,
+ addEvent: addEvent,
+ removeEvent: removeEvent,
+ eventTarget: eventTarget,
+ preventDefault: preventDefault,
+ stopPropagation: stopPropagation,
+ hasClass: hasClass,
+ addClass: addClass,
+ removeClass: removeClass,
+ closest: closest,
+ getAttr: getAttr,
+ nodeListToArray: nodeListToArray,
+ patterns: {},
+ init: init
+ };
-// ┌────────────────────┐
-// │ Class Manipulation │
-// └────────────────────┘
+ // ┌──────────────────────┐
+ // │ DOM Event Management │
+ // └──────────────────────┘
-// check if an element has a specific class
-calcite.dom.hasClass = function (domNode, className) {
- var exp = new RegExp(' ' + className + ' ');
- if (exp.test(' ' + domNode.className + ' ')) {
- return true;
+ // returns standard interaction event, later will add touch support
+ function click () {
+ return 'click';
}
- return false;
-};
-
-// add one or more classes to an element
-calcite.dom.addClass = function (domNode, classes) {
- classes = classes.split(' ');
-
- for (var i = 0; i < classes.length; i++) {
- if (!calcite.dom.hasClass(domNode, classes[i])) {
- domNode.className += ' ' + classes[i];
+ // add a callback function to an event on a DOM node
+ function addEvent (domNode, e, fn) {
+ if (domNode.addEventListener) {
+ return domNode.addEventListener(e, fn, false);
+ } else if (domNode.attachEvent) {
+ return domNode.attachEvent('on' + e, fn);
}
}
-};
-
-// remove one or more classes from an element
-calcite.dom.removeClass = function (domNode, classes) {
- classes = classes.split(' ');
-
- for (var i = 0; i < classes.length; i++) {
- var newClass = ' ' + domNode.className.replace( /[\t\r\n]/g, ' ') + ' ';
- if (calcite.dom.hasClass(domNode, classes[i])) {
- while (newClass.indexOf(' ' + classes[i] + ' ') >= 0) {
- newClass = newClass.replace(' ' + classes[i] + ' ', ' ');
- }
-
- domNode.className = newClass.replace(/^\s+|\s+$/g, '');
+ // remove a specific function binding from a DOM node event
+ function removeEvent (domNode, e, fn) {
+ if (domNode.removeEventListener) {
+ return domNode.removeEventListener(e, fn, false);
+ } else if (domNode.detachEvent) {
+ return domNode.detachEvent('on' + e, fn);
}
}
-};
-
-// ┌───────────────┐
-// │ DOM Traversal │
-// └───────────────┘
-
-// returns closest element up the DOM tree matching a given class
-calcite.dom.closest = function (className, context) {
- var result, current;
- for (current = context; current; current = current.parentNode) {
- if (current.nodeType === 1 && calcite.dom.hasClass(current, className)) {
- result = current;
- break;
- }
- }
- return current;
-};
-// get an attribute for an element
-calcite.dom.getAttr = function(domNode, attr) {
- if (domNode.getAttribute) {
- return domNode.getAttribute(attr);
+ // get the target element of an event
+ function eventTarget (e) {
+ return e.target || e.srcElement;
}
- var result;
- var attrs = domNode.attributes;
-
- for (var i = 0; i < attrs.length; i++) {
- if (attrs[i].nodeName === attr) {
- result = attrs[i].nodeValue;
+ // prevent default behavior of an event
+ function preventDefault (e) {
+ if (e.preventDefault) {
+ return e.preventDefault();
+ } else if (e.returnValue) {
+ e.returnValue = false;
}
}
- return result;
-};
-
-// ┌───────────────────┐
-// │ Object Conversion │
-// └───────────────────┘
-
-// turn a domNodeList into an array
-calcite.dom.nodeListToArray = function (domNodeList) {
- var array = [];
- for (var i = 0; i < domNodeList.length; i++) {
- array.push(domNodeList[i]);
- }
- return array;
-};
-
-// ┌────────────────────┐
-// │ Array Manipulation │
-// └────────────────────┘
-
-calcite.arr = {};
-
-// return the index of an object in an array with optional offset
-calcite.arr.indexOf = function (obj, arr, offset) {
- var i = offset || 0;
-
- if (arr.indexOf) {
- return arr.indexOf(obj, i);
- }
-
- for (i; i < arr.length; i++) {
- if (arr[i] === obj) {
- return i;
+ // stop and event from bubbling up the DOM tree
+ function stopPropagation (e) {
+ e = e || window.event;
+ if (e.stopPropagation) {
+ return e.stopPropagation();
+ }
+ if (e.cancelBubble) {
+ e.cancelBubble = true;
}
}
- return -1;
-};
-
-// ┌───────────────────────────┐
-// │ Browser Feature Detection │
-// └───────────────────────────┘
-// detect features like touch, ie, etc.
-
-calcite.browser = {};
-
-// detect touch, could be improved for more coverage
-calcite.browser.isTouch = function () {
- if (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0)) {
- return true;
- }
- return false;
-};
-
-// ┌─────────────┐
-// │ JS Patterns │
-// └─────────────┘
-// javascript logic for ui patterns
-
-function findElements (className) {
- var elements = document.querySelectorAll(className);
- if (elements.length) {
- return calcite.dom.nodeListToArray(elements);
- } else {
- return false;
- }
-}
+ // ┌────────────────────┐
+ // │ Class Manipulation │
+ // └────────────────────┘
-// remove 'is-active' class from every element in an array
-function removeActive (array) {
- if (typeof array == 'object') {
- array = calcite.dom.nodeListToArray(array);
+ // check if an element has a specific class
+ function hasClass (domNode, className) {
+ var elementClass = ' ' + domNode.className + ' ';
+ return elementClass.indexOf(' ' + className + ' ') !== -1;
}
- array.forEach(function (item) {
- calcite.dom.removeClass(item, 'is-active');
- });
-}
-
-// remove 'is-active' from array, add to element
-function toggleActive (array, el) {
- var isActive = calcite.dom.hasClass(el, 'is-active');
- if (isActive) {
- calcite.dom.removeClass(el, 'is-active');
- } else {
- removeActive(array);
- calcite.dom.addClass(el, 'is-active');
- }
-}
-
-// ┌───────────┐
-// │ Accordion │
-// └───────────┘
-// collapsible accordion list
-calcite.accordion = function () {
- var accordions = findElements('.js-accordion');
-
- if (!accordions) {
- return;
+ // add one or more classes to an element
+ function addClass (domNode, classes) {
+ classes.split(' ').forEach(function (c) {
+ if (!hasClass(domNode, c)) {
+ domNode.className += ' ' + c;
+ }
+ });
}
- for (var i = 0; i < accordions.length; i++) {
- var children = accordions[i].children;
- for (var j = 0; j < children.length; j++) {
- calcite.dom.addEvent(children[j], calcite.dom.event(), toggleAccordion);
- }
+ // remove one or more classes from an element
+ function removeClass (domNode, classes) {
+ var elementClass = ' ' + domNode.className + ' ';
+ classes.split(' ').forEach(function (c) {
+ elementClass = elementClass.replace(' ' + c + ' ', ' ');
+ });
+ domNode.className = elementClass.trim();
}
- function toggleAccordion (event) {
- var parent = calcite.dom.closest('accordion-section', calcite.dom.eventTarget(event));
- if (calcite.dom.hasClass(parent, 'is-active')) {
- calcite.dom.removeClass(parent, 'is-active');
+ // if domNode has the class, remove it, else add it
+ function toggleClass (domNode, className) {
+ if (hasClass(domNode, className)) {
+ removeClass(domNode, className);
} else {
- calcite.dom.addClass(parent, 'is-active');
+ addClass(domNode, className);
}
}
-};
+ // ┌─────┐
+ // │ DOM │
+ // └─────┘
-// ┌──────────┐
-// │ Carousel │
-// └──────────┘
-// show carousel with any number of slides
-
-calcite.carousel = function () {
-
- var carousels = findElements('.js-carousel');
-
- if (!carousels) {
- return;
+ // returns closest element up the DOM tree matching a given class
+ function closest (className, context) {
+ var result, current;
+ for (current = context; current; current = current.parentNode) {
+ if (current.nodeType === 1 && hasClass(current, className)) {
+ result = current;
+ break;
+ }
+ }
+ return current;
}
- for (var i = 0; i < carousels.length; i++) {
-
- var carousel = carousels[i];
- var wrapper = carousel.querySelectorAll('.carousel-slides')[0];
- var slides = carousel.querySelectorAll('.carousel-slide');
- var toggles = calcite.dom.nodeListToArray(carousel.querySelectorAll('.js-carousel-link'));
-
- wrapper.style.width = slides.length * 100 + '%';
-
- calcite.dom.addClass(slides[0], 'is-active');
- calcite.dom.addClass(carousel, 'is-first-slide');
-
- for (var k = 0; k < slides.length; k++) {
- slides[k].style.width = 100 / slides.length + '%';
+ // get an attribute for an element
+ function getAttr (domNode, attr) {
+ if (domNode.getAttribute) {
+ return domNode.getAttribute(attr);
}
- for (var j = 0; j < toggles.length; j++) {
- calcite.dom.addEvent(toggles[j], calcite.dom.event(), toggleSlide);
+ var result;
+ var attrs = domNode.attributes;
+
+ for (var i = 0; i < attrs.length; i++) {
+ if (attrs[i].nodeName === attr) {
+ result = attrs[i].nodeValue;
+ }
}
+ return result;
}
- function toggleSlide (e) {
- calcite.dom.preventDefault(e);
- var link = calcite.dom.eventTarget(e);
- var index = calcite.dom.getAttr(link, 'data-slide');
- var carousel = calcite.dom.closest('carousel', link);
- var current = carousel.querySelectorAll('.carousel-slide.is-active')[0];
- var slides = carousel.querySelectorAll('.carousel-slide');
- var wrapper = carousel.querySelectorAll('.carousel-slides')[0];
-
- if (index == 'prev') {
- index = calcite.arr.indexOf(current, slides);
- if (index === 0) { index = 1; }
- } else if (index == 'next') {
- index = calcite.arr.indexOf(current, slides) + 2;
- if (index > slides.length) { index = slides.length; }
+ // turn a domNodeList into an array
+ function nodeListToArray (domNodeList) {
+ var array = [];
+ for (var i = 0; i < domNodeList.length; i++) {
+ array.push(domNodeList[i]);
}
-
- calcite.dom.removeClass(carousel, 'is-first-slide is-last-slide');
-
- if (index == slides.length) { calcite.dom.addClass(carousel, 'is-last-slide');}
- if (index == 1) { calcite.dom.addClass(carousel, 'is-first-slide');}
-
- removeActive(slides);
- calcite.dom.addClass(slides[index - 1], 'is-active');
- var offset = (index - 1)/slides.length * -100 + '%';
- wrapper.style.transform= 'translate3d(' + offset + ',0,0)';
+ return array;
}
-};
-
-// ┌──────────┐
-// │ Dropdown │
-// └──────────┘
-// show and hide dropdown menus
+ // ┌─────────────┐
+ // │ JS Patterns │
+ // └─────────────┘
+ // helper functions for ui patterns
-calcite.dropdown = function () {
- var toggles = findElements('.js-dropdown-toggle');
- var dropdowns = findElements('.js-dropdown');
- if (!dropdowns) {
- return;
+ // return an array of elements matching a query
+ function findElements (query) {
+ var elements = document.querySelectorAll(query);
+ return nodeListToArray(elements);
}
- function closeAllDropdowns () {
- for (var i = 0; i < dropdowns.length; i++) {
- calcite.dom.removeClass(dropdowns[i], 'is-active');
+ // remove 'is-active' class from every element in an array
+ function removeActive (array) {
+ if (typeof array == 'object') {
+ array = nodeListToArray(array);
}
+ array.forEach(function (item) {
+ removeClass(item, 'is-active');
+ });
}
- function toggleDropdown (dropdown) {
- var isActive = calcite.dom.hasClass(dropdown, 'is-active');
+ // remove 'is-active' from array, add to element
+ function toggleActive (array, el) {
+ var isActive = hasClass(el, 'is-active');
if (isActive) {
- calcite.dom.removeClass(dropdown, 'is-active');
+ removeClass(el, 'is-active');
} else {
- closeAllDropdowns();
- calcite.dom.addClass(dropdown, 'is-active');
- calcite.dom.addEvent(document.body, calcite.dom.event(), function(event) {
- closeAllDropdowns();
- });
+ removeActive(array);
+ addClass(el, 'is-active');
}
}
- function bindDropdown (toggle) {
- calcite.dom.addEvent(toggle, calcite.dom.event(), function(event) {
- calcite.dom.preventDefault(event);
- calcite.dom.stopPropagation(event);
- var dropdown = calcite.dom.closest('js-dropdown', toggle);
- toggleDropdown(dropdown);
+ // ┌───────────┐
+ // │ Accordion │
+ // └───────────┘
+ // collapsible accordion list
+ calcite.patterns.accordion = function () {
+ findElements('.js-accordion').forEach(function (accordion) {
+ nodeListToArray(accordion.children).forEach(function (child) {
+ addEvent(child, click(), toggleAccordion);
+ });
});
- }
- for (var i = 0; i < toggles.length; i++) {
- bindDropdown(toggles[i]);
- }
-};
-
-// ┌────────┐
-// │ Drawer │
-// └────────┘
-// show and hide drawers
-calcite.drawer = function () {
+ function toggleAccordion (e) {
+ var parent = closest('accordion-section', eventTarget(e));
+ toggleClass(parent, 'is-active');
+ }
+ };
- var toggles = findElements('.js-drawer-toggle');
- var drawers = findElements('.js-drawer');
+ // ┌──────────┐
+ // │ Dropdown │
+ // └──────────┘
+ // show and hide dropdown menus
+ calcite.patterns.dropdown = function () {
+ var toggles = findElements('.js-dropdown-toggle');
+ var dropdowns = findElements('.js-dropdown');
+
+ function closeAllDropdowns () {
+ removeEvent(document.body, click(), closeAllDropdowns);
+ dropdowns.forEach(function (dropdown) {
+ removeClass(dropdown, 'is-active');
+ });
+ }
- if (!drawers) {
- return;
- }
+ function bindToggle (toggle) {
+ addEvent(toggle, click(), function (e) {
+ preventDefault(e);
+ stopPropagation(e);
+ var dropdown = closest('js-dropdown', toggle);
+ closeAllDropdowns();
+ addClass(dropdown, 'is-active');
+ addEvent(document.body, click(), closeAllDropdowns);
+ });
+ }
- function bindToggle (toggle) {
- calcite.dom.addEvent(toggle, calcite.dom.event(), function(event) {
- calcite.dom.preventDefault(event);
- var target = calcite.dom.getAttr(toggle, 'data-drawer');
- for (var i = 0; i < drawers.length; i++) {
- var drawer = drawers[i];
- var isTarget = calcite.dom.getAttr(drawers[i], 'data-drawer');
- if (target == isTarget) {
- toggleActive(drawers, drawer);
- }
- }
- });
- }
+ toggles.forEach(bindToggle);
+ };
- function bindDrawer (drawer) {
- calcite.dom.addEvent(drawer, calcite.dom.event(), function(event) {
- if (calcite.dom.hasClass(event.target, 'drawer')) {
+ // ┌────────┐
+ // │ Drawer │
+ // └────────┘
+ // show and hide drawers
+ calcite.patterns.drawer = function () {
+ var toggles = findElements('.js-drawer-toggle');
+ var drawers = findElements('.js-drawer');
+
+ toggles.forEach(function (toggle) {
+ addEvent(toggle, click(), function (e) {
+ preventDefault(e);
+ var drawerId = getAttr(toggle, 'data-drawer');
+ var drawer = document.querySelector('.js-drawer[data-drawer="' + drawerId + '"');
toggleActive(drawers, drawer);
- }
+ });
});
- }
- for (var i = 0; i < toggles.length; i++) {
- bindToggle(toggles[i]);
- }
- for (var j = 0; j < drawers.length; j++) {
- bindDrawer(drawers[j]);
- }
-};
-
-// ┌───────────────┐
-// │ Expanding Nav │
-// └───────────────┘
-// show and hide exanding nav located under topnav
-calcite.expandingNav = function () {
- var toggles = findElements('.js-expanding-toggle');
- var expanders = findElements('.js-expanding');
-
- if (!expanders) {
- return;
- }
-
- function bindToggle (toggle) {
- calcite.dom.addEvent(toggle, calcite.dom.event(), function(event) {
- calcite.dom.preventDefault(event);
-
- var sectionName = calcite.dom.getAttr(toggle, 'data-expanding-nav');
- var sections = document.querySelectorAll('.js-expanding-nav');
- var section = document.querySelectorAll('.js-expanding-nav[data-expanding-nav="' + sectionName + '"]')[0];
- var expander = calcite.dom.closest('js-expanding', section);
- var isOpen = calcite.dom.hasClass(expander, 'is-active');
- var shouldClose = calcite.dom.hasClass(section, 'is-active');
-
- if (isOpen) {
- if (shouldClose) {
- calcite.dom.removeClass(expander, 'is-active');
+ drawers.forEach(function (drawer) {
+ addEvent(drawer, click(), function (e) {
+ if (hasClass(eventTarget(e), 'drawer')) {
+ toggleActive(drawers, drawer);
}
- toggleActive(sections, section);
- } else {
- toggleActive(sections, section);
- calcite.dom.addClass(expander, 'is-active');
- }
-
+ });
});
- }
-
- for (var i = 0; i < toggles.length; i++) {
- bindToggle(toggles[i]);
- }
-};
-
-// ┌───────┐
-// │ Modal │
-// └───────┘
-// show and hide modal dialogues
-
-calcite.modal = function () {
+ };
- var toggles = findElements('.js-modal-toggle');
- var modals = findElements('.js-modal');
+ // ┌───────────────┐
+ // │ Expanding Nav │
+ // └───────────────┘
+ // show and hide exanding nav located under topnav
+ calcite.patterns.expandingNav = function () {
+ var toggles = findElements('.js-expanding-toggle');
+ var expanders = findElements('.js-expanding');
+ var sections = document.querySelectorAll('.js-expanding-nav');
+
+ toggles.forEach(function (toggle) {
+ addEvent(toggle, click(), function (e) {
+ preventDefault(e);
+
+ var sectionId = getAttr(toggle, 'data-expanding-nav');
+ var section = document.querySelector('.js-expanding-nav[data-expanding-nav="' + sectionId + '"]');
+ var expander = closest('js-expanding', section);
+ var isOpen = hasClass(expander, 'is-active');
+ var shouldClose = hasClass(section, 'is-active');
- if (!modals) {
- return;
- }
+ toggleActive(sections, section);
- function bindToggle (toggle) {
- calcite.dom.addEvent(toggle, calcite.dom.event(), function(event) {
- calcite.dom.preventDefault(event);
- var target = calcite.dom.getAttr(toggle, 'data-modal');
- for (var i = 0; i < modals.length; i++) {
- var modal = modals[i];
- var isTarget = calcite.dom.getAttr(modals[i], 'data-modal');
- if (target == isTarget) {
- toggleActive(modals, modal);
+ if (isOpen && shouldClose) {
+ removeClass(expander, 'is-active');
+ } else {
+ addClass(expander, 'is-active');
}
- }
+ });
});
- }
+ };
- function bindModal (modal) {
- calcite.dom.addEvent(modal, calcite.dom.event(), function(event) {
- calcite.dom.preventDefault(event);
- toggleActive(modals, modal);
+ // ┌───────┐
+ // │ Modal │
+ // └───────┘
+ // show and hide modal dialogues
+ calcite.patterns.modal = function () {
+ var toggles = findElements('.js-modal-toggle');
+ var modals = findElements('.js-modal');
+
+ toggles.forEach(function (toggle) {
+ addEvent(toggle, click(), function (e) {
+ preventDefault(e);
+ var modal;
+ var modalId = getAttr(toggle, 'data-modal');
+ if (modalId) {
+ modal = document.querySelector('.js-modal[data-modal="' + modalId + '"');
+ } else {
+ modal = closest('js-modal', toggle);
+ }
+ toggleActive(modals, modal);
+ });
});
- }
-
- for (var i = 0; i < toggles.length; i++) {
- bindToggle(toggles[i]);
- }
- for (var j = 0; j < modals.length; j++) {
- bindModal(modals[j]);
- }
-};
-
-
-// ┌──────┐
-// │ Tabs │
-// └──────┘
-// tabbed content pane
-calcite.tabs = function () {
- var tabs = findElements('.js-tab');
- var tabGroups = findElements('.js-tab-group');
-
- if (!tabs) {
- return;
- }
-
- // set max width for each tab
- for (var j = 0; j < tabGroups.length; j++) {
- var tabsInGroup = tabGroups[j].querySelectorAll('.js-tab');
- var percent = 100 / tabsInGroup.length;
- for (var k = 0; k < tabsInGroup.length; k++){
- tabsInGroup[k].style.maxWidth = percent + '%';
- }
- }
-
- function switchTab (event) {
- calcite.dom.preventDefault(event);
-
- var tab = calcite.dom.closest('js-tab', calcite.dom.eventTarget(event));
- var tabGroup = calcite.dom.closest('js-tab-group', tab);
- var tabs = tabGroup.querySelectorAll('.js-tab');
- var contents = tabGroup.querySelectorAll('.js-tab-section');
- var index = calcite.arr.indexOf(tab, tabs);
-
- removeActive(tabs);
- removeActive(contents);
-
- calcite.dom.addClass(tab, 'is-active');
- calcite.dom.addClass(contents[index], 'is-active');
- }
-
- // attach the switchTab event to all tabs
- for (var i = 0; i < tabs.length; i++) {
- calcite.dom.addEvent(tabs[i], calcite.dom.event(), switchTab);
- }
-
-};
-
-// ┌────────┐
-// │ Sticky │
-// └────────┘
-// sticks things to the window
+ modals.forEach(function (modal) {
+ addEvent(modal, click(), function (e) {
+ stopPropagation(e);
+ if (eventTarget(e) === modal) {
+ toggleActive(modals, modal);
+ }
+ });
+ });
+ };
-calcite.sticky = function () {
- var elements = findElements('.js-sticky');
+ // ┌──────┐
+ // │ Tabs │
+ // └──────┘
+ // tabbed content pane
+ calcite.patterns.tabs = function () {
+ var tabs = findElements('.js-tab');
+ var tabGroups = findElements('.js-tab-group');
+
+ // set max width for each tab
+ tabGroups.forEach(function (tab) {
+ var tabsInGroup = tab.querySelectorAll('.js-tab');
+ var percent = 100 / tabsInGroup.length;
+ for (var i = 0; i < tabsInGroup.length; i++) {
+ tabsInGroup[i].style.maxWidth = percent + '%';
+ }
+ });
- if (!elements) {
- return;
- }
+ function switchTab (e) {
+ preventDefault(e);
- var stickies = [];
+ var tab = closest('js-tab', eventTarget(e));
+ var tabGroup = closest('js-tab-group', tab);
+ var tabs = tabGroup.querySelectorAll('.js-tab');
+ var contents = tabGroup.querySelectorAll('.js-tab-section');
+ var index = nodeListToArray(tabs).indexOf(tab);
- for (var i = 0; i < elements.length; i++) {
- var el = elements[i];
- var top = el.offsetTop;
- var dataTop = calcite.dom.getAttr(el, 'data-top');
+ removeActive(tabs);
+ removeActive(contents);
- if (dataTop) {
- top = top - parseInt(dataTop, 0);
+ addClass(tab, 'is-active');
+ addClass(contents[index], 'is-active');
}
- stickies.push({
- active: false,
- top: top,
- shim: el.cloneNode('deep'),
- element: el
+ tabs.forEach(function (tab) {
+ addEvent(tab, click(), switchTab);
});
- }
+ };
- function handleScroll(item, offset) {
- var elem = item.element;
- var parent = elem.parentNode;
- var distance = item.top - offset;
- var dataTop = calcite.dom.getAttr(el, 'data-top');
-
- if (distance < 1 && !item.active) {
- item.shim.style.visiblity = 'hidden';
- parent.insertBefore(item.shim, elem);
- calcite.dom.addClass(elem, 'is-sticky');
- item.active = true;
- elem.style.top = dataTop + 'px';
- } else if (item.active && offset < item.top){
- parent.removeChild(item.shim);
- calcite.dom.removeClass(elem, 'is-sticky');
- elem.style.top = null;
- item.active = false;
- }
- }
+ // ┌────────┐
+ // │ Sticky │
+ // └────────┘
+ // sticks things to the window
+ calcite.patterns.sticky = function () {
+ var elements = findElements('.js-sticky');
+ var stickies = elements.map(function (el) {
+ var offset = el.offsetTop;
+ var dataTop = getAttr(el, 'data-top') || 0;
+ return {
+ active: false,
+ top: offset - parseInt(dataTop, 0),
+ shim: el.cloneNode('deep'),
+ element: el
+ };
+ });
- calcite.dom.addEvent(window, 'scroll', function() {
- var offset = window.pageYOffset;
- for (var i = 0; i < stickies.length; i++) {
- handleScroll(stickies[i], offset);
+ function handleScroll(item, offset) {
+ var el = item.element;
+ var parent = el.parentNode;
+ var distance = item.top - offset;
+ var dataTop = getAttr(el, 'data-top');
+
+ if (distance < 1 && !item.active) {
+ item.shim.style.visiblity = 'hidden';
+ parent.insertBefore(item.shim, el);
+ addClass(el, 'is-sticky');
+ item.active = true;
+ el.style.top = dataTop + 'px';
+ } else if (item.active && offset < item.top) {
+ parent.removeChild(item.shim);
+ removeClass(el, 'is-sticky');
+ el.style.top = null;
+ item.active = false;
+ }
}
- });
-};
+ addEvent(window, 'scroll', function () {
+ var offset = window.pageYOffset;
+ stickies.forEach(function (sticky) {
+ handleScroll(sticky, offset);
+ });
+ });
-// ┌────────────────────┐
-// │ Initialize Calcite │
-// └────────────────────┘
-// start up Calcite and attach all the patterns
-// optionally pass an array of patterns you'd like to watch
+ };
-calcite.init = function (patterns) {
- if (patterns) {
- for (var i = 0; i < patterns.length; i++) {
- calcite[patterns[i]]();
- }
- } else {
- calcite.modal();
- calcite.dropdown();
- calcite.drawer();
- calcite.expandingNav();
- calcite.tabs();
- calcite.accordion();
- calcite.carousel();
- calcite.sticky();
+ // ┌────────────────────┐
+ // │ Initialize Calcite │
+ // └────────────────────┘
+ // start up Calcite and attach all the patterns
+ // optionally pass an array of patterns you'd like to watch
+ function init (patterns) {
+ patterns = patterns || Object.keys(calcite.patterns);
+ patterns.forEach(function (pattern) {
+ calcite.patterns[pattern]();
+ });
}
- // add a touch class to the body
- if ( calcite.browser.isTouch() ) {
- calcite.dom.addClass(document.body, 'calcite-touch');
+ // ┌────────────────┐
+ // │ Expose Calcite │
+ // └────────────────┘
+ // make calcite available to amd, common-js, or globally
+ if (typeof define === 'function' && define.amd) {
+ define(function () { return calcite; });
+ } else if (typeof exports === 'object') {
+ module.exports = calcite;
+ } else {
+ // if something called calcite already exists,
+ // save it for recovery via calcite.noConflict()
+ var oldCalcite = window.calcite;
+ calcite.noConflict = function () {
+ window.calcite = oldCalcite;
+ return this;
+ };
+ window.calcite = calcite;
}
-};
-
-// ┌───────────────────┐
-// │ Expose Calcite.js │
-// └───────────────────┘
-// implementation borrowed from Leaflet
-
-// define calcite as a global variable, saving the original to restore later if needed
-function expose () {
- var oldCalcite = window.calcite;
-
- calcite.noConflict = function () {
- window.calcite = oldCalcite;
- return this;
- };
-
- window.calcite = calcite;
-}
-
-// no NPM/AMD for now because it just causes issues
-// @TODO: bust them into AMD & NPM distros
-
-// // define Calcite for CommonJS module pattern loaders (NPM, Browserify)
-// if (typeof module === 'object' && typeof module.exports === 'object') {
-// module.exports = calcite;
-// }
-
-// // define Calcite as an AMD module
-// else if (typeof define === 'function' && define.amd) {
-// define(calcite);
-// }
-
-expose();
})();
diff --git a/lib/sass/calcite-web.scss b/lib/sass/calcite-web.scss
index ac25cd297..b58d8e59d 100644
--- a/lib/sass/calcite-web.scss
+++ b/lib/sass/calcite-web.scss
@@ -113,7 +113,6 @@ $include-sticky: true !default;
@import "calcite-web/patterns/tabs";
@import "calcite-web/patterns/modal";
@import "calcite-web/patterns/accordion";
-@import "calcite-web/patterns/carousel";
@import "calcite-web/patterns/drawers";
// Modifiers
diff --git a/lib/sass/calcite-web/patterns/_carousel.scss b/lib/sass/calcite-web/patterns/_carousel.scss
deleted file mode 100644
index 50f52f0ab..000000000
--- a/lib/sass/calcite-web/patterns/_carousel.scss
+++ /dev/null
@@ -1,56 +0,0 @@
-// ┌──────────┐
-// │ Carousel │
-// └──────────┘
-// ↳ http://esri.github.io/calcite-web/patterns/#carousel
-// ↳ patterns → _carousel.md
-
-@mixin carousel() {
- overflow-x: hidden;
- position: relative;
-}
-
- @mixin carousel-slides() {
- @include transform(translate3d(0,0,0));
- @include transition-prefixed(transform $transition);
- }
-
- @mixin carousel-slide() {
- position: relative;
- float: left;
- height: 100%;
- }
-
- // Next and Prev Buttons
- %carousel-button {
- position: absolute;
- top: 50%;
- margin-top: -$baseline/2;
- width: $baseline;
- height: $baseline;
- text-align: center;
- color: $white;
- background: $transparent-black;
- z-index: 10;
- }
-
- @mixin carousel-prev() {
- @extend %carousel-button;
- left: 0;
- }
-
- @mixin carousel-next() {
- @extend %carousel-button;
- right: 0;
- }
-
-@if $include-carousel == true {
-.carousel { @include carousel()}
- .carousel-slides { @include carousel-slides()}
- .carousel-slide { @include carousel-slide()}
- .carousel-prev { @include carousel-prev()}
- .carousel-next { @include carousel-next()}
- .is-first-slide.carousel-prev, .is-last-slide.carousel-next {
- pointer-events: none;
- opacity: 0.2;
- }
-}
diff --git a/package.json b/package.json
index 24a77a356..3d585c395 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "calcite-web",
- "version": "0.2.3",
+ "version": "0.3.0",
"description": "SASS & CSS Framework for Esri websites",
"private": true,
"homepage": "https://github.com/esri/calcite-web",