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};