1 | 'use strict';
|
2 |
|
3 | import { Box } from './foundation.util.box';
|
4 | import { Plugin } from './foundation.plugin';
|
5 | import { rtl as Rtl } from './foundation.util.core';
|
6 |
|
7 | const POSITIONS = ['left', 'right', 'top', 'bottom'];
|
8 | const VERTICAL_ALIGNMENTS = ['top', 'bottom', 'center'];
|
9 | const HORIZONTAL_ALIGNMENTS = ['left', 'right', 'center'];
|
10 |
|
11 | const ALIGNMENTS = {
|
12 | 'left': VERTICAL_ALIGNMENTS,
|
13 | 'right': VERTICAL_ALIGNMENTS,
|
14 | 'top': HORIZONTAL_ALIGNMENTS,
|
15 | 'bottom': HORIZONTAL_ALIGNMENTS
|
16 | }
|
17 |
|
18 | function nextItem(item, array) {
|
19 | var currentIdx = array.indexOf(item);
|
20 | if(currentIdx === array.length - 1) {
|
21 | return array[0];
|
22 | } else {
|
23 | return array[currentIdx + 1];
|
24 | }
|
25 | }
|
26 |
|
27 |
|
28 | class Positionable extends Plugin {
|
29 | |
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | _init() {
|
40 | this.triedPositions = {};
|
41 | this.position = this.options.position === 'auto' ? this._getDefaultPosition() : this.options.position;
|
42 | this.alignment = this.options.alignment === 'auto' ? this._getDefaultAlignment() : this.options.alignment;
|
43 | }
|
44 |
|
45 | _getDefaultPosition () {
|
46 | return 'bottom';
|
47 | }
|
48 |
|
49 | _getDefaultAlignment() {
|
50 | switch(this.position) {
|
51 | case 'bottom':
|
52 | case 'top':
|
53 | return Rtl() ? 'right' : 'left';
|
54 | case 'left':
|
55 | case 'right':
|
56 | return 'bottom';
|
57 | }
|
58 | }
|
59 |
|
60 | |
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 | _reposition() {
|
67 | if(this._alignmentsExhausted(this.position)) {
|
68 | this.position = nextItem(this.position, POSITIONS);
|
69 | this.alignment = ALIGNMENTS[this.position][0];
|
70 | } else {
|
71 | this._realign();
|
72 | }
|
73 | }
|
74 |
|
75 | |
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 | _realign() {
|
82 | this._addTriedPosition(this.position, this.alignment)
|
83 | this.alignment = nextItem(this.alignment, ALIGNMENTS[this.position])
|
84 | }
|
85 |
|
86 | _addTriedPosition(position, alignment) {
|
87 | this.triedPositions[position] = this.triedPositions[position] || []
|
88 | this.triedPositions[position].push(alignment);
|
89 | }
|
90 |
|
91 | _positionsExhausted() {
|
92 | var isExhausted = true;
|
93 | for(var i = 0; i < POSITIONS.length; i++) {
|
94 | isExhausted = isExhausted && this._alignmentsExhausted(POSITIONS[i]);
|
95 | }
|
96 | return isExhausted;
|
97 | }
|
98 |
|
99 | _alignmentsExhausted(position) {
|
100 | return this.triedPositions[position] && this.triedPositions[position].length == ALIGNMENTS[position].length;
|
101 | }
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 | _getVOffset() {
|
111 | return this.options.vOffset;
|
112 | }
|
113 |
|
114 | _getHOffset() {
|
115 | return this.options.hOffset;
|
116 | }
|
117 |
|
118 |
|
119 | _setPosition($anchor, $element, $parent) {
|
120 | if($anchor.attr('aria-expanded') === 'false'){ return false; }
|
121 | var $eleDims = Box.GetDimensions($element),
|
122 | $anchorDims = Box.GetDimensions($anchor);
|
123 |
|
124 |
|
125 | $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this._getVOffset(), this._getHOffset()));
|
126 |
|
127 | if(!this.options.allowOverlap) {
|
128 | var overlaps = {};
|
129 | var minOverlap = 100000000;
|
130 |
|
131 | var minCoordinates = {position: this.position, alignment: this.alignment};
|
132 | while(!this._positionsExhausted()) {
|
133 | let overlap = Box.OverlapArea($element, $parent, false, false, this.options.allowBottomOverlap);
|
134 | if(overlap === 0) {
|
135 | return;
|
136 | }
|
137 |
|
138 | if(overlap < minOverlap) {
|
139 | minOverlap = overlap;
|
140 | minCoordinates = {position: this.position, alignment: this.alignment};
|
141 | }
|
142 |
|
143 | this._reposition();
|
144 |
|
145 | $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this._getVOffset(), this._getHOffset()));
|
146 | }
|
147 |
|
148 |
|
149 | this.position = minCoordinates.position;
|
150 | this.alignment = minCoordinates.alignment;
|
151 | $element.offset(Box.GetExplicitOffsets($element, $anchor, this.position, this.alignment, this._getVOffset(), this._getHOffset()));
|
152 | }
|
153 | }
|
154 |
|
155 | }
|
156 |
|
157 | Positionable.defaults = {
|
158 | |
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | position: 'auto',
|
165 | |
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 | alignment: 'auto',
|
172 | |
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 | allowOverlap: false,
|
181 | |
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 | allowBottomOverlap: true,
|
190 | |
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 | vOffset: 0,
|
197 | |
198 |
|
199 |
|
200 |
|
201 |
|
202 |
|
203 | hOffset: 0,
|
204 | }
|
205 |
|
206 | export {Positionable};
|