1+ import PositionCollision from "./Enumerators/PositionCollision" ;
12import { Alignments } from "./Interfaces/Alignments" ;
3+ import FitPositionData from "./Interfaces/FitPositionData" ;
4+ import PositionOptions from "./Interfaces/PositionOptions" ;
25import * as types from "./Types/AlignmentTypes" ;
36
47export class HoverPosition {
58 private _bodyDims : ElementDimensions ;
69 private _anchorDims : ElementDimensions ;
710 private _hoverBoxDims : ElementDimensions ;
811
12+ /**
13+ * The top to be applied to the element
14+ */
915 top : string ;
16+
17+ /**
18+ * The left to be applied to the element
19+ */
1020 left : string ;
1121
22+ /**
23+ * Get the position placement for one element relative to another
24+ * @param options The options to help attain the `top` & `left`
25+ */
1226 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" ;
1529
1630 this . _bodyDims = {
1731 height : document . body . offsetHeight ,
1832 width : document . body . offsetWidth ,
1933 top : 0 ,
2034 left : 0 ,
2135 } ;
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" ;
3052
3153 this . _hoverBoxDims = {
32- height : options . hoverBox . offsetHeight ,
33- width : options . hoverBox . offsetWidth ,
54+ height : options . target . offsetHeight ,
55+ width : options . target . offsetWidth ,
3456 top : 0 ,
3557 left : 0 ,
3658 } ;
3759
38- options . hoverBox . style . display = originalDisplay ;
60+ options . target . style . display = originalDisplay ;
3961
4062 const myPos = HoverPosition . parse (
4163 options . my ,
@@ -68,15 +90,13 @@ export class HoverPosition {
6890 }
6991 }
7092
71- // eslint-disable-next-line complexity
7293 private calculatePosition (
7394 my : CombinedAlignment ,
7495 at : CombinedAlignment ,
7596 options : PositionOptions
7697 ) : 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 ] ;
80100
81101 if (
82102 options . collision === PositionCollision . ignore ||
@@ -86,16 +106,16 @@ export class HoverPosition {
86106 }
87107
88108 if ( options . collision === PositionCollision . flipfit ) {
89- const newOptions = Object . assign ( options , { collision : PositionCollision . ignore } ) ;
90-
91109 return this . calculatePosition (
92110 HoverPosition . flip ( my ) ,
93111 HoverPosition . flip ( at ) ,
94- newOptions
112+ Object . assign ( options , { collision : PositionCollision . ignore } )
95113 ) ;
96114 }
97115
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+ ) ;
99119
100120 if ( myFits . length === 0 ) {
101121 return { top : fitData . top . value , left : fitData . left . value } ;
@@ -123,55 +143,52 @@ export class HoverPosition {
123143 } else if ( bestAltHorizFits . length > 0 ) {
124144 myFits = bestAltHorizFits ;
125145 } else {
126- let tempFits : FitPositionData [ ] ;
127-
128146 if ( options . bestFitPreference === "vertical" ) {
129147 // If it's center then we don't care about overlay. Infact, it's prefered!
130148 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 (
140158 ( f ) => f . my . endsWith ( " " + flippedMy ) && f . at . endsWith ( " " + flippedAt )
141159 ) ;
142160
143- if ( tempFits . length === 0 && bothCenter ) {
161+ if ( myFits . length === 0 && bothCenter ) {
144162 // What about to the right?
145- tempFits = myFits . filter (
163+ myFits = myFits . filter (
146164 ( f ) =>
147165 f . my . endsWith ( " " + HoverPosition . flipAlignment ( flippedMy ) ) &&
148166 f . at . endsWith ( " " + HoverPosition . flipAlignment ( flippedAt ) )
149167 ) ;
150168 }
151169 } 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 + " " )
163181 ) ;
164182
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 (
167186 ( 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 ) + " " )
170189 ) ;
171190 }
172191 }
173-
174- myFits = tempFits ;
175192 }
176193
177194 if ( myFits . length === 0 ) {
@@ -265,7 +282,7 @@ export class HoverPosition {
265282
266283 const willCollide = top < 0 || top + this . _hoverBoxDims . height > this . _bodyDims . height ;
267284
268- return { value : top , willCollide : willCollide } ;
285+ return { value : top , willCollide : willCollide , mayOverflow : myV === "top" } ;
269286 }
270287
271288 private calculateLeft (
@@ -368,39 +385,6 @@ export class HoverPosition {
368385 }
369386}
370387
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-
404388export type PositionAlignment = types . PositionAlignment ;
405389export type CombinedAlignment = types . CombinedAlignment ;
406390export type VerticalAlignment = types . VerticalAlignment ;
0 commit comments