1
+ import PositionCollision from "./Enumerators/PositionCollision" ;
1
2
import { Alignments } from "./Interfaces/Alignments" ;
3
+ import FitPositionData from "./Interfaces/FitPositionData" ;
4
+ import PositionOptions from "./Interfaces/PositionOptions" ;
2
5
import * as types from "./Types/AlignmentTypes" ;
3
6
4
7
export class HoverPosition {
5
8
private _bodyDims : ElementDimensions ;
6
9
private _anchorDims : ElementDimensions ;
7
10
private _hoverBoxDims : ElementDimensions ;
8
11
12
+ /**
13
+ * The top to be applied to the element
14
+ */
9
15
top : string ;
16
+
17
+ /**
18
+ * The left to be applied to the element
19
+ */
10
20
left : string ;
11
21
22
+ /**
23
+ * Get the position placement for one element relative to another
24
+ * @param options The options to help attain the `top` & `left`
25
+ */
12
26
constructor ( options : PositionOptions ) {
13
- const originalDisplay = options . hoverBox . style . display ;
14
- options . hoverBox . style . display = "none" ;
27
+ const originalDisplay = options . target . style . display ;
28
+ options . target . style . display = "none" ;
15
29
16
30
this . _bodyDims = {
17
31
height : document . body . offsetHeight ,
18
32
width : document . body . offsetWidth ,
19
33
top : 0 ,
20
34
left : 0 ,
21
35
} ;
22
- this . _anchorDims = {
23
- height : options . anchor . offsetHeight ,
24
- width : options . anchor . offsetWidth ,
25
- top : options . anchor . offsetTop ,
26
- left : options . anchor . offsetLeft ,
27
- } ;
28
-
29
- options . hoverBox . style . display = "block" ;
36
+ this . _anchorDims =
37
+ options . anchor instanceof MouseEvent
38
+ ? {
39
+ height : 10 ,
40
+ width : 10 ,
41
+ top : options . anchor . screenX ,
42
+ left : options . anchor . screenY ,
43
+ }
44
+ : {
45
+ height : options . anchor . offsetHeight ,
46
+ width : options . anchor . offsetWidth ,
47
+ top : options . anchor . offsetTop ,
48
+ left : options . anchor . offsetLeft ,
49
+ } ;
50
+
51
+ options . target . style . display = "block" ;
30
52
31
53
this . _hoverBoxDims = {
32
- height : options . hoverBox . offsetHeight ,
33
- width : options . hoverBox . offsetWidth ,
54
+ height : options . target . offsetHeight ,
55
+ width : options . target . offsetWidth ,
34
56
top : 0 ,
35
57
left : 0 ,
36
58
} ;
37
59
38
- options . hoverBox . style . display = originalDisplay ;
60
+ options . target . style . display = originalDisplay ;
39
61
40
62
const myPos = HoverPosition . parse (
41
63
options . my ,
@@ -68,15 +90,13 @@ export class HoverPosition {
68
90
}
69
91
}
70
92
71
- // eslint-disable-next-line complexity
72
93
private calculatePosition (
73
94
my : CombinedAlignment ,
74
95
at : CombinedAlignment ,
75
96
options : PositionOptions
76
97
) : FitPosition {
77
- const fitDataArray = this . getFitPositions ( ) ;
78
-
79
- const fitData = fitDataArray . filter ( ( f ) => f . my === my && f . at === at ) [ 0 ] ;
98
+ const fitDataArray = this . getFitPositions ( ) ,
99
+ fitData = fitDataArray . filter ( ( f ) => f . my === my && f . at === at ) [ 0 ] ;
80
100
81
101
if (
82
102
options . collision === PositionCollision . ignore ||
@@ -86,16 +106,16 @@ export class HoverPosition {
86
106
}
87
107
88
108
if ( options . collision === PositionCollision . flipfit ) {
89
- const newOptions = Object . assign ( options , { collision : PositionCollision . ignore } ) ;
90
-
91
109
return this . calculatePosition (
92
110
HoverPosition . flip ( my ) ,
93
111
HoverPosition . flip ( at ) ,
94
- newOptions
112
+ Object . assign ( options , { collision : PositionCollision . ignore } )
95
113
) ;
96
114
}
97
115
98
- let myFits = fitDataArray . filter ( ( f ) => ! f . top . willCollide && ! f . left . willCollide ) ;
116
+ let myFits = fitDataArray . filter (
117
+ ( f ) => ( ! f . top . willCollide || f . top . mayOverflow ) && ! f . left . willCollide
118
+ ) ;
99
119
100
120
if ( myFits . length === 0 ) {
101
121
return { top : fitData . top . value , left : fitData . left . value } ;
@@ -123,55 +143,52 @@ export class HoverPosition {
123
143
} else if ( bestAltHorizFits . length > 0 ) {
124
144
myFits = bestAltHorizFits ;
125
145
} else {
126
- let tempFits : FitPositionData [ ] ;
127
-
128
146
if ( options . bestFitPreference === "vertical" ) {
129
147
// If it's center then we don't care about overlay. Infact, it's prefered!
130
148
const bothCenter =
131
- parsedMy . horizontal === "center" && parsedAt . horizontal === "center" ;
132
- const flippedMy = bothCenter
133
- ? "left" // <= Does pushing to the left fit?
134
- : HoverPosition . flipAlignment ( parsedMy . horizontal ) ;
135
- const flippedAt = bothCenter
136
- ? "left" // <= Does pushing to the left fit?
137
- : HoverPosition . flipAlignment ( parsedMy . horizontal ) ;
138
-
139
- tempFits = myFits . filter (
149
+ parsedMy . horizontal === "center" && parsedAt . horizontal === "center" ,
150
+ flippedMy = bothCenter
151
+ ? "left" // <= Does pushing to the left fit?
152
+ : HoverPosition . flipAlignment ( parsedMy . horizontal ) ,
153
+ flippedAt = bothCenter
154
+ ? "left" // <= Does pushing to the left fit?
155
+ : HoverPosition . flipAlignment ( parsedMy . horizontal ) ;
156
+
157
+ myFits = myFits . filter (
140
158
( f ) => f . my . endsWith ( " " + flippedMy ) && f . at . endsWith ( " " + flippedAt )
141
159
) ;
142
160
143
- if ( tempFits . length === 0 && bothCenter ) {
161
+ if ( myFits . length === 0 && bothCenter ) {
144
162
// What about to the right?
145
- tempFits = myFits . filter (
163
+ myFits = myFits . filter (
146
164
( f ) =>
147
165
f . my . endsWith ( " " + HoverPosition . flipAlignment ( flippedMy ) ) &&
148
166
f . at . endsWith ( " " + HoverPosition . flipAlignment ( flippedAt ) )
149
167
) ;
150
168
}
151
169
} else {
152
- const bothCenter = parsedMy . vertical === "center" && parsedAt . vertical === "center" ;
153
- const flippedMy = bothCenter
154
- ? "top"
155
- : HoverPosition . flipAlignment ( parsedMy . vertical ) ;
156
-
157
- const flippedAt = bothCenter
158
- ? "top"
159
- : HoverPosition . flipAlignment ( parsedMy . vertical ) ;
160
-
161
- tempFits = myFits . filter (
162
- ( f ) => f . my . endsWith ( " " + flippedMy ) && f . at . endsWith ( " " + flippedAt )
170
+ // If it's center then we don't care about overlay. Infact, it's prefered!
171
+ const bothCenter = parsedMy . vertical === "center" && parsedAt . vertical === "center" ,
172
+ flippedMy = bothCenter
173
+ ? "top" // <= Does pushing to the top fit?
174
+ : HoverPosition . flipAlignment ( parsedMy . vertical ) ,
175
+ flippedAt = bothCenter
176
+ ? "top" // <= Does pushing to the top fit?
177
+ : HoverPosition . flipAlignment ( parsedMy . vertical ) ;
178
+
179
+ myFits = myFits . filter (
180
+ ( f ) => f . my . startsWith ( flippedMy + " " ) && f . at . startsWith ( flippedAt + " " )
163
181
) ;
164
182
165
- if ( tempFits . length === 0 && bothCenter ) {
166
- tempFits = myFits . filter (
183
+ if ( myFits . length === 0 && bothCenter ) {
184
+ // What about to the bottom?
185
+ myFits = myFits . filter (
167
186
( f ) =>
168
- f . my . endsWith ( " " + HoverPosition . flipAlignment ( flippedMy ) ) &&
169
- f . at . endsWith ( " " + HoverPosition . flipAlignment ( flippedAt ) )
187
+ f . my . startsWith ( HoverPosition . flipAlignment ( flippedMy ) + " " ) &&
188
+ f . at . startsWith ( HoverPosition . flipAlignment ( flippedAt ) + " " )
170
189
) ;
171
190
}
172
191
}
173
-
174
- myFits = tempFits ;
175
192
}
176
193
177
194
if ( myFits . length === 0 ) {
@@ -265,7 +282,7 @@ export class HoverPosition {
265
282
266
283
const willCollide = top < 0 || top + this . _hoverBoxDims . height > this . _bodyDims . height ;
267
284
268
- return { value : top , willCollide : willCollide } ;
285
+ return { value : top , willCollide : willCollide , mayOverflow : myV === "top" } ;
269
286
}
270
287
271
288
private calculateLeft (
@@ -368,39 +385,6 @@ export class HoverPosition {
368
385
}
369
386
}
370
387
371
- interface CalculationOutcome {
372
- value : number ;
373
- willCollide : boolean ;
374
- }
375
-
376
- interface FitPosition {
377
- top : number ;
378
- left : number ;
379
- }
380
-
381
- export interface PositionOptions {
382
- my : PositionAlignment ;
383
- at : PositionAlignment ;
384
- anchor : HTMLElement ;
385
- hoverBox : HTMLElement ;
386
- collision ?: PositionCollision ;
387
- bestFitPreference ?: "horizontal" | "vertical" ;
388
- defaults ?: { my : CombinedAlignment ; at : CombinedAlignment } ;
389
- }
390
-
391
- export enum PositionCollision {
392
- bestFit ,
393
- flipfit ,
394
- ignore ,
395
- }
396
-
397
- export interface FitPositionData {
398
- my : CombinedAlignment ;
399
- at : CombinedAlignment ;
400
- top : CalculationOutcome ;
401
- left : CalculationOutcome ;
402
- }
403
-
404
388
export type PositionAlignment = types . PositionAlignment ;
405
389
export type CombinedAlignment = types . CombinedAlignment ;
406
390
export type VerticalAlignment = types . VerticalAlignment ;
0 commit comments