ol-contextmenu 5.4.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +141 -171
- package/dist/ol-contextmenu.d.ts +136 -3
- package/dist/ol-contextmenu.iife.js +4 -3
- package/dist/ol-contextmenu.js +256 -241
- package/dist/ol-contextmenu.umd.cjs +4 -3
- package/package.json +49 -39
- package/dist/constants.d.ts +0 -14
- package/dist/helpers/dom.d.ts +0 -18
- package/dist/main.d.ts +0 -55
- package/dist/ol-contextmenu.css +0 -8
- package/dist/types.d.ts +0 -55
package/README.md
CHANGED
|
@@ -1,253 +1,223 @@
|
|
|
1
|
-
# OpenLayers
|
|
1
|
+
# 🗺️ OpenLayers Context Menu
|
|
2
|
+
|
|
3
|
+
> A customizable, feature-rich context menu extension for OpenLayers maps
|
|
2
4
|
|
|
3
5
|
<p align="center">
|
|
4
|
-
<a href="https://github.com/jonataswalker/ol-contextmenu/actions/workflows/
|
|
5
|
-
<img src="https://github.com/jonataswalker/ol-contextmenu/actions/workflows/
|
|
6
|
+
<a href="https://github.com/jonataswalker/ol-contextmenu/actions/workflows/ci.yml">
|
|
7
|
+
<img src="https://github.com/jonataswalker/ol-contextmenu/actions/workflows/ci.yml/badge.svg?branch=master" alt="Build Status">
|
|
6
8
|
</a>
|
|
7
9
|
<a href="https://www.npmjs.com/package/ol-contextmenu">
|
|
8
10
|
<img src="https://img.shields.io/npm/v/ol-contextmenu.svg" alt="npm version">
|
|
9
11
|
</a>
|
|
10
12
|
<a href="https://img.shields.io/npm/dm/ol-contextmenu">
|
|
11
|
-
<img alt="npm" src="https://img.shields.io/npm/dm/ol-contextmenu">
|
|
13
|
+
<img alt="npm downloads" src="https://img.shields.io/npm/dm/ol-contextmenu">
|
|
12
14
|
</a>
|
|
13
15
|
<a href="https://github.com/jonataswalker/ol-contextmenu/blob/master/LICENSE">
|
|
14
|
-
<img src="https://img.shields.io/npm/l/ol-contextmenu.svg" alt="
|
|
16
|
+
<img src="https://img.shields.io/npm/l/ol-contextmenu.svg" alt="MIT License">
|
|
15
17
|
</a>
|
|
16
18
|
</p>
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
<p align="center">
|
|
21
|
+
<img src="https://raw.githubusercontent.com/jonataswalker/ol-contextmenu/screenshot/images/anim.gif" alt="Context Menu Demo" />
|
|
22
|
+
</p>
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
**Quick Links:** [Demo](#-demo) • [Installation](#-installation) • [Quick Start](#-quick-start) • [API](#-api) • [TypeScript](#-typescript) • [Examples](#-examples)
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
[CodeSandbox](https://codesandbox.io/s/openlayers-custom-context-menu-5s99kb?file=/src/index.js)
|
|
26
|
+
---
|
|
26
27
|
|
|
27
|
-
##
|
|
28
|
+
## ✨ Features
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
- 🎯 **Easy Integration** - Works seamlessly with OpenLayers 7.x - 10.x
|
|
31
|
+
- 🎨 **Fully Customizable** - Control width, icons, styles, and appearance
|
|
32
|
+
- 🔗 **Nested Submenus** - Unlimited nesting levels for complex menu structures
|
|
33
|
+
- 📦 **Zero CSS Dependencies** - Styles are bundled inline (since v6.0)
|
|
34
|
+
- 🎪 **Event-Driven** - React to `beforeopen`, `open`, and `close` events
|
|
35
|
+
- 🌐 **Multiple Trigger Types** - Context menu, click, or double-click
|
|
36
|
+
- 📱 **Viewport-Aware** - Automatically repositions to stay visible at screen edges
|
|
37
|
+
- 💪 **TypeScript Support** - Full type definitions included out of the box
|
|
38
|
+
- ⚡ **Lightweight** - Minimal footprint with only one dependency (`tiny-emitter`)
|
|
39
|
+
- ✅ **Well Tested** - 191 tests covering all functionality
|
|
30
40
|
|
|
31
|
-
|
|
41
|
+
## 🎪 Demo
|
|
32
42
|
|
|
33
|
-
|
|
43
|
+
**Try it live:**
|
|
44
|
+
- 🎮 [CodeSandbox](https://codesandbox.io/s/openlayers-custom-context-menu-5s99kb?file=/src/index.js) - Interactive demo with full source
|
|
45
|
+
- 📝 [JSFiddle](https://jsfiddle.net/jonataswalker/ooxs1w5d/) - Quick playground
|
|
46
|
+
- 💻 [Local Examples](./examples) - Webpack, Vite, and CDN examples
|
|
34
47
|
|
|
35
|
-
|
|
48
|
+
## 📦 Installation
|
|
36
49
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
**npm (Recommended):**
|
|
51
|
+
```bash
|
|
52
|
+
npm install ol ol-contextmenu
|
|
40
53
|
```
|
|
41
54
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
```HTML
|
|
47
|
-
<link href="https://unpkg.com/ol-contextmenu/dist/ol-contextmenu.css" rel="stylesheet">
|
|
48
|
-
<script src="https://unpkg.com/ol-contextmenu"></script>
|
|
55
|
+
**CDN:**
|
|
56
|
+
```html
|
|
57
|
+
<script src="https://cdn.jsdelivr.net/npm/ol-contextmenu"></script>
|
|
49
58
|
```
|
|
50
59
|
|
|
51
|
-
|
|
60
|
+
**Requires:** OpenLayers 7.0.0 or higher
|
|
52
61
|
|
|
53
|
-
|
|
62
|
+
📖 See [Getting Started Guide](docs/getting-started.md) for detailed installation instructions and setup.
|
|
54
63
|
|
|
55
|
-
|
|
64
|
+
## 🚀 Quick Start
|
|
56
65
|
|
|
57
66
|
```javascript
|
|
67
|
+
import ContextMenu from 'ol-contextmenu';
|
|
68
|
+
|
|
58
69
|
const contextmenu = new ContextMenu({
|
|
59
70
|
width: 170,
|
|
60
|
-
defaultItems: true, //
|
|
71
|
+
defaultItems: true, // Includes Zoom In/Zoom Out
|
|
61
72
|
items: [
|
|
62
73
|
{
|
|
63
74
|
text: 'Center map here',
|
|
64
|
-
|
|
65
|
-
|
|
75
|
+
callback: (obj, map) => {
|
|
76
|
+
map.getView().setCenter(obj.coordinate);
|
|
77
|
+
},
|
|
66
78
|
},
|
|
67
79
|
{
|
|
68
80
|
text: 'Add a Marker',
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
icon: 'img/marker.png', // this can be relative or absolute
|
|
72
|
-
callback: marker,
|
|
81
|
+
icon: 'img/marker.png',
|
|
82
|
+
callback: addMarker,
|
|
73
83
|
},
|
|
74
|
-
'-', //
|
|
84
|
+
'-', // Separator
|
|
75
85
|
],
|
|
76
86
|
});
|
|
87
|
+
|
|
77
88
|
map.addControl(contextmenu);
|
|
78
89
|
```
|
|
79
90
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
If you provide `items {Array}` a submenu will be created as a child of the current item.
|
|
91
|
+
📖 See [Getting Started Guide](docs/getting-started.md) for complete setup instructions and [Examples](docs/examples.md) for advanced patterns.
|
|
83
92
|
|
|
84
|
-
|
|
85
|
-
const all_items = [
|
|
86
|
-
{
|
|
87
|
-
text: 'Some Actions',
|
|
88
|
-
items: [
|
|
89
|
-
// <== this is a submenu
|
|
90
|
-
{
|
|
91
|
-
text: 'Action 1',
|
|
92
|
-
callback: action,
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
text: 'Other action',
|
|
96
|
-
callback: action2,
|
|
97
|
-
},
|
|
98
|
-
],
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
text: 'Add a Marker',
|
|
102
|
-
icon: 'img/marker.png',
|
|
103
|
-
callback: marker,
|
|
104
|
-
},
|
|
105
|
-
'-', // this is a separator
|
|
106
|
-
];
|
|
107
|
-
```
|
|
93
|
+
## 📖 Documentation
|
|
108
94
|
|
|
109
|
-
|
|
95
|
+
- **[Getting Started](docs/getting-started.md)** - Installation and basic setup
|
|
96
|
+
- **[API Reference](docs/api-reference.md)** - Complete API documentation
|
|
97
|
+
- **[TypeScript Guide](docs/typescript.md)** - TypeScript usage and type definitions
|
|
98
|
+
- **[Examples & Recipes](docs/examples.md)** - Common patterns and code examples
|
|
99
|
+
- **[Troubleshooting](docs/troubleshooting.md)** - Common issues and solutions
|
|
110
100
|
|
|
111
|
-
|
|
112
|
-
const removeMarker = function (obj) {
|
|
113
|
-
vectorLayer.getSource().removeFeature(obj.data.marker);
|
|
114
|
-
};
|
|
115
|
-
const removeMarkerItem = {
|
|
116
|
-
text: 'Remove this Marker',
|
|
117
|
-
icon: 'img/marker.png',
|
|
118
|
-
callback: removeMarker,
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
let restore = false;
|
|
122
|
-
contextmenu.on('open', function (evt) {
|
|
123
|
-
const feature = map.forEachFeatureAtPixel(evt.pixel, function (ft, l) {
|
|
124
|
-
return ft;
|
|
125
|
-
});
|
|
126
|
-
if (feature) {
|
|
127
|
-
contextmenu.clear();
|
|
128
|
-
removeMarkerItem.data = { marker: feature };
|
|
129
|
-
contextmenu.push(removeMarkerItem);
|
|
130
|
-
restore = true;
|
|
131
|
-
} else if (restore) {
|
|
132
|
-
contextmenu.clear();
|
|
133
|
-
contextmenu.extend(contextmenu_items);
|
|
134
|
-
contextmenu.extend(contextmenu.getDefaultItems());
|
|
135
|
-
restore = false;
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
```
|
|
101
|
+
## 📘 TypeScript
|
|
139
102
|
|
|
140
|
-
|
|
103
|
+
Full TypeScript support with comprehensive type definitions included:
|
|
141
104
|
|
|
142
|
-
|
|
105
|
+
```typescript
|
|
106
|
+
import ContextMenu, { type Item } from 'ol-contextmenu';
|
|
143
107
|
|
|
144
|
-
|
|
108
|
+
const items: Item[] = [
|
|
109
|
+
{
|
|
110
|
+
text: 'Center map here',
|
|
111
|
+
callback: (obj, map) => {
|
|
112
|
+
map.getView().setCenter(obj.coordinate);
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
];
|
|
145
116
|
|
|
146
|
-
|
|
117
|
+
const contextmenu = new ContextMenu({ width: 170, items });
|
|
118
|
+
```
|
|
147
119
|
|
|
148
|
-
|
|
149
|
-
- `defaultItems`: `true`; Whether the default items (which are: Zoom In/Out) are enabled
|
|
150
|
-
- `width`: `150`; The menu's width
|
|
151
|
-
- `items`: `[]`; An array of object|string
|
|
120
|
+
📖 See the [TypeScript Guide](docs/typescript.md) for complete type definitions and usage patterns.
|
|
152
121
|
|
|
153
|
-
##
|
|
122
|
+
## 🎯 API
|
|
154
123
|
|
|
155
|
-
|
|
124
|
+
### Constructor
|
|
156
125
|
|
|
157
|
-
|
|
126
|
+
```javascript
|
|
127
|
+
new ContextMenu(options)
|
|
128
|
+
```
|
|
158
129
|
|
|
159
|
-
|
|
130
|
+
**Options:**
|
|
131
|
+
- `width` (number, default: `150`) - Menu width in pixels
|
|
132
|
+
- `defaultItems` (boolean, default: `true`) - Include default Zoom In/Out items
|
|
133
|
+
- `items` (Item[], default: `[]`) - Array of menu items
|
|
134
|
+
- `eventType` (string, default: `'contextmenu'`) - Event type to trigger menu
|
|
160
135
|
|
|
161
|
-
|
|
136
|
+
### Methods
|
|
162
137
|
|
|
163
|
-
|
|
138
|
+
- `clear()` - Remove all items
|
|
139
|
+
- `close()` - Close menu programmatically
|
|
140
|
+
- `extend(items)` - Add multiple items
|
|
141
|
+
- `push(item)` - Add single item
|
|
142
|
+
- `pop()` / `shift()` - Remove items
|
|
143
|
+
- `getDefaultItems()` - Get default items array
|
|
144
|
+
- `isOpen()` - Check if menu is open
|
|
145
|
+
- `enable()` / `disable()` - Control menu state
|
|
164
146
|
|
|
165
|
-
|
|
147
|
+
### Events
|
|
166
148
|
|
|
167
|
-
|
|
149
|
+
- `beforeopen` - Fired before menu opens
|
|
150
|
+
- `open` - Fired when menu opens
|
|
151
|
+
- `close` - Fired when menu closes
|
|
168
152
|
|
|
169
|
-
|
|
153
|
+
📖 See [API Reference](docs/api-reference.md) for complete documentation with examples.
|
|
170
154
|
|
|
171
|
-
|
|
172
|
-
const contextmenu = new ContextMenu();
|
|
173
|
-
map.addControl(contextmenu);
|
|
155
|
+
## 🎨 Examples
|
|
174
156
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
callback: marker,
|
|
181
|
-
},
|
|
182
|
-
];
|
|
183
|
-
contextmenu.extend(add_later);
|
|
184
|
-
```
|
|
157
|
+
- **[Examples & Recipes](docs/examples.md)** - Common patterns and code examples
|
|
158
|
+
- **[Local Examples](./examples)** - Complete working projects:
|
|
159
|
+
- [CDN Example](./examples/contextmenu.html) - No build tools required
|
|
160
|
+
- [Webpack Example](./examples/my-project-with-webpack) - Integration with Webpack
|
|
161
|
+
- [Vite Example](./examples/my-project-with-vite) - Modern setup with Vite + TypeScript
|
|
185
162
|
|
|
186
|
-
|
|
163
|
+
## 🔧 Troubleshooting
|
|
187
164
|
|
|
188
|
-
|
|
165
|
+
Common issues:
|
|
166
|
+
- **Menu doesn't appear:** Ensure `map.addControl(contextmenu)` is called
|
|
167
|
+
- **Menu cut off:** Update to v5.5.0+ for automatic viewport positioning
|
|
168
|
+
- **TypeScript errors:** Use `import ContextMenu, { type Item } from 'ol-contextmenu'`
|
|
189
169
|
|
|
190
|
-
|
|
170
|
+
📖 See the [Troubleshooting Guide](docs/troubleshooting.md) for detailed solutions and common issues.
|
|
191
171
|
|
|
192
|
-
|
|
172
|
+
## 🌍 Browser Support
|
|
193
173
|
|
|
194
|
-
|
|
174
|
+
Modern browsers supporting ES6+:
|
|
175
|
+
- Chrome/Edge (latest)
|
|
176
|
+
- Firefox (latest)
|
|
177
|
+
- Safari (latest)
|
|
195
178
|
|
|
196
|
-
|
|
179
|
+
## 🤝 Contributing
|
|
197
180
|
|
|
198
|
-
|
|
181
|
+
Contributions are welcome! We appreciate:
|
|
182
|
+
- 🐛 Bug reports
|
|
183
|
+
- 💡 Feature requests
|
|
184
|
+
- 📝 Documentation improvements
|
|
185
|
+
- ✨ Code contributions
|
|
199
186
|
|
|
200
|
-
|
|
187
|
+
Please read our [Contributing Guidelines](CONTRIBUTING.md) before submitting a PR.
|
|
201
188
|
|
|
202
|
-
|
|
189
|
+
### Development Setup
|
|
203
190
|
|
|
204
|
-
|
|
191
|
+
```bash
|
|
192
|
+
# Clone the repository
|
|
193
|
+
git clone https://github.com/jonataswalker/ol-contextmenu.git
|
|
194
|
+
cd ol-contextmenu
|
|
205
195
|
|
|
206
|
-
|
|
196
|
+
# Install dependencies
|
|
197
|
+
npm install
|
|
207
198
|
|
|
208
|
-
|
|
199
|
+
# Run tests
|
|
200
|
+
npm test
|
|
209
201
|
|
|
210
|
-
|
|
202
|
+
# Start development server
|
|
203
|
+
npm run dev
|
|
211
204
|
|
|
212
|
-
|
|
205
|
+
# Build for production
|
|
206
|
+
npm run build
|
|
207
|
+
```
|
|
213
208
|
|
|
214
|
-
##
|
|
209
|
+
## 📄 License
|
|
215
210
|
|
|
216
|
-
|
|
211
|
+
MIT © [Jonatas Walker](https://github.com/jonataswalker)
|
|
217
212
|
|
|
218
|
-
|
|
219
|
-
contextmenu.on('beforeopen', function (evt) {
|
|
220
|
-
const feature = map.forEachFeatureAtPixel(evt.pixel, function (ft, l) {
|
|
221
|
-
return ft;
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
if (feature) {
|
|
225
|
-
// open only on features
|
|
226
|
-
contextmenu.enable();
|
|
227
|
-
} else {
|
|
228
|
-
contextmenu.disable();
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
```
|
|
213
|
+
## 🙏 Acknowledgments
|
|
232
214
|
|
|
233
|
-
|
|
215
|
+
Built with ❤️ for the [OpenLayers](https://openlayers.org/) community.
|
|
234
216
|
|
|
235
|
-
|
|
236
|
-
contextmenu.on('open', function (evt) {
|
|
237
|
-
const feature = map.forEachFeatureAtPixel(evt.pixel, function (ft, l) {
|
|
238
|
-
return ft;
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
if (feature) {
|
|
242
|
-
// add some other items to the menu
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
```
|
|
217
|
+
Special thanks to all [contributors](https://github.com/jonataswalker/ol-contextmenu/graphs/contributors) who have helped improve this project.
|
|
246
218
|
|
|
247
|
-
|
|
219
|
+
---
|
|
248
220
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
});
|
|
253
|
-
```
|
|
221
|
+
<p align="center">
|
|
222
|
+
<sub>Made with ❤️ by the community</sub>
|
|
223
|
+
</p>
|
package/dist/ol-contextmenu.d.ts
CHANGED
|
@@ -1,3 +1,136 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
|
|
1
|
+
import { CombinedOnSignature } from 'ol/Observable';
|
|
2
|
+
import { Coordinate } from 'ol/coordinate';
|
|
3
|
+
import { default as default_2 } from 'ol/control/Control';
|
|
4
|
+
import { default as default_3 } from 'ol/Map';
|
|
5
|
+
import { default as default_4 } from 'ol/events/Event';
|
|
6
|
+
import { default as default_5 } from 'ol/MapBrowserEvent';
|
|
7
|
+
import { EventsKey } from 'ol/events';
|
|
8
|
+
import { EventTypes as EventTypes_2 } from 'ol/Observable';
|
|
9
|
+
import { ObjectEvent } from 'ol/Object';
|
|
10
|
+
import { OnSignature } from 'ol/Observable';
|
|
11
|
+
import { Pixel } from 'ol/pixel';
|
|
12
|
+
import { TinyEmitter } from 'tiny-emitter';
|
|
13
|
+
import { Types } from 'ol/ObjectEventType';
|
|
14
|
+
|
|
15
|
+
export declare type CallbackObject = {
|
|
16
|
+
coordinate: Coordinate;
|
|
17
|
+
data: unknown;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
declare class ContextMenu extends default_2 {
|
|
21
|
+
protected map: default_3;
|
|
22
|
+
protected emitter: TinyEmitter;
|
|
23
|
+
protected container: HTMLDivElement;
|
|
24
|
+
protected coordinate: Coordinate;
|
|
25
|
+
protected pixel: Pixel;
|
|
26
|
+
protected contextMenuEventListener: (evt: MouseEvent) => void;
|
|
27
|
+
protected entryCallbackEventListener: (evt: MouseEvent) => void;
|
|
28
|
+
protected mapMoveListener: () => void;
|
|
29
|
+
protected lineHeight: number;
|
|
30
|
+
protected disabled: boolean;
|
|
31
|
+
protected opened: boolean;
|
|
32
|
+
protected items: Item[];
|
|
33
|
+
protected menuEntries: Map<string, MenuEntry>;
|
|
34
|
+
on: OnSignature<EventTypes_2 | `${CustomEventTypes.CLOSE}`, default_4, EventsKey> & OnSignature<`${CustomEventTypes.OPEN}` | `${CustomEventTypes.BEFOREOPEN}`, ContextMenuEvent, EventsKey> & OnSignature<Types, ObjectEvent, EventsKey> & CombinedOnSignature<EventTypes_2 | Types | `${CustomEventTypes.OPEN}` | `${CustomEventTypes.CLOSE}` | `${CustomEventTypes.BEFOREOPEN}`, EventsKey>;
|
|
35
|
+
once: OnSignature<EventTypes_2 | `${CustomEventTypes.CLOSE}`, default_4, EventsKey> & OnSignature<`${CustomEventTypes.OPEN}` | `${CustomEventTypes.BEFOREOPEN}`, ContextMenuEvent, EventsKey> & OnSignature<Types, ObjectEvent, EventsKey> & CombinedOnSignature<EventTypes_2 | Types | `${CustomEventTypes.OPEN}` | `${CustomEventTypes.CLOSE}` | `${CustomEventTypes.BEFOREOPEN}`, EventsKey>;
|
|
36
|
+
un: OnSignature<EventTypes_2 | `${CustomEventTypes.CLOSE}`, default_4, void> & OnSignature<`${CustomEventTypes.OPEN}` | `${CustomEventTypes.BEFOREOPEN}`, ContextMenuEvent, EventsKey> & OnSignature<Types, ObjectEvent, void> & CombinedOnSignature<EventTypes_2 | Types | `${CustomEventTypes.OPEN}` | `${CustomEventTypes.CLOSE}` | `${CustomEventTypes.BEFOREOPEN}`, void>;
|
|
37
|
+
options: Options;
|
|
38
|
+
constructor(opts?: Partial<Options>);
|
|
39
|
+
clear(): void;
|
|
40
|
+
enable(): void;
|
|
41
|
+
disable(): void;
|
|
42
|
+
getDefaultItems(): SingleItem[];
|
|
43
|
+
countItems(): number;
|
|
44
|
+
extend(items: Item[]): void;
|
|
45
|
+
closeMenu(): void;
|
|
46
|
+
isOpen(): boolean;
|
|
47
|
+
updatePosition(pixel: Pixel): void;
|
|
48
|
+
pop(): void;
|
|
49
|
+
shift(): void;
|
|
50
|
+
push(item: Item): void;
|
|
51
|
+
setMap(map: default_3): void;
|
|
52
|
+
protected removeListeners(): void;
|
|
53
|
+
protected removeMenuEntry(id: string): void;
|
|
54
|
+
protected handleContextMenu(evt: MouseEvent): void;
|
|
55
|
+
protected openMenu(evt: MouseEvent): void;
|
|
56
|
+
protected getMenuEntriesLength(): number;
|
|
57
|
+
protected calculateMenuSize(): {
|
|
58
|
+
h: number;
|
|
59
|
+
w: number;
|
|
60
|
+
};
|
|
61
|
+
protected calculateVerticalPosition(mapSize: [number, number], spaceLeft: {
|
|
62
|
+
h: number;
|
|
63
|
+
w: number;
|
|
64
|
+
}, menuSize: {
|
|
65
|
+
h: number;
|
|
66
|
+
w: number;
|
|
67
|
+
}): number;
|
|
68
|
+
protected adjustPositionAfterRender(mapSize: [number, number], menuSize: {
|
|
69
|
+
h: number;
|
|
70
|
+
w: number;
|
|
71
|
+
}): void;
|
|
72
|
+
protected positionContainer(): void;
|
|
73
|
+
protected handleMapMove(): void;
|
|
74
|
+
protected handleEntryCallback(evt: MouseEvent): void;
|
|
75
|
+
protected handleAddMenuEntry(item: MenuEntry, element: HTMLLIElement): void;
|
|
76
|
+
}
|
|
77
|
+
export default ContextMenu;
|
|
78
|
+
|
|
79
|
+
export declare class ContextMenuEvent extends default_5<any> {
|
|
80
|
+
constructor(options: {
|
|
81
|
+
map: default_3;
|
|
82
|
+
originalEvent: MouseEvent | PointerEvent;
|
|
83
|
+
type: `${CustomEventTypes.OPEN}` | `${CustomEventTypes.BEFOREOPEN}`;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export declare enum CustomEventTypes {
|
|
88
|
+
BEFOREOPEN = "beforeopen",
|
|
89
|
+
OPEN = "open",
|
|
90
|
+
CLOSE = "close",
|
|
91
|
+
ADD_MENU_ENTRY = "add-menu-entry"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export declare enum EventTypes {
|
|
95
|
+
CONTEXTMENU = "contextmenu",
|
|
96
|
+
CLICK = "click",
|
|
97
|
+
DBLCLICK = "dblclick"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export declare type Item = SingleItem | ItemSeparator | ItemWithNested;
|
|
101
|
+
|
|
102
|
+
export declare type ItemSeparator = '-';
|
|
103
|
+
|
|
104
|
+
export declare type ItemWithNested = {
|
|
105
|
+
classname?: string;
|
|
106
|
+
icon?: string;
|
|
107
|
+
items: Item[];
|
|
108
|
+
text: string;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export declare type MenuEntry = {
|
|
112
|
+
callback: null | SingleItem['callback'];
|
|
113
|
+
data: unknown;
|
|
114
|
+
id: string;
|
|
115
|
+
isInsideSubmenu: boolean;
|
|
116
|
+
isSeparator: boolean;
|
|
117
|
+
isSubmenu: boolean;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export declare type Options = {
|
|
121
|
+
defaultItems: boolean;
|
|
122
|
+
eventType: `${EventTypes}`;
|
|
123
|
+
items: Item[];
|
|
124
|
+
scrollAt: number;
|
|
125
|
+
width: number;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export declare type SingleItem = {
|
|
129
|
+
callback: (object: CallbackObject, map: default_3) => void;
|
|
130
|
+
classname?: string;
|
|
131
|
+
data?: unknown;
|
|
132
|
+
icon?: string;
|
|
133
|
+
text: string;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export { }
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
|
-
* ol-contextmenu -
|
|
3
|
+
* ol-contextmenu - v6.0.0
|
|
4
4
|
* https://github.com/jonataswalker/ol-contextmenu
|
|
5
|
-
* Built:
|
|
5
|
+
* Built: 2026-01-05T22:44:21.378Z
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
(function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode('.ol-ctx-menu-container{position:absolute;padding:8px;background:#fff;color:#222;font-size:13px;border-radius:5px;box-shadow:#0003 3px 3px 5px;box-sizing:border-box}.ol-ctx-menu-container div,.ol-ctx-menu-container span,.ol-ctx-menu-container a,.ol-ctx-menu-container img,.ol-ctx-menu-container ul,.ol-ctx-menu-container li{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline}.ol-ctx-menu-container a img{border:none}.ol-ctx-menu-container *,.ol-ctx-menu-container *:before,.ol-ctx-menu-container *:after{box-sizing:inherit}.ol-ctx-menu-container.ol-ctx-menu-hidden{opacity:0;visibility:hidden;transition:visibility 0s linear .3s,opacity .3s}.ol-ctx-menu-container ul{list-style:none}.ol-ctx-menu-container li{position:relative;line-height:20px;padding:2px 5px;white-space:nowrap}.ol-ctx-menu-container li:not(.ol-ctx-menu-separator):hover{cursor:pointer;background-color:#333;color:#eee}.ol-ctx-menu-container li.ol-ctx-menu-submenu .ol-ctx-menu-container{border:1px solid #eee;padding:8px;top:0;opacity:0;visibility:hidden;transition:visibility 0s linear .3s,opacity .3s}.ol-ctx-menu-container li.ol-ctx-menu-submenu:after{position:absolute;top:7px;right:10px;content:"";display:inline-block;width:.6em;height:.6em;border-right:.3em solid #222;border-top:.3em solid #222;transform:rotate(45deg)}.ol-ctx-menu-container li.ol-ctx-menu-submenu:hover:after{border-color:#eee}.ol-ctx-menu-container:not(.ol-ctx-menu-hidden) li.ol-ctx-menu-submenu:hover>.ol-ctx-menu-container{opacity:1;visibility:visible;transition-delay:0s}.ol-ctx-menu-container li.ol-ctx-menu-separator{padding:0}.ol-ctx-menu-container li.ol-ctx-menu-separator hr{border:0;height:1px;background-image:linear-gradient(to left,#0000,#000000bf,#0000)}.ol-ctx-menu-icon{text-indent:20px;background-size:20px auto;background-repeat:no-repeat;background-position:left center}.ol-ctx-menu-zoom-in{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABaUlEQVQ4T72U7VHCQBCGn90GtAMuNGCswFiBWIFQgWMFxg6wArECsQKhArEBiB1Qwa1zgQn5IAYcxv13k71n3919L8KJQ07M47+BzgG9TRfZ/JBuWhS6BJFHRJICYrZGZIz3z5Ct2+B7gG6I6kt+wewdkQVwjtkAkR5mC8yu26A1oItR/cTsOweQBdgutD8G7jGm2PJ2n8oqUKIpIjd4HxTM8gvaT/F+AlmWnyWaIXKF95eNguFzTYFhNsdWu9kFgFlaFMANUH3D8wDLoLgSTSD2il8NCe2ZXQBxWDGwxmyUzzOMBZ7wy7Qb2K0wQfXjMOBuhlFpZtNty5sFaTQBuTusZdymeqs1SpYKcO9HkE3KbTd9WFijMHJQ5hBNEAYNq5Qd0dhyke0GiE4QzjqfW23mHT8Hl4DG4Lce3FPE7AtbBSdsbNqpoJLgYkRnNeUV+xwJDHTnUEkxHGbhBXUs5TjJjew/KPy94g+NRaIVRYmMXwAAAABJRU5ErkJggg==)}.ol-ctx-menu-container li:hover.ol-ctx-menu-zoom-in{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABc0lEQVQ4T71U21ECQRDsJgGdvQDECMQIxAjECMQILCPwzAAjECIQI0AiEDPQAPaWCBhrcKHuCUcV5f7dY3v6tUscefHIePhfwBBCF8CZqRCReRs1tQxDCH1VfQLQz4EsSY4AvIjIsgm8AhhCGKrqa9zwrqoLAKckB5HtguR1E2gBMITQU9VPAD8GICIGtl3e+xHJBwBT59xtHcsCYJZlUwA3kcGHbfDep51OZywi3/acZZm9vyJ5WR5o38uACmDunNt6ZwAkUxFZDwghDFT1jeSjiJinhVUBVNVJkiTDKO8CQA+AsbNQ7s1Ps0VVn5MkSfcCtmBoDZi1Bdx4eJ7zbBolrwPy3o9J3rWSHPs3A1BbjVKlYBaIyDgvu9LDXDU2RTZmXVW1oKyLxRD+OrkOrJLy5mVM0iaftDhuhVbsvBzMglzKUNW6IV/OOWtCM8MmVvEkmbwt83LaB19fdgOtVquUZJeknaDdobTwbOcvBzPcN/AXH1DFFWP7u9oAAAAASUVORK5CYII=)}.ol-ctx-menu-zoom-out{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABU0lEQVQ4T72U7VECMRRFz3sNaAdkacC1AtcKxApcKnCsQOwAK3CtQKxAqEBsANYOqCDPyTIC+8WCw5jfybn33dxEOPGSE/P4b6BzQG89RT47ZJoWhy5B5BGRZAMxWyEyxvtnyFdt8AagS1F9KQ6YvSMyB84xGyDSw2yO2XUbtAJ0MaqfmH0XAPIA2y7tj4F7jAm2uG1yWQZKNEHkBu+Dg2njWBJNEbnC+8uaIFRuWfuG2QxbbrOrUd0A1Tc8D7AIjkur7DAAsVf8MiWMZ3ZR2m02LPIMscATfjHqBnY7TFD9OAy4zTCCPG/MUKMM5O6wkXFr9dZq7FQqqHk/hDzbFa73cFONTZFDdRyiCcKg5rrSiLaXkiI6RjjrfG6VzDs+B5eAxuDXeYpmNRGzL2wZ/wof+du4GNFpBVqqz5HA4MM5VEYYDrOs+1I6Q9u/4Q8O9wN/AGgWjBVqQjjgAAAAAElFTkSuQmCC)}.ol-ctx-menu-container li:hover.ol-ctx-menu-zoom-out{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABYklEQVQ4T72U4VHCQBCF36tA91KAWIFYgViBWIFYgWMFYgdYgVCBWAFSgdiBFpAsFWSdxcDkQoBkhnF/ZjbfvX377ogjF4/Mw/8CVbUD4MynEJF5k2lqFapqz8yeAPRKkCXJEYAXEVnugm8BVXVgZq/FD+9mtgBwSrJfqF2QvN4FjYCq2jWzTwA/DhARh20qTdMRyQcA0xDCbZ3KCJhl2RTATaHgo+6HLMv8+xXJy+qB3l8FGoB5CKHsXcRV1b6ZvZF8FBH3NKotoJlNkiQZFONdlLtJ3rufbouZPSdJMjwIbKDQEzBrClx7eC4i33Uepmk6JnnXaOQifzMAtdGoRApugYiMI1uqKkrRWAfZo9MxM1+UZzFewl8mN4nYdVM83L7BkwbXLUrF3sfBLQDQBbDy08x8vOohXyEE71lVq9emuEk+3gZa3XYroCvwFyjP8yHJDsnxwaU08GxvS2uFhw78BbzWrxXgMbsHAAAAAElFTkSuQmCC)}')),document.head.appendChild(e)}}catch(n){console.error("vite-plugin-css-injected-by-js",n)}})();
|
|
9
|
+
var ContextMenu=(function(m,u){"use strict";var B=Object.defineProperty;var j=(m,u,d)=>u in m?B(m,u,{enumerable:!0,configurable:!0,writable:!0,value:d}):m[u]=d;var c=(m,u,d)=>j(m,typeof u!="symbol"?u+"":u,d);var d={exports:{}},A;function $(){if(A)return d.exports;A=1;function o(){}return o.prototype={on:function(i,t,e){var s=this.e||(this.e={});return(s[i]||(s[i]=[])).push({fn:t,ctx:e}),this},once:function(i,t,e){var s=this;function n(){s.off(i,n),t.apply(e,arguments)}return n._=t,this.on(i,n,e)},emit:function(i){var t=[].slice.call(arguments,1),e=((this.e||(this.e={}))[i]||[]).slice(),s=0,n=e.length;for(s;s<n;s++)e[s].fn.apply(e[s].ctx,t);return this},off:function(i,t){var e=this.e||(this.e={}),s=e[i],n=[];if(s&&t)for(var a=0,h=s.length;a<h;a++)s[a].fn!==t&&s[a].fn._!==t&&n.push(s[a]);return n.length?e[i]=n:delete e[i],this}},d.exports=o,d.exports.TinyEmitter=o,d.exports}var _=$(),L=(o=>(o.CONTEXTMENU="contextmenu",o.CLICK="click",o.DBLCLICK="dblclick",o))(L||{}),f=(o=>(o.BEFOREOPEN="beforeopen",o.OPEN="open",o.CLOSE="close",o.ADD_MENU_ENTRY="add-menu-entry",o))(f||{});class C extends u{constructor(i){super(i.type,i.map,i.originalEvent)}}const D={defaultItems:!0,eventType:L.CONTEXTMENU,items:[],scrollAt:4,width:150},E="ol-ctx-menu",r={container:`${E}-container`,hidden:`${E}-hidden`,icon:`${E}-icon`,separator:`${E}-separator`,submenu:`${E}-submenu`,unselectable:"ol-unselectable",zoomIn:`${E}-zoom-in`,zoomOut:`${E}-zoom-out`},P=8*2,S=10,M=2,T=[{callback:(o,i)=>{const t=i.getView();t.animate({center:o.coordinate,duration:700,zoom:Number(t.getZoom())+1})},classname:`${r.zoomIn} ${r.icon}`,text:"Zoom In"},{callback:(o,i)=>{const t=i.getView();t.animate({center:o.coordinate,duration:700,zoom:Number(t.getZoom())-1})},classname:`${r.zoomOut} ${r.icon}`,text:"Zoom Out"}];function v(o){const i=document.createDocumentFragment(),t=document.createElement("div");for(t.innerHTML=o;t.firstChild;)i.append(t.firstChild);return i}function k(o){const i=document.importNode(o),t=o.offsetWidth;i.style.cssText=`position: fixed; top: 0; left: 0; overflow: auto; visibility: hidden; pointer-events: none; height: unset; max-height: unset; width: ${t}px`;const e=v("<span>Foo</span>"),s=v("<span>Foo</span>"),n=document.createElement("li"),a=document.createElement("li");n.append(e),a.append(s),i.append(n),i.append(a),o.parentNode?.append(i);const h=i.offsetHeight/2;return o.parentNode?.removeChild(i),h}function I({emitter:o,isInsideSubmenu:i=!1,isSubmenu:t=!1,item:e,parentNode:s}){const n=`_${Math.random().toString(36).slice(2,11)}`;if(typeof e!="string"&&"text"in e){const x=`<span>${e.text}</span>`,y=v(x),l=document.createElement("li");e.classname=e.classname||"",e.icon&&(e.classname===""?e.classname=r.icon:e.classname.includes(r.icon)===!1&&(e.classname+=` ${r.icon}`),l.setAttribute("style",`background-image:url(${e.icon})`)),l.id=n,l.className=e.classname,l.append(y),s.append(l);const O={callback:"callback"in e?e.callback:null,data:"data"in e?e.data:null,id:n,isInsideSubmenu:i,isSeparator:!1,isSubmenu:t};return o.emit(f.ADD_MENU_ENTRY,O,l),l}const a=`<li id="${n}" class="${r.separator}"><hr></li>`,h=v(a);s.append(h);const g=s.lastChild,b={callback:null,data:null,id:n,isInsideSubmenu:!1,isSeparator:!0,isSubmenu:!1};return o.emit(f.ADD_MENU_ENTRY,b,g),g}function N({container:o,emitter:i,isInsideSubmenu:t,items:e,menuWidth:s}){e.forEach(n=>{if(typeof n!="string"&&"items"in n&&Array.isArray(n.items)){const a=I({emitter:i,isSubmenu:!0,item:n,parentNode:o});a.classList.add(r.submenu);const h=document.createElement("ul");h.classList.add(r.container),h.style.width=`${s}px`,a.append(h),N({container:h,emitter:i,isInsideSubmenu:!0,items:n.items,menuWidth:s})}else I({emitter:i,isInsideSubmenu:t,isSubmenu:!1,item:n,parentNode:o})})}function w(o,i){if(!o)throw new Error(i)}class H extends m{constructor(t={}){w(typeof t=="object","@param `opts` should be object type!");const e=document.createElement("div");super({element:e});c(this,"map");c(this,"emitter",new _.TinyEmitter);c(this,"container");c(this,"coordinate",[]);c(this,"pixel",[]);c(this,"contextMenuEventListener");c(this,"entryCallbackEventListener");c(this,"mapMoveListener");c(this,"lineHeight",0);c(this,"disabled");c(this,"opened");c(this,"items",[]);c(this,"menuEntries",new Map);c(this,"options");this.options={...D,...t};const s=document.createElement("ul");e.append(s),e.style.width=`${this.options.width}px`,e.classList.add(r.container,r.unselectable,r.hidden),this.container=e,this.contextMenuEventListener=n=>{this.handleContextMenu(n)},this.entryCallbackEventListener=n=>{this.handleEntryCallback(n)},this.mapMoveListener=()=>{this.handleMapMove()},this.disabled=!1,this.opened=!1,window.addEventListener("beforeunload",()=>{this.removeListeners()},{once:!0})}clear(){for(const t of this.menuEntries.keys())this.removeMenuEntry(t);this.container.replaceChildren(),this.container.append(document.createElement("ul"))}enable(){this.disabled=!1}disable(){this.disabled=!0}getDefaultItems(){return T}countItems(){return this.menuEntries.size}extend(t){w(Array.isArray(t),"@param `items` should be an Array."),N({container:this.container.firstElementChild,emitter:this.emitter,items:t,menuWidth:this.options.width})}closeMenu(){this.opened=!1,this.container.classList.add(r.hidden),this.dispatchEvent(f.CLOSE)}isOpen(){return this.opened}updatePosition(t){w(Array.isArray(t),"@param `pixel` should be an Array."),this.isOpen()&&(this.pixel=t,this.positionContainer())}pop(){const t=Array.from(this.menuEntries.keys()).pop();t&&this.removeMenuEntry(t)}shift(){const t=Array.from(this.menuEntries.keys()).shift();t&&this.removeMenuEntry(t)}push(t){t&&this.extend([t])}setMap(t){if(super.setMap(t),t){this.map=t,t.getViewport().addEventListener(this.options.eventType,this.contextMenuEventListener,!1),t.on("movestart",()=>{this.handleMapMove()}),this.emitter.on(f.ADD_MENU_ENTRY,(s,n)=>{this.handleAddMenuEntry(s,n)},this),this.items=this.options.defaultItems?this.options.items.concat(T):this.options.items,N({container:this.container.firstElementChild,emitter:this.emitter,items:this.items,menuWidth:this.options.width});const e=this.getMenuEntriesLength();this.lineHeight=e>0?this.container.offsetHeight/e:k(this.container)}else this.removeListeners(),this.clear()}removeListeners(){this.map.getViewport().removeEventListener(this.options.eventType,this.contextMenuEventListener,!1),this.emitter.off(f.ADD_MENU_ENTRY)}removeMenuEntry(t){let e=document.getElementById(t);e?.remove(),e=null,this.menuEntries.delete(t)}handleContextMenu(t){this.coordinate=this.map.getEventCoordinate(t),this.pixel=this.map.getEventPixel(t),this.dispatchEvent(new C({map:this.map,originalEvent:t,type:f.BEFOREOPEN})),!this.disabled&&(this.options.eventType===L.CONTEXTMENU&&(t.stopPropagation(),t.preventDefault()),setTimeout(()=>{this.openMenu(t)}),t.target?.addEventListener("pointerdown",e=>{this.opened&&(e.stopPropagation(),this.closeMenu())},{once:!0}))}openMenu(t){this.opened=!0,this.positionContainer(),this.container.classList.remove(r.hidden),this.dispatchEvent(new C({map:this.map,originalEvent:t,type:f.OPEN}))}getMenuEntriesLength(){return Array.from(this.menuEntries).filter(([,t])=>t.isSeparator===!1&&t.isSubmenu===!1&&t.isInsideSubmenu===!1).length}calculateMenuSize(){const t=this.getMenuEntriesLength(),s=Math.round(this.lineHeight*t)+P,n=this.container.offsetHeight,a=!this.container.classList.contains(r.hidden);return{h:n>0&&a?n:s,w:this.container.offsetWidth}}calculateVerticalPosition(t,e,s){let n;return e.h>=s.h?(n=this.pixel[1]-S,n+s.h>t[1]&&(n=Math.max(0,t[1]-s.h-M))):(n=this.pixel[1]-s.h,n<0&&(n=s.h<=t[1]?0:Math.max(0,t[1]-s.h)),n+s.h>t[1]&&(n=Math.max(0,t[1]-s.h-M))),Math.max(0,Math.min(n,t[1]-s.h-M))}adjustPositionAfterRender(t,e){if(!this.container.classList.contains(r.hidden)){const s=this.container.offsetHeight;s>0&&s!==e.h&&(Number.parseInt(this.container.style.top,10)||0)+s>t[1]&&(this.container.style.top=`${Math.max(0,t[1]-s-M)}px`)}}positionContainer(){const t=this.map.getSize()||[0,0],e=[t[0]||0,t[1]||0],s={h:e[1]-this.pixel[1],w:e[0]-this.pixel[0]},n=this.calculateMenuSize(),a=s.w>=n.w?this.pixel[0]+5:this.pixel[0]-n.w;this.container.style.left=`${a}px`;const h=this.calculateVerticalPosition(e,s,n);this.container.style.top=`${h}px`,this.container.style.right="auto",this.container.style.bottom="auto",this.adjustPositionAfterRender(e,n),s.w-=n.w;const g=y=>Array.from(y.children).filter(l=>l.tagName==="LI"&&l.classList.contains(r.submenu));let b=0;const x=(y,l)=>{b+=1,g(y).forEach(R=>{const U=l>=n.w?n.w-8:(n.w+8)*-1,p=R.querySelector(`ul.${r.container}`),F=Math.round(this.lineHeight*Array.from(p.children).filter(V=>V.tagName==="LI").length);p.style.left=`${U}px`,p.style.right="auto",p.style.top=s.h>=F+n.h?"0":`-${p.offsetHeight-25}px`,p.style.bottom="auto",p.style.zIndex=String(b),g(p).length>0&&x(p,l-n.w)})};x(this.container.firstElementChild,s.w)}handleMapMove(){this.closeMenu()}handleEntryCallback(t){t.preventDefault(),t.stopPropagation();const e=t.currentTarget,s=this.menuEntries.get(e.id);if(!s)return;const n={coordinate:this.coordinate,data:s.data};this.closeMenu(),s.callback?.(n,this.map)}handleAddMenuEntry(t,e){this.menuEntries.set(t.id,t),this.positionContainer(),"callback"in t&&typeof t.callback=="function"&&e.addEventListener("click",this.entryCallbackEventListener,!1)}}return H})(ol.control.Control,ol.MapBrowserEvent);
|