vendor/assets/js/foundation.dropdown.js.es6 in foundation-rails-6.3.1.0 vs vendor/assets/js/foundation.dropdown.js.es6 in foundation-rails-6.4.1.0
- old
+ new
@@ -1,32 +1,42 @@
'use strict';
-!function($) {
+import $ from 'jquery';
+import { Keyboard } from './foundation.util.keyboard';
+import { GetYoDigits } from './foundation.util.core';
+import { Positionable } from './foundation.positionable';
+import { Triggers } from './foundation.util.triggers';
+
+
/**
* Dropdown module.
* @module foundation.dropdown
* @requires foundation.util.keyboard
* @requires foundation.util.box
* @requires foundation.util.triggers
*/
-
-class Dropdown {
+class Dropdown extends Positionable {
/**
* Creates a new instance of a dropdown.
* @class
+ * @name Dropdown
* @param {jQuery} element - jQuery object to make into a dropdown.
* Object should be of the dropdown panel, rather than its anchor.
* @param {Object} options - Overrides to the default plugin settings.
*/
- constructor(element, options) {
+ _setup(element, options) {
this.$element = element;
this.options = $.extend({}, Dropdown.defaults, this.$element.data(), options);
+ this.className = 'Dropdown'; // ie9 back compat
+
+ // Triggers init is idempotent, just need to make sure it is initialized
+ Triggers.init($);
+
this._init();
- Foundation.registerPlugin(this, 'Dropdown');
- Foundation.Keyboard.register('Dropdown', {
+ Keyboard.register('Dropdown', {
'ENTER': 'open',
'SPACE': 'open',
'ESCAPE': 'close'
});
}
@@ -52,118 +62,51 @@
if(this.options.parentClass){
this.$parent = this.$element.parents('.' + this.options.parentClass);
}else{
this.$parent = null;
}
- this.options.positionClass = this.getPositionClass();
- this.counter = 4;
- this.usedPositions = [];
+
this.$element.attr({
'aria-hidden': 'true',
'data-yeti-box': $id,
'data-resize': $id,
- 'aria-labelledby': this.$anchor[0].id || Foundation.GetYoDigits(6, 'dd-anchor')
+ 'aria-labelledby': this.$anchor[0].id || GetYoDigits(6, 'dd-anchor')
});
+ super._init();
this._events();
}
- /**
- * Helper function to determine current orientation of dropdown pane.
- * @function
- * @returns {String} position - string value of a position class.
- */
- getPositionClass() {
- var verticalPosition = this.$element[0].className.match(/(top|left|right|bottom)/g);
- verticalPosition = verticalPosition ? verticalPosition[0] : '';
- var horizontalPosition = /float-(\S+)/.exec(this.$anchor[0].className);
- horizontalPosition = horizontalPosition ? horizontalPosition[1] : '';
- var position = horizontalPosition ? horizontalPosition + ' ' + verticalPosition : verticalPosition;
-
- return position;
+ _getDefaultPosition() {
+ // handle legacy classnames
+ var position = this.$element[0].className.match(/(top|left|right|bottom)/g);
+ if(position) {
+ return position[0];
+ } else {
+ return 'bottom'
+ }
}
- /**
- * Adjusts the dropdown panes orientation by adding/removing positioning classes.
- * @function
- * @private
- * @param {String} position - position class to remove.
- */
- _reposition(position) {
- this.usedPositions.push(position ? position : 'bottom');
- //default, try switching to opposite side
- if(!position && (this.usedPositions.indexOf('top') < 0)){
- this.$element.addClass('top');
- }else if(position === 'top' && (this.usedPositions.indexOf('bottom') < 0)){
- this.$element.removeClass(position);
- }else if(position === 'left' && (this.usedPositions.indexOf('right') < 0)){
- this.$element.removeClass(position)
- .addClass('right');
- }else if(position === 'right' && (this.usedPositions.indexOf('left') < 0)){
- this.$element.removeClass(position)
- .addClass('left');
+ _getDefaultAlignment() {
+ // handle legacy float approach
+ var horizontalPosition = /float-(\S+)/.exec(this.$anchor[0].className);
+ if(horizontalPosition) {
+ return horizontalPosition[1];
}
- //if default change didn't work, try bottom or left first
- else if(!position && (this.usedPositions.indexOf('top') > -1) && (this.usedPositions.indexOf('left') < 0)){
- this.$element.addClass('left');
- }else if(position === 'top' && (this.usedPositions.indexOf('bottom') > -1) && (this.usedPositions.indexOf('left') < 0)){
- this.$element.removeClass(position)
- .addClass('left');
- }else if(position === 'left' && (this.usedPositions.indexOf('right') > -1) && (this.usedPositions.indexOf('bottom') < 0)){
- this.$element.removeClass(position);
- }else if(position === 'right' && (this.usedPositions.indexOf('left') > -1) && (this.usedPositions.indexOf('bottom') < 0)){
- this.$element.removeClass(position);
- }
- //if nothing cleared, set to bottom
- else{
- this.$element.removeClass(position);
- }
- this.classChanged = true;
- this.counter--;
+ return super._getDefaultAlignment();
}
+
+
/**
- * Sets the position and orientation of the dropdown pane, checks for collisions.
+ * Sets the position and orientation of the dropdown pane, checks for collisions if allow-overlap is not true.
* Recursively calls itself if a collision is detected, with a new position class.
* @function
* @private
*/
_setPosition() {
- if(this.$anchor.attr('aria-expanded') === 'false'){ return false; }
- var position = this.getPositionClass(),
- $eleDims = Foundation.Box.GetDimensions(this.$element),
- $anchorDims = Foundation.Box.GetDimensions(this.$anchor),
- _this = this,
- direction = (position === 'left' ? 'left' : ((position === 'right') ? 'left' : 'top')),
- param = (direction === 'top') ? 'height' : 'width',
- offset = (param === 'height') ? this.options.vOffset : this.options.hOffset;
-
- if(($eleDims.width >= $eleDims.windowDims.width) || (!this.counter && !Foundation.Box.ImNotTouchingYou(this.$element, this.$parent))){
- var newWidth = $eleDims.windowDims.width,
- parentHOffset = 0;
- if(this.$parent){
- var $parentDims = Foundation.Box.GetDimensions(this.$parent),
- parentHOffset = $parentDims.offset.left;
- if ($parentDims.width < newWidth){
- newWidth = $parentDims.width;
- }
- }
-
- this.$element.offset(Foundation.Box.GetOffsets(this.$element, this.$anchor, 'center bottom', this.options.vOffset, this.options.hOffset + parentHOffset, true)).css({
- 'width': newWidth - (this.options.hOffset * 2),
- 'height': 'auto'
- });
- this.classChanged = true;
- return false;
- }
-
- this.$element.offset(Foundation.Box.GetOffsets(this.$element, this.$anchor, position, this.options.vOffset, this.options.hOffset));
-
- while(!Foundation.Box.ImNotTouchingYou(this.$element, this.$parent, true) && this.counter){
- this._reposition(position);
- this._setPosition();
- }
+ super._setPosition(this.$anchor, this.$element, this.$parent);
}
/**
* Adds event listeners to the element utilizing the triggers utility library.
* @function
@@ -210,13 +153,13 @@
}
}
this.$anchor.add(this.$element).on('keydown.zf.dropdown', function(e) {
var $target = $(this),
- visibleFocusableElements = Foundation.Keyboard.findFocusable(_this.$element);
+ visibleFocusableElements = Keyboard.findFocusable(_this.$element);
- Foundation.Keyboard.handleKey(e, 'Dropdown', {
+ Keyboard.handleKey(e, 'Dropdown', {
open: function() {
if ($target.is(_this.$anchor)) {
_this.open();
_this.$element.attr('tabindex', -1).focus();
e.preventDefault();
@@ -265,25 +208,27 @@
*/
this.$element.trigger('closeme.zf.dropdown', this.$element.attr('id'));
this.$anchor.addClass('hover')
.attr({'aria-expanded': true});
// this.$element/*.show()*/;
+
+ this.$element.addClass('is-opening');
this._setPosition();
- this.$element.addClass('is-open')
+ this.$element.removeClass('is-opening').addClass('is-open')
.attr({'aria-hidden': false});
if(this.options.autoFocus){
- var $focusable = Foundation.Keyboard.findFocusable(this.$element);
+ var $focusable = Keyboard.findFocusable(this.$element);
if($focusable.length){
$focusable.eq(0).focus();
}
}
if(this.options.closeOnClick){ this._addBodyHandler(); }
if (this.options.trapFocus) {
- Foundation.Keyboard.trapFocus(this.$element);
+ Keyboard.trapFocus(this.$element);
}
/**
* Fires once the dropdown is visible.
* @event Dropdown#show
@@ -304,29 +249,18 @@
.attr({'aria-hidden': true});
this.$anchor.removeClass('hover')
.attr('aria-expanded', false);
- if(this.classChanged){
- var curPositionClass = this.getPositionClass();
- if(curPositionClass){
- this.$element.removeClass(curPositionClass);
- }
- this.$element.addClass(this.options.positionClass)
- /*.hide()*/.css({height: '', width: ''});
- this.classChanged = false;
- this.counter = 4;
- this.usedPositions.length = 0;
- }
/**
* Fires once the dropdown is no longer visible.
* @event Dropdown#hide
*/
this.$element.trigger('hide.zf.dropdown', [this.$element]);
if (this.options.trapFocus) {
- Foundation.Keyboard.releaseFocus(this.$element);
+ Keyboard.releaseFocus(this.$element);
}
}
/**
* Toggles the dropdown pane's visibility.
@@ -343,15 +277,15 @@
/**
* Destroys the dropdown.
* @function
*/
- destroy() {
+ _destroy() {
this.$element.off('.zf.trigger').hide();
this.$anchor.off('.zf.dropdown');
+ $(document.body).off('click.zf.dropdown');
- Foundation.unregisterPlugin(this);
}
}
Dropdown.defaults = {
/**
@@ -384,28 +318,59 @@
hoverPane: false,
/**
* Number of pixels between the dropdown pane and the triggering element on open.
* @option
* @type {number}
- * @default 1
+ * @default 0
*/
- vOffset: 1,
+ vOffset: 0,
/**
* Number of pixels between the dropdown pane and the triggering element on open.
* @option
* @type {number}
- * @default 1
+ * @default 0
*/
- hOffset: 1,
+ hOffset: 0,
/**
- * Class applied to adjust open position. JS will test and fill this in.
+ * DEPRECATED: Class applied to adjust open position.
* @option
* @type {string}
* @default ''
*/
positionClass: '',
+
/**
+ * Position of dropdown. Can be left, right, bottom, top, or auto.
+ * @option
+ * @type {string}
+ * @default 'auto'
+ */
+ position: 'auto',
+ /**
+ * Alignment of dropdown relative to anchor. Can be left, right, bottom, top, center, or auto.
+ * @option
+ * @type {string}
+ * @default 'auto'
+ */
+ alignment: 'auto',
+ /**
+ * Allow overlap of container/window. If false, dropdown will first try to position as defined by data-position and data-alignment, but reposition if it would cause an overflow.
+ * @option
+ * @type {boolean}
+ * @default false
+ */
+ allowOverlap: false,
+ /**
+ * Allow overlap of only the bottom of the container. This is the most common
+ * behavior for dropdowns, allowing the dropdown to extend the bottom of the
+ * screen but not otherwise influence or break out of the container.
+ * @option
+ * @type {boolean}
+ * @default true
+ */
+ allowBottomOverlap: true,
+ /**
* Allow the plugin to trap focus to the dropdown pane if opened with keyboard commands.
* @option
* @type {boolean}
* @default false
*/
@@ -424,9 +389,6 @@
* @default false
*/
closeOnClick: false
}
-// Window exports
-Foundation.plugin(Dropdown, 'Dropdown');
-
-}(jQuery);
+export {Dropdown};