Skip to content
This repository was archived by the owner on Dec 12, 2022. It is now read-only.

Commit d52972e

Browse files
committed
Large Overhaul
Separated the various elements into their own files Tweaked the extension and added some comments for clarity
1 parent d2d7248 commit d52972e

File tree

8 files changed

+112
-92
lines changed

8 files changed

+112
-92
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
2-
"name": "hoverposition",
3-
"version": "0.0.0-alpha.0",
2+
"name": "hover-position",
3+
"version": "0.0.0-alpha.1",
44
"description": "A compact tool to help position an element against another. Similar to JQuery `.position()`",
55
"main": "src/index.js",
66
"scripts": {
77
"test": "echo \"Error: no test specified\" && exit 1"
88
},
99
"repository": {
1010
"type": "git",
11-
"url": "git+https://github.com/TopMarksDevelopment/npm-hover-position.git"
11+
"url": "git+https://github.com/TopMarksDevelopment/JavaScript.HoverPosition.git"
1212
},
1313
"keywords": [
1414
"JQuery position",
@@ -18,9 +18,9 @@
1818
"author": "TopMarksDevelopment",
1919
"license": "MIT",
2020
"bugs": {
21-
"url": "https://github.com/TopMarksDevelopment/npm-hover-position/issues"
21+
"url": "https://github.com/TopMarksDevelopment/JavaScript.HoverPosition/issues"
2222
},
23-
"homepage": "https://github.com/TopMarksDevelopment/npm-hover-position#readme",
23+
"homepage": "https://github.com/TopMarksDevelopment/JavaScript.HoverPosition#readme",
2424
"devDependencies": {
2525
"typescript": "^4.2.4"
2626
}

src/Enumerators/PositionCollision.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
enum PositionCollision {
2+
bestFit,
3+
flipfit,
4+
ignore,
5+
}
6+
7+
export default PositionCollision;

src/Interfaces/CalculationOutcome.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
interface CalculationOutcome {
2+
value: number;
3+
willCollide: boolean;
4+
mayOverflow?: boolean;
5+
}

src/Interfaces/FitPosition.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
interface FitPosition {
2+
top: number;
3+
left: number;
4+
}

src/Interfaces/FitPositionData.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { CombinedAlignment } from "../Types/AlignmentTypes";
2+
3+
export default interface FitPositionData {
4+
my: CombinedAlignment;
5+
at: CombinedAlignment;
6+
top: CalculationOutcome;
7+
left: CalculationOutcome;
8+
}

src/Interfaces/PositionOptions.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import PositionCollision from "../Enumerators/PositionCollision";
2+
import { CombinedAlignment, PositionAlignment } from "../Types/AlignmentTypes";
3+
4+
export default interface PositionOptions {
5+
my: PositionAlignment;
6+
at: PositionAlignment;
7+
anchor: HTMLElement | MouseEvent;
8+
target: HTMLElement;
9+
collision?: PositionCollision;
10+
bestFitPreference?: "horizontal" | "vertical";
11+
defaults?: { my: CombinedAlignment; at: CombinedAlignment };
12+
}

src/index.ts

Lines changed: 69 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,63 @@
1+
import PositionCollision from "./Enumerators/PositionCollision";
12
import { Alignments } from "./Interfaces/Alignments";
3+
import FitPositionData from "./Interfaces/FitPositionData";
4+
import PositionOptions from "./Interfaces/PositionOptions";
25
import * as types from "./Types/AlignmentTypes";
36

47
export 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-
404388
export type PositionAlignment = types.PositionAlignment;
405389
export type CombinedAlignment = types.CombinedAlignment;
406390
export type VerticalAlignment = types.VerticalAlignment;

0 commit comments

Comments
 (0)