@emuanalytics/flow-rdf-core 4.0.4 → 4.0.5

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.
Files changed (89) hide show
  1. package/README.md +1 -64
  2. package/dist/index.d.mts +36 -31
  3. package/dist/index.d.ts +36 -31
  4. package/dist/index.mjs +20 -3249
  5. package/dist/lib/context/context-registry.mjs +63 -0
  6. package/dist/lib/context/debugger.mjs +240 -0
  7. package/dist/lib/context/flow-context.mjs +61 -0
  8. package/dist/lib/context/global-state.mjs +70 -0
  9. package/dist/lib/context/map-registry.mjs +59 -0
  10. package/dist/lib/context/query-registry.mjs +53 -0
  11. package/dist/lib/errors/flow-error.mjs +10 -0
  12. package/dist/lib/errors/flow-expression-error.mjs +14 -0
  13. package/dist/lib/errors/flow-map-error.mjs +9 -0
  14. package/dist/lib/expressions/expression-parser.mjs +198 -0
  15. package/dist/lib/expressions/functions/from-palette.mjs +75 -0
  16. package/dist/lib/expressions/functions/regex.mjs +25 -0
  17. package/dist/lib/expressions/functions/state.mjs +25 -0
  18. package/dist/lib/expressions/parser-helpers.mjs +37 -0
  19. package/dist/lib/expressions/reactive-expression.mjs +253 -0
  20. package/dist/lib/map/flow-core-map.mjs +749 -0
  21. package/dist/lib/map/plugins/annotations/annotation-layers.mjs +163 -0
  22. package/dist/lib/map/plugins/annotations/annotation.events.mjs +27 -0
  23. package/dist/lib/map/plugins/annotations/annotation.plugin.mjs +498 -0
  24. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/modes/DirectModeOverride.mjs +67 -0
  25. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/modes/DragCircleMode.mjs +65 -0
  26. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/utils/create_supplementary_points_circle.mjs +15 -0
  27. package/dist/lib/map/plugins/annotations/maplibre-gl-draw-circle/lib/utils/drag_pan.mjs +20 -0
  28. package/dist/lib/map/plugins/data-join.plugin.mjs +173 -0
  29. package/dist/lib/map/plugins/export/export.plugin.mjs +21 -0
  30. package/dist/lib/map/plugins/export/map-exporter.mjs +177 -0
  31. package/dist/lib/map/plugins/hover-effect.plugin.mjs +58 -0
  32. package/dist/lib/map/plugins/index.d.mts +37 -72
  33. package/dist/lib/map/plugins/index.d.ts +37 -72
  34. package/dist/lib/map/plugins/index.mjs +11 -1810
  35. package/dist/lib/map/plugins/keyboard-shortcut-manager.mjs +120 -0
  36. package/dist/lib/map/plugins/map-plugin-base.mjs +25 -0
  37. package/dist/lib/map/plugins/plugin-manager.mjs +135 -0
  38. package/dist/lib/map/plugins/popup.plugin.mjs +55 -0
  39. package/dist/lib/map/plugins/selection/selection.events.mjs +21 -0
  40. package/dist/lib/map/plugins/selection/selection.plugin.mjs +136 -0
  41. package/dist/lib/map/plugins/selection/source-selection.mjs +156 -0
  42. package/dist/lib/map/plugins/url-sync.plugin.mjs +55 -0
  43. package/dist/lib/map/support/image-registry.mjs +82 -0
  44. package/dist/lib/map/support/map-events.mjs +71 -0
  45. package/dist/lib/map/support/map-feature-stream.mjs +158 -0
  46. package/dist/lib/map/support/map-popup.mjs +159 -0
  47. package/dist/lib/map/types.mjs +7 -0
  48. package/dist/lib/map-style/map-data-source.mjs +192 -0
  49. package/dist/lib/map-style/map-layer-style-builder-generated.mjs +1307 -0
  50. package/dist/lib/map-style/map-layer-style-builder.mjs +86 -0
  51. package/dist/lib/map-style/map-layer.mjs +223 -0
  52. package/dist/lib/map-style/map-style.mjs +178 -0
  53. package/dist/lib/queries/async-committer.mjs +30 -0
  54. package/dist/lib/queries/query.mjs +172 -0
  55. package/dist/lib/state-store/decorators.mjs +83 -0
  56. package/dist/lib/state-store/state-store.mjs +534 -0
  57. package/dist/lib/state-store/utils.mjs +7 -0
  58. package/dist/lib/utils/announce.mjs +12 -0
  59. package/dist/lib/utils/assert.mjs +12 -0
  60. package/dist/{shared/flow-rdf-core.187cb345.mjs → lib/utils/debounce.mjs} +3 -39
  61. package/dist/lib/utils/decorators.mjs +59 -0
  62. package/dist/lib/utils/deferred.mjs +11 -0
  63. package/dist/lib/utils/ensure-array.mjs +16 -0
  64. package/dist/{shared/flow-rdf-core.1f0901c5.mjs → lib/utils/feature-type-checks.mjs} +2 -2
  65. package/dist/lib/utils/functional.mjs +22 -0
  66. package/dist/lib/utils/global.mjs +18 -0
  67. package/dist/lib/utils/index.mjs +8 -5
  68. package/dist/lib/utils/random-id.mjs +9 -0
  69. package/dist/lib/utils/string-utils.mjs +3 -0
  70. package/dist/lib/utils/throttle.mjs +20 -0
  71. package/dist/{shared/flow-rdf-core.25e5f6d7.mjs → lib/utils/type-checks.mjs} +1 -26
  72. package/dist/lib/utils/watchable-promise.mjs +55 -0
  73. package/dist/package.json.mjs +3 -0
  74. package/dist/shared/{flow-rdf-core.4dde996e.d.cts → flow-rdf-core.8b5acf14.d.mts} +1315 -172
  75. package/dist/shared/{flow-rdf-core.4dde996e.d.ts → flow-rdf-core.8b5acf14.d.ts} +1315 -172
  76. package/package.json +4 -8
  77. package/dist/index.cjs +0 -3316
  78. package/dist/index.d.cts +0 -90
  79. package/dist/lib/map/plugins/index.cjs +0 -1850
  80. package/dist/lib/map/plugins/index.d.cts +0 -345
  81. package/dist/lib/utils/index.cjs +0 -48
  82. package/dist/lib/utils/index.d.cts +0 -160
  83. package/dist/shared/flow-rdf-core.06e85a6c.cjs +0 -1394
  84. package/dist/shared/flow-rdf-core.4dde996e.d.mts +0 -1916
  85. package/dist/shared/flow-rdf-core.55c169ca.cjs +0 -150
  86. package/dist/shared/flow-rdf-core.63162669.d.cts +0 -10
  87. package/dist/shared/flow-rdf-core.65045caf.mjs +0 -1360
  88. package/dist/shared/flow-rdf-core.a82b9be4.cjs +0 -34
  89. package/dist/shared/flow-rdf-core.f50908f0.cjs +0 -102
@@ -0,0 +1,120 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => {
4
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
+ return value;
6
+ };
7
+ class KeyboardShortcutManager {
8
+ constructor() {
9
+ __publicField(this, "listeners");
10
+ /**
11
+ * Handles key events of the root handler and passes them down to listeners.
12
+ * @param event
13
+ */
14
+ __publicField(this, "handleKeyboardEvent", (event) => {
15
+ if (this.isInputEvent(event))
16
+ return;
17
+ const key = this.keyDefinitionFromEvent(event);
18
+ for (const listener of this.listeners) {
19
+ const handler = listener.bindings[key];
20
+ if (handler) {
21
+ const propagate = handler(event);
22
+ if (propagate === true) {
23
+ continue;
24
+ } else {
25
+ event.stopPropagation();
26
+ return;
27
+ }
28
+ }
29
+ }
30
+ });
31
+ this.listeners = [];
32
+ document.addEventListener("keydown", this.handleKeyboardEvent);
33
+ }
34
+ /**
35
+ * Adds listener to keyboard events.
36
+ * @param bindings Object containing key/value pairs of keyCode/eventHandler function. Can add multiple comma separated `,` keyCodes for the same handler. Can optionally return `false` to stop propagation of other handlers.
37
+ * @param priority Can optionally set the priority of event handler, higher value means higher priority. Defaults to 0.
38
+ * @returns Function that can be used to unbind the listener.
39
+ */
40
+ addListener(bindings, priority) {
41
+ bindings = this.splitEventBindings(bindings);
42
+ const listener = this.addToListeners({ bindings, priority: priority ?? 0 });
43
+ const removeListener = () => {
44
+ this.removeFromListeners(listener);
45
+ };
46
+ return removeListener;
47
+ }
48
+ /**
49
+ * Splits multiple keyCode events and creates a new bindings map.
50
+ * @param bindings
51
+ */
52
+ splitEventBindings(bindings) {
53
+ const splitBindings = {};
54
+ for (const event in bindings) {
55
+ event.split(",").forEach((splitEvent) => {
56
+ splitBindings[this.normalizeKeyDefinition(splitEvent)] = bindings[event];
57
+ });
58
+ }
59
+ return splitBindings;
60
+ }
61
+ /**
62
+ * Checks is the event comes from an input type.
63
+ * @param event
64
+ */
65
+ isInputEvent(event) {
66
+ if (event.target instanceof Node) {
67
+ switch (event.target.nodeName) {
68
+ case "INPUT":
69
+ case "SELECT":
70
+ case "TEXTAREA":
71
+ return true;
72
+ default:
73
+ return false;
74
+ }
75
+ }
76
+ return false;
77
+ }
78
+ /**
79
+ * Adds listener into internal array of listeners and sorts in `descending` order.
80
+ * @param listener
81
+ */
82
+ addToListeners(listener) {
83
+ this.listeners.push(listener);
84
+ this.listeners.sort((a, b) => b.priority - a.priority);
85
+ return listener;
86
+ }
87
+ /**
88
+ * Removes listener from internal array of listeners.
89
+ * @param listenerToRemove
90
+ */
91
+ removeFromListeners(listenerToRemove) {
92
+ this.listeners = this.listeners.filter((listener) => listener !== listenerToRemove);
93
+ }
94
+ normalizeKeyDefinition(keyDef) {
95
+ const parts = keyDef.split("+");
96
+ const ev = {
97
+ ctrlKey: parts.includes("Ctrl"),
98
+ shiftKey: parts.includes("Shift"),
99
+ altKey: parts.includes("Alt"),
100
+ metaKey: parts.includes("Meta"),
101
+ code: parts[parts.length - 1]
102
+ };
103
+ return this.keyDefinitionFromEvent(ev);
104
+ }
105
+ keyDefinitionFromEvent(ev) {
106
+ let keyDef = "";
107
+ if (ev.ctrlKey)
108
+ keyDef += "Ctrl+";
109
+ if (ev.shiftKey)
110
+ keyDef += "Shift+";
111
+ if (ev.altKey)
112
+ keyDef += "Alt+";
113
+ if (ev.metaKey)
114
+ keyDef += "Meta+";
115
+ keyDef += ev.code;
116
+ return keyDef;
117
+ }
118
+ }
119
+
120
+ export { KeyboardShortcutManager };
@@ -0,0 +1,25 @@
1
+ import { EventEmitter } from 'eventemitter3';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
5
+ var __publicField = (obj, key, value) => {
6
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
7
+ return value;
8
+ };
9
+ class MapPluginBase extends EventEmitter {
10
+ constructor(pluginManager, options) {
11
+ super();
12
+ __publicField(this, "_pluginManager");
13
+ __publicField(this, "options");
14
+ this._pluginManager = pluginManager;
15
+ this.options = options ?? {};
16
+ }
17
+ get map() {
18
+ return this._pluginManager.map;
19
+ }
20
+ get pluginManager() {
21
+ return this._pluginManager;
22
+ }
23
+ }
24
+
25
+ export { MapPluginBase };
@@ -0,0 +1,135 @@
1
+ import Debug from 'debug';
2
+ import { capitalize } from '../../utils/string-utils.mjs';
3
+ import { isString } from '../../utils/type-checks.mjs';
4
+ import { KeyboardShortcutManager } from './keyboard-shortcut-manager.mjs';
5
+ import { FlowCoreError } from '../../errors/flow-error.mjs';
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __publicField = (obj, key, value) => {
10
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
11
+ return value;
12
+ };
13
+ const debug = Debug("flow:map-plugin-manager");
14
+ class PluginManager {
15
+ constructor(map) {
16
+ __publicField(this, "_map");
17
+ __publicField(this, "_keyboardShortcutManager", new KeyboardShortcutManager());
18
+ __publicField(this, "_plugins", /* @__PURE__ */ new Map());
19
+ __publicField(this, "_initialized", false);
20
+ __publicField(this, "_exclusivePlugins", /* @__PURE__ */ new Set());
21
+ this._map = map;
22
+ this.map.initialized.then(() => {
23
+ this._initialized = true;
24
+ for (const plugin of this._plugins.values()) {
25
+ if (plugin.initialize) {
26
+ plugin.initialize();
27
+ }
28
+ }
29
+ });
30
+ }
31
+ get map() {
32
+ return this._map;
33
+ }
34
+ usePlugin(plugin, options) {
35
+ if (this._plugins.has(plugin)) {
36
+ const oldInstance = this._plugins.get(plugin);
37
+ if (oldInstance.destroy) {
38
+ oldInstance.destroy();
39
+ }
40
+ }
41
+ const pluginInstance = new plugin(this, options);
42
+ this._plugins.set(plugin, pluginInstance);
43
+ debug(`FlowMap<${this.map.id}>: Using plugin '${pluginInstance.name}'`);
44
+ if (this._initialized && pluginInstance.initialize)
45
+ pluginInstance.initialize();
46
+ return pluginInstance;
47
+ }
48
+ getPlugin(pluginOrName) {
49
+ let name;
50
+ let pluginInstance;
51
+ if (isString(pluginOrName)) {
52
+ pluginInstance = Array.from(this._plugins.values()).find((p) => p.name === pluginOrName);
53
+ name = pluginOrName;
54
+ } else {
55
+ pluginInstance = this._plugins.get(pluginOrName);
56
+ name = pluginOrName.name;
57
+ }
58
+ if (!pluginInstance) {
59
+ throw new FlowCoreError(`<${name}>: plugin not registered`);
60
+ }
61
+ return pluginInstance;
62
+ }
63
+ removePlugin(pluginOrName) {
64
+ let pluginConstructor;
65
+ let pluginInstance;
66
+ if (isString(pluginOrName)) {
67
+ const pair = Array.from(this._plugins.entries()).find(([_, p]) => p.name === pluginOrName);
68
+ pluginConstructor = pair && pair[0];
69
+ pluginInstance = pair && pair[1];
70
+ } else {
71
+ pluginConstructor = pluginOrName;
72
+ pluginInstance = this._plugins.get(pluginOrName);
73
+ }
74
+ if (pluginInstance && pluginInstance.destroy) {
75
+ pluginInstance.destroy();
76
+ }
77
+ if (pluginConstructor)
78
+ this._plugins.delete(pluginConstructor);
79
+ return this;
80
+ }
81
+ removeAllPlugins() {
82
+ this._plugins.forEach((plugin) => {
83
+ if (plugin.destroy)
84
+ plugin.destroy();
85
+ });
86
+ this._plugins.clear();
87
+ return this;
88
+ }
89
+ setExclusivePlugin(plugin) {
90
+ this._exclusivePlugins.add(plugin);
91
+ this.forwardSetExclusivePlugin();
92
+ return this;
93
+ }
94
+ removeExclusivePlugin(plugin) {
95
+ this._exclusivePlugins.delete(plugin);
96
+ this.forwardSetExclusivePlugin();
97
+ return this;
98
+ }
99
+ addKeyboardShortcuts(keyboardShortcuts, priority = 0) {
100
+ return this._keyboardShortcutManager.addListener(keyboardShortcuts, priority);
101
+ }
102
+ forwardEvent(eventName, ...args) {
103
+ const handlerName = `handle${capitalize(eventName)}`;
104
+ this._plugins.forEach((plugin) => {
105
+ if (this._exclusivePlugins.size && !this._exclusivePlugins.has(plugin))
106
+ return;
107
+ const handler = plugin[handlerName];
108
+ if (handler) {
109
+ handler.apply(plugin, args);
110
+ }
111
+ });
112
+ }
113
+ forwardStyleChangeEvent(event) {
114
+ this._plugins.forEach((plugin) => {
115
+ if (this._exclusivePlugins.size && !this._exclusivePlugins.has(plugin))
116
+ return;
117
+ if (plugin.handleBeforeStyleChanged) {
118
+ const result = plugin.handleBeforeStyleChanged(event);
119
+ if (result) {
120
+ event.newStyle = result;
121
+ }
122
+ }
123
+ });
124
+ return event.newStyle;
125
+ }
126
+ forwardSetExclusivePlugin() {
127
+ this._plugins.forEach((plugin) => {
128
+ if (plugin.handleSetExclusivePlugin) {
129
+ plugin.handleSetExclusivePlugin(Array.from(this._exclusivePlugins));
130
+ }
131
+ });
132
+ }
133
+ }
134
+
135
+ export { PluginManager };
@@ -0,0 +1,55 @@
1
+ import { ensureArray } from '../../utils/ensure-array.mjs';
2
+ import { isArray } from '../../utils/type-checks.mjs';
3
+ import { MapPopup } from '../support/map-popup.mjs';
4
+ import { MapPluginBase } from './map-plugin-base.mjs';
5
+ import { FlowCoreError } from '../../errors/flow-error.mjs';
6
+
7
+ var __defProp = Object.defineProperty;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __publicField = (obj, key, value) => {
10
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
11
+ return value;
12
+ };
13
+ class PopupPlugin extends MapPluginBase {
14
+ constructor(pluginManager) {
15
+ super(pluginManager);
16
+ __publicField(this, "_popupTemplates", /* @__PURE__ */ new Map());
17
+ __publicField(this, "_popups", /* @__PURE__ */ new Set());
18
+ __publicField(this, "name", "PopupPlugin");
19
+ }
20
+ registerPopup(id, options) {
21
+ this._popupTemplates.set(id, options);
22
+ }
23
+ openPopup(id, eventOrLngLat, feature) {
24
+ const popupOptions = this._popupTemplates.get(id);
25
+ if (!popupOptions) {
26
+ throw new FlowCoreError(`MapPopup: no popup with id '${id}'`);
27
+ }
28
+ const popup = new MapPopup(this.map, popupOptions);
29
+ this._popups.add(popup);
30
+ popup.once("closed", () => {
31
+ this._popups.delete(popup);
32
+ });
33
+ if (isArray(eventOrLngLat)) {
34
+ popup.open(eventOrLngLat, feature);
35
+ } else {
36
+ popup.open(eventOrLngLat);
37
+ }
38
+ return popup;
39
+ }
40
+ closeAllPopups() {
41
+ this._popups.forEach((popup) => popup.close());
42
+ this._popups.clear();
43
+ }
44
+ closePopupsForSources(sources) {
45
+ sources = ensureArray(sources);
46
+ this._popups.forEach((popup) => {
47
+ if (popup.feature?.source && sources.indexOf(popup.feature?.source) >= 0) {
48
+ popup.close();
49
+ this._popups.delete(popup);
50
+ }
51
+ });
52
+ }
53
+ }
54
+
55
+ export { PopupPlugin };
@@ -0,0 +1,21 @@
1
+ import { MapEvent } from '../../support/map-events.mjs';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
5
+ var __publicField = (obj, key, value) => {
6
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
7
+ return value;
8
+ };
9
+ class SelectionEvent extends MapEvent {
10
+ constructor(type, map, sourceIdentifier, features) {
11
+ super(type, map);
12
+ __publicField(this, "source");
13
+ __publicField(this, "sourceLayer");
14
+ __publicField(this, "features");
15
+ this.source = sourceIdentifier.source;
16
+ this.sourceLayer = sourceIdentifier.sourceLayer;
17
+ this.features = features;
18
+ }
19
+ }
20
+
21
+ export { SelectionEvent };
@@ -0,0 +1,136 @@
1
+ import { FlowMapError } from '../../../errors/flow-map-error.mjs';
2
+ import { ensureArray } from '../../../utils/ensure-array.mjs';
3
+ import { isStringFull } from '../../../utils/type-checks.mjs';
4
+ import { MapPluginBase } from '../map-plugin-base.mjs';
5
+ import { PopupPlugin } from '../popup.plugin.mjs';
6
+ import { SourceSelection } from './source-selection.mjs';
7
+
8
+ var __defProp = Object.defineProperty;
9
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
+ var __publicField = (obj, key, value) => {
11
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
12
+ return value;
13
+ };
14
+ class SelectionPlugin extends MapPluginBase {
15
+ constructor(pluginManager) {
16
+ super(pluginManager);
17
+ __publicField(this, "_sourceSelections", /* @__PURE__ */ new Map());
18
+ __publicField(this, "_enablePopups", true);
19
+ __publicField(this, "name", "SelectionPlugin");
20
+ }
21
+ clearSelection(source) {
22
+ const sourceSelection = this._sourceSelections.get(source);
23
+ if (!sourceSelection) {
24
+ return;
25
+ }
26
+ switch (sourceSelection.options.mode) {
27
+ case "single":
28
+ case "multi":
29
+ sourceSelection.updateSelection(/* @__PURE__ */ new Set());
30
+ }
31
+ }
32
+ deselectFeatures(source, features) {
33
+ const sourceSelection = this._sourceSelections.get(source);
34
+ if (!sourceSelection) {
35
+ return;
36
+ }
37
+ features = ensureArray(features);
38
+ const currentSelection = new Set(sourceSelection.selectedFeatureIds);
39
+ switch (sourceSelection.options.mode) {
40
+ case "single":
41
+ case "multi":
42
+ features.forEach((f) => currentSelection.delete(f));
43
+ sourceSelection.updateSelection(currentSelection);
44
+ }
45
+ }
46
+ destroy() {
47
+ this._sourceSelections.forEach((sel) => sel.destroy());
48
+ this._sourceSelections.clear();
49
+ }
50
+ disableSelection(source) {
51
+ if (this._sourceSelections.has(source)) {
52
+ this._sourceSelections.get(source).destroy();
53
+ this._sourceSelections.delete(source);
54
+ }
55
+ }
56
+ enableSelection(source, options) {
57
+ if (options.popup) {
58
+ try {
59
+ this.map.getPlugin(PopupPlugin);
60
+ } catch (e) {
61
+ throw new FlowMapError(`enableSelection(): Popup plugin is required for automatic selection popups`);
62
+ }
63
+ }
64
+ this.disableSelection(source);
65
+ this._sourceSelections.set(source, new SourceSelection(this, source, options));
66
+ }
67
+ handleBeforeSourceRemoved(event) {
68
+ if (this._sourceSelections.has(event.source)) {
69
+ this._sourceSelections.get(event.source)?.destroy();
70
+ this._sourceSelections.delete(event.source);
71
+ }
72
+ }
73
+ handleClick(event) {
74
+ const multiSelect = event.originalEvent.shiftKey || event.originalEvent.altKey || event.originalEvent.metaKey;
75
+ const clickedFeature = event.features[0];
76
+ const clickedSource = clickedFeature?.source;
77
+ const selectionOptions = this._sourceSelections.get(clickedSource)?.options;
78
+ this._sourceSelections.forEach(
79
+ (sel) => sel.handleClick(event.lngLat, clickedFeature, selectionOptions, multiSelect, this._enablePopups)
80
+ );
81
+ }
82
+ selectFeatures(source, features) {
83
+ const sourceSelection = this._sourceSelections.get(source);
84
+ if (!sourceSelection) {
85
+ return;
86
+ }
87
+ features = ensureArray(features);
88
+ if (features.length === 0) {
89
+ this.clearSelection(source);
90
+ return;
91
+ }
92
+ let currentSelection = new Set(sourceSelection.selectedFeatureIds);
93
+ switch (sourceSelection.options.mode) {
94
+ case "single":
95
+ currentSelection = /* @__PURE__ */ new Set([features[0]]);
96
+ sourceSelection.updateSelection(currentSelection);
97
+ break;
98
+ case "multi":
99
+ features.forEach((f) => currentSelection.add(f));
100
+ sourceSelection.updateSelection(currentSelection);
101
+ break;
102
+ }
103
+ if (sourceSelection.selectedFeatureIds.size && isStringFull(sourceSelection.options.group)) {
104
+ this._sourceSelections.forEach((s) => {
105
+ if (s != sourceSelection && s.options.clear === "group" && s.options.group == sourceSelection.options.group) {
106
+ s.updateSelection(/* @__PURE__ */ new Set());
107
+ }
108
+ });
109
+ }
110
+ }
111
+ selectedFeatureIds(source) {
112
+ const sourceSelection = this._sourceSelections.get(source);
113
+ if (!sourceSelection) {
114
+ return [];
115
+ }
116
+ return Array.from(sourceSelection.selectedFeatureIds);
117
+ }
118
+ selectedFeatureStream(source) {
119
+ const sourceSelection = this._sourceSelections.get(source);
120
+ if (!sourceSelection) {
121
+ throw new FlowMapError("no source selection");
122
+ }
123
+ return sourceSelection.selectedFeatureStream;
124
+ }
125
+ enablePopups(enabled = true) {
126
+ this._enablePopups = true;
127
+ }
128
+ disablePopups() {
129
+ if (this._enablePopups) {
130
+ this.map.getPlugin(PopupPlugin).closeAllPopups();
131
+ this._enablePopups = false;
132
+ }
133
+ }
134
+ }
135
+
136
+ export { SelectionPlugin };
@@ -0,0 +1,156 @@
1
+ import { assert } from '../../../utils/assert.mjs';
2
+ import { ensureArray } from '../../../utils/ensure-array.mjs';
3
+ import { isStringFull } from '../../../utils/type-checks.mjs';
4
+ import { isFeatureWithId } from '../../types.mjs';
5
+ import { PopupPlugin } from '../popup.plugin.mjs';
6
+ import { SelectionEvent } from './selection.events.mjs';
7
+
8
+ var __defProp = Object.defineProperty;
9
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
+ var __publicField = (obj, key, value) => {
11
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
12
+ return value;
13
+ };
14
+ class SourceSelection {
15
+ constructor(plugin, source, options) {
16
+ __publicField(this, "_featureStream");
17
+ __publicField(this, "_map");
18
+ __publicField(this, "_options");
19
+ __publicField(this, "_plugin");
20
+ __publicField(this, "_popups", /* @__PURE__ */ new Map());
21
+ __publicField(this, "_selectedFeatureIds", /* @__PURE__ */ new Set());
22
+ __publicField(this, "_sourceId");
23
+ __publicField(this, "_stateSubscription", null);
24
+ this._plugin = plugin;
25
+ this._map = plugin.map;
26
+ this._options = options;
27
+ const sourceType = plugin.map.getSource(source).type;
28
+ this._sourceId = {
29
+ source,
30
+ sourceLayer: sourceType === "vector" ? options.sourceLayer ?? "geometry" : void 0
31
+ };
32
+ this._featureStream = this._map.featureStream({
33
+ source,
34
+ sourceLayer: options.sourceLayer,
35
+ featureIds: []
36
+ });
37
+ if (options.selectionState) {
38
+ this._stateSubscription = this._map.context.state.toStream(options.selectionState, true).subscribe((val) => {
39
+ val = new Set(ensureArray(val));
40
+ this.updateSelection(val);
41
+ });
42
+ }
43
+ }
44
+ get options() {
45
+ return this._options;
46
+ }
47
+ get selectedFeatureIds() {
48
+ return this._selectedFeatureIds;
49
+ }
50
+ get selectedFeatureStream() {
51
+ return this._featureStream;
52
+ }
53
+ destroy() {
54
+ this._popups.forEach((popup) => popup.close());
55
+ this._stateSubscription?.unsubscribe();
56
+ this._selectedFeatureIds = /* @__PURE__ */ new Set();
57
+ this.updateMapSelectionState();
58
+ }
59
+ handleClick(lngLat, feature, selectionOptions, multiSelect, enablePopups) {
60
+ if (this._options.mode === "none") {
61
+ return;
62
+ }
63
+ const selectedFeatureIDs = new Set(this._selectedFeatureIds);
64
+ let clearSelection = false;
65
+ let toggleFeature = false;
66
+ let showPopup = false;
67
+ if (feature && feature.source === this._sourceId.source) {
68
+ if (this._options.mode === "single") {
69
+ if (selectedFeatureIDs.has(feature.id)) {
70
+ toggleFeature = true;
71
+ } else {
72
+ clearSelection = true;
73
+ toggleFeature = true;
74
+ }
75
+ } else {
76
+ if (!multiSelect) {
77
+ clearSelection = true;
78
+ }
79
+ toggleFeature = true;
80
+ }
81
+ } else if (feature && feature.source !== this._sourceId.source) {
82
+ clearSelection = this._options.clear === "group" ? selectionOptions?.group === this._options.group : !!this._options.clear;
83
+ } else {
84
+ clearSelection = !multiSelect;
85
+ }
86
+ if (clearSelection) {
87
+ selectedFeatureIDs.clear();
88
+ }
89
+ if (toggleFeature) {
90
+ assert(isFeatureWithId(feature));
91
+ if (selectedFeatureIDs.has(feature.id) && multiSelect) {
92
+ selectedFeatureIDs.delete(feature.id);
93
+ if (this._popups.has(feature.id))
94
+ this._popups.get(feature.id).close();
95
+ } else {
96
+ selectedFeatureIDs.add(feature.id);
97
+ showPopup = true;
98
+ }
99
+ }
100
+ this.updateSelection(selectedFeatureIDs);
101
+ if (enablePopups && showPopup && this._options.popup) {
102
+ assert(isFeatureWithId(feature));
103
+ if (!this._popups.has(feature.id)) {
104
+ const popup = this._map.getPlugin(PopupPlugin).openPopup(this._options.popup, lngLat, feature);
105
+ if (popup) {
106
+ popup.on("closed", () => {
107
+ this._popups.delete(feature.id);
108
+ });
109
+ this._popups.set(feature.id, popup);
110
+ }
111
+ }
112
+ }
113
+ }
114
+ updateSelection(featureIds) {
115
+ if (!this.areSetsEqual(featureIds, this._selectedFeatureIds)) {
116
+ this._selectedFeatureIds = featureIds;
117
+ this.updateMapSelectionState();
118
+ this.updateStateStore();
119
+ this._featureStream.featureIds = Array.from(featureIds);
120
+ this._popups.forEach((popup) => {
121
+ assert(isFeatureWithId(popup.feature));
122
+ if (popup.feature.id && !featureIds.has(popup.feature.id)) {
123
+ popup.close();
124
+ }
125
+ });
126
+ const event = new SelectionEvent(
127
+ "selectionChanged",
128
+ this._map,
129
+ this._sourceId,
130
+ Array.from(this._selectedFeatureIds)
131
+ );
132
+ this._plugin.emit(event.type, event);
133
+ }
134
+ }
135
+ areSetsEqual(a, b) {
136
+ return a.size === b.size && [...a].every((value) => b.has(value));
137
+ }
138
+ updateMapSelectionState() {
139
+ this._map.removeFeatureState(this._sourceId, "selected", false);
140
+ this._selectedFeatureIds.forEach(
141
+ (id) => this._map.setFeatureState({ ...this._sourceId, id }, { selected: true }, false)
142
+ );
143
+ }
144
+ updateStateStore() {
145
+ if (isStringFull(this._options.selectionState)) {
146
+ const selectedIds = Array.from(this._selectedFeatureIds);
147
+ if (this._options.mode === "single") {
148
+ this._map.context.state.set(this._options.selectionState, selectedIds[0]);
149
+ } else if (this._options.mode === "multi") {
150
+ this._map.context.state.set(this._options.selectionState, selectedIds);
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ export { SourceSelection };