From 9d55cf44a46371125e424d5dd2205598e325cdf7 Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Fri, 19 May 2017 16:44:03 -0700
Subject: [PATCH 01/25] Add visual test
---
test/visual/dropdown/offsets.html | 49 +++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
create mode 100644 test/visual/dropdown/offsets.html
diff --git a/test/visual/dropdown/offsets.html b/test/visual/dropdown/offsets.html
new file mode 100644
index 0000000000..5f847019d6
--- /dev/null
+++ b/test/visual/dropdown/offsets.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+ Foundation for Sites Testing
+
+
+
+
+
Dropdown: Positioning Content
+
+
These dropdowns test various positioning and position offsets
+
+
This dropdown should be offset by 10 down and 30 to the right
+
+
+
This dropdown should be offset by 10 down and 30 to the right
+
+
This dropdown has position left and alignment center, and should go off the screen left because it has allow-overlap true
+
+
+
This dropdown should go off the screen left because it is 100% explicitly positioned.
+
+
This dropdown has position left and should go left if there is room and otherwise below
+
+
+
This dropdown should go left if there is room and otherwise below
+
+
This dropdown has position bottom and alignment left should align with its top right corner at the bottom right of the button
+
+
+
This dropdown has position bottom and alignment left should align with its top right corner at the bottom right of the button
+
+
This dropdown has position left and alignment top should align with its bottom right corner at the bottom left corner of the butotn
+
+
+
This dropdown has position left and alignment top should align with its bottom right corner at the bottom left corner of the butotn
+
+
+
+
+
+
+
+
From 4a4814ab48a117b452961ce30cf297f213609d9e Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Fri, 11 Nov 2016 13:16:22 -0800
Subject: [PATCH 02/25] Box should always take into account vOffset and hOffset
---
js/foundation.util.box.js | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/js/foundation.util.box.js b/js/foundation.util.box.js
index d1f220928e..ca13f8c9cf 100644
--- a/js/foundation.util.box.js
+++ b/js/foundation.util.box.js
@@ -116,55 +116,55 @@ function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) {
switch (position) {
case 'top':
return {
- left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width : $anchorDims.offset.left),
+ left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset: $anchorDims.offset.left + hOffset),
top: $anchorDims.offset.top - ($eleDims.height + vOffset)
}
break;
case 'left':
return {
left: $anchorDims.offset.left - ($eleDims.width + hOffset),
- top: $anchorDims.offset.top
+ top: $anchorDims.offset.top + vOffset
}
break;
case 'right':
return {
left: $anchorDims.offset.left + $anchorDims.width + hOffset,
- top: $anchorDims.offset.top
+ top: $anchorDims.offset.top + vOffset
}
break;
case 'center top':
return {
- left: ($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2),
+ left: ($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2) + hOffset,
top: $anchorDims.offset.top - ($eleDims.height + vOffset)
}
break;
case 'center bottom':
return {
- left: isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)),
+ left: isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)) + hOffset,
top: $anchorDims.offset.top + $anchorDims.height + vOffset
}
break;
case 'center left':
return {
left: $anchorDims.offset.left - ($eleDims.width + hOffset),
- top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2)
+ top: ($anchorDims.offset.top + vOffset + ($anchorDims.height / 2)) - ($eleDims.height / 2)
}
break;
case 'center right':
return {
left: $anchorDims.offset.left + $anchorDims.width + hOffset + 1,
- top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2)
+ top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2) + vOffset
}
break;
case 'center':
return {
- left: ($eleDims.windowDims.offset.left + ($eleDims.windowDims.width / 2)) - ($eleDims.width / 2),
- top: ($eleDims.windowDims.offset.top + ($eleDims.windowDims.height / 2)) - ($eleDims.height / 2)
+ left: ($eleDims.windowDims.offset.left + ($eleDims.windowDims.width / 2)) - ($eleDims.width / 2) + hOffset,
+ top: ($eleDims.windowDims.offset.top + ($eleDims.windowDims.height / 2)) - ($eleDims.height / 2 + vOffset)
}
break;
case 'reveal':
return {
- left: ($eleDims.windowDims.width - $eleDims.width) / 2,
+ left: ($eleDims.windowDims.width - $eleDims.width) / 2 + hOffset,
top: $eleDims.windowDims.offset.top + vOffset
}
case 'reveal full':
@@ -175,7 +175,7 @@ function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) {
break;
case 'left bottom':
return {
- left: $anchorDims.offset.left,
+ left: $anchorDims.offset.left - hOffset,
top: $anchorDims.offset.top + $anchorDims.height + vOffset
};
break;
@@ -187,7 +187,7 @@ function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) {
break;
default:
return {
- left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width : $anchorDims.offset.left + hOffset),
+ left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset: $anchorDims.offset.left + hOffset),
top: $anchorDims.offset.top + $anchorDims.height + vOffset
}
}
From f224b0c8da35e50f06deba114dcfb28d4bea6e8a Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Fri, 19 May 2017 21:32:34 -0700
Subject: [PATCH 03/25] Create explicit positioning method for Box and move
dropdown positioning to be EXPLICIT
---
js/foundation.dropdown.js | 194 +++++++++++-------
js/foundation.util.box.js | 137 ++++++++-----
.../visual/dropdown/explicit-positioning.html | 127 ++++++++++++
test/visual/dropdown/offsets.html | 130 +++++++++---
test/visual/dropdown/overflow.html | 92 +++++++++
5 files changed, 531 insertions(+), 149 deletions(-)
create mode 100644 test/visual/dropdown/explicit-positioning.html
create mode 100644 test/visual/dropdown/overflow.html
diff --git a/js/foundation.dropdown.js b/js/foundation.dropdown.js
index db63ba200e..5b078b1e0e 100644
--- a/js/foundation.dropdown.js
+++ b/js/foundation.dropdown.js
@@ -3,7 +3,7 @@
import $ from 'jquery';
import { Keyboard } from './foundation.util.keyboard';
import { Box } from './foundation.util.box';
-import { GetYoDigits } from './foundation.util.core';
+import { GetYoDigits, rtl as Rtl } from './foundation.util.core';
import { Plugin } from './foundation.plugin';
// import "foundation.util.triggers.js";
@@ -18,6 +18,17 @@ import { Plugin } from './foundation.plugin';
* @requires foundation.util.triggers
*/
+const POSITIONS = ['left', 'right', 'top', 'bottom'];
+const VERTICAL_ALIGNMENTS = ['top', 'bottom', 'center'];
+const HORIZONTAL_ALIGNMENTS = ['left', 'right', 'center'];
+
+const ALIGNMENTS = {
+ 'left': VERTICAL_ALIGNMENTS,
+ 'right': VERTICAL_ALIGNMENTS,
+ 'top': HORIZONTAL_ALIGNMENTS,
+ 'bottom': HORIZONTAL_ALIGNMENTS
+}
+
class Dropdown extends Plugin {
/**
* Creates a new instance of a dropdown.
@@ -61,9 +72,9 @@ class Dropdown extends Plugin {
}else{
this.$parent = null;
}
- this.options.positionClass = this.getPositionClass();
- this.counter = 4;
- this.usedPositions = [];
+ this._setupPositionAndAlignment();
+
+ this.triedPositions = {};
this.$element.attr({
'aria-hidden': 'true',
'data-yeti-box': $id,
@@ -73,6 +84,18 @@ class Dropdown extends Plugin {
this._events();
}
+ _setupPositionAndAlignment() {
+ if(this.options.position === 'left' || this.options.position === 'right') {
+ this.isHorizontallyPositioned = true;
+ }
+ if(this.options.position === 'top' || this.options.position === 'bottom') {
+ this.isVerticallyPositioned = true;
+ }
+
+ this.position = this.options.position === 'auto' ? this._getDefaultPosition() : this.options.position;
+ this.alignment = this.options.alignment === 'auto' ? this._getDefaultAlignment() : this.options.alignment;
+ }
+
/**
* Helper function to determine current orientation of dropdown pane.
* @function
@@ -88,86 +111,97 @@ class Dropdown extends Plugin {
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'
+ }
+ }
+
+ _getDefaultAlignment() {
+ // handle legacy float appraoch
+ var horizontalPosition = /float-(\S+)/.exec(this.$anchor[0].className);
+ if(horizontalPosition) {
+ return horizontalPosition[1];
+ }
+
+ switch(this.position) {
+ case 'bottom':
+ case 'top':
+ return Rtl() ? 'left' : 'right';
+ case 'left':
+ case 'right':
+ return 'bottom';
+ }
+ }
+
/**
- * Adjusts the dropdown panes orientation by adding/removing positioning classes.
+ * Adjusts the dropdown pane possible positions by iterating through alignments
+ * and positions. NOTE: Only used if position is auto, otherwise only alignments
+ * will be tried within the specified position.
* @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');
- }
+ _reposition() {
+ }
+
- //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);
+ /**
+ * Adjusts the dropdown pane possible positions by iterating through alignments
+ * on the current position.
+ * @function
+ * @private
+ */
+ _realign() {
+ this._addTriedPosition(this.position, this.alignment)
+ var alignments = ALIGNMENTS[this.position]
+ var currentIdx = alignments.indexOf(this.alignment);
+ if(currentIdx === alignments.length - 1) {
+ this.alignment = alignments[0];
+ } else {
+ this.alignment = alignments[currentIdx + 1];
}
- //if nothing cleared, set to bottom
- else{
- this.$element.removeClass(position);
+ }
+
+ _addTriedPosition(position, alignment) {
+ this.triedPositions[position] = this.triedPositions[position] || []
+ this.triedPositions[position].push(alignment);
+ }
+
+ _positionsExhausted() {
+ if(this.options.position === 'auto') {
+ } else {
+ return this.triedPositions[this.position] && this.triedPositions[this.position].length == ALIGNMENTS[this.position].length;
}
- this.classChanged = true;
- this.counter--;
}
/**
- * 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 = Box.GetDimensions(this.$element),
- $anchorDims = 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 && !Box.ImNotTouchingYou(this.$element, this.$parent))){
- var newWidth = $eleDims.windowDims.width,
- parentHOffset = 0;
- if(this.$parent){
- var $parentDims = Box.GetDimensions(this.$parent),
- parentHOffset = $parentDims.offset.left;
- if ($parentDims.width < newWidth){
- newWidth = $parentDims.width;
- }
- }
+ var $eleDims = Box.GetDimensions(this.$element),
+ $anchorDims = Box.GetDimensions(this.$anchor);
- this.$element.offset(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(Box.GetOffsets(this.$element, this.$anchor, position, this.options.vOffset, this.options.hOffset));
+ this.$element.offset(Box.GetExplicitOffsets(this.$element, this.$anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
- while(!Box.ImNotTouchingYou(this.$element, this.$parent, true) && this.counter){
- this._reposition(position);
- this._setPosition();
+ if(!this.options.allowOverlap) {
+ while(!Box.ImNotTouchingYou(this.$element, this.$parent, this.isVerticallyPositioned, this.isHorizontallyPositioned) && !this._positionsExhausted()){
+ if(this.options.position === 'auto') {
+ this._reposition();
+ } else {
+ console.log('realigning');
+ this._realign();
+ }
+ this._setPosition();
+ }
}
}
@@ -393,16 +427,16 @@ Dropdown.defaults = {
* 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.
* @option
@@ -410,6 +444,28 @@ Dropdown.defaults = {
* @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 the plugin to trap focus to the dropdown pane if opened with keyboard commands.
* @option
diff --git a/js/foundation.util.box.js b/js/foundation.util.box.js
index ca13f8c9cf..a5c6d082fb 100644
--- a/js/foundation.util.box.js
+++ b/js/foundation.util.box.js
@@ -6,7 +6,8 @@ import { rtl as Rtl } from "./foundation.util.core";
var Box = {
ImNotTouchingYou: ImNotTouchingYou,
GetDimensions: GetDimensions,
- GetOffsets: GetOffsets
+ GetOffsets: GetOffsets,
+ GetExplicitOffsets: GetExplicitOffsets
}
/**
@@ -99,7 +100,9 @@ function GetDimensions(elem, test){
/**
* Returns an object of top and left integer pixel values for dynamically rendered elements,
- * such as: Tooltip, Reveal, and Dropdown
+ * such as: Tooltip, Reveal, and Dropdown. Maintained for backwards compatibility, and where
+ * you don't know alignment, but generally from
+ * 6.4 forward you should use GetExplicitOffsets, as GetOffsets conflates position and alignment.
* @function
* @param {jQuery} element - jQuery object for the element being positioned.
* @param {jQuery} anchor - jQuery object for the element's anchor point.
@@ -110,58 +113,34 @@ function GetDimensions(elem, test){
* TODO alter/rewrite to work with `em` values as well/instead of pixels
*/
function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) {
- var $eleDims = GetDimensions(element),
- $anchorDims = anchor ? GetDimensions(anchor) : null;
-
switch (position) {
case 'top':
- return {
- left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset: $anchorDims.offset.left + hOffset),
- top: $anchorDims.offset.top - ($eleDims.height + vOffset)
- }
- break;
- case 'left':
- return {
- left: $anchorDims.offset.left - ($eleDims.width + hOffset),
- top: $anchorDims.offset.top + vOffset
- }
- break;
- case 'right':
- return {
- left: $anchorDims.offset.left + $anchorDims.width + hOffset,
- top: $anchorDims.offset.top + vOffset
- }
- break;
+ return Rtl() ?
+ GetExplicitOffsets(element, anchor, 'top', 'left', vOffset, hOffset, isOverflow) :
+ GetExplicitOffsets(element, anchor, 'top', 'right', vOffset, hOffset, isOverflow);
+ case 'bottom':
+ return Rtl() ?
+ GetExplicitOffsets(element, anchor, 'bottom', 'left', vOffset, hOffset, isOverflow) :
+ GetExplicitOffsets(element, anchor, 'bottom', 'right', vOffset, hOffset, isOverflow);
case 'center top':
- return {
- left: ($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2) + hOffset,
- top: $anchorDims.offset.top - ($eleDims.height + vOffset)
- }
- break;
+ return GetExplicitOffsets(element, anchor, 'top', 'center', vOffset, hOffset, isOverflow);
case 'center bottom':
- return {
- left: isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)) + hOffset,
- top: $anchorDims.offset.top + $anchorDims.height + vOffset
- }
- break;
+ return GetExplicitOffsets(element, anchor, 'bottom', 'center', vOffset, hOffset, isOverflow);
case 'center left':
- return {
- left: $anchorDims.offset.left - ($eleDims.width + hOffset),
- top: ($anchorDims.offset.top + vOffset + ($anchorDims.height / 2)) - ($eleDims.height / 2)
- }
- break;
+ return GetExplicitOffsets(element, anchor, 'left', 'center', vOffset, hOffset, isOverflow);
case 'center right':
- return {
- left: $anchorDims.offset.left + $anchorDims.width + hOffset + 1,
- top: ($anchorDims.offset.top + ($anchorDims.height / 2)) - ($eleDims.height / 2) + vOffset
- }
- break;
+ return GetExplicitOffsets(element, anchor, 'right', 'center', vOffset, hOffset, isOverflow);
+ case 'left bottom':
+ return GetExplicitOffsets(element, anchor, 'bottom', 'left', vOffset, hOffset, isOverflow);
+ case 'right bottom':
+ return GetExplicitOffsets(element, anchor, 'bottom', 'right', vOffset, hOffset, isOverflow);
+ // Backwards compatibility... this along with the reveal and reveal full
+ // classes are the only ones that didn't reference anchor
case 'center':
return {
left: ($eleDims.windowDims.offset.left + ($eleDims.windowDims.width / 2)) - ($eleDims.width / 2) + hOffset,
top: ($eleDims.windowDims.offset.top + ($eleDims.windowDims.height / 2)) - ($eleDims.height / 2 + vOffset)
}
- break;
case 'reveal':
return {
left: ($eleDims.windowDims.width - $eleDims.width) / 2 + hOffset,
@@ -173,24 +152,72 @@ function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) {
top: $eleDims.windowDims.offset.top
}
break;
- case 'left bottom':
- return {
- left: $anchorDims.offset.left - hOffset,
- top: $anchorDims.offset.top + $anchorDims.height + vOffset
- };
- break;
- case 'right bottom':
- return {
- left: $anchorDims.offset.left + $anchorDims.width + hOffset - $eleDims.width,
- top: $anchorDims.offset.top + $anchorDims.height + vOffset
- };
- break;
default:
return {
left: (Rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset: $anchorDims.offset.left + hOffset),
top: $anchorDims.offset.top + $anchorDims.height + vOffset
}
+
+ }
+
+}
+
+function GetExplicitOffsets(element, anchor, position, alignment, vOffset, hOffset, isOverflow) {
+ var $eleDims = GetDimensions(element),
+ $anchorDims = anchor ? GetDimensions(anchor) : null;
+
+ var topVal, leftVal;
+
+ // set position related attribute
+
+ switch (position) {
+ case 'top':
+ topVal = $anchorDims.offset.top - ($eleDims.height + vOffset);
+ break;
+ case 'bottom':
+ topVal = $anchorDims.offset.top + $anchorDims.height + vOffset;
+ break;
+ case 'left':
+ leftVal = $anchorDims.offset.left - ($eleDims.width + hOffset);
+ break;
+ case 'right':
+ leftVal = $anchorDims.offset.left + $anchorDims.width + hOffset;
+ break;
+ }
+
+
+ // set alignment related attribute
+ switch (position) {
+ case 'top':
+ case 'bottom':
+ switch (alignment) {
+ case 'left':
+ leftVal = $anchorDims.offset.left + hOffset;
+ break;
+ case 'right':
+ leftVal = $anchorDims.offset.left - $eleDims.width + $anchorDims.width - hOffset;
+ break;
+ case 'center':
+ leftVal = isOverflow ? hOffset : (($anchorDims.offset.left + ($anchorDims.width / 2)) - ($eleDims.width / 2)) + hOffset;
+ break;
+ }
+ break;
+ case 'right':
+ case 'left':
+ switch (alignment) {
+ case 'bottom':
+ topVal = $anchorDims.offset.top - vOffset + $anchorDims.height - $eleDims.height;
+ break;
+ case 'top':
+ topVal = $anchorDims.offset.top + vOffset
+ break;
+ case 'center':
+ topVal = ($anchorDims.offset.top + vOffset + ($anchorDims.height / 2)) - ($eleDims.height / 2)
+ break;
+ }
+ break;
}
+ return {top: topVal, left: leftVal};
}
export {Box};
diff --git a/test/visual/dropdown/explicit-positioning.html b/test/visual/dropdown/explicit-positioning.html
new file mode 100644
index 0000000000..935ca2422f
--- /dev/null
+++ b/test/visual/dropdown/explicit-positioning.html
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+ Foundation for Sites Testing
+
+
+
+
+
Dropdown: Explicit Positioning Content - no offsets
+
+
These dropdowns test various positioning and alignments. Valid
+ positions are left/right/top/bottom. Valid alignments are
+ left/right/top/bottom/center. Left align means left sides should line up.
+ Right align means right sides should line up. Center align means centers should line up.
+
+
+
Top and Bottom positioned
+
+
+
Bottom Left
+
+
+
This dropdown has position bottom and alignment left should align with its top left corner at the bottom left of the button
+
+
+
+
+
Bottom Center
+
+
+
This dropdown has position bottom and alignment center should align below the button with its center aligned with the center of the button
+
+
+
+
+
Bottom Right
+
+
+
This dropdown has position bottom and alignment right should align with its top right corner at the bottom right of the button
+
+
+
+
+
Top Left
+
+
+
This dropdown has position top and alignment left should align with its bottom left corner at the top left of the button
+
+
+
+
+
Top Center
+
+
+
This dropdown has position top and alignment center should align above with its center aligned with the center of the button
+
+
+
+
+
Top Right
+
+
+
This dropdown has position top and alignment right should align with its bottom right corner at the top right of the button
+
+
+
+
+
+
Left and Right Positioned
+
+
+
Right Top
+
+
+
This dropdown has position right and alignment top should align with its top left corner at the top right of the button
+
+
+
+
Left Top
+
+
+
This dropdown has position left and alignment top should align with its top right corner at the top left of the button
+
+
+
+
+
Right Center
+
+
+
This dropdown has position right and alignment center should align to the right of the button with the center of the dropdown vertically aligned with the center of the button
+
+
+
+
Left Center
+
+
+
This dropdown has position left and alignment center should align to the left of the button with the center of the dropdown vertically aligned with the center of the button
+
+
+
+
+
Right Bottom
+
+
+
This dropdown has position right and alignment bottom should align with its bottom left corner at the bottom right of the button
+
+
+
+
Left Bottom
+
+
+
This dropdown has position left and alignment bottom should align with its bottom right corner at the bottom left of the button
Dropdown: Explicit Positioning Content - with offsets
-
These dropdowns test various positioning and position offsets
+
These dropdowns test various positioning and alignments WITH OFFSETS.
+ Valid positions are left/right/top/bottom. Valid alignments are
+ left/right/top/bottom/center. Left align means left sides should line up.
+ Right align means right sides should line up. Center align means centers should line up.
+ Positive Offsets should always be applied in a direction to create
+ space between the anchor and the dropdown.
+
-
This dropdown should be offset by 10 down and 30 to the right
-
-
-
This dropdown should be offset by 10 down and 30 to the right
-
-
This dropdown has position left and alignment center, and should go off the screen left because it has allow-overlap true
-
-
-
This dropdown should go off the screen left because it is 100% explicitly positioned.
-
-
This dropdown has position left and should go left if there is room and otherwise below
-
-
-
This dropdown should go left if there is room and otherwise below
-
-
This dropdown has position bottom and alignment left should align with its top right corner at the bottom right of the button
-
-
-
This dropdown has position bottom and alignment left should align with its top right corner at the bottom right of the button
+
Top and Bottom positioned
+
+
+
Bottom Left
+
+
+
This offset should push right and down.
+
+
+
+
+
Bottom Center
+
+
+
This offset should push right and down.
+
+
+
+
+
Bottom Right
+
+
+
This offset should push left and down.
+
+
+
+
+
Top Left
+
+
+
This offset should push right and up.
+
+
+
+
+
Top Center
+
+
+
This offset should push right and up.
+
+
+
+
+
Top Right
+
+
+
This offset should push left and up.
+
+
-
This dropdown has position left and alignment top should align with its bottom right corner at the bottom left corner of the butotn
-
-
-
This dropdown has position left and alignment top should align with its bottom right corner at the bottom left corner of the butotn
+
+
+
Left and Right Positioned
+
+
+
Right Top
+
+
+
This offset should push right and down.
+
+
+
+
Left Top
+
+
+
This offset should push left and down.
+
+
+
+
+
Right Center
+
+
+
This offset should push right and down.
+
+
+
+
Left Center
+
+
+
This offset should push left and down.
+
+
+
+
+
Right Bottom
+
+
+
This offset should push right and up.
+
+
+
+
Left Bottom
+
+
+
This offset should push left and up.
+
+
diff --git a/test/visual/dropdown/overflow.html b/test/visual/dropdown/overflow.html
new file mode 100644
index 0000000000..ba26f45980
--- /dev/null
+++ b/test/visual/dropdown/overflow.html
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+ Foundation for Sites Testing
+
+
+
+
+
Dropdown: Overflow Handling
+
+
These dropdowns test various overflow situations.
+
+
Right and Left Overflow
+
+
+
Bottom Right Default Behavior (no overflow)
+
+
+
This dropdown should be on the bottom aligned with the left, all inside the container
+
+
+
+
Bottom Left Default Behavior (no overflow)
+
+
+
This dropdown should be on the bottom aligned with the right, all inside the container
+
+
+
+
+
Bottom Right Overflow Allowed
+
+
+
This dropdown should be on the bottom aligned with the right, breaking out of the container
+
+
+
+
+
Bottom Left Overflow Allowed
+
+
+
This dropdown should be on the bottom aligned with the left, breaking out of the container
+
+
+
+
+
Bottom and Top Overflow
+
+
+
Right Bottom Default Behavior (no overflow)
+
+
+
This dropdown should be on the right aligned with the top of the button after realigning
+
+
+
+
Left Bottom Default Behavior (no overflow)
+
+
+
This dropdown should be on the left aligned with the top of the button after realigning
+
+
+
+
+
Right Top Default Behavior (no overflow)
+
+
+
This dropdown should be on the right aligned with the bottom of the button after realigning
+
+
+
+
+
Left Top Default Behavior (no overflow)
+
+
+
This dropdown should be on the left aligned with the bottom of the button after realigning
+
+
+
+
+
+
+
+
+
+
+
From dbfb530f6ea06a91fa522b09ecccc8ee8118e020 Mon Sep 17 00:00:00 2001
From: harry
Date: Sat, 20 May 2017 20:52:41 +0530
Subject: [PATCH 04/25] Add a test case: Overflow right left
---
test/visual/dropdown/overflow-right-left.html | 53 +++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 test/visual/dropdown/overflow-right-left.html
diff --git a/test/visual/dropdown/overflow-right-left.html b/test/visual/dropdown/overflow-right-left.html
new file mode 100644
index 0000000000..69ff2e2470
--- /dev/null
+++ b/test/visual/dropdown/overflow-right-left.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+ Foundation for Sites Testing
+
+
+
+
+
Right and Left (no overflow)
+
+
+
Top Right Default Behavior (no overflow)
+
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Soluta, molestias libero autem expedita placeat sequi.
+
+
+
+
Top Left Default Behavior (no overflow)
+
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. A accusantium iure dolorem, earum maiores eos?
+
+
+
+
Bottom Right Default Behavior (no overflow)
+
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maxime tempora officiis quam, libero quo cumque!
+
+
+
+
+
Bottom Left Default Behavior (no overflow)
+
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt nostrum tempora harum dolore consectetur reiciendis.
+
+
+
+
+
+
+
+
+
+
+
From 16ed4c0684af1d4617b400bb1a75a51c3570d183 Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Tue, 23 May 2017 20:48:06 -0700
Subject: [PATCH 05/25] update whitespace in util box test
---
test/javascript/util/box.js | 78 ++++++++++++++++++-------------------
1 file changed, 39 insertions(+), 39 deletions(-)
diff --git a/test/javascript/util/box.js b/test/javascript/util/box.js
index d9947db703..0f1073429c 100644
--- a/test/javascript/util/box.js
+++ b/test/javascript/util/box.js
@@ -1,51 +1,51 @@
describe('Foundation box', function () {
- var $html;
-
- afterEach(function () {
- if ($html) {
- $html.remove();
- }
+ var $html;
+
+ afterEach(function () {
+ if ($html) {
+ $html.remove();
+ }
+ });
+
+ describe('GetDimensions()', function () {
+ it('should be unable to get dimensions for window', function(done) {
+ try {
+ Foundation.Box.GetDimensions($("window"));
+
+ should.fail();
+ } catch (err) {
+ done();
+ }
});
- describe('GetDimensions()', function () {
- it('should be unable to get dimensions for window', function(done) {
- try {
- Foundation.Box.GetDimensions($("window"));
-
- should.fail();
- } catch (err) {
- done();
- }
- });
-
- it('should be unable to get dimensions for document', function(done) {
- try {
- Foundation.Box.GetDimensions($("document"));
+ it('should be unable to get dimensions for document', function(done) {
+ try {
+ Foundation.Box.GetDimensions($("document"));
- should.fail();
- } catch (err) {
- done();
- }
- });
+ should.fail();
+ } catch (err) {
+ done();
+ }
+ });
- it('height and width of element', function () {
- $html = $('').appendTo('body');
+ it('height and width of element', function () {
+ $html = $('').appendTo('body');
- var dims = Foundation.Box.GetDimensions($("#rect-test"));
+ var dims = Foundation.Box.GetDimensions($("#rect-test"));
- dims.width.should.equal(200);
- dims.height.should.equal(100);
- });
+ dims.width.should.equal(200);
+ dims.height.should.equal(100);
+ });
- it('parent height of element', function () {
- $html = $('
').appendTo('body');
+ it('parent height of element', function () {
+ $html = $('
From feed071420fccc57f0c995f230df69571480a31c Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Tue, 23 May 2017 21:36:06 -0700
Subject: [PATCH 07/25] Remove extraneous console logs
---
js/foundation.dropdown.js | 2 --
1 file changed, 2 deletions(-)
diff --git a/js/foundation.dropdown.js b/js/foundation.dropdown.js
index ffe7492e0d..c5493d06b7 100644
--- a/js/foundation.dropdown.js
+++ b/js/foundation.dropdown.js
@@ -151,7 +151,6 @@ class Dropdown extends Plugin {
if(this._alignmentsExhausted(this.position)) {
this.position = nextItem(this.position, POSITIONS);
this.alignment = ALIGNMENTS[this.position][0];
- console.log('alignments exhausted, repositioned to ', this.position, this.alignment);
} else {
this._realign();
}
@@ -167,7 +166,6 @@ class Dropdown extends Plugin {
_realign() {
this._addTriedPosition(this.position, this.alignment)
this.alignment = nextItem(this.alignment, ALIGNMENTS[this.position])
- console.log('realigning to', this.position, this.alignment);
}
_addTriedPosition(position, alignment) {
From f21481e86334a7e0968152724122a91ac5d31b58 Mon Sep 17 00:00:00 2001
From: harry
Date: Wed, 24 May 2017 21:46:08 +0530
Subject: [PATCH 08/25] Add a test case overflow right left allowed!
---
.../dropdown/overflow-right-left-allowed.html | 53 +++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 test/visual/dropdown/overflow-right-left-allowed.html
diff --git a/test/visual/dropdown/overflow-right-left-allowed.html b/test/visual/dropdown/overflow-right-left-allowed.html
new file mode 100644
index 0000000000..6a91fb73ab
--- /dev/null
+++ b/test/visual/dropdown/overflow-right-left-allowed.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+ Foundation for Sites Testing
+
+
+
+
+
Right and Left Overflow Allowed
+
+
+
Top Right Default Behavior Overflow Allowed
+
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Soluta, molestias libero autem expedita placeat sequi.
+
+
+
+
Top Left Default Behavior Overflow Allowed
+
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. A accusantium iure dolorem, earum maiores eos?
+
+
+
+
Bottom Right Default Behavior Overflow Allowed
+
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maxime tempora officiis quam, libero quo cumque!
+
+
+
+
+
Bottom Left Default Behavior Overflow Allowed
+
+
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Incidunt nostrum tempora harum dolore consectetur reiciendis.
+
+
+
+
+
+
+
+
+
+
+
From 52ad8178c633c803f68e983bd189159afc30c19f Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Wed, 24 May 2017 20:22:41 -0700
Subject: [PATCH 09/25] Handle the fact that bottoms are special and commonly
we want to allow overflow that way even if no other way.
---
js/foundation.dropdown.js | 13 +++++++++++--
js/foundation.util.box.js | 10 +++++-----
2 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/js/foundation.dropdown.js b/js/foundation.dropdown.js
index c5493d06b7..500686528b 100644
--- a/js/foundation.dropdown.js
+++ b/js/foundation.dropdown.js
@@ -133,7 +133,7 @@ class Dropdown extends Plugin {
switch(this.position) {
case 'bottom':
case 'top':
- return Rtl() ? 'left' : 'right';
+ return Rtl() ? 'right' : 'left';
case 'left':
case 'right':
return 'bottom';
@@ -205,7 +205,7 @@ class Dropdown extends Plugin {
// default coordinates to how we start, in case we can't figure out better
var minCoordinates = {position: this.position, alignment: this.alignment};
while(!this._positionsExhausted()) {
- let overlap = Box.OverlapArea(this.$element, this.$parent);
+ let overlap = Box.OverlapArea(this.$element, this.$parent, false, false, this.options.allowBottomOverlap);
if(overlap === 0) {
return;
}
@@ -488,6 +488,15 @@ Dropdown.defaults = {
* @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
diff --git a/js/foundation.util.box.js b/js/foundation.util.box.js
index f00bcc74bc..7e989e75cf 100644
--- a/js/foundation.util.box.js
+++ b/js/foundation.util.box.js
@@ -21,13 +21,13 @@ var Box = {
* @default if no parent object passed, detects collisions with `window`.
* @returns {Boolean} - true if collision free, false if a collision in any direction.
*/
-function ImNotTouchingYou(element, parent, lrOnly, tbOnly) {
- return OverlapArea(element, parent, lrOnly, tbOnly) === 0;
+function ImNotTouchingYou(element, parent, lrOnly, tbOnly, ignoreBottom) {
+ return OverlapArea(element, parent, lrOnly, tbOnly, ignoreBottom) === 0;
};
-function OverlapArea(element, parent, lrOnly, tbOnly) {
+function OverlapArea(element, parent, lrOnly, tbOnly, ignoreBottom) {
var eleDims = GetDimensions(element),
- topOver, bottomOver, leftOver, rightOver;
+ topOver, bottomOver, leftOver, rightOver;
if (parent) {
var parDims = GetDimensions(parent);
@@ -43,7 +43,7 @@ function OverlapArea(element, parent, lrOnly, tbOnly) {
rightOver = eleDims.windowDims.width - (eleDims.offset.left + eleDims.width);
}
- bottomOver = Math.min(bottomOver, 0);
+ bottomOver = ignoreBottom ? 0 : Math.min(bottomOver, 0);
topOver = Math.min(topOver, 0);
leftOver = Math.min(leftOver, 0);
rightOver = Math.min(rightOver, 0);
From 0753f64152899ef2b36baf61632d89c66f7a773e Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Wed, 24 May 2017 21:30:58 -0700
Subject: [PATCH 10/25] Add deprecation notice to GetOffsets
---
js/foundation.util.box.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/js/foundation.util.box.js b/js/foundation.util.box.js
index 7e989e75cf..ba960eafd9 100644
--- a/js/foundation.util.box.js
+++ b/js/foundation.util.box.js
@@ -120,6 +120,7 @@ function GetDimensions(elem, test){
* TODO alter/rewrite to work with `em` values as well/instead of pixels
*/
function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) {
+ console.log("NOTE: GetOffsets is deprecated in favor of GetExplicitOffsets and will be removed in 6.5");
switch (position) {
case 'top':
return Rtl() ?
From 0b91a8f65d17067f290877e904621f4fd12cdd6a Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Wed, 24 May 2017 21:31:24 -0700
Subject: [PATCH 11/25] Refactor dropdown to factor out positionable logic, in
preparation of extending to tooltip
---
js/foundation.dropdown.js | 135 ++----------------------
js/foundation.positionable.js | 191 ++++++++++++++++++++++++++++++++++
2 files changed, 198 insertions(+), 128 deletions(-)
create mode 100644 js/foundation.positionable.js
diff --git a/js/foundation.dropdown.js b/js/foundation.dropdown.js
index dd3116448f..5de0f472e4 100644
--- a/js/foundation.dropdown.js
+++ b/js/foundation.dropdown.js
@@ -2,9 +2,8 @@
import $ from 'jquery';
import { Keyboard } from './foundation.util.keyboard';
-import { Box } from './foundation.util.box';
-import { GetYoDigits, rtl as Rtl } from './foundation.util.core';
-import { Plugin } from './foundation.plugin';
+import { GetYoDigits } from './foundation.util.core';
+import { Positionable } from './foundation.positionable';
// import "foundation.util.triggers.js";
// TODO: Figure out what a triggers import "means", since triggers are always accessed indirectly.
@@ -17,28 +16,7 @@ import { Plugin } from './foundation.plugin';
* @requires foundation.util.box
* @requires foundation.util.triggers
*/
-
-const POSITIONS = ['left', 'right', 'top', 'bottom'];
-const VERTICAL_ALIGNMENTS = ['top', 'bottom', 'center'];
-const HORIZONTAL_ALIGNMENTS = ['left', 'right', 'center'];
-
-const ALIGNMENTS = {
- 'left': VERTICAL_ALIGNMENTS,
- 'right': VERTICAL_ALIGNMENTS,
- 'top': HORIZONTAL_ALIGNMENTS,
- 'bottom': HORIZONTAL_ALIGNMENTS
-}
-
-function nextItem(item, array) {
- var currentIdx = array.indexOf(item);
- if(currentIdx === array.length - 1) {
- return array[0];
- } else {
- return array[currentIdx + 1];
- }
-}
-
-class Dropdown extends Plugin {
+class Dropdown extends Positionable {
/**
* Creates a new instance of a dropdown.
* @class
@@ -81,23 +59,17 @@ class Dropdown extends Plugin {
}else{
this.$parent = null;
}
- this._setupPositionAndAlignment();
- this.triedPositions = {};
this.$element.attr({
'aria-hidden': 'true',
'data-yeti-box': $id,
'data-resize': $id,
'aria-labelledby': this.$anchor[0].id || GetYoDigits(6, 'dd-anchor')
});
+ super._init();
this._events();
}
- _setupPositionAndAlignment() {
- this.position = this.options.position === 'auto' ? this._getDefaultPosition() : this.options.position;
- this.alignment = this.options.alignment === 'auto' ? this._getDefaultAlignment() : this.options.alignment;
- }
-
/**
* Helper function to determine current orientation of dropdown pane.
* @function
@@ -124,67 +96,17 @@ class Dropdown extends Plugin {
}
_getDefaultAlignment() {
- // handle legacy float appraoch
+ // handle legacy float approach
var horizontalPosition = /float-(\S+)/.exec(this.$anchor[0].className);
if(horizontalPosition) {
return horizontalPosition[1];
}
- switch(this.position) {
- case 'bottom':
- case 'top':
- return Rtl() ? 'right' : 'left';
- case 'left':
- case 'right':
- return 'bottom';
- }
+ return super._getDefaultAlignment();
}
- /**
- * Adjusts the dropdown pane possible positions by iterating through alignments
- * and positions. NOTE: Only used if position is auto, otherwise only alignments
- * will be tried within the specified position.
- * @function
- * @private
- */
- _reposition() {
- if(this._alignmentsExhausted(this.position)) {
- this.position = nextItem(this.position, POSITIONS);
- this.alignment = ALIGNMENTS[this.position][0];
- } else {
- this._realign();
- }
- }
- /**
- * Adjusts the dropdown pane possible positions by iterating through alignments
- * on the current position.
- * @function
- * @private
- */
- _realign() {
- this._addTriedPosition(this.position, this.alignment)
- this.alignment = nextItem(this.alignment, ALIGNMENTS[this.position])
- }
-
- _addTriedPosition(position, alignment) {
- this.triedPositions[position] = this.triedPositions[position] || []
- this.triedPositions[position].push(alignment);
- }
-
- _positionsExhausted() {
- var isExhausted = true;
- for(var i = 0; i < POSITIONS.length; i++) {
- isExhausted = isExhausted && this._alignmentsExhausted(POSITIONS[i]);
- }
- return isExhausted;
- }
-
- _alignmentsExhausted(position) {
- return this.triedPositions[position] && this.triedPositions[position].length == ALIGNMENTS[position].length;
- }
-
/**
* 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.
@@ -192,39 +114,7 @@ class Dropdown extends Plugin {
* @private
*/
_setPosition() {
- if(this.$anchor.attr('aria-expanded') === 'false'){ return false; }
- var $eleDims = Box.GetDimensions(this.$element),
- $anchorDims = Box.GetDimensions(this.$anchor);
-
-
- this.$element.offset(Box.GetExplicitOffsets(this.$element, this.$anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
-
- if(!this.options.allowOverlap) {
- var overlaps = {};
- var minOverlap = 100000000;
- // default coordinates to how we start, in case we can't figure out better
- var minCoordinates = {position: this.position, alignment: this.alignment};
- while(!this._positionsExhausted()) {
- let overlap = Box.OverlapArea(this.$element, this.$parent, false, false, this.options.allowBottomOverlap);
- if(overlap === 0) {
- return;
- }
-
- if(overlap < minOverlap) {
- minOverlap = overlap;
- minCoordinates = {position: this.position, alignment: this.alignment};
- }
-
- this._reposition();
-
- this.$element.offset(Box.GetExplicitOffsets(this.$element, this.$anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
- }
- // If we get through the entire loop, there was no non-overlapping
- // position available. Pick the version with least overlap.
- this.position = minCoordinates.position;
- this.alignment = minCoordinates.alignment;
- this.$element.offset(Box.GetExplicitOffsets(this.$element, this.$anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
- }
+ super._setPosition(this.$anchor, this.$element, this.$parent);
}
/**
@@ -371,17 +261,6 @@ class Dropdown extends Plugin {
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
diff --git a/js/foundation.positionable.js b/js/foundation.positionable.js
new file mode 100644
index 0000000000..81dcea57da
--- /dev/null
+++ b/js/foundation.positionable.js
@@ -0,0 +1,191 @@
+'use strict';
+
+import { Box } from './foundation.util.box';
+import { Plugin } from './foundation.plugin';
+import { rtl as Rtl } from './foundation.util.core';
+
+const POSITIONS = ['left', 'right', 'top', 'bottom'];
+const VERTICAL_ALIGNMENTS = ['top', 'bottom', 'center'];
+const HORIZONTAL_ALIGNMENTS = ['left', 'right', 'center'];
+
+const ALIGNMENTS = {
+ 'left': VERTICAL_ALIGNMENTS,
+ 'right': VERTICAL_ALIGNMENTS,
+ 'top': HORIZONTAL_ALIGNMENTS,
+ 'bottom': HORIZONTAL_ALIGNMENTS
+}
+
+function nextItem(item, array) {
+ var currentIdx = array.indexOf(item);
+ if(currentIdx === array.length - 1) {
+ return array[0];
+ } else {
+ return array[currentIdx + 1];
+ }
+}
+
+
+class Positionable extends Plugin {
+ /**
+ * Abstract class encapsulating the tether-like explicit positioning logic
+ * including repositioning based on overlap.
+ * Expects classes to define defaults for vOffset, hOffset, position,
+ * alignment, allowOverlap, and allowBottomOverlap. They can do this by
+ * extending the defaults, or (for now recommended due to the way docs are
+ * generated) by explicitly declaring them.
+ *
+ **/
+
+ _init() {
+ this.triedPositions = {};
+ this.position = this.options.position === 'auto' ? this._getDefaultPosition() : this.options.position;
+ this.alignment = this.options.alignment === 'auto' ? this._getDefaultAlignment() : this.options.alignment;
+ }
+
+ _getDefaultPosition () {
+ return 'bottom';
+ }
+
+ _getDefaultAlignment() {
+ switch(this.position) {
+ case 'bottom':
+ case 'top':
+ return Rtl() ? 'right' : 'left';
+ case 'left':
+ case 'right':
+ return 'bottom';
+ }
+ }
+
+ /**
+ * Adjusts the positionable possible positions by iterating through alignments
+ * and positions.
+ * @function
+ * @private
+ */
+ _reposition() {
+ if(this._alignmentsExhausted(this.position)) {
+ this.position = nextItem(this.position, POSITIONS);
+ this.alignment = ALIGNMENTS[this.position][0];
+ } else {
+ this._realign();
+ }
+ }
+
+ /**
+ * Adjusts the dropdown pane possible positions by iterating through alignments
+ * on the current position.
+ * @function
+ * @private
+ */
+ _realign() {
+ this._addTriedPosition(this.position, this.alignment)
+ this.alignment = nextItem(this.alignment, ALIGNMENTS[this.position])
+ }
+
+ _addTriedPosition(position, alignment) {
+ this.triedPositions[position] = this.triedPositions[position] || []
+ this.triedPositions[position].push(alignment);
+ }
+
+ _positionsExhausted() {
+ var isExhausted = true;
+ for(var i = 0; i < POSITIONS.length; i++) {
+ isExhausted = isExhausted && this._alignmentsExhausted(POSITIONS[i]);
+ }
+ return isExhausted;
+ }
+
+ _alignmentsExhausted(position) {
+ return this.triedPositions[position] && this.triedPositions[position].length == ALIGNMENTS[position].length;
+ }
+
+
+ _setPosition($anchor, $element, $parent) {
+ if($anchor.attr('aria-expanded') === 'false'){ return false; }
+ var $eleDims = Box.GetDimensions($element),
+ $anchorDims = Box.GetDimensions($anchor);
+
+
+ $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
+
+ if(!this.options.allowOverlap) {
+ var overlaps = {};
+ var minOverlap = 100000000;
+ // default coordinates to how we start, in case we can't figure out better
+ var minCoordinates = {position: this.position, alignment: this.alignment};
+ while(!this._positionsExhausted()) {
+ let overlap = Box.OverlapArea($element, $parent, false, false, this.options.allowBottomOverlap);
+ if(overlap === 0) {
+ return;
+ }
+
+ if(overlap < minOverlap) {
+ minOverlap = overlap;
+ minCoordinates = {position: this.position, alignment: this.alignment};
+ }
+
+ this._reposition();
+
+ $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
+ }
+ // If we get through the entire loop, there was no non-overlapping
+ // position available. Pick the version with least overlap.
+ this.position = minCoordinates.position;
+ this.alignment = minCoordinates.alignment;
+ $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
+ }
+ }
+
+}
+
+Positionable.defaults = {
+ /**
+ * Position of positionable relative to anchor. Can be left, right, bottom, top, or auto.
+ * @option
+ * @type {string}
+ * @default 'auto'
+ */
+ position: 'auto',
+ /**
+ * Alignment of positionable 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 positionable 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,
+ /**
+ * Number of pixels the positionable should be separated vertically from anchor
+ * @option
+ * @type {number}
+ * @default 0
+ */
+ vOffset: 0,
+ /**
+ * Number of pixels the positionable should be separated horizontally from anchor
+ * @option
+ * @type {number}
+ * @default 0
+ */
+ hOffset: 0,
+}
+
+export {Positionable};
From c2b573ac41040b36c2538a83afbc8909c6550aea Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Wed, 24 May 2017 21:49:58 -0700
Subject: [PATCH 12/25] Add deprecation notice to positionClass on dropdown
---
js/foundation.dropdown.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/js/foundation.dropdown.js b/js/foundation.dropdown.js
index 5de0f472e4..e838de9f8e 100644
--- a/js/foundation.dropdown.js
+++ b/js/foundation.dropdown.js
@@ -341,7 +341,7 @@ Dropdown.defaults = {
*/
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 ''
From 1feba9f4a94034d1f55e7e4bf280dcaa41068cb0 Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Wed, 24 May 2017 21:55:33 -0700
Subject: [PATCH 13/25] First work towards positioning tooltips in the same way
we're doing dropdowns
---
js/foundation.tooltip.js | 140 ++++++++++++++-------------------------
1 file changed, 48 insertions(+), 92 deletions(-)
diff --git a/js/foundation.tooltip.js b/js/foundation.tooltip.js
index 3cb6d6d434..7bd38c392e 100644
--- a/js/foundation.tooltip.js
+++ b/js/foundation.tooltip.js
@@ -2,10 +2,9 @@
import $ from 'jquery';
-import { Box } from './foundation.util.box';
import { GetYoDigits } from './foundation.util.core';
import { MediaQuery } from './foundation.util.mediaQuery';
-import { Plugin } from './foundation.plugin';
+import { Positionable } from './foundation.positionable';
// import "foundation.util.triggers";
@@ -17,7 +16,7 @@ import { Plugin } from './foundation.plugin';
* @requires foundation.util.triggers
*/
-class Tooltip extends Plugin {
+class Tooltip extends Positionable {
/**
* Creates a new instance of a Tooltip.
* @class
@@ -41,7 +40,6 @@ class Tooltip extends Plugin {
_init() {
var elemId = this.$element.attr('aria-describedby') || GetYoDigits(6, 'tooltip');
- this.options.positionClass = this.options.positionClass || this._getPositionClass(this.$element);
this.options.tipText = this.options.tipText || this.$element.attr('title');
this.template = this.options.template ? $(this.options.template) : this._buildTemplate(elemId);
@@ -63,25 +61,20 @@ class Tooltip extends Plugin {
'data-resize': elemId
}).addClass(this.options.triggerClass);
- //helper variables to track movement on collisions
- this.usedPositions = [];
- this.counter = 4;
- this.classChanged = false;
-
+ super._init();
this._events();
}
- /**
- * Grabs the current positioning class, if present, and returns the value or an empty string.
- * @private
- */
- _getPositionClass(element) {
- if (!element) { return ''; }
- // var position = element.attr('class').match(/top|left|right/g);
- var position = element[0].className.match(/\b(top|left|right)\b/g);
- position = position ? position[0] : '';
- return position;
- };
+ _getDefaultPosition() {
+ // handle legacy classnames
+ var position = this.$element[0].className.match(/\b(top|left|right|bottom)\b/g);
+ return position ? position[0] : 'top';
+ }
+
+ _getDefaultAlignment() {
+ return 'center';
+ }
+
/**
* builds the tooltip element, adds attributes, and returns the template.
* @private
@@ -98,74 +91,13 @@ class Tooltip extends Plugin {
return $template;
}
- /**
- * Function that gets called if a collision event is detected.
- * @param {String} position - positioning class to try
- * @private
- */
- _reposition(position) {
- this.usedPositions.push(position ? position : 'bottom');
-
- //default, try switching to opposite side
- if (!position && (this.usedPositions.indexOf('top') < 0)) {
- this.template.addClass('top');
- } else if (position === 'top' && (this.usedPositions.indexOf('bottom') < 0)) {
- this.template.removeClass(position);
- } else if (position === 'left' && (this.usedPositions.indexOf('right') < 0)) {
- this.template.removeClass(position)
- .addClass('right');
- } else if (position === 'right' && (this.usedPositions.indexOf('left') < 0)) {
- this.template.removeClass(position)
- .addClass('left');
- }
-
- //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.template.addClass('left');
- } else if (position === 'top' && (this.usedPositions.indexOf('bottom') > -1) && (this.usedPositions.indexOf('left') < 0)) {
- this.template.removeClass(position)
- .addClass('left');
- } else if (position === 'left' && (this.usedPositions.indexOf('right') > -1) && (this.usedPositions.indexOf('bottom') < 0)) {
- this.template.removeClass(position);
- } else if (position === 'right' && (this.usedPositions.indexOf('left') > -1) && (this.usedPositions.indexOf('bottom') < 0)) {
- this.template.removeClass(position);
- }
- //if nothing cleared, set to bottom
- else {
- this.template.removeClass(position);
- }
- this.classChanged = true;
- this.counter--;
- }
-
/**
* sets the position class of an element and recursively calls itself until there are no more possible positions to attempt, or the tooltip element is no longer colliding.
* if the tooltip is larger than the screen width, default to full width - any user selected margin
* @private
*/
_setPosition() {
- var position = this._getPositionClass(this.template),
- $tipDims = Box.GetDimensions(this.template),
- $anchorDims = Box.GetDimensions(this.$element),
- direction = (position === 'left' ? 'left' : ((position === 'right') ? 'left' : 'top')),
- param = (direction === 'top') ? 'height' : 'width',
- offset = (param === 'height') ? this.options.vOffset : this.options.hOffset,
- _this = this;
-
- if (($tipDims.width >= $tipDims.windowDims.width) || (!this.counter && !Box.ImNotTouchingYou(this.template))) {
- this.template.offset(Box.GetOffsets(this.template, this.$element, 'center bottom', this.options.vOffset, this.options.hOffset, true)).css({
- 'width': $anchorDims.windowDims.width - (this.options.hOffset * 2),
- 'height': 'auto'
- });
- return false;
- }
-
- this.template.offset(Box.GetOffsets(this.template, this.$element,'center ' + (position || 'bottom'), this.options.vOffset, this.options.hOffset));
-
- while(!Box.ImNotTouchingYou(this.template) && this.counter) {
- this._reposition(position);
- this._setPosition();
- }
+ super._setPosition(this.$element, this.template);
}
/**
@@ -183,6 +115,7 @@ class Tooltip extends Plugin {
var _this = this;
this.template.css('visibility', 'hidden').show();
this._setPosition();
+ this.template.removeClass('top bottom left right').addClass(this.position)
/**
* Fires to close all other open tooltips on the page
@@ -221,15 +154,6 @@ class Tooltip extends Plugin {
}).fadeOut(this.options.fadeOutDuration, function() {
_this.isActive = false;
_this.isClick = false;
- if (_this.classChanged) {
- _this.template
- .removeClass(_this._getPositionClass(_this.template))
- .addClass(_this.options.positionClass);
-
- _this.usedPositions = [];
- _this.counter = 4;
- _this.classChanged = false;
- }
});
/**
* fires when the tooltip is hidden
@@ -432,12 +356,44 @@ Tooltip.defaults = {
*/
clickOpen: true,
/**
- * Additional positioning classes, set by the JS
+ * DEPRECATED Additional positioning classes, set by the JS
* @option
* @type {string}
* @default ''
*/
positionClass: '',
+ /**
+ * Position of tooltip. Can be left, right, bottom, top, or auto.
+ * @option
+ * @type {string}
+ * @default 'auto'
+ */
+ position: 'auto',
+ /**
+ * Alignment of tooltip 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, tooltip 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.
+ * Less common for tooltips.
+ * @option
+ * @type {boolean}
+ * @default false
+ */
+ allowBottomOverlap: false,
/**
* Distance, in pixels, the template should push away from the anchor on the Y axis.
* @option
From db3ecbde613e648b607dbd110c558f07a05410a1 Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Wed, 24 May 2017 22:06:41 -0700
Subject: [PATCH 14/25] Change offset behavior in centered case to make it more
intuitive, get tooltips behaving properly
---
js/foundation.positionable.js | 29 ++++++++++++++++++++++++++---
test/visual/dropdown/offsets.html | 10 +++++-----
2 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/js/foundation.positionable.js b/js/foundation.positionable.js
index 81dcea57da..4fde158d0a 100644
--- a/js/foundation.positionable.js
+++ b/js/foundation.positionable.js
@@ -101,13 +101,36 @@ class Positionable extends Plugin {
}
+ // When we're trying to center, we don't want to apply offset that's going to
+ // take us just off center, so wrap around to return 0 for the appropriate
+ // offset in those alignments. TODO: Figure out if we want to make this
+ // configurable behavior... it feels more intuitive, especially for tooltips, but
+ // it's possible someone might actually want to start from center and then nudge
+ // slightly off.
+ _getVOffset() {
+ if(this.alignment === 'center' && (this.position === 'left' || this.position === 'right')) {
+ return 0;
+ } else {
+ return this.options.vOffset;
+ }
+ }
+
+ _getHOffset() {
+ if(this.alignment === 'center' && (this.position === 'top' || this.position === 'bottom')) {
+ return 0;
+ } else {
+ return this.options.hOffset;
+ }
+ }
+
+
_setPosition($anchor, $element, $parent) {
if($anchor.attr('aria-expanded') === 'false'){ return false; }
var $eleDims = Box.GetDimensions($element),
$anchorDims = Box.GetDimensions($anchor);
- $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
+ $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this._getVOffset(), this._getHOffset()));
if(!this.options.allowOverlap) {
var overlaps = {};
@@ -127,13 +150,13 @@ class Positionable extends Plugin {
this._reposition();
- $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
+ $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this._getVOffset(), this._getHOffset()));
}
// If we get through the entire loop, there was no non-overlapping
// position available. Pick the version with least overlap.
this.position = minCoordinates.position;
this.alignment = minCoordinates.alignment;
- $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this.options.vOffset, this.options.hOffset));
+ $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this._getVOffset(), this._getHOffset()));
}
}
diff --git a/test/visual/dropdown/offsets.html b/test/visual/dropdown/offsets.html
index 25ec2a8e08..cbc3f0a948 100644
--- a/test/visual/dropdown/offsets.html
+++ b/test/visual/dropdown/offsets.html
@@ -11,7 +11,7 @@
Dropdown: Explicit Positioning Content - with offsets
-
These dropdowns test various positioning and alignments WITH OFFSETS.
+
These dropdowns test various positioning and alignments WITH OFFSETS.
Valid positions are left/right/top/bottom. Valid alignments are
left/right/top/bottom/center. Left align means left sides should line up.
Right align means right sides should line up. Center align means centers should line up.
@@ -33,7 +33,7 @@
Top and Bottom positioned
Bottom Center
-
This offset should push right and down.
+
This offset should push down and ignore the horizontal offset.
@@ -57,7 +57,7 @@
Top and Bottom positioned
Top Center
-
This offset should push right and up.
+
This offset should push up and ignore the horizontal offset.
@@ -92,14 +92,14 @@
Left and Right Positioned
Right Center
-
This offset should push right and down.
+
This offset should push right and ignore the vertical offset.
Left Center
-
This offset should push left and down.
+
This offset should push left and ignore the vertical offset.
From 978c7dc2bbdaea840990365afb4717b2298dd494 Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Wed, 24 May 2017 22:13:04 -0700
Subject: [PATCH 15/25] Get rid of no longer used method on dropdown
---
js/foundation.dropdown.js | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/js/foundation.dropdown.js b/js/foundation.dropdown.js
index e838de9f8e..8cc99ba2a8 100644
--- a/js/foundation.dropdown.js
+++ b/js/foundation.dropdown.js
@@ -70,21 +70,6 @@ class Dropdown extends Positionable {
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);
From 19290e3cc510d59c202399ccf2c0c27d24e1a420 Mon Sep 17 00:00:00 2001
From: harry
Date: Thu, 25 May 2017 23:53:24 +0530
Subject: [PATCH 16/25] Added test case for explicit positioning!
---
test/visual/tooltip/explicit-positioning.html | 103 ++++++++++++++++++
1 file changed, 103 insertions(+)
create mode 100644 test/visual/tooltip/explicit-positioning.html
diff --git a/test/visual/tooltip/explicit-positioning.html b/test/visual/tooltip/explicit-positioning.html
new file mode 100644
index 0000000000..3c6f19368f
--- /dev/null
+++ b/test/visual/tooltip/explicit-positioning.html
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+ Foundation for Sites Testing
+
+
+
+
+
Tooltips: Explicit Positioning Content - no offsets
+
+
These tooltips test various positioning and alignments. Valid
+ positions are left/right/top/bottom. Valid alignments are
+ left/right/top/bottom/center. Left align means left sides should line up.
+ Right align means right sides should line up. Center align means centers should line up.
+
+
+
Top and Bottom positioned
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Left and Right Positioned
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 3bcc87e3e6cfabac72167669da900b3706650e85 Mon Sep 17 00:00:00 2001
From: Kevin Ball
Date: Wed, 31 May 2017 10:49:32 -0700
Subject: [PATCH 17/25] Fix tooltip positioning to separately account for
voffset/hoffset and tooltip width/height
---
js/foundation.positionable.js | 12 ++--------
js/foundation.tooltip.js | 38 +++++++++++++++++++++++++++----
test/visual/dropdown/offsets.html | 8 +++----
3 files changed, 40 insertions(+), 18 deletions(-)
diff --git a/js/foundation.positionable.js b/js/foundation.positionable.js
index 4fde158d0a..04f7ee3671 100644
--- a/js/foundation.positionable.js
+++ b/js/foundation.positionable.js
@@ -108,19 +108,11 @@ class Positionable extends Plugin {
// it's possible someone might actually want to start from center and then nudge
// slightly off.
_getVOffset() {
- if(this.alignment === 'center' && (this.position === 'left' || this.position === 'right')) {
- return 0;
- } else {
- return this.options.vOffset;
- }
+ return this.options.vOffset;
}
_getHOffset() {
- if(this.alignment === 'center' && (this.position === 'top' || this.position === 'bottom')) {
- return 0;
- } else {
- return this.options.hOffset;
- }
+ return this.options.hOffset;
}
diff --git a/js/foundation.tooltip.js b/js/foundation.tooltip.js
index 7bd38c392e..2da0157723 100644
--- a/js/foundation.tooltip.js
+++ b/js/foundation.tooltip.js
@@ -75,6 +75,22 @@ class Tooltip extends Positionable {
return 'center';
}
+ _getHOffset() {
+ if(this.position === 'left' || this.position === 'right') {
+ return this.options.hOffset + this.options.tooltipWidth;
+ } else {
+ return this.options.hOffset
+ }
+ }
+
+ _getVOffset() {
+ if(this.position === 'top' || this.position === 'bottom') {
+ return this.options.vOffset + this.options.tooltipHeight;
+ } else {
+ return this.options.vOffset
+ }
+ }
+
/**
* builds the tooltip element, adds attributes, and returns the template.
* @private
@@ -398,16 +414,30 @@ Tooltip.defaults = {
* Distance, in pixels, the template should push away from the anchor on the Y axis.
* @option
* @type {number}
- * @default 10
+ * @default 0
+ */
+ vOffset: 0,
+ /**
+ * Distance, in pixels, the template should push away from the anchor on the X axis
+ * @option
+ * @type {number}
+ * @default 0
+ */
+ hOffset: 0,
+ /**
+ * Distance, in pixels, the template spacing auto-adjust for a vertical tooltip
+ * @option
+ * @type {number}
+ * @default 14
*/
- vOffset: 10,
+ tooltipHeight: 14,
/**
- * Distance, in pixels, the template should push away from the anchor on the X axis, if aligned to a side.
+ * Distance, in pixels, the template spacing auto-adjust for a horizontal tooltip
* @option
* @type {number}
* @default 12
*/
- hOffset: 12,
+ tooltipWidth: 12,
/**
* Allow HTML in tooltip. Warning: If you are loading user-generated content into tooltips,
* allowing HTML may open yourself up to XSS attacks.
diff --git a/test/visual/dropdown/offsets.html b/test/visual/dropdown/offsets.html
index cbc3f0a948..6f286bf0d6 100644
--- a/test/visual/dropdown/offsets.html
+++ b/test/visual/dropdown/offsets.html
@@ -33,7 +33,7 @@
Top and Bottom positioned
Bottom Center
-
This offset should push down and ignore the horizontal offset.
+
This offset should push down and to the global right (right)
@@ -57,7 +57,7 @@
Top and Bottom positioned
Top Center
-
This offset should push up and ignore the horizontal offset.
+
This offset should push up and to the global right (right)
@@ -92,14 +92,14 @@
Left and Right Positioned
Right Center
-
This offset should push right and ignore the vertical offset.
+
This offset should push right and down
Left Center
-
This offset should push left and ignore the vertical offset.
+
This offset should push left and down
From 2a947a1462e86eb7c7053f48bee3e5c096388124 Mon Sep 17 00:00:00 2001
From: harry
Date: Wed, 31 May 2017 23:53:18 +0530
Subject: [PATCH 18/25] Dropdown: Add explicit positioning docs!
---
docs/pages/dropdown.md | 182 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 182 insertions(+)
diff --git a/docs/pages/dropdown.md b/docs/pages/dropdown.md
index cd18d1fac2..8dd405f845 100644
--- a/docs/pages/dropdown.md
+++ b/docs/pages/dropdown.md
@@ -92,3 +92,185 @@ Adding `.float-right` or `.float-left` to the anchor will change the direction o
Just some junk that needs to be said. Or not. Your choice.
+
+---
+
+## Explicit Positioning
+
+Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to do the same.
+
+These dropdowns test various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up.
+
+#### Top and Bottom positioned
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
Bottom Left
+
+
+
This dropdown has position bottom and alignment left should align with its top left corner at the bottom left of the button
+
+
+
+
+
Bottom Center
+
+
+
This dropdown has position bottom and alignment center should align below the button with its center aligned with the center of the button
+
+
+
+
+
Bottom Right
+
+
+
This dropdown has position bottom and alignment right should align with its top right corner at the bottom right of the button
+
+
+
+
+
Top Left
+
+
+
This dropdown has position top and alignment left should align with its bottom left corner at the top left of the button
+
+
+
+
+
Top Center
+
+
+
This dropdown has position top and alignment center should align above with its center aligned with the center of the button
+
+
+
+
+
Top Right
+
+
+
This dropdown has position top and alignment right should align with its bottom right corner at the top right of the button
+
+
+
+
+
+
+#### Left and Right Positioned
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
Right Top
+
+
+
This dropdown has position right and alignment top should align with its top left corner at the top right of the button
+
+
+
+
Left Top
+
+
+
This dropdown has position left and alignment top should align with its top right corner at the top left of the button
+
+
+
+
+
Right Center
+
+
+
This dropdown has position right and alignment center should align to the right of the button with the center of the dropdown vertically aligned with the center of the button
+
+
+
+
Left Center
+
+
+
This dropdown has position left and alignment center should align to the left of the button with the center of the dropdown vertically aligned with the center of the button
+
+
+
+
+
Right Bottom
+
+
+
This dropdown has position right and alignment bottom should align with its bottom left corner at the bottom right of the button
+
+
+
+
Left Bottom
+
+
+
This dropdown has position left and alignment bottom should align with its bottom right corner at the bottom left of the button
+
+
+
+
\ No newline at end of file
From 8c947a7b6a339317df14e4ccc8c0de2ce5feb367 Mon Sep 17 00:00:00 2001
From: harry
Date: Wed, 31 May 2017 23:58:48 +0530
Subject: [PATCH 19/25] Docs update!
---
docs/pages/dropdown.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/docs/pages/dropdown.md b/docs/pages/dropdown.md
index 8dd405f845..54f8728c8f 100644
--- a/docs/pages/dropdown.md
+++ b/docs/pages/dropdown.md
@@ -97,7 +97,7 @@ Adding `.float-right` or `.float-left` to the anchor will change the direction o
## Explicit Positioning
-Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to do the same.
+Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions.
These dropdowns test various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up.
@@ -273,4 +273,3 @@ These dropdowns test various positioning and alignments. Valid positions are lef
-
\ No newline at end of file
From 8d566a2279e9247c510982b944ed9684ea4e0cdb Mon Sep 17 00:00:00 2001
From: harry
Date: Wed, 31 May 2017 23:59:17 +0530
Subject: [PATCH 20/25] More docs update
---
docs/pages/dropdown.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/dropdown.md b/docs/pages/dropdown.md
index 54f8728c8f..8dab5ce9c6 100644
--- a/docs/pages/dropdown.md
+++ b/docs/pages/dropdown.md
@@ -97,7 +97,7 @@ Adding `.float-right` or `.float-left` to the anchor will change the direction o
## Explicit Positioning
-Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions.
+Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions of the box.
These dropdowns test various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up.
From b05a847ecee2006ca03b0f01ae2d6c1f7510a7f2 Mon Sep 17 00:00:00 2001
From: harry
Date: Thu, 1 Jun 2017 00:00:53 +0530
Subject: [PATCH 21/25] More grammar corrections!
---
docs/pages/dropdown.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/pages/dropdown.md b/docs/pages/dropdown.md
index 8dab5ce9c6..7f57f68d99 100644
--- a/docs/pages/dropdown.md
+++ b/docs/pages/dropdown.md
@@ -97,7 +97,7 @@ Adding `.float-right` or `.float-left` to the anchor will change the direction o
## Explicit Positioning
-Won't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions of the box.
+Wouldn't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions of the box.
These dropdowns test various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up.
From b9c5d12e82c8f25c4d51d503c88f9f2550fdbc69 Mon Sep 17 00:00:00 2001
From: harry
Date: Thu, 1 Jun 2017 00:12:19 +0530
Subject: [PATCH 22/25] Highlight new feature in 6.4!
---
docs/pages/dropdown.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/docs/pages/dropdown.md b/docs/pages/dropdown.md
index 7f57f68d99..d026b17f09 100644
--- a/docs/pages/dropdown.md
+++ b/docs/pages/dropdown.md
@@ -97,6 +97,10 @@ Adding `.float-right` or `.float-left` to the anchor will change the direction o
## Explicit Positioning
+
+
New in v6.4: Heads up! This explicit positioning model is a new feature in v6.4.
+
+
Wouldn't it be great if you can define both positions at the dropdown element. Dropdown has a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions of the box.
These dropdowns test various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up.
From be677c5270e599b4592bb889ae8f39b9902d78fc Mon Sep 17 00:00:00 2001
From: harry
Date: Thu, 1 Jun 2017 01:11:29 +0530
Subject: [PATCH 23/25] Add tooltip docs for explicit positioning!
---
docs/pages/tooltip.md | 143 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 143 insertions(+)
diff --git a/docs/pages/tooltip.md b/docs/pages/tooltip.md
index 05185fffb7..2bd8298cb9 100644
--- a/docs/pages/tooltip.md
+++ b/docs/pages/tooltip.md
@@ -95,3 +95,146 @@ home by force; but I was too well assured of the old negro's disposition, to hop
index
of his fortune." Upon the whole, I was sadly vexed and puzzled, but, at length, I concluded to make a virtue of necessity -to dig with a good will, and thus the sooner to convince the visionary, by ocular demonstration, of the fallacy of the opinions he entertained.
```
+
+---
+
+## Explicit Positioning
+
+
+
New in v6.4: Heads up! This explicit positioning model is a new feature in v6.4.
+
+
+Now with tooltips you can define both positions for the tip. These tooltips have a fully explicit positioning model through which you can use both `data-position` and `data-alignment` to define both positions of the box.
+
+These dropdowns sets various positioning and alignments. Valid positions are left/right/top/bottom. Valid alignments are left/right/top/bottom/center. Left align means left sides should line up. Right align means right sides should line up. Center align means centers should line up.
+
+#### Top and Bottom positioned
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#### Left and Right Positioned
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+```
+
+