@open-pioneer/selection 1.4.0 → 1.5.0-dev.20260910103330

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 (60) hide show
  1. package/CHANGELOG.md +47 -29
  2. package/README.md +28 -9
  3. package/_virtual/hooks.js +1 -1
  4. package/_virtual/source-info.js +1 -1
  5. package/api.d.ts +27 -2
  6. package/i18n/de.yaml +0 -6
  7. package/i18n/en.yaml +0 -6
  8. package/index.d.ts +1 -1
  9. package/index.js +1 -1
  10. package/interactions/ExtentSelectionInteraction.d.ts +12 -0
  11. package/interactions/ExtentSelectionInteraction.js +57 -0
  12. package/interactions/ExtentSelectionInteraction.js.map +1 -0
  13. package/model/SelectionViewModel.d.ts +30 -0
  14. package/model/SelectionViewModel.js +270 -0
  15. package/model/SelectionViewModel.js.map +1 -0
  16. package/model/SelectionViewModelFactory.d.ts +21 -0
  17. package/model/SelectionViewModelFactory.js +43 -0
  18. package/model/SelectionViewModelFactory.js.map +1 -0
  19. package/model/index.d.ts +1 -0
  20. package/model/index.js +2 -0
  21. package/model/index.js.map +1 -0
  22. package/package.json +34 -15
  23. package/services.d.ts +2 -7
  24. package/services.js +2 -17
  25. package/services.js.map +1 -1
  26. package/{VectorSelectionSource.d.ts → sources/VectorSelectionSource.d.ts} +3 -7
  27. package/{VectorSelectionSource.js → sources/VectorSelectionSource.js} +12 -9
  28. package/sources/VectorSelectionSource.js.map +1 -0
  29. package/sources/VectorSelectionSourceFactory.d.ts +7 -0
  30. package/sources/VectorSelectionSourceFactory.js +19 -0
  31. package/sources/VectorSelectionSourceFactory.js.map +1 -0
  32. package/{Selection.d.ts → ui/Selection.d.ts} +1 -1
  33. package/ui/Selection.js +97 -0
  34. package/ui/Selection.js.map +1 -0
  35. package/ui/SelectionSourceItem.d.ts +7 -0
  36. package/ui/SelectionSourceItem.js +38 -0
  37. package/ui/SelectionSourceItem.js.map +1 -0
  38. package/ui/useSelectionSourceId.d.ts +9 -0
  39. package/ui/useSelectionSourceId.js +23 -0
  40. package/ui/useSelectionSourceId.js.map +1 -0
  41. package/ui/useSelectionViewModel.d.ts +11 -0
  42. package/ui/useSelectionViewModel.js +43 -0
  43. package/ui/useSelectionViewModel.js.map +1 -0
  44. package/ui/useSourceStatus.d.ts +8 -0
  45. package/ui/useSourceStatus.js +27 -0
  46. package/ui/useSourceStatus.js.map +1 -0
  47. package/DragController.d.ts +0 -33
  48. package/DragController.js +0 -175
  49. package/DragController.js.map +0 -1
  50. package/Selection.js +0 -232
  51. package/Selection.js.map +0 -1
  52. package/SelectionController.d.ts +0 -20
  53. package/SelectionController.js +0 -58
  54. package/SelectionController.js.map +0 -1
  55. package/VectorSelectionSource.js.map +0 -1
  56. /package/{SelectionTooltipContent.d.ts → ui/SelectionTooltipContent.d.ts} +0 -0
  57. /package/{SelectionTooltipContent.js → ui/SelectionTooltipContent.js} +0 -0
  58. /package/{SelectionTooltipContent.js.map → ui/SelectionTooltipContent.js.map} +0 -0
  59. /package/{selection.css → ui/selection.css} +0 -0
  60. /package/{selection.css.map → ui/selection.css.map} +0 -0
@@ -0,0 +1,97 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { createListCollection, VStack, Select, Portal } from '@chakra-ui/react';
3
+ import { useMapModelValue } from '@open-pioneer/map';
4
+ import { useCommonComponentProps } from '@open-pioneer/react-utils';
5
+ import { useReactiveSnapshot } from '@open-pioneer/reactivity';
6
+ import { useIntl } from '../_virtual/hooks.js';
7
+ import { useMemo } from 'react';
8
+ import { SelectionSourceItem } from './SelectionSourceItem.js';
9
+ import { useSelectionSourceId } from './useSelectionSourceId.js';
10
+ import { useSelectionViewModel } from './useSelectionViewModel.js';
11
+ import { useSourceStatus } from './useSourceStatus.js';
12
+
13
+ const Selection = (props) => {
14
+ const { sources, onSelectionComplete, onSelectionSourceChanged } = props;
15
+ const map = useMapModelValue(props);
16
+ const viewModel = useSelectionViewModel(
17
+ map,
18
+ sources,
19
+ onSelectionComplete,
20
+ onSelectionSourceChanged
21
+ );
22
+ return viewModel && /* @__PURE__ */ jsx(SelectionReady, { viewModel, ...props });
23
+ };
24
+ function SelectionReady(props) {
25
+ const { viewModel } = props;
26
+ const { containerProps } = useCommonComponentProps("selection", props);
27
+ const intl = useIntl();
28
+ const sources = useReactiveSnapshot(() => viewModel.sources, [viewModel]);
29
+ const currentSource = useReactiveSnapshot(() => viewModel.currentSource, [viewModel]);
30
+ const ariaMessage = useReactiveSnapshot(() => viewModel.ariaMessage, [viewModel]);
31
+ const getSourceId = useSelectionSourceId();
32
+ const sourceOptionsCollection = useMemo(
33
+ () => createListCollection({
34
+ items: sources,
35
+ isItemDisabled: () => {
36
+ return false;
37
+ },
38
+ itemToString: (item) => item.label,
39
+ itemToValue: (item) => getSourceId(item)
40
+ }),
41
+ [sources, getSourceId]
42
+ );
43
+ let triggerItem;
44
+ if (currentSource) {
45
+ triggerItem = /* @__PURE__ */ jsx(SelectionSourceItem, { source: currentSource });
46
+ } else {
47
+ triggerItem = null;
48
+ }
49
+ return /* @__PURE__ */ jsx(VStack, { ...containerProps, gap: 2, children: /* @__PURE__ */ jsxs(
50
+ Select.Root,
51
+ {
52
+ className: "selection-source",
53
+ collection: sourceOptionsCollection,
54
+ value: currentSource ? [getSourceId(currentSource)] : [],
55
+ onValueChange: (details) => {
56
+ const source = details.items[0];
57
+ if (source) {
58
+ viewModel.currentSource = source;
59
+ }
60
+ },
61
+ lazyMount: true,
62
+ unmountOnExit: true,
63
+ children: [
64
+ /* @__PURE__ */ jsx(Select.Label, { children: intl.formatMessage({ id: "selectSource" }) }),
65
+ /* @__PURE__ */ jsxs(Select.Control, { children: [
66
+ /* @__PURE__ */ jsx(Select.Trigger, { "aria-description": ariaMessage, children: /* @__PURE__ */ jsx(
67
+ Select.ValueText,
68
+ {
69
+ placeholder: intl.formatMessage({ id: "selectionPlaceholder" }),
70
+ children: triggerItem
71
+ }
72
+ ) }),
73
+ /* @__PURE__ */ jsx(Select.IndicatorGroup, { children: /* @__PURE__ */ jsx(Select.Indicator, {}) })
74
+ ] }),
75
+ /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Select.Positioner, { children: /* @__PURE__ */ jsx(Select.Content, { className: "selection-source-options", children: sourceOptionsCollection.items.map((item) => /* @__PURE__ */ jsx(SelectionSourceItemContent, { source: item }, getSourceId(item))) }) }) })
76
+ ]
77
+ }
78
+ ) });
79
+ }
80
+ function SelectionSourceItemContent(props) {
81
+ const { source } = props;
82
+ const isDisabled = useSourceStatus(source).kind === "unavailable";
83
+ return /* @__PURE__ */ jsx(
84
+ Select.Item,
85
+ {
86
+ className: "selection-source-option",
87
+ item: source,
88
+ justifyContent: "flex-start",
89
+ pointerEvents: "auto",
90
+ "aria-disabled": isDisabled ? "true" : void 0,
91
+ children: /* @__PURE__ */ jsx(SelectionSourceItem, { source })
92
+ }
93
+ );
94
+ }
95
+
96
+ export { Selection };
97
+ //# sourceMappingURL=Selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Selection.js","sources":["Selection.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { createListCollection, Portal, Select, VStack } from \"@chakra-ui/react\";\nimport { MapModelProps, useMapModelValue } from \"@open-pioneer/map\";\nimport { CommonComponentProps, useCommonComponentProps } from \"@open-pioneer/react-utils\";\nimport { useReactiveSnapshot } from \"@open-pioneer/reactivity\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { FC, useMemo } from \"react\";\nimport { SelectionResult, SelectionSource } from \"../api\";\nimport { SelectionViewModel } from \"../model\";\nimport { SelectionSourceItem } from \"./SelectionSourceItem\";\nimport { useSelectionSourceId } from \"./useSelectionSourceId\";\nimport { useSelectionViewModel } from \"./useSelectionViewModel\";\nimport { useSourceStatus } from \"./useSourceStatus\";\n\n/**\n * Properties supported by the {@link Selection} component.\n */\nexport interface SelectionProps extends CommonComponentProps, MapModelProps {\n /**\n * Array of selection sources available for spatial selection.\n */\n sources: SelectionSource[];\n\n /**\n * This handler is called whenever the user has successfully selected\n * some items.\n */\n onSelectionComplete?(event: SelectionCompleteEvent): void;\n\n /**\n * This handler is called whenever the user has changed the selected source\n */\n onSelectionSourceChanged?(event: SelectionSourceChangedEvent): void;\n}\n\nexport interface SelectionCompleteEvent {\n /** The source that returned the {@link results}. */\n source: SelectionSource;\n\n /** Results selected by the user. */\n results: SelectionResult[];\n}\n\nexport interface SelectionSourceChangedEvent {\n /** The new selected source */\n source: SelectionSource | undefined;\n}\n\n/**\n * A component that allows the user to perform a spatial selection on a given set of {@link SelectionSource}.\n */\nexport const Selection: FC<SelectionProps> = (props) => {\n const { sources, onSelectionComplete, onSelectionSourceChanged } = props;\n const map = useMapModelValue(props);\n const viewModel = useSelectionViewModel(\n map,\n sources,\n onSelectionComplete,\n onSelectionSourceChanged\n );\n return viewModel && <SelectionReady viewModel={viewModel} {...props} />;\n};\n\nfunction SelectionReady(props: CommonComponentProps & { viewModel: SelectionViewModel }) {\n const { viewModel } = props;\n const { containerProps } = useCommonComponentProps(\"selection\", props);\n const intl = useIntl();\n\n // Subscribe to relevant view model state.\n const sources = useReactiveSnapshot(() => viewModel.sources, [viewModel]);\n const currentSource = useReactiveSnapshot(() => viewModel.currentSource, [viewModel]);\n const ariaMessage = useReactiveSnapshot(() => viewModel.ariaMessage, [viewModel]);\n\n // Translate sources array to a collection for chakra's select control.\n const getSourceId = useSelectionSourceId();\n const sourceOptionsCollection = useMemo(\n () =>\n createListCollection({\n items: sources,\n isItemDisabled: () => {\n return false;\n },\n itemToString: (item) => item.label,\n itemToValue: (item) => getSourceId(item)\n }),\n [sources, getSourceId]\n );\n\n let triggerItem;\n if (currentSource) {\n triggerItem = <SelectionSourceItem source={currentSource} />;\n } else {\n triggerItem = null;\n }\n\n return (\n <VStack {...containerProps} gap={2}>\n <Select.Root\n className=\"selection-source\"\n collection={sourceOptionsCollection}\n value={currentSource ? [getSourceId(currentSource)] : []}\n onValueChange={(details) => {\n const source = details.items[0];\n if (source) {\n viewModel.currentSource = source;\n }\n }}\n lazyMount={true}\n unmountOnExit={true}\n >\n <Select.Label>{intl.formatMessage({ id: \"selectSource\" })}</Select.Label>\n\n <Select.Control>\n <Select.Trigger aria-description={ariaMessage}>\n <Select.ValueText\n placeholder={intl.formatMessage({ id: \"selectionPlaceholder\" })}\n >\n {triggerItem}\n </Select.ValueText>\n </Select.Trigger>\n <Select.IndicatorGroup>\n <Select.Indicator />\n </Select.IndicatorGroup>\n </Select.Control>\n\n <Portal>\n <Select.Positioner>\n <Select.Content className=\"selection-source-options\">\n {sourceOptionsCollection.items.map((item) => (\n <SelectionSourceItemContent source={item} key={getSourceId(item)} />\n ))}\n </Select.Content>\n </Select.Positioner>\n </Portal>\n </Select.Root>\n </VStack>\n );\n}\n\n/**\n * Renders a dropdown item in the menu.\n */\nfunction SelectionSourceItemContent(props: { source: SelectionSource }) {\n const { source } = props;\n const isDisabled = useSourceStatus(source).kind === \"unavailable\";\n return (\n <Select.Item\n className=\"selection-source-option\"\n item={source}\n justifyContent=\"flex-start\"\n // Override pointer-events: none rule for disabled items; we want to show the tooltip on hover\n pointerEvents=\"auto\"\n aria-disabled={isDisabled ? \"true\" : undefined}\n >\n <SelectionSourceItem source={source} />\n </Select.Item>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { SelectionSource } from "../api";
2
+ /**
3
+ * Renders a selection source in the dropdown menu (option or current selection).
4
+ */
5
+ export declare function SelectionSourceItem(props: {
6
+ source: SelectionSource | undefined;
7
+ }): import("react").JSX.Element;
@@ -0,0 +1,38 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { Flex, Box, chakra, Icon } from '@chakra-ui/react';
3
+ import { Tooltip } from '@open-pioneer/chakra-snippets/tooltip';
4
+ import { useReactiveSnapshot } from '@open-pioneer/reactivity';
5
+ import { LuTriangleAlert } from 'react-icons/lu';
6
+ import { useSourceStatus } from './useSourceStatus.js';
7
+
8
+ function SelectionSourceItem(props) {
9
+ const { source } = props;
10
+ const label = useReactiveSnapshot(() => source?.label, [source]);
11
+ const status = useSourceStatus(source);
12
+ const isAvailable = status.kind === "available";
13
+ const clazz = isAvailable ? "selection-source-value" : "selection-source-value selection-source-value--disabled";
14
+ return /* @__PURE__ */ jsxs(Flex, { className: clazz, direction: "row", alignItems: "center", grow: 1, children: [
15
+ label,
16
+ status.kind === "unavailable" && /* @__PURE__ */ jsx(Box, { ml: 2, children: /* @__PURE__ */ jsx(
17
+ Tooltip,
18
+ {
19
+ content: status.reason,
20
+ positioning: { placement: "right" },
21
+ openDelay: 500,
22
+ children: /* @__PURE__ */ jsx(chakra.span, { children: /* @__PURE__ */ jsx(
23
+ Icon,
24
+ {
25
+ color: "red",
26
+ className: "warning-icon",
27
+ "aria-label": status.reason,
28
+ "aria-hidden": void 0,
29
+ children: /* @__PURE__ */ jsx(LuTriangleAlert, {})
30
+ }
31
+ ) })
32
+ }
33
+ ) })
34
+ ] });
35
+ }
36
+
37
+ export { SelectionSourceItem };
38
+ //# sourceMappingURL=SelectionSourceItem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectionSourceItem.js","sources":["SelectionSourceItem.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Box, chakra, Flex, Icon } from \"@chakra-ui/react\";\nimport { Tooltip } from \"@open-pioneer/chakra-snippets/tooltip\";\nimport { useReactiveSnapshot } from \"@open-pioneer/reactivity\";\nimport { LuTriangleAlert } from \"react-icons/lu\";\nimport { SelectionSource } from \"../api\";\nimport { useSourceStatus } from \"./useSourceStatus\";\n\n/**\n * Renders a selection source in the dropdown menu (option or current selection).\n */\nexport function SelectionSourceItem(props: { source: SelectionSource | undefined }) {\n const { source } = props;\n const label = useReactiveSnapshot(() => source?.label, [source]);\n const status = useSourceStatus(source);\n const isAvailable = status.kind === \"available\";\n const clazz = isAvailable\n ? \"selection-source-value\"\n : \"selection-source-value selection-source-value--disabled\";\n\n return (\n <Flex className={clazz} direction=\"row\" alignItems=\"center\" grow={1}>\n {label}\n {status.kind === \"unavailable\" && (\n <Box ml={2}>\n <Tooltip\n content={status.reason}\n positioning={{ placement: \"right\" }}\n openDelay={500}\n >\n <chakra.span>\n <Icon\n color=\"red\"\n className=\"warning-icon\"\n aria-label={status.reason}\n aria-hidden={undefined} // Overwrite icon default so the label gets read\n >\n <LuTriangleAlert />\n </Icon>\n </chakra.span>\n </Tooltip>\n </Box>\n )}\n </Flex>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAaO,SAAS,oBAAoB,KAAA,EAAgD;AAChF,EAAA,MAAM,EAAE,QAAO,GAAI,KAAA;AACnB,EAAA,MAAM,QAAQ,mBAAA,CAAoB,MAAM,QAAQ,KAAA,EAAO,CAAC,MAAM,CAAC,CAAA;AAC/D,EAAA,MAAM,MAAA,GAAS,gBAAgB,MAAM,CAAA;AACrC,EAAA,MAAM,WAAA,GAAc,OAAO,IAAA,KAAS,WAAA;AACpC,EAAA,MAAM,KAAA,GAAQ,cACR,wBAAA,GACA,yDAAA;AAEN,EAAA,uBACI,IAAA,CAAC,QAAK,SAAA,EAAW,KAAA,EAAO,WAAU,KAAA,EAAM,UAAA,EAAW,QAAA,EAAS,IAAA,EAAM,CAAA,EAC7D,QAAA,EAAA;AAAA,IAAA,KAAA;AAAA,IACA,OAAO,IAAA,KAAS,aAAA,oBACb,GAAA,CAAC,GAAA,EAAA,EAAI,IAAI,CAAA,EACL,QAAA,kBAAA,GAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACG,SAAS,MAAA,CAAO,MAAA;AAAA,QAChB,WAAA,EAAa,EAAE,SAAA,EAAW,OAAA,EAAQ;AAAA,QAClC,SAAA,EAAW,GAAA;AAAA,QAEX,QAAA,kBAAA,GAAA,CAAC,MAAA,CAAO,IAAA,EAAP,EACG,QAAA,kBAAA,GAAA;AAAA,UAAC,IAAA;AAAA,UAAA;AAAA,YACG,KAAA,EAAM,KAAA;AAAA,YACN,SAAA,EAAU,cAAA;AAAA,YACV,cAAY,MAAA,CAAO,MAAA;AAAA,YACnB,aAAA,EAAa,MAAA;AAAA,YAEb,8BAAC,eAAA,EAAA,EAAgB;AAAA;AAAA,SACrB,EACJ;AAAA;AAAA,KACJ,EACJ;AAAA,GAAA,EAER,CAAA;AAER;;;;"}
@@ -0,0 +1,9 @@
1
+ import { SelectionSource } from "../api";
2
+ export type GetSelectionSourceId = (selectionSource: SelectionSource) => string;
3
+ /**
4
+ * Assigns unique IDs to selection sources.
5
+ *
6
+ * Sources that define their own {@link SelectionSource.id} keep that id;
7
+ * for all other sources an id is generated.
8
+ */
9
+ export declare function useSelectionSourceId(): GetSelectionSourceId;
@@ -0,0 +1,23 @@
1
+ import { useRef, useCallback } from 'react';
2
+
3
+ const GENERATED_ID_PREFIX = "__anon_source_";
4
+ function useSelectionSourceId() {
5
+ const sourceIds = useRef(void 0);
6
+ const counter = useRef(0);
7
+ if (!sourceIds.current) {
8
+ sourceIds.current = /* @__PURE__ */ new WeakMap();
9
+ }
10
+ return useCallback((source) => {
11
+ if (source.id != null) {
12
+ return source.id;
13
+ }
14
+ const ids = sourceIds.current;
15
+ if (!ids.has(source)) {
16
+ ids.set(source, `${GENERATED_ID_PREFIX}${counter.current++}`);
17
+ }
18
+ return ids.get(source);
19
+ }, []);
20
+ }
21
+
22
+ export { useSelectionSourceId };
23
+ //# sourceMappingURL=useSelectionSourceId.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSelectionSourceId.js","sources":["useSelectionSourceId.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { useCallback, useRef } from \"react\";\nimport { SelectionSource } from \"../api\";\n\nexport type GetSelectionSourceId = (selectionSource: SelectionSource) => string;\n\nconst GENERATED_ID_PREFIX = \"__anon_source_\";\n\n/**\n * Assigns unique IDs to selection sources.\n *\n * Sources that define their own {@link SelectionSource.id} keep that id;\n * for all other sources an id is generated.\n */\nexport function useSelectionSourceId(): GetSelectionSourceId {\n const sourceIds = useRef<WeakMap<SelectionSource, string>>(undefined);\n const counter = useRef(0);\n // oxlint-disable-next-line react/refs\n if (!sourceIds.current) {\n sourceIds.current = new WeakMap();\n }\n\n return useCallback((source: SelectionSource) => {\n if (source.id != null) {\n return source.id;\n }\n\n // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n const ids = sourceIds.current!;\n if (!ids.has(source)) {\n ids.set(source, `${GENERATED_ID_PREFIX}${counter.current++}`);\n }\n // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n return ids.get(source)!;\n }, []);\n}\n"],"names":[],"mappings":";;AAQA,MAAM,mBAAA,GAAsB,gBAAA;AAQrB,SAAS,oBAAA,GAA6C;AACzD,EAAA,MAAM,SAAA,GAAY,OAAyC,MAAS,CAAA;AACpE,EAAA,MAAM,OAAA,GAAU,OAAO,CAAC,CAAA;AAExB,EAAA,IAAI,CAAC,UAAU,OAAA,EAAS;AACpB,IAAA,SAAA,CAAU,OAAA,uBAAc,OAAA,EAAQ;AAAA,EACpC;AAEA,EAAA,OAAO,WAAA,CAAY,CAAC,MAAA,KAA4B;AAC5C,IAAA,IAAI,MAAA,CAAO,MAAM,IAAA,EAAM;AACnB,MAAA,OAAO,MAAA,CAAO,EAAA;AAAA,IAClB;AAGA,IAAA,MAAM,MAAM,SAAA,CAAU,OAAA;AACtB,IAAA,IAAI,CAAC,GAAA,CAAI,GAAA,CAAI,MAAM,CAAA,EAAG;AAClB,MAAA,GAAA,CAAI,IAAI,MAAA,EAAQ,CAAA,EAAG,mBAAmB,CAAA,EAAG,OAAA,CAAQ,SAAS,CAAA,CAAE,CAAA;AAAA,IAChE;AAEA,IAAA,OAAO,GAAA,CAAI,IAAI,MAAM,CAAA;AAAA,EACzB,CAAA,EAAG,EAAE,CAAA;AACT;;;;"}
@@ -0,0 +1,11 @@
1
+ import { MapModel } from "@open-pioneer/map";
2
+ import { SelectionSource } from "../api";
3
+ import { SelectionViewModel } from "../model";
4
+ import { SelectionCompleteEvent, SelectionSourceChangedEvent } from "./Selection";
5
+ /**
6
+ * Initializes the (internal) view model used by the selection component.
7
+ *
8
+ * The view model contains the primary widget state.
9
+ * It is a long lived instance that is updated in place when relevant react props are changed.
10
+ */
11
+ export declare function useSelectionViewModel(map: MapModel, sources: SelectionSource[], onSelectionComplete: ((event: SelectionCompleteEvent) => void) | undefined, onSelectionSourceChanged: ((event: SelectionSourceChangedEvent) => void) | undefined): SelectionViewModel | undefined;
@@ -0,0 +1,43 @@
1
+ import { watchValue } from '@conterra/reactivity-core';
2
+ import { useService } from '../_virtual/hooks.js';
3
+ import { useEffectEvent, useState, useEffect } from 'react';
4
+
5
+ function useSelectionViewModel(map, sources, onSelectionComplete, onSelectionSourceChanged) {
6
+ const viewModelFactory = useService("selection.ViewModelFactory");
7
+ const onComplete = useEffectEvent((source, results) => {
8
+ onSelectionComplete?.({ source, results });
9
+ });
10
+ const onChange = useEffectEvent((source) => {
11
+ onSelectionSourceChanged?.({ source });
12
+ });
13
+ const [viewModel, setViewModel] = useState();
14
+ useEffect(() => {
15
+ const vm = viewModelFactory.createViewModel({
16
+ map,
17
+ onSelectionComplete: onComplete
18
+ });
19
+ setViewModel(vm);
20
+ return () => {
21
+ setViewModel(void 0);
22
+ vm.destroy();
23
+ };
24
+ }, [viewModelFactory, map]);
25
+ useEffect(() => {
26
+ if (viewModel) {
27
+ viewModel.sources = sources;
28
+ }
29
+ }, [viewModel, sources]);
30
+ useEffect(() => {
31
+ if (!viewModel) {
32
+ return;
33
+ }
34
+ const handle = watchValue(() => viewModel.currentSource, onChange, {
35
+ immediate: true
36
+ });
37
+ return () => handle.destroy();
38
+ }, [viewModel]);
39
+ return viewModel;
40
+ }
41
+
42
+ export { useSelectionViewModel };
43
+ //# sourceMappingURL=useSelectionViewModel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSelectionViewModel.js","sources":["useSelectionViewModel.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { watchValue } from \"@conterra/reactivity-core\";\nimport { MapModel } from \"@open-pioneer/map\";\nimport { useService } from \"open-pioneer:react-hooks\";\nimport { useEffect, useEffectEvent, useState } from \"react\";\nimport { SelectionResult, SelectionSource } from \"../api\";\nimport { SelectionViewModel } from \"../model\";\nimport { SelectionViewModelFactory } from \"../services\";\nimport { SelectionCompleteEvent, SelectionSourceChangedEvent } from \"./Selection\";\n\n/**\n * Initializes the (internal) view model used by the selection component.\n *\n * The view model contains the primary widget state.\n * It is a long lived instance that is updated in place when relevant react props are changed.\n */\nexport function useSelectionViewModel(\n map: MapModel,\n sources: SelectionSource[],\n onSelectionComplete: ((event: SelectionCompleteEvent) => void) | undefined,\n onSelectionSourceChanged: ((event: SelectionSourceChangedEvent) => void) | undefined\n): SelectionViewModel | undefined {\n const viewModelFactory = useService<SelectionViewModelFactory>(\"selection.ViewModelFactory\");\n const onComplete = useEffectEvent((source: SelectionSource, results: SelectionResult[]) => {\n onSelectionComplete?.({ source, results });\n });\n const onChange = useEffectEvent((source: SelectionSource | undefined) => {\n onSelectionSourceChanged?.({ source });\n });\n\n // Construct view model\n const [viewModel, setViewModel] = useState<SelectionViewModel>();\n useEffect(() => {\n const vm = viewModelFactory.createViewModel({\n map,\n onSelectionComplete: onComplete\n });\n\n setViewModel(vm);\n return () => {\n setViewModel(undefined);\n vm.destroy();\n };\n }, [viewModelFactory, map]);\n\n // Sync sources --> view model\n useEffect(() => {\n if (viewModel) {\n viewModel.sources = sources;\n }\n }, [viewModel, sources]);\n\n // Sync current source --> react callbacks\n useEffect(() => {\n if (!viewModel) {\n return;\n }\n\n const handle = watchValue(() => viewModel.currentSource, onChange, {\n immediate: true\n });\n return () => handle.destroy();\n }, [viewModel]);\n\n return viewModel;\n}\n"],"names":[],"mappings":";;;;AAkBO,SAAS,qBAAA,CACZ,GAAA,EACA,OAAA,EACA,mBAAA,EACA,wBAAA,EAC8B;AAC9B,EAAA,MAAM,gBAAA,GAAmB,WAAsC,4BAA4B,CAAA;AAC3F,EAAA,MAAM,UAAA,GAAa,cAAA,CAAe,CAAC,MAAA,EAAyB,OAAA,KAA+B;AACvF,IAAA,mBAAA,GAAsB,EAAE,MAAA,EAAQ,OAAA,EAAS,CAAA;AAAA,EAC7C,CAAC,CAAA;AACD,EAAA,MAAM,QAAA,GAAW,cAAA,CAAe,CAAC,MAAA,KAAwC;AACrE,IAAA,wBAAA,GAA2B,EAAE,QAAQ,CAAA;AAAA,EACzC,CAAC,CAAA;AAGD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,QAAA,EAA6B;AAC/D,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,MAAM,EAAA,GAAK,iBAAiB,eAAA,CAAgB;AAAA,MACxC,GAAA;AAAA,MACA,mBAAA,EAAqB;AAAA,KACxB,CAAA;AAED,IAAA,YAAA,CAAa,EAAE,CAAA;AACf,IAAA,OAAO,MAAM;AACT,MAAA,YAAA,CAAa,MAAS,CAAA;AACtB,MAAA,EAAA,CAAG,OAAA,EAAQ;AAAA,IACf,CAAA;AAAA,EACJ,CAAA,EAAG,CAAC,gBAAA,EAAkB,GAAG,CAAC,CAAA;AAG1B,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,IAAI,SAAA,EAAW;AACX,MAAA,SAAA,CAAU,OAAA,GAAU,OAAA;AAAA,IACxB;AAAA,EACJ,CAAA,EAAG,CAAC,SAAA,EAAW,OAAO,CAAC,CAAA;AAGvB,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,IAAI,CAAC,SAAA,EAAW;AACZ,MAAA;AAAA,IACJ;AAEA,IAAA,MAAM,MAAA,GAAS,UAAA,CAAW,MAAM,SAAA,CAAU,eAAe,QAAA,EAAU;AAAA,MAC/D,SAAA,EAAW;AAAA,KACd,CAAA;AACD,IAAA,OAAO,MAAM,OAAO,OAAA,EAAQ;AAAA,EAChC,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,OAAO,SAAA;AACX;;;;"}
@@ -0,0 +1,8 @@
1
+ import { SelectionSource } from "../api";
2
+ export type SimpleStatus = {
3
+ kind: "available";
4
+ } | {
5
+ kind: "unavailable";
6
+ reason: string;
7
+ };
8
+ export declare function useSourceStatus(source: SelectionSource | undefined): SimpleStatus;
@@ -0,0 +1,27 @@
1
+ import { useReactiveSnapshot } from '@open-pioneer/reactivity';
2
+ import { useIntl } from '../_virtual/hooks.js';
3
+ import { getSourceStatus } from '../model/SelectionViewModel.js';
4
+
5
+ function useSourceStatus(source) {
6
+ const intl = useIntl();
7
+ const sourceStatus = useReactiveSnapshot(() => {
8
+ if (!source) {
9
+ return {
10
+ kind: "unavailable",
11
+ reason: intl.formatMessage({ id: "sourceNotAvailable" })
12
+ };
13
+ }
14
+ const status = getSourceStatus(source);
15
+ if (status.kind === "available") {
16
+ return status;
17
+ }
18
+ return {
19
+ kind: "unavailable",
20
+ reason: status.reason ?? intl.formatMessage({ id: "sourceNotAvailable" })
21
+ };
22
+ }, [source, intl]);
23
+ return sourceStatus;
24
+ }
25
+
26
+ export { useSourceStatus };
27
+ //# sourceMappingURL=useSourceStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSourceStatus.js","sources":["useSourceStatus.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { useReactiveSnapshot } from \"@open-pioneer/reactivity\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { SelectionSource } from \"../api\";\nimport { getSourceStatus } from \"../model\";\n\nexport type SimpleStatus =\n | {\n kind: \"available\";\n }\n | {\n kind: \"unavailable\";\n reason: string;\n };\n\nexport function useSourceStatus(source: SelectionSource | undefined): SimpleStatus {\n const intl = useIntl();\n const sourceStatus = useReactiveSnapshot((): SimpleStatus => {\n if (!source) {\n return {\n kind: \"unavailable\",\n reason: intl.formatMessage({ id: \"sourceNotAvailable\" })\n };\n }\n\n const status = getSourceStatus(source);\n if (status.kind === \"available\") {\n return status;\n }\n return {\n kind: \"unavailable\",\n reason: status.reason ?? intl.formatMessage({ id: \"sourceNotAvailable\" })\n };\n }, [source, intl]);\n return sourceStatus;\n}\n"],"names":[],"mappings":";;;;AAiBO,SAAS,gBAAgB,MAAA,EAAmD;AAC/E,EAAA,MAAM,OAAO,OAAA,EAAQ;AACrB,EAAA,MAAM,YAAA,GAAe,oBAAoB,MAAoB;AACzD,IAAA,IAAI,CAAC,MAAA,EAAQ;AACT,MAAA,OAAO;AAAA,QACH,IAAA,EAAM,aAAA;AAAA,QACN,QAAQ,IAAA,CAAK,aAAA,CAAc,EAAE,EAAA,EAAI,sBAAsB;AAAA,OAC3D;AAAA,IACJ;AAEA,IAAA,MAAM,MAAA,GAAS,gBAAgB,MAAM,CAAA;AACrC,IAAA,IAAI,MAAA,CAAO,SAAS,WAAA,EAAa;AAC7B,MAAA,OAAO,MAAA;AAAA,IACX;AACA,IAAA,OAAO;AAAA,MACH,IAAA,EAAM,aAAA;AAAA,MACN,MAAA,EAAQ,OAAO,MAAA,IAAU,IAAA,CAAK,cAAc,EAAE,EAAA,EAAI,sBAAsB;AAAA,KAC5E;AAAA,EACJ,CAAA,EAAG,CAAC,MAAA,EAAQ,IAAI,CAAC,CAAA;AACjB,EAAA,OAAO,YAAA;AACX;;;;"}
@@ -1,33 +0,0 @@
1
- import { Resource } from "@open-pioneer/core";
2
- import { MapModel } from "@open-pioneer/map";
3
- import Geometry from "ol/geom/Geometry";
4
- import PointerInteraction from "ol/interaction/Pointer";
5
- import OlMap from "ol/Map";
6
- interface InteractionResource extends Resource {
7
- interaction: PointerInteraction;
8
- }
9
- export declare class DragController {
10
- #private;
11
- constructor(map: MapModel, tooltipMessage: string, tooltipDisabledMessage: string, onExtentSelected: (geometry: Geometry) => void);
12
- initViewport(olMap: OlMap): HTMLElement;
13
- /**
14
- * Method for destroying the controller when it is no longer needed
15
- */
16
- destroy(): void;
17
- /**
18
- * The current tooltip text shown to the user.
19
- */
20
- get tooltipText(): string;
21
- setActive(isActive: boolean): void;
22
- /**
23
- * Method for testing purposes only
24
- * @returns InteractionResource of class DragBox
25
- */
26
- getDragboxInteraction(): InteractionResource | undefined;
27
- /**
28
- * Method for testing purposes only
29
- * @returns InteractionResource of class DragPan
30
- */
31
- getDragPanInteraction(): InteractionResource | undefined;
32
- }
33
- export {};
package/DragController.js DELETED
@@ -1,175 +0,0 @@
1
- import { reactive, watch } from '@conterra/reactivity-core';
2
- import { mouseActionButton } from 'ol/events/condition.js';
3
- import { DragBox, DragPan } from 'ol/interaction.js';
4
- import { createElement } from 'react';
5
- import { SelectionTooltipContent } from './SelectionTooltipContent.js';
6
-
7
- const ACTIVE_CLASS = "selection-active";
8
- const INACTIVE_CLASS = "selection-inactive";
9
- class DragController {
10
- #tooltip;
11
- #interactionResources = [];
12
- #olMap;
13
- #isActive = reactive(true);
14
- #tooltipMessage;
15
- #tooltipDisabledMessage;
16
- #tooltipSync;
17
- constructor(map, tooltipMessage, tooltipDisabledMessage, onExtentSelected) {
18
- this.#olMap = map.olMap;
19
- const viewPort = this.initViewport(this.#olMap);
20
- this.#interactionResources.push(
21
- this.#createDragBox(this.#olMap, onExtentSelected, viewPort, this.#interactionResources)
22
- );
23
- this.#interactionResources.push(
24
- this.#createDrag(this.#olMap, viewPort, this.#interactionResources)
25
- );
26
- this.#tooltip = this.#createHelpTooltip(map, tooltipMessage);
27
- this.#tooltipMessage = tooltipMessage;
28
- this.#tooltipDisabledMessage = tooltipDisabledMessage;
29
- this.#tooltipSync = watch(
30
- () => [this.#isActive.value, this.tooltipText],
31
- ([isActive, tooltipText]) => {
32
- const tooltipContent = createElement(SelectionTooltipContent, {
33
- content: tooltipText
34
- });
35
- this.#tooltip.setContent(tooltipContent);
36
- viewPort.classList.toggle(ACTIVE_CLASS, isActive);
37
- viewPort.classList.toggle(INACTIVE_CLASS, !isActive);
38
- },
39
- {
40
- immediate: true
41
- }
42
- );
43
- }
44
- initViewport(olMap) {
45
- const viewPort = olMap.getViewport();
46
- viewPort.classList.add(ACTIVE_CLASS);
47
- viewPort.oncontextmenu = (e) => {
48
- e.preventDefault();
49
- return false;
50
- };
51
- return viewPort;
52
- }
53
- /**
54
- * Method for destroying the controller when it is no longer needed
55
- */
56
- destroy() {
57
- this.#tooltipSync?.destroy();
58
- this.#tooltipSync = void 0;
59
- this.#tooltip.destroy();
60
- this.#interactionResources.forEach((interaction) => {
61
- interaction.destroy();
62
- });
63
- }
64
- /**
65
- * The current tooltip text shown to the user.
66
- */
67
- get tooltipText() {
68
- const isActive = this.#isActive.value;
69
- return isActive ? this.#tooltipMessage : this.#tooltipDisabledMessage;
70
- }
71
- setActive(isActive) {
72
- if (this.#isActive.value === isActive) {
73
- return;
74
- }
75
- if (isActive) {
76
- this.#interactionResources.forEach(
77
- (interaction) => this.#olMap.addInteraction(interaction.interaction)
78
- );
79
- this.#isActive.value = true;
80
- } else {
81
- this.#interactionResources.forEach(
82
- (interaction) => this.#olMap.removeInteraction(interaction.interaction)
83
- );
84
- this.#isActive.value = false;
85
- }
86
- }
87
- /**
88
- * Method to create a simple extent-selection
89
- */
90
- #createDragBox(olMap, onExtentSelected, viewPort, interactionResources) {
91
- const dragBox = new DragBox({
92
- className: "selection-drag-box",
93
- condition: mouseActionButton
94
- });
95
- olMap.addInteraction(dragBox);
96
- dragBox.on("boxend", function() {
97
- onExtentSelected(dragBox.getGeometry());
98
- });
99
- const interactionResource = {
100
- interaction: dragBox,
101
- destroy() {
102
- olMap.removeInteraction(dragBox);
103
- interactionResources.splice(interactionResources.indexOf(this));
104
- dragBox.dispose();
105
- viewPort.classList.remove(ACTIVE_CLASS);
106
- viewPort.classList.remove(INACTIVE_CLASS);
107
- viewPort.oncontextmenu = null;
108
- }
109
- };
110
- return interactionResource;
111
- }
112
- /**
113
- * Method to activate pan with right-mouse-click
114
- */
115
- #createDrag(olMap, viewPort, interactionResources) {
116
- const condition = function(mapBrowserEvent) {
117
- const originalEvent = mapBrowserEvent.originalEvent;
118
- return "button" in originalEvent && originalEvent.button == 2;
119
- };
120
- const drag = new DragPan({
121
- condition
122
- });
123
- olMap.addInteraction(drag);
124
- const interactionResource = {
125
- interaction: drag,
126
- destroy() {
127
- olMap.removeInteraction(drag);
128
- interactionResources.splice(interactionResources.indexOf(this));
129
- drag.dispose();
130
- viewPort.classList.remove(ACTIVE_CLASS);
131
- viewPort.classList.remove(INACTIVE_CLASS);
132
- viewPort.oncontextmenu = null;
133
- }
134
- };
135
- return interactionResource;
136
- }
137
- /**
138
- * Method to generate a tooltip on the mouse cursor
139
- */
140
- #createHelpTooltip(map, message) {
141
- const tooltipContent = createElement(SelectionTooltipContent, {
142
- content: message
143
- });
144
- const tooltipOverlay = map.overlays.add({
145
- content: tooltipContent,
146
- position: "follow-pointer",
147
- offset: [15, 0],
148
- positioning: "center-left",
149
- ariaRole: "tooltip",
150
- className: "selection-tooltip printing-hide"
151
- });
152
- return tooltipOverlay;
153
- }
154
- /**
155
- * Method for testing purposes only
156
- * @returns InteractionResource of class DragBox
157
- */
158
- getDragboxInteraction() {
159
- return this.#interactionResources.find(
160
- (interactionResource) => interactionResource.interaction instanceof DragBox
161
- );
162
- }
163
- /**
164
- * Method for testing purposes only
165
- * @returns InteractionResource of class DragPan
166
- */
167
- getDragPanInteraction() {
168
- return this.#interactionResources.find(
169
- (interactionResource) => interactionResource.interaction instanceof DragPan
170
- );
171
- }
172
- }
173
-
174
- export { DragController };
175
- //# sourceMappingURL=DragController.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"DragController.js","sources":["DragController.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { reactive, watch } from \"@conterra/reactivity-core\";\nimport { Resource } from \"@open-pioneer/core\";\nimport { MapModel, Overlay } from \"@open-pioneer/map\";\nimport { MapBrowserEvent } from \"ol\";\nimport { mouseActionButton } from \"ol/events/condition\";\nimport Geometry from \"ol/geom/Geometry\";\nimport { DragBox, DragPan } from \"ol/interaction\";\nimport PointerInteraction from \"ol/interaction/Pointer\";\nimport OlMap from \"ol/Map\";\nimport { createElement } from \"react\";\nimport { SelectionTooltipContent } from \"./SelectionTooltipContent\";\n\ninterface InteractionResource extends Resource {\n interaction: PointerInteraction;\n}\n\nconst ACTIVE_CLASS = \"selection-active\";\nconst INACTIVE_CLASS = \"selection-inactive\";\n\nexport class DragController {\n #tooltip: Overlay;\n #interactionResources: InteractionResource[] = [];\n #olMap: OlMap;\n #isActive = reactive(true);\n #tooltipMessage: string;\n #tooltipDisabledMessage: string;\n #tooltipSync: Resource | undefined;\n\n constructor(\n map: MapModel,\n tooltipMessage: string,\n tooltipDisabledMessage: string,\n onExtentSelected: (geometry: Geometry) => void\n ) {\n this.#olMap = map.olMap;\n const viewPort = this.initViewport(this.#olMap);\n this.#interactionResources.push(\n this.#createDragBox(this.#olMap, onExtentSelected, viewPort, this.#interactionResources)\n );\n this.#interactionResources.push(\n this.#createDrag(this.#olMap, viewPort, this.#interactionResources)\n );\n\n this.#tooltip = this.#createHelpTooltip(map, tooltipMessage);\n this.#tooltipMessage = tooltipMessage;\n this.#tooltipDisabledMessage = tooltipDisabledMessage;\n\n this.#tooltipSync = watch(\n () => [this.#isActive.value, this.tooltipText],\n ([isActive, tooltipText]) => {\n const tooltipContent = createElement(SelectionTooltipContent, {\n content: tooltipText\n });\n this.#tooltip.setContent(tooltipContent);\n viewPort.classList.toggle(ACTIVE_CLASS, isActive);\n viewPort.classList.toggle(INACTIVE_CLASS, !isActive);\n },\n {\n immediate: true\n }\n );\n }\n\n initViewport(olMap: OlMap) {\n const viewPort = olMap.getViewport();\n viewPort.classList.add(ACTIVE_CLASS);\n\n viewPort.oncontextmenu = (e) => {\n e.preventDefault();\n return false;\n };\n return viewPort;\n }\n\n /**\n * Method for destroying the controller when it is no longer needed\n */\n destroy() {\n this.#tooltipSync?.destroy();\n this.#tooltipSync = undefined;\n\n this.#tooltip.destroy();\n this.#interactionResources.forEach((interaction) => {\n interaction.destroy();\n });\n }\n\n /**\n * The current tooltip text shown to the user.\n */\n get tooltipText(): string {\n const isActive = this.#isActive.value;\n return isActive ? this.#tooltipMessage : this.#tooltipDisabledMessage;\n }\n\n setActive(isActive: boolean) {\n if (this.#isActive.value === isActive) {\n return;\n }\n\n if (isActive) {\n this.#interactionResources.forEach((interaction) =>\n this.#olMap.addInteraction(interaction.interaction)\n );\n this.#isActive.value = true;\n } else {\n this.#interactionResources.forEach((interaction) =>\n this.#olMap.removeInteraction(interaction.interaction)\n );\n this.#isActive.value = false;\n }\n }\n\n /**\n * Method to create a simple extent-selection\n */\n #createDragBox(\n olMap: OlMap,\n onExtentSelected: (geometry: Geometry) => void,\n viewPort: HTMLElement,\n interactionResources: InteractionResource[]\n ): InteractionResource {\n const dragBox = new DragBox({\n className: \"selection-drag-box\",\n condition: mouseActionButton\n });\n\n olMap.addInteraction(dragBox);\n dragBox.on(\"boxend\", function () {\n onExtentSelected(dragBox.getGeometry());\n });\n\n const interactionResource: InteractionResource = {\n interaction: dragBox,\n destroy() {\n olMap.removeInteraction(dragBox);\n interactionResources.splice(interactionResources.indexOf(this));\n dragBox.dispose();\n viewPort.classList.remove(ACTIVE_CLASS);\n viewPort.classList.remove(INACTIVE_CLASS);\n viewPort.oncontextmenu = null;\n }\n };\n return interactionResource;\n }\n\n /**\n * Method to activate pan with right-mouse-click\n */\n #createDrag(\n olMap: OlMap,\n viewPort: HTMLElement,\n interactionResources: InteractionResource[]\n ): InteractionResource {\n const condition = function (mapBrowserEvent: MapBrowserEvent) {\n const originalEvent = mapBrowserEvent.originalEvent;\n return \"button\" in originalEvent && originalEvent.button == 2;\n };\n const drag = new DragPan({\n condition: condition\n });\n\n olMap.addInteraction(drag);\n\n const interactionResource: InteractionResource = {\n interaction: drag,\n destroy() {\n olMap.removeInteraction(drag);\n interactionResources.splice(interactionResources.indexOf(this));\n drag.dispose();\n viewPort.classList.remove(ACTIVE_CLASS);\n viewPort.classList.remove(INACTIVE_CLASS);\n viewPort.oncontextmenu = null;\n }\n };\n\n return interactionResource;\n }\n\n /**\n * Method to generate a tooltip on the mouse cursor\n */\n #createHelpTooltip(map: MapModel, message: string): Overlay {\n const tooltipContent = createElement(SelectionTooltipContent, {\n content: message\n });\n const tooltipOverlay = map.overlays.add({\n content: tooltipContent,\n position: \"follow-pointer\",\n offset: [15, 0],\n positioning: \"center-left\",\n ariaRole: \"tooltip\",\n className: \"selection-tooltip printing-hide\"\n });\n\n return tooltipOverlay;\n }\n\n /**\n * Method for testing purposes only\n * @returns InteractionResource of class DragBox\n */\n getDragboxInteraction() {\n return this.#interactionResources.find(\n (interactionResource) => interactionResource.interaction instanceof DragBox\n );\n }\n\n /**\n * Method for testing purposes only\n * @returns InteractionResource of class DragPan\n */\n getDragPanInteraction() {\n return this.#interactionResources.find(\n (interactionResource) => interactionResource.interaction instanceof DragPan\n );\n }\n}\n"],"names":[],"mappings":"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"}