Skip to content

Commit caacc4e

Browse files
committed
v14.4.3
1 parent 22b7f48 commit caacc4e

File tree

7 files changed

+269
-6
lines changed

7 files changed

+269
-6
lines changed

build/cjs/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/esm/index.mjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "gleap",
3-
"version": "14.4.2",
3+
"version": "14.4.3",
44
"main": "build/cjs/index.js",
55
"module": "build/esm/index.mjs",
66
"exports": {
@@ -71,7 +71,6 @@
7171
},
7272
"dependencies": {
7373
"@floating-ui/dom": "^1.6.3",
74-
"pick-dom-element": "^0.2.3",
7574
"unique-selector": "^0.5.0"
7675
}
7776
}

published/14.4.3/index.js

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

published/latest/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ElementPicker.js

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/**
2+
* Utility function to get the bounding rectangle of an element.
3+
* @param {HTMLElement} el - The target element.
4+
* @returns {Object} An object containing x, y, width, and height.
5+
*/
6+
function getElementBounds(el) {
7+
const rect = el.getBoundingClientRect();
8+
return {
9+
x: window.pageXOffset + rect.left,
10+
y: window.pageYOffset + rect.top,
11+
width: el.offsetWidth,
12+
height: el.offsetHeight,
13+
};
14+
}
15+
16+
/**
17+
* Class representing an overlay element.
18+
*/
19+
class ElementOverlay {
20+
/**
21+
* Create an ElementOverlay.
22+
* @param {Object} options - Configuration options.
23+
* @param {string} [options.className] - CSS class name for the overlay.
24+
* @param {Object} [options.style] - Style overrides for the overlay.
25+
*/
26+
constructor(options) {
27+
// Create and style the overlay element.
28+
this.overlay = document.createElement("div");
29+
this.overlay.className = options.className || "_ext-element-overlay";
30+
this.overlay.style.background =
31+
(options.style && options.style.background) || "rgba(250, 240, 202, 0.2)";
32+
this.overlay.style.borderColor =
33+
(options.style && options.style.borderColor) || "#F95738";
34+
this.overlay.style.borderStyle =
35+
(options.style && options.style.borderStyle) || "solid";
36+
this.overlay.style.borderRadius =
37+
(options.style && options.style.borderRadius) || "1px";
38+
this.overlay.style.borderWidth =
39+
(options.style && options.style.borderWidth) || "1px";
40+
this.overlay.style.boxSizing =
41+
(options.style && options.style.boxSizing) || "border-box";
42+
this.overlay.style.cursor =
43+
(options.style && options.style.cursor) || "crosshair";
44+
this.overlay.style.position =
45+
(options.style && options.style.position) || "absolute";
46+
this.overlay.style.zIndex =
47+
(options.style && options.style.zIndex) || "2147483647";
48+
this.overlay.style.margin =
49+
(options.style && options.style.margin) || "0px";
50+
this.overlay.style.padding =
51+
(options.style && options.style.padding) || "0px";
52+
53+
// Create a container that will host a Shadow DOM.
54+
this.shadowContainer = document.createElement("div");
55+
this.shadowContainer.className = "_ext-element-overlay-container";
56+
this.shadowContainer.style.position = "absolute";
57+
this.shadowContainer.style.top = "0px";
58+
this.shadowContainer.style.left = "0px";
59+
this.shadowContainer.style.margin = "0px";
60+
this.shadowContainer.style.padding = "0px";
61+
this.shadowRoot = this.shadowContainer.attachShadow({ mode: "open" });
62+
}
63+
64+
/**
65+
* Adds the overlay to the DOM.
66+
* @param {Node} parent - The parent element.
67+
* @param {boolean} useShadowDOM - Whether to use Shadow DOM.
68+
*/
69+
addToDOM(parent, useShadowDOM) {
70+
this.usingShadowDOM = useShadowDOM;
71+
if (useShadowDOM) {
72+
parent.insertBefore(this.shadowContainer, parent.firstChild);
73+
this.shadowRoot.appendChild(this.overlay);
74+
} else {
75+
parent.appendChild(this.overlay);
76+
}
77+
}
78+
79+
/**
80+
* Removes the overlay from the DOM.
81+
*/
82+
removeFromDOM() {
83+
this.setBounds({ x: 0, y: 0, width: 0, height: 0 });
84+
if (this.overlay.remove) {
85+
this.overlay.remove();
86+
} else if (this.overlay.parentNode) {
87+
this.overlay.parentNode.removeChild(this.overlay);
88+
}
89+
if (this.usingShadowDOM) {
90+
if (this.shadowContainer.remove) {
91+
this.shadowContainer.remove();
92+
} else if (this.shadowContainer.parentNode) {
93+
this.shadowContainer.parentNode.removeChild(this.shadowContainer);
94+
}
95+
}
96+
}
97+
98+
/**
99+
* Enables pointer events on the overlay.
100+
*/
101+
captureCursor() {
102+
this.overlay.style.pointerEvents = "auto";
103+
}
104+
105+
/**
106+
* Disables pointer events on the overlay.
107+
*/
108+
ignoreCursor() {
109+
this.overlay.style.pointerEvents = "none";
110+
}
111+
112+
/**
113+
* Sets the position and size of the overlay.
114+
* @param {Object} bounds - The bounding box.
115+
* @param {number} bounds.x - The x-coordinate.
116+
* @param {number} bounds.y - The y-coordinate.
117+
* @param {number} bounds.width - The width.
118+
* @param {number} bounds.height - The height.
119+
*/
120+
setBounds(bounds) {
121+
const { x, y, width, height } = bounds;
122+
this.overlay.style.left = x + "px";
123+
this.overlay.style.top = y + "px";
124+
this.overlay.style.width = width + "px";
125+
this.overlay.style.height = height + "px";
126+
}
127+
}
128+
129+
/**
130+
* Class representing an element picker that uses the overlay.
131+
*/
132+
class ElementPicker {
133+
/**
134+
* Create an ElementPicker.
135+
* @param {Object} [overlayOptions] - Options to pass to ElementOverlay.
136+
*/
137+
constructor(overlayOptions) {
138+
this.active = false;
139+
this.overlay = new ElementOverlay(overlayOptions || {});
140+
this.handleMouseMove = this.handleMouseMove.bind(this);
141+
this.handleClick = this.handleClick.bind(this);
142+
this.tick = this.tick.bind(this);
143+
}
144+
145+
/**
146+
* Starts the element picking process.
147+
* @param {Object} options - Picker options.
148+
* @param {Node} [options.parentElement] - Parent element to attach the overlay.
149+
* @param {boolean} [options.useShadowDOM=true] - Whether to use Shadow DOM.
150+
* @param {Function} [options.onClick] - Callback invoked on click.
151+
* @param {Function} [options.onHover] - Callback invoked on hover.
152+
* @param {Function} [options.elementFilter] - Function to filter elements.
153+
* @returns {boolean} True if started successfully, false if already active.
154+
*/
155+
start(options) {
156+
if (this.active) {
157+
return false;
158+
}
159+
this.active = true;
160+
this.options = options;
161+
162+
document.addEventListener("mousemove", this.handleMouseMove, true);
163+
document.addEventListener("click", this.handleClick, true);
164+
165+
const parentElement = options.parentElement || document.body;
166+
const useShadowDOM =
167+
options.useShadowDOM !== undefined ? options.useShadowDOM : true;
168+
this.overlay.addToDOM(parentElement, useShadowDOM);
169+
this.tick();
170+
171+
return true;
172+
}
173+
174+
/**
175+
* Stops the element picking process.
176+
*/
177+
stop() {
178+
this.active = false;
179+
this.options = undefined;
180+
181+
document.removeEventListener("mousemove", this.handleMouseMove, true);
182+
document.removeEventListener("click", this.handleClick, true);
183+
184+
this.overlay.removeFromDOM();
185+
this.target = undefined;
186+
this.mouseX = undefined;
187+
this.mouseY = undefined;
188+
189+
if (this.tickReq) {
190+
window.cancelAnimationFrame(this.tickReq);
191+
}
192+
}
193+
194+
/**
195+
* Handles mouse move events.
196+
* @param {MouseEvent} event - The mousemove event.
197+
*/
198+
handleMouseMove(event) {
199+
this.mouseX = event.clientX;
200+
this.mouseY = event.clientY;
201+
}
202+
203+
/**
204+
* Handles click events.
205+
* @param {MouseEvent} event - The click event.
206+
*/
207+
handleClick(event) {
208+
event.preventDefault();
209+
event.stopPropagation();
210+
event.stopImmediatePropagation();
211+
212+
if (this.target && this.options && this.options.onClick) {
213+
this.options.onClick(this.target);
214+
}
215+
}
216+
217+
/**
218+
* The tick function that continuously updates the target element.
219+
*/
220+
tick() {
221+
this.updateTarget();
222+
this.tickReq = window.requestAnimationFrame(this.tick);
223+
}
224+
225+
/**
226+
* Updates the target element based on the current mouse position.
227+
*/
228+
updateTarget() {
229+
if (this.mouseX === undefined || this.mouseY === undefined) {
230+
return;
231+
}
232+
233+
// Temporarily ignore the overlay's pointer events to detect the underlying element.
234+
this.overlay.ignoreCursor();
235+
const elAtCursor = document.elementFromPoint(this.mouseX, this.mouseY);
236+
const newTarget = elAtCursor;
237+
this.overlay.captureCursor();
238+
239+
// If the target hasn't changed, do nothing.
240+
if (!newTarget || newTarget === this.target) {
241+
return;
242+
}
243+
244+
// If an element filter is provided and the new target doesn't match, clear the target.
245+
if (this.options && this.options.elementFilter) {
246+
if (!this.options.elementFilter(newTarget)) {
247+
this.target = undefined;
248+
this.overlay.setBounds({ x: 0, y: 0, width: 0, height: 0 });
249+
return;
250+
}
251+
}
252+
253+
this.target = newTarget;
254+
const bounds = getElementBounds(newTarget);
255+
this.overlay.setBounds(bounds);
256+
257+
if (this.options && this.options.onHover) {
258+
this.options.onHover(newTarget);
259+
}
260+
}
261+
}
262+
263+
export default ElementPicker;

src/GleapAdminHelper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { ElementPicker } from "pick-dom-element";
21
import unique from "unique-selector";
2+
import ElementPicker from "./ElementPicker";
33

44
class GleapAdminHelper {
55
picker = null;

0 commit comments

Comments
 (0)