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

Commit 54a19a9

Browse files
feat: contextmenu handler
1 parent 6dd3573 commit 54a19a9

8 files changed

Lines changed: 492 additions & 21 deletions

File tree

example.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ Promise.all([
2525

2626
console.log("clicked on Shape", feature, e);
2727
},
28+
contextMenu: (e, feature): void => {
29+
e.originalEvent.preventDefault(); // Prevent the default context menu from showing
30+
L.popup()
31+
.setLatLng(e.latlng)
32+
.setContent(`You right clicked on ${feature.properties.NAZKR_ENG}`)
33+
.openOn(map);
34+
35+
console.log("clicked on Shape", feature, e);
36+
},
2837
hover: (e: LeafletMouseEvent, feature) => {
2938
console.log("hovered on Shape", feature, e);
3039
},
@@ -45,6 +54,14 @@ Promise.all([
4554

4655
console.log("clicked on Line", feature, e);
4756
},
57+
contextMenu: (e: LeafletMouseEvent, feature) => {
58+
e.originalEvent.preventDefault(); // Prevent the default context menu from showing
59+
//set up a standalone popup (use a popup as a layer)
60+
L.popup()
61+
.setLatLng(e.latlng)
62+
.setContent(`right clicked on Line ${feature.properties.name}`)
63+
.openOn(map);
64+
},
4865
hover: (e: LeafletMouseEvent, feature) => {
4966
console.log("hovered on Line", feature, e);
5067
},
@@ -73,6 +90,16 @@ Promise.all([
7390

7491
console.log("clicked on Point", feature, e);
7592
},
93+
contextMenu: (e: LeafletMouseEvent, feature) => {
94+
e.originalEvent.preventDefault(); // Prevent the default context menu from showing
95+
//set up a standalone popup (use a popup as a layer)
96+
L.popup()
97+
.setLatLng(feature)
98+
.setContent(
99+
`You right clicked the point at longitude:${e.latlng.lng}, latitude:${e.latlng.lat}`
100+
)
101+
.openOn(map);
102+
},
76103
data: points,
77104
});
78105

@@ -99,6 +126,18 @@ Promise.all([
99126

100127
console.log("clicked on Point", feature, e);
101128
},
129+
contextMenu: (e: LeafletMouseEvent, feature) => {
130+
e.originalEvent.preventDefault(); // Prevent the default context menu from showing
131+
//set up a standalone popup (use a popup as a layer)
132+
L.popup()
133+
.setLatLng(feature)
134+
.setContent(
135+
`You right clicked the point at longitude:${e.latlng.lng}, latitude:${e.latlng.lat}`
136+
)
137+
.openOn(map);
138+
139+
console.log("clicked on Point", feature, e);
140+
},
102141
hover: (e: LeafletMouseEvent, feature) => {
103142
console.log("hovered on Point", feature, e);
104143
},
@@ -130,6 +169,16 @@ Promise.all([
130169

131170
console.log("clicked on Point", feature, e);
132171
},
172+
contextMenu: (e, feature) => {
173+
e.originalEvent.preventDefault(); // Prevent the default context menu from showing
174+
//set up a standalone popup (use a popup as a layer)
175+
L.popup()
176+
.setLatLng(feature.geometry.coordinates)
177+
.setContent("You right clicked on:" + feature.properties.name)
178+
.openOn(map);
179+
180+
console.log("clicked on Point", feature, e);
181+
},
133182
data: {
134183
//geojson
135184
type: "FeatureCollection",
@@ -172,6 +221,15 @@ Promise.all([
172221

173222
console.log("clicked on Shape", feature, e);
174223
},
224+
contextMenu: (e, feature) => {
225+
e.originalEvent.preventDefault(); // Prevent the default context menu from showing
226+
L.popup()
227+
.setLatLng(e.latlng)
228+
.setContent(`You right clicked on ${feature.properties.name}`)
229+
.openOn(map);
230+
231+
console.log("clicked on Shape", feature, e);
232+
},
175233
hover: (e, feature) => {
176234
console.log("hovered on Shape", feature, e);
177235
},

src/base-gl-layer.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { LeafletMouseEvent, Map } from "leaflet";
22

33
import { IColor } from "./color";
4-
import { IPixel } from "./pixel"
4+
import { IPixel } from "./pixel";
55
import { CanvasOverlay, ICanvasOverlayDrawEvent } from "./canvas-overlay";
66
import { notProperlyDefined } from "./errors";
77
import { MapMatrix } from "./map-matrix";
@@ -34,13 +34,15 @@ export interface IBaseGlLayerSettings {
3434
[name: string]: IShaderVariable;
3535
};
3636
setupClick?: (map: Map) => void;
37+
setupContextMenu?: (map: Map) => void;
3738
setupHover?: SetupHoverCallback;
3839
sensitivity?: number;
3940
sensitivityHover?: number;
4041
vertexShaderSource?: (() => string) | string;
4142
fragmentShaderSource?: (() => string) | string;
4243
canvas?: HTMLCanvasElement;
4344
click?: EventCallback;
45+
contextMenu?: EventCallback;
4446
hover?: EventCallback;
4547
hoverOff?: EventCallback;
4648
color?: ColorCallback | IColor | null;
@@ -59,7 +61,7 @@ export const defaults: Partial<IBaseGlLayerSettings> = {
5961
export type ColorCallback = (featureIndex: number, feature: any) => IColor;
6062

6163
export abstract class BaseGlLayer<
62-
T extends IBaseGlLayerSettings = IBaseGlLayerSettings
64+
T extends IBaseGlLayerSettings = IBaseGlLayerSettings,
6365
> {
6466
bytes = 0;
6567
active: boolean;
@@ -160,10 +162,10 @@ export abstract class BaseGlLayer<
160162
this.matrix = null;
161163
this.vertices = null;
162164
this.vertexLines = null;
163-
try{
164-
this.mapCenterPixels = this.map.project(this.map.getCenter(), 0)
165-
} catch(err){
166-
this.mapCenterPixels = {x:-0,y:-0}
165+
try {
166+
this.mapCenterPixels = this.map.project(this.map.getCenter(), 0);
167+
} catch (err) {
168+
this.mapCenterPixels = { x: -0, y: -0 };
167169
}
168170
const preserveDrawingBuffer = Boolean(settings.preserveDrawingBuffer);
169171
const layer = (this.layer = new CanvasOverlay(
@@ -235,6 +237,9 @@ export abstract class BaseGlLayer<
235237
if (settings.click && settings.setupClick) {
236238
settings.setupClick(this.map);
237239
}
240+
if (settings.contextMenu && settings.setupContextMenu) {
241+
settings.setupContextMenu(this.map);
242+
}
238243
if (settings.hover && settings.setupHover) {
239244
settings.setupHover(this.map, this.hoverWait);
240245
}
@@ -417,6 +422,14 @@ export abstract class BaseGlLayer<
417422
}
418423
}
419424

425+
contextMenu(e: LeafletMouseEvent, feature: any): boolean | undefined {
426+
if (!this.settings.contextMenu) return;
427+
const result = this.settings.contextMenu(e, feature);
428+
if (result !== undefined) {
429+
return result;
430+
}
431+
}
432+
420433
hover(e: LeafletMouseEvent, feature: any): boolean | undefined {
421434
if (!this.settings.hover) return;
422435
const result = this.settings.hover(e, feature);

src/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class Glify {
2929
longitudeKey = 1;
3030
latitudeKey = 0;
3131
clickSetupMaps: Map[] = [];
32+
contextMenuSetupMaps: Map[] = [];
3233
hoverSetupMaps: Map[] = [];
3334
shader = shader;
3435

@@ -63,6 +64,7 @@ export class Glify {
6364
points(settings: Partial<IPointsSettings>): Points {
6465
const points = new this.Points({
6566
setupClick: this.setupClick.bind(this),
67+
setupContextMenu: this.setupContextMenu.bind(this),
6668
setupHover: this.setupHover.bind(this),
6769
latitudeKey: glify.latitudeKey,
6870
longitudeKey: glify.longitudeKey,
@@ -81,6 +83,7 @@ export class Glify {
8183
lines(settings: Partial<ILinesSettings>): Lines {
8284
const lines = new this.Lines({
8385
setupClick: this.setupClick.bind(this),
86+
setupContextMenu: this.setupContextMenu.bind(this),
8487
setupHover: this.setupHover.bind(this),
8588
latitudeKey: this.latitudeKey,
8689
longitudeKey: this.longitudeKey,
@@ -99,6 +102,7 @@ export class Glify {
99102
shapes(settings: Partial<IShapesSettings>): Shapes {
100103
const shapes = new this.Shapes({
101104
setupClick: this.setupClick.bind(this),
105+
setupContextMenu: this.setupContextMenu.bind(this),
102106
setupHover: this.setupHover.bind(this),
103107
latitudeKey: this.latitudeKey,
104108
longitudeKey: this.longitudeKey,
@@ -130,6 +134,23 @@ export class Glify {
130134
});
131135
}
132136

137+
setupContextMenu(map: Map): void {
138+
if (this.contextMenuSetupMaps.includes(map)) return;
139+
this.clickSetupMaps.push(map);
140+
map.on("contextmenu", (e: LeafletMouseEvent) => {
141+
e.originalEvent.preventDefault(); // Prevent the default context menu from showing
142+
let hit;
143+
hit = this.Points.tryContextMenu(e, map, this.pointsInstances);
144+
if (hit !== undefined) return hit;
145+
146+
hit = this.Lines.tryContextMenu(e, map, this.linesInstances);
147+
if (hit !== undefined) return hit;
148+
149+
hit = this.Shapes.tryContextMenu(e, map, this.shapesInstances);
150+
if (hit !== undefined) return hit;
151+
});
152+
}
153+
133154
setupHover(map: Map, hoverWait?: number, immediate?: false): void {
134155
if (this.hoverSetupMaps.includes(map)) return;
135156
this.hoverSetupMaps.push(map);

src/lines.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,89 @@ export class Lines extends BaseGlLayer<ILinesSettings> {
411411
}
412412
}
413413

414+
// attempts to click the top-most Lines instance
415+
static tryContextMenu(
416+
e: LeafletMouseEvent,
417+
map: Map,
418+
instances: Lines[]
419+
): boolean | undefined {
420+
let foundFeature: Feature<LineString | MultiLineString> | null = null;
421+
let foundLines: Lines | null = null;
422+
423+
instances.forEach((instance: Lines): void => {
424+
const { latitudeKey, longitudeKey, sensitivity, weight, scale, active } =
425+
instance;
426+
if (!active) return;
427+
if (instance.map !== map) return;
428+
function checkContextMenu(
429+
coordinate: Position,
430+
prevCoordinate: Position,
431+
feature: Feature<LineString | MultiLineString>,
432+
chosenWeight: number
433+
): void {
434+
const distance = latLngDistance(
435+
e.latlng.lng,
436+
e.latlng.lat,
437+
prevCoordinate[longitudeKey],
438+
prevCoordinate[latitudeKey],
439+
coordinate[longitudeKey],
440+
coordinate[latitudeKey]
441+
);
442+
if (distance <= sensitivity + chosenWeight / scale) {
443+
foundFeature = feature;
444+
foundLines = instance;
445+
}
446+
}
447+
instance.data.features.forEach(
448+
(feature: Feature<LineString | MultiLineString>, i: number): void => {
449+
const chosenWeight =
450+
typeof weight === "function" ? weight(i, feature) : weight;
451+
const { coordinates, type } = feature.geometry;
452+
if (type === "LineString") {
453+
for (let i = 1; i < coordinates.length; i++) {
454+
checkContextMenu(
455+
coordinates[i] as Position,
456+
coordinates[i - 1] as Position,
457+
feature,
458+
chosenWeight
459+
);
460+
}
461+
} else if (type === "MultiLineString") {
462+
// TODO: Unit test
463+
for (let i = 0; i < coordinates.length; i++) {
464+
const coordinate = coordinates[i];
465+
for (let j = 0; j < coordinate.length; j++) {
466+
if (j === 0 && i > 0) {
467+
const prevCoordinates = coordinates[i - 1];
468+
const lastPositions =
469+
prevCoordinates[prevCoordinates.length - 1];
470+
checkContextMenu(
471+
lastPositions as Position,
472+
coordinates[i][j] as Position,
473+
feature,
474+
chosenWeight
475+
);
476+
} else if (j > 0) {
477+
checkContextMenu(
478+
coordinates[i][j] as Position,
479+
coordinates[i][j - 1] as Position,
480+
feature,
481+
chosenWeight
482+
);
483+
}
484+
}
485+
}
486+
}
487+
}
488+
);
489+
});
490+
491+
if (foundLines && foundFeature) {
492+
const result = (foundLines as Lines).contextMenu(e, foundFeature);
493+
return result !== undefined ? result : undefined;
494+
}
495+
}
496+
414497
hoveringFeatures: Array<Feature<LineString | MultiLineString>> = [];
415498
// hovers all touching Lines instances
416499
static tryHover(

0 commit comments

Comments
 (0)