@open-pioneer/feature-editing 1.5.0-dev.20260911123645 → 1.5.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/README.md +54 -12
  3. package/_virtual/hooks.js.map +1 -1
  4. package/_virtual/source-info.js +7 -1
  5. package/_virtual/source-info.js.map +1 -1
  6. package/api/editor/context.js.map +1 -1
  7. package/api/editor/editor.d.ts +13 -0
  8. package/api/editor/editor.js.map +1 -1
  9. package/api/model/SelectionAvailability.d.ts +39 -0
  10. package/i18n/de.yaml +8 -0
  11. package/i18n/en.yaml +8 -0
  12. package/implementation/FeatureEditor.js +5 -0
  13. package/implementation/FeatureEditor.js.map +1 -1
  14. package/implementation/components/action-selector/ActionSelector.d.ts +12 -5
  15. package/implementation/components/action-selector/ActionSelector.js +27 -2
  16. package/implementation/components/action-selector/ActionSelector.js.map +1 -1
  17. package/implementation/components/action-selector/DrawingControls.js.map +1 -1
  18. package/implementation/components/action-selector/SelectButton.d.ts +6 -4
  19. package/implementation/components/action-selector/SelectButton.js +20 -5
  20. package/implementation/components/action-selector/SelectButton.js.map +1 -1
  21. package/implementation/components/action-selector/TemplateItem.js.map +1 -1
  22. package/implementation/components/action-selector/TemplateSelector.js.map +1 -1
  23. package/implementation/components/controls/ColorPickerControl.js.map +1 -1
  24. package/implementation/components/controls/ComboBoxControl.js.map +1 -1
  25. package/implementation/components/controls/DefaultControl.js.map +1 -1
  26. package/implementation/components/controls/NumberFieldControl.js.map +1 -1
  27. package/implementation/components/controls/RadioGroupControl.js.map +1 -1
  28. package/implementation/components/controls/SelectControl.js.map +1 -1
  29. package/implementation/components/controls/option-hooks.js.map +1 -1
  30. package/implementation/components/property-editor/ButtonRow.js.map +1 -1
  31. package/implementation/components/property-editor/CancelConfirmationDialog.js.map +1 -1
  32. package/implementation/components/property-editor/DeleteConfirmationDialog.js.map +1 -1
  33. package/implementation/components/property-editor/PropertyEditor.js +25 -5
  34. package/implementation/components/property-editor/PropertyEditor.js.map +1 -1
  35. package/implementation/components/property-editor/PropertyField.js.map +1 -1
  36. package/implementation/components/property-editor/PropertyForm.js.map +1 -1
  37. package/implementation/context/PropertyFormContext.d.ts +4 -1
  38. package/implementation/context/PropertyFormContext.js +56 -6
  39. package/implementation/context/PropertyFormContext.js.map +1 -1
  40. package/implementation/context/usePropertyFormContext.js.map +1 -1
  41. package/implementation/editor/editorHooks.d.ts +6 -0
  42. package/implementation/editor/editorHooks.js +34 -5
  43. package/implementation/editor/editorHooks.js.map +1 -1
  44. package/implementation/editor/useEditingCallbacks.js +1 -1
  45. package/implementation/editor/useEditingCallbacks.js.map +1 -1
  46. package/implementation/geometry-editing/controller/DrawingSession.js.map +1 -1
  47. package/implementation/geometry-editing/controller/EditingController.js.map +1 -1
  48. package/implementation/geometry-editing/controller/UndoManager.js.map +1 -1
  49. package/implementation/geometry-editing/controller/geometryState.js.map +1 -1
  50. package/implementation/geometry-editing/interactions/BaseInteraction.js.map +1 -1
  51. package/implementation/geometry-editing/interactions/DoubleClickInteraction.js.map +1 -1
  52. package/implementation/geometry-editing/interactions/DrawingInteraction.js +2 -13
  53. package/implementation/geometry-editing/interactions/DrawingInteraction.js.map +1 -1
  54. package/implementation/geometry-editing/interactions/HighlightingInteraction.js.map +1 -1
  55. package/implementation/geometry-editing/interactions/KeyboardInteraction.js.map +1 -1
  56. package/implementation/geometry-editing/interactions/ModificationInteraction.js +2 -10
  57. package/implementation/geometry-editing/interactions/ModificationInteraction.js.map +1 -1
  58. package/implementation/geometry-editing/interactions/SelectionInteraction.js +6 -13
  59. package/implementation/geometry-editing/interactions/SelectionInteraction.js.map +1 -1
  60. package/implementation/geometry-editing/interactions/SnappingInteraction.js.map +1 -1
  61. package/implementation/geometry-editing/overlays.d.ts +9 -0
  62. package/implementation/geometry-editing/overlays.js +21 -0
  63. package/implementation/geometry-editing/overlays.js.map +1 -0
  64. package/implementation/geometry-editing/useGeometryEditing.js.map +1 -1
  65. package/index.d.ts +1 -0
  66. package/package.json +12 -13
  67. package/implementation/featureEditing.css +0 -15
  68. package/implementation/featureEditing.css.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,10 +1,18 @@
1
1
  # @open-pioneer/feature-editing
2
2
 
3
- ## 1.5.0-dev.20260911123645
3
+ ## 1.5.0
4
4
 
5
5
  ### Minor Changes
6
6
 
7
+ - 40748ad: Disable the feature selection button when there are no visible layers for selection. The enabling / disabling behavior can be customized by using the new `resolveSelectionAvailability` prop.
7
8
  - f790fda: Update to Chakra 3.37.0
9
+ - a9c1213: Rework the tooltips rendered on the map: help tooltips now look like chakra's tooltips, and the measurement tooltips (active and finished measurements) are also derived from chakra's tooltip styles.
10
+
11
+ The built-in styles of these tooltips are no longer shipped as plain css, chakra style props are used instead.
12
+ The existing css classes (`editing-tooltip`, `measurement-tooltip`, `measurement-active-tooltip`, `measurement-finished-tooltip`, `selection-tooltip`) remain on the overlay's element.
13
+
14
+ - a186f5d: Update core packages to 4.8.0.
15
+ - 1d303e3: When editing an existing feature: do not show the "confirm cancel" dialog when the feature wasn't actually modifified.
8
16
 
9
17
  ### Patch Changes
10
18
 
package/README.md CHANGED
@@ -328,18 +328,19 @@ const featureWriter: FeatureWriter = {
328
328
 
329
329
  The `FeatureEditor` component accepts the following props:
330
330
 
331
- | Prop | Type | Required | Description |
332
- | -------------------------------- | ---------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
333
- | `map` | `MapModel` | No | Map model to use (defaults to context map) |
334
- | `templates` | `FeatureTemplate[]` | Yes | Feature templates defining the types of features that can be created or edited |
335
- | `writer` | `FeatureWriter` | Yes | Storage implementation for feature create, update, and delete operations |
336
- | `selectableLayers` | `Layer[]` | No | Layers from which features can be selected. Defaults to layers matching template layer IDs |
337
- | `snappableLayers` | `Layer[]` | No | Layers for snapping during drawing/modification. Defaults to `selectableLayers` |
338
- | `resolveFormTemplate` | `(context) => FormTemplate \| undefined` | No | Custom function to determine which form template to use when editing an existing feature (see below) |
339
- | `showActionBar` | `boolean` | No | Whether to show undo/redo/finish/reset controls during drawing (default: `true`) |
340
- | `successNotifierDisplayDuration` | `number \| false` | No | Duration in ms to display success notifications. By default, never disappears. Use `false` to completely hide the notification. |
341
- | `failureNotifierDisplayDuration` | `number \| false` | No | Duration in ms to display failure notifications. By default, never disappears. Use `false` to completely hide the notification. |
342
- | `onEditingStepChange` | `(newEditingStep) => void` | No | Callback invoked when the editing workflow step changes |
331
+ | Prop | Type | Required | Description |
332
+ | -------------------------------- | ---------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
333
+ | `map` | `MapModel` | No | Map model to use (defaults to context map) |
334
+ | `templates` | `FeatureTemplate[]` | Yes | Feature templates defining the types of features that can be created or edited |
335
+ | `writer` | `FeatureWriter` | Yes | Storage implementation for feature create, update, and delete operations |
336
+ | `selectableLayers` | `Layer[]` | No | Layers from which features can be selected. Defaults to layers matching template layer IDs |
337
+ | `snappableLayers` | `Layer[]` | No | Layers for snapping during drawing/modification. Defaults to `selectableLayers` |
338
+ | `resolveSelectionAvailability` | `(context) => SelectionAvailability` | No | Custom function to enable or disable the selection interaction. By default: disabled when there are no visible selectable layers. |
339
+ | `resolveFormTemplate` | `(context) => FormTemplate \| undefined` | No | Custom function to determine which form template to use when editing an existing feature (see below) |
340
+ | `showActionBar` | `boolean` | No | Whether to show undo/redo/finish/reset controls during drawing (default: `true`) |
341
+ | `successNotifierDisplayDuration` | `number \| false` | No | Duration in ms to display success notifications. By default, never disappears. Use `false` to completely hide the notification. |
342
+ | `failureNotifierDisplayDuration` | `number \| false` | No | Duration in ms to display failure notifications. By default, never disappears. Use `false` to completely hide the notification. |
343
+ | `onEditingStepChange` | `(newEditingStep) => void` | No | Callback invoked when the editing workflow step changes |
343
344
 
344
345
  ### Interaction Options
345
346
 
@@ -355,6 +356,47 @@ The FeatureEditor also accepts OpenLayers interaction options for fine-grained c
355
356
 
356
357
  The drawing options will be merged with those of the selected feature template (if any are given), with the ones of the template taking precedence.
357
358
 
359
+ ### Customizing the Availability of the Selection Interaction
360
+
361
+ Editing an existing feature requires the user to select it on the map first.
362
+ By default, the selection is only available if at least one of the selectable layers is visible; otherwise the selection button is disabled and a hint is shown to the user.
363
+
364
+ Use the `resolveSelectionAvailability` prop to implement a different rule, for example if selection should also require a certain zoom level:
365
+
366
+ ```tsx
367
+ import type {
368
+ SelectionAvailability,
369
+ SelectionAvailabilityContext
370
+ } from "@open-pioneer/feature-editing";
371
+
372
+ // Use a function that does not change its value on every render.
373
+ const customResolveSelectionAvailability = useCallback(
374
+ ({ mapModel, layers }: SelectionAvailabilityContext): SelectionAvailability => {
375
+ if (!layers.some((layer) => layer.visible)) {
376
+ return { status: "unavailable", reason: "Please activate an editable layer." };
377
+ }
378
+ if ((mapModel.zoomLevel ?? 0) < 12) {
379
+ return { status: "unavailable", reason: "Please zoom in to select a feature." };
380
+ }
381
+ return { status: "available" };
382
+ },
383
+ []
384
+ );
385
+
386
+ <FeatureEditor
387
+ templates={templates}
388
+ writer={featureWriter}
389
+ resolveSelectionAvailability={customResolveSelectionAvailability}
390
+ />;
391
+ ```
392
+
393
+ The `context` contains the `mapModel` and the `layers` that would be used for the selection interaction (i.e. `selectableLayers`, or the layers derived from the templates).
394
+ The function is evaluated reactively: if it accesses reactive values (such as `layer.visible` or the map's zoom level),
395
+ the availability is recomputed automatically when those values change.
396
+ When `status` is `"unavailable"`, the optional `reason` is presented to the user.
397
+
398
+ Use `useCallback` or any alternative measure to ensure that the function does not change too frequently.
399
+
358
400
  ### Resolving Form Templates
359
401
 
360
402
  When the user selects an existing feature on the map, the FeatureEditor needs to determine which form template to use.
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
1
+ {"version":3,"file":"hooks.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
@@ -1,4 +1,10 @@
1
+ const sourceId$3 = "@open-pioneer/feature-editing/implementation/editor/editorHooks";
2
+
3
+ const sourceId$2 = "@open-pioneer/feature-editing/implementation/components/property-editor/PropertyEditor";
4
+
5
+ const sourceId$1 = "@open-pioneer/feature-editing/implementation/context/PropertyFormContext";
6
+
1
7
  const sourceId = "@open-pioneer/feature-editing/implementation/editor/useEditingCallbacks";
2
8
 
3
- export { sourceId };
9
+ export { sourceId$3 as sourceId, sourceId$1, sourceId$2, sourceId as sourceId$3 };
4
10
  //# sourceMappingURL=source-info.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"source-info.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
1
+ {"version":3,"file":"source-info.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sources":["context.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ReactiveMap } from \"@conterra/reactivity-core\";\nimport type { Layer } from \"@open-pioneer/map\";\n// oxlint-disable-next-line @typescript-eslint/no-unused-vars\nimport type { useReactiveSnapshot } from \"@open-pioneer/reactivity\";\nimport type { Feature } from \"ol\";\nimport { useCustomFormContext as useCustomFormContextImpl } from \"../../implementation/context/usePropertyFormContext\";\n// oxlint-disable-next-line @typescript-eslint/no-unused-vars\nimport type { CreationStep, ModificationStep, UpdateStep } from \"../model/EditingStep\";\n// oxlint-disable-next-line @typescript-eslint/no-unused-vars\nimport type { DynamicFormTemplate, FeatureTemplate } from \"../model/FeatureTemplate\";\n\n/**\n * React hook for accessing the property form context within a custom form.\n *\n * Provides access to the {@link CustomFormContext} instance, which contains the current feature\n * being edited and methods to read and update its properties. This hook must be called from within\n * a component that is rendered inside a custom property form (see {@link DynamicFormTemplate}).\n *\n * The context also allows controlling the form's validity by setting the `isValid` property,\n * which determines whether the save button is enabled.\n *\n * @returns The current {@link CustomFormContext} instance.\n * @throws Error if called outside of a custom property form context (e.g., when no feature is being\n * edited).\n *\n * @example\n * ```tsx\n * import { useReactiveSnapshot, DISPATCH_SYNC } from \"@open-pioneer/reactivity\";\n *\n * function CustomForm() {\n * const context = useCustomFormContext();\n * const name = useReactiveSnapshot(\n * () => (context.properties.get(\"name\") as string) ?? \"\",\n * [context],\n * DISPATCH_SYNC\n * );\n *\n * useEffect(() => {\n * context.isValid = name.length >= 1;\n * }, [context, name]);\n *\n * return <Input value={name} onChange={(e) => context.properties.set(\"name\", e.target.value)} />;\n * }\n * ```\n *\n * @group Editor\n */\nexport const useCustomFormContext: () => CustomFormContext = useCustomFormContextImpl;\n\n/**\n * Context object providing access to feature properties and editing state during form editing.\n *\n * The context manages the state of a feature being edited, including its properties, validation\n * status, and associated metadata. It provides reactive property management through a\n * {@link ReactiveMap}, allowing components to automatically re-render when properties change.\n *\n * Access this context in custom forms using the {@link useCustomFormContext} hook.\n *\n * @group Editor\n */\nexport interface CustomFormContext {\n /**\n * The OpenLayers feature being edited.\n *\n * Provides direct access to the feature object, which includes its geometry and properties.\n */\n readonly feature: Feature;\n\n /**\n * Reactive map of feature properties.\n *\n * Provides reactive access to all feature properties (excluding geometry). Use `get()` to read\n * property values and `set()` to update them. Components can use {@link useReactiveSnapshot}\n * to automatically re-render when values change.\n *\n * @example\n * ```ts\n * const name = context.properties.get(\"name\");\n * context.properties.set(\"name\", \"New Name\");\n * ```\n */\n readonly properties: ReactiveMap<string, unknown>;\n\n /**\n * Returns all feature properties as a plain JavaScript object.\n *\n * Converts the reactive properties map to a standard object with string keys and unknown\n * values. Useful when you need to pass properties to functions that expect plain objects.\n *\n * Note that a new object is created on every change.\n * Prefer to use fine grained accesses to {@link properties} if performance matters.\n *\n * @returns A plain object containing all feature properties (excluding geometry).\n */\n readonly getPropertiesAsObject: () => Record<string, unknown>;\n\n /**\n * The current editing step containing the feature and associated metadata.\n *\n * Provides access to the complete editing step, which can be either a {@link CreationStep}\n * or {@link UpdateStep}.\n */\n readonly editingStep: ModificationStep;\n\n /**\n * The current editing mode.\n *\n * Returns `\"create\"` when creating a new feature, or `\"update\"` when editing an existing\n * feature. Use this to conditionally render different UI or apply different logic based on the\n * editing mode.\n */\n readonly mode: Mode;\n\n /**\n * The feature template used to create the feature, if in creation mode.\n *\n * Returns the {@link FeatureTemplate} when creating a new feature (`mode === \"create\"`), or\n * `undefined` when editing an existing feature (`mode === \"update\"`).\n */\n readonly template: FeatureTemplate | undefined;\n\n /**\n * The layer containing the feature being edited, if in update mode.\n *\n * Returns the {@link Layer} when editing an existing feature (`mode === \"update\"`), or\n * `undefined` when creating a new feature (`mode === \"create\"`) or if the layer is not\n * available.\n */\n readonly layer: Layer | undefined;\n\n /**\n * Whether the form is currently valid.\n *\n * Controls the enabled state of the save button in the property editor. Set this to `true`\n * when the form passes validation, or `false` when there are validation errors. For\n * declarative forms, this is managed automatically based on field validation rules.\n */\n isValid: boolean;\n}\n\n/**\n * The editing mode for a feature.\n *\n * - `\"create\"`: Creating a new feature\n * - `\"update\"`: Editing an existing feature\n *\n * @group Editor\n */\nexport type Mode = \"create\" | \"update\";\n"],"names":["useCustomFormContextImpl"],"mappings":";;AAkDO,MAAM,oBAAA,GAAgDA;;;;"}
1
+ {"version":3,"file":"context.js","sources":["context.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ReactiveMap } from \"@conterra/reactivity-core\";\nimport type { Layer } from \"@open-pioneer/map\";\n// oxlint-disable-next-line @typescript-eslint/no-unused-vars\nimport type { useReactiveSnapshot } from \"@open-pioneer/reactivity\";\nimport type { Feature } from \"ol\";\nimport { useCustomFormContext as useCustomFormContextImpl } from \"../../implementation/context/usePropertyFormContext\";\n// oxlint-disable-next-line @typescript-eslint/no-unused-vars\nimport type { CreationStep, ModificationStep, UpdateStep } from \"../model/EditingStep\";\n// oxlint-disable-next-line @typescript-eslint/no-unused-vars\nimport type { DynamicFormTemplate, FeatureTemplate } from \"../model/FeatureTemplate\";\n\n/**\n * React hook for accessing the property form context within a custom form.\n *\n * Provides access to the {@link CustomFormContext} instance, which contains the current feature\n * being edited and methods to read and update its properties. This hook must be called from within\n * a component that is rendered inside a custom property form (see {@link DynamicFormTemplate}).\n *\n * The context also allows controlling the form's validity by setting the `isValid` property,\n * which determines whether the save button is enabled.\n *\n * @returns The current {@link CustomFormContext} instance.\n * @throws Error if called outside of a custom property form context (e.g., when no feature is being\n * edited).\n *\n * @example\n * ```tsx\n * import { useReactiveSnapshot, DISPATCH_SYNC } from \"@open-pioneer/reactivity\";\n *\n * function CustomForm() {\n * const context = useCustomFormContext();\n * const name = useReactiveSnapshot(\n * () => (context.properties.get(\"name\") as string) ?? \"\",\n * [context],\n * DISPATCH_SYNC\n * );\n *\n * useEffect(() => {\n * context.isValid = name.length >= 1;\n * }, [context, name]);\n *\n * return <Input value={name} onChange={(e) => context.properties.set(\"name\", e.target.value)} />;\n * }\n * ```\n *\n * @group Editor\n */\nexport const useCustomFormContext: () => CustomFormContext = useCustomFormContextImpl;\n\n/**\n * Context object providing access to feature properties and editing state during form editing.\n *\n * The context manages the state of a feature being edited, including its properties, validation\n * status, and associated metadata. It provides reactive property management through a\n * {@link ReactiveMap}, allowing components to automatically re-render when properties change.\n *\n * Access this context in custom forms using the {@link useCustomFormContext} hook.\n *\n * @group Editor\n */\nexport interface CustomFormContext {\n /**\n * The OpenLayers feature being edited.\n *\n * Provides direct access to the feature object, which includes its geometry and properties.\n */\n readonly feature: Feature;\n\n /**\n * Reactive map of feature properties.\n *\n * Provides reactive access to all feature properties (excluding geometry). Use `get()` to read\n * property values and `set()` to update them. Components can use {@link useReactiveSnapshot}\n * to automatically re-render when values change.\n *\n * @example\n * ```ts\n * const name = context.properties.get(\"name\");\n * context.properties.set(\"name\", \"New Name\");\n * ```\n */\n readonly properties: ReactiveMap<string, unknown>;\n\n /**\n * Returns all feature properties as a plain JavaScript object.\n *\n * Converts the reactive properties map to a standard object with string keys and unknown\n * values. Useful when you need to pass properties to functions that expect plain objects.\n *\n * Note that a new object is created on every change.\n * Prefer to use fine grained accesses to {@link properties} if performance matters.\n *\n * @returns A plain object containing all feature properties (excluding geometry).\n */\n readonly getPropertiesAsObject: () => Record<string, unknown>;\n\n /**\n * The current editing step containing the feature and associated metadata.\n *\n * Provides access to the complete editing step, which can be either a {@link CreationStep}\n * or {@link UpdateStep}.\n */\n readonly editingStep: ModificationStep;\n\n /**\n * The current editing mode.\n *\n * Returns `\"create\"` when creating a new feature, or `\"update\"` when editing an existing\n * feature. Use this to conditionally render different UI or apply different logic based on the\n * editing mode.\n */\n readonly mode: Mode;\n\n /**\n * The feature template used to create the feature, if in creation mode.\n *\n * Returns the {@link FeatureTemplate} when creating a new feature (`mode === \"create\"`), or\n * `undefined` when editing an existing feature (`mode === \"update\"`).\n */\n readonly template: FeatureTemplate | undefined;\n\n /**\n * The layer containing the feature being edited, if in update mode.\n *\n * Returns the {@link Layer} when editing an existing feature (`mode === \"update\"`), or\n * `undefined` when creating a new feature (`mode === \"create\"`) or if the layer is not\n * available.\n */\n readonly layer: Layer | undefined;\n\n /**\n * Whether the form is currently valid.\n *\n * Controls the enabled state of the save button in the property editor. Set this to `true`\n * when the form passes validation, or `false` when there are validation errors. For\n * declarative forms, this is managed automatically based on field validation rules.\n */\n isValid: boolean;\n}\n\n/**\n * The editing mode for a feature.\n *\n * - `\"create\"`: Creating a new feature\n * - `\"update\"`: Editing an existing feature\n *\n * @group Editor\n */\nexport type Mode = \"create\" | \"update\";\n"],"names":["useCustomFormContextImpl"],"mappings":";;AAkDO,MAAM,oBAAA,GAAgDA;;"}
@@ -6,6 +6,7 @@ import type { EditingStep } from "../model/EditingStep";
6
6
  import type { FeatureTemplate, FormTemplate } from "../model/FeatureTemplate";
7
7
  import type { FeatureWriter } from "../model/FeatureWriter";
8
8
  import type { InteractionOptions } from "../model/InteractionOptions";
9
+ import { SelectionAvailability, SelectionAvailabilityContext } from "../model/SelectionAvailability";
9
10
  /**
10
11
  * React component that provides a complete editing interface for creating and modifying map
11
12
  * features.
@@ -79,6 +80,18 @@ export interface FeatureEditorProps extends CommonComponentProps, MapModelProps,
79
80
  * @default selectableLayers
80
81
  */
81
82
  readonly snappableLayers?: Layer[];
83
+ /**
84
+ * A _reactive_ function that returns the selection interaction's availability state.
85
+ *
86
+ * The selection interaction is used by the editor to pick a feature from the map for editing.
87
+ *
88
+ * This function **SHOULD** be rather stable: you should use `useCallback` or equivalent mechanisms to avoid
89
+ * passing in a new function every time.
90
+ *
91
+ * Default: the selection interaction is available if there are any editable layers (as configured by {@link selectableLayers} or its default value)
92
+ * that are _visible_.
93
+ */
94
+ readonly resolveSelectionAvailability?: (context: SelectionAvailabilityContext) => SelectionAvailability;
82
95
  /**
83
96
  * Whether to show the action bar with undo/redo/finish/reset controls during drawing.
84
97
  *
@@ -1 +1 @@
1
- {"version":3,"file":"editor.js","sources":["editor.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Layer, MapModelProps } from \"@open-pioneer/map\";\nimport { CommonComponentProps } from \"@open-pioneer/react-utils\";\nimport Feature from \"ol/Feature\";\nimport { FC } from \"react\";\nimport { FeatureEditor as FeatureEditorImpl } from \"../../implementation/FeatureEditor\";\nimport type { EditingStep } from \"../model/EditingStep\";\nimport type { FeatureTemplate, FormTemplate } from \"../model/FeatureTemplate\";\nimport type { FeatureWriter } from \"../model/FeatureWriter\";\nimport type { InteractionOptions } from \"../model/InteractionOptions\";\n\n/**\n * React component that provides a complete editing interface for creating and modifying map\n * features.\n *\n * The Editor component manages the entire editing workflow, including feature creation, selection,\n * modification, and deletion. It renders different UI states based on the current editing step:\n * - An action selector for choosing feature templates and initiating drawing, or selecting\n * existing features.\n * - A property editor for modifying feature attributes and geometry.\n *\n * @example\n * ```tsx\n * <Editor\n * map={mapModel}\n * templates={featureTemplates}\n * storage={{\n * addFeature: async (feature) => { ... },\n * updateFeature: async (feature) => { ... },\n * deleteFeature: async (feature) => { ... }\n * }}\n * selectableLayers={[myLayer]}\n * />\n * ```\n *\n * @group Editor\n * @expandType EditorProps\n */\nexport const FeatureEditor: FC<FeatureEditorProps> = FeatureEditorImpl;\n\n/**\n * Props for the {@link FeatureEditor} component.\n *\n * @group Editor\n */\nexport interface FeatureEditorProps\n extends CommonComponentProps, MapModelProps, InteractionOptions {\n /**\n * Feature templates defining the types of features that can be created or edited.\n *\n * Each template specifies the geometry type, drawing options, and form configuration\n * for a specific feature type.\n */\n readonly templates: FeatureTemplate[];\n\n /**\n * Responsible for persisting changes to your data source.\n */\n readonly writer: FeatureWriter;\n\n /**\n * Optional function to provide custom form templates for features.\n *\n * When specified, this function is called to determine which form template to use\n * when editing an existing feature. If not provided, the first feature template\n * matching the feature's layer ID will be used.\n *\n * @returns The form template to use for editing the feature's properties, or `undefined` if no\n * template is available.\n */\n readonly resolveFormTemplate?: (context: FormTemplateContext) => FormTemplate | undefined;\n\n /**\n * Layers from which features can be selected for editing.\n *\n * When not specified, the layer IDs from the feature templates are used to determine\n * which layers are selectable. Only specify layers that contain features you want to\n * allow users to edit.\n */\n readonly selectableLayers?: Layer[];\n\n /**\n * Layers to which drawn geometries will snap.\n *\n * Enables snapping to features in these layers during drawing and modification operations.\n * Defaults to {@link selectableLayers} if not specified.\n *\n * @default selectableLayers\n */\n readonly snappableLayers?: Layer[];\n\n /**\n * Whether to show the action bar with undo/redo/finish/reset controls during drawing.\n *\n * When `false`, the action bar is hidden. Only applies to templates with geometry types\n * that support these actions (e.g., `Polygon`, `LineString`).\n *\n * @default true\n */\n readonly showActionBar?: boolean;\n\n /**\n * Duration in milliseconds to display success notifications.\n *\n * Controls how long success messages (e.g., \"Feature saved\") are shown to the user.\n * By default, notifiers will never disappear.\n *\n * Use `false` to hide the notification altogether.\n */\n readonly successNotifierDisplayDuration?: number | false;\n\n /**\n * Duration in milliseconds to display failure notifications.\n *\n * Controls how long error messages are shown to the user.\n * By default, notifiers will never disappear.\n *\n * Use `false` to hide the notification altogether.\n */\n readonly failureNotifierDisplayDuration?: number | false;\n\n /**\n * Optional callback invoked when the editing step changes.\n *\n * Called whenever the editing workflow transitions between steps (e.g., from drawing to\n * attribute editing, or from attribute editing back to the initial state).\n *\n * Use this to track or respond to changes in the editing workflow state.\n */\n readonly onEditingStepChange?: (newEditingStep: EditingStep) => void;\n}\n\n/** @group Editor */\nexport interface FormTemplateContext {\n /** The OpenLayers feature being edited. */\n feature: Feature;\n\n /** The layer containing the feature, or `undefined` if not available. */\n layer: Layer | undefined;\n}\n"],"names":["FeatureEditorImpl"],"mappings":";;AAwCO,MAAM,aAAA,GAAwCA;;;;"}
1
+ {"version":3,"file":"editor.js","sources":["editor.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { Layer, MapModelProps } from \"@open-pioneer/map\";\nimport { CommonComponentProps } from \"@open-pioneer/react-utils\";\nimport Feature from \"ol/Feature\";\nimport { FC } from \"react\";\nimport { FeatureEditor as FeatureEditorImpl } from \"../../implementation/FeatureEditor\";\nimport type { EditingStep } from \"../model/EditingStep\";\nimport type { FeatureTemplate, FormTemplate } from \"../model/FeatureTemplate\";\nimport type { FeatureWriter } from \"../model/FeatureWriter\";\nimport type { InteractionOptions } from \"../model/InteractionOptions\";\nimport {\n SelectionAvailability,\n SelectionAvailabilityContext\n} from \"../model/SelectionAvailability\";\n\n/**\n * React component that provides a complete editing interface for creating and modifying map\n * features.\n *\n * The Editor component manages the entire editing workflow, including feature creation, selection,\n * modification, and deletion. It renders different UI states based on the current editing step:\n * - An action selector for choosing feature templates and initiating drawing, or selecting\n * existing features.\n * - A property editor for modifying feature attributes and geometry.\n *\n * @example\n * ```tsx\n * <Editor\n * map={mapModel}\n * templates={featureTemplates}\n * storage={{\n * addFeature: async (feature) => { ... },\n * updateFeature: async (feature) => { ... },\n * deleteFeature: async (feature) => { ... }\n * }}\n * selectableLayers={[myLayer]}\n * />\n * ```\n *\n * @group Editor\n * @expandType EditorProps\n */\nexport const FeatureEditor: FC<FeatureEditorProps> = FeatureEditorImpl;\n\n/**\n * Props for the {@link FeatureEditor} component.\n *\n * @group Editor\n */\nexport interface FeatureEditorProps\n extends CommonComponentProps, MapModelProps, InteractionOptions {\n /**\n * Feature templates defining the types of features that can be created or edited.\n *\n * Each template specifies the geometry type, drawing options, and form configuration\n * for a specific feature type.\n */\n readonly templates: FeatureTemplate[];\n\n /**\n * Responsible for persisting changes to your data source.\n */\n readonly writer: FeatureWriter;\n\n /**\n * Optional function to provide custom form templates for features.\n *\n * When specified, this function is called to determine which form template to use\n * when editing an existing feature. If not provided, the first feature template\n * matching the feature's layer ID will be used.\n *\n * @returns The form template to use for editing the feature's properties, or `undefined` if no\n * template is available.\n */\n readonly resolveFormTemplate?: (context: FormTemplateContext) => FormTemplate | undefined;\n\n /**\n * Layers from which features can be selected for editing.\n *\n * When not specified, the layer IDs from the feature templates are used to determine\n * which layers are selectable. Only specify layers that contain features you want to\n * allow users to edit.\n */\n readonly selectableLayers?: Layer[];\n\n /**\n * Layers to which drawn geometries will snap.\n *\n * Enables snapping to features in these layers during drawing and modification operations.\n * Defaults to {@link selectableLayers} if not specified.\n *\n * @default selectableLayers\n */\n readonly snappableLayers?: Layer[];\n\n /**\n * A _reactive_ function that returns the selection interaction's availability state.\n *\n * The selection interaction is used by the editor to pick a feature from the map for editing.\n *\n * This function **SHOULD** be rather stable: you should use `useCallback` or equivalent mechanisms to avoid\n * passing in a new function every time.\n *\n * Default: the selection interaction is available if there are any editable layers (as configured by {@link selectableLayers} or its default value)\n * that are _visible_.\n */\n readonly resolveSelectionAvailability?: (\n context: SelectionAvailabilityContext\n ) => SelectionAvailability;\n\n /**\n * Whether to show the action bar with undo/redo/finish/reset controls during drawing.\n *\n * When `false`, the action bar is hidden. Only applies to templates with geometry types\n * that support these actions (e.g., `Polygon`, `LineString`).\n *\n * @default true\n */\n readonly showActionBar?: boolean;\n\n /**\n * Duration in milliseconds to display success notifications.\n *\n * Controls how long success messages (e.g., \"Feature saved\") are shown to the user.\n * By default, notifiers will never disappear.\n *\n * Use `false` to hide the notification altogether.\n */\n readonly successNotifierDisplayDuration?: number | false;\n\n /**\n * Duration in milliseconds to display failure notifications.\n *\n * Controls how long error messages are shown to the user.\n * By default, notifiers will never disappear.\n *\n * Use `false` to hide the notification altogether.\n */\n readonly failureNotifierDisplayDuration?: number | false;\n\n /**\n * Optional callback invoked when the editing step changes.\n *\n * Called whenever the editing workflow transitions between steps (e.g., from drawing to\n * attribute editing, or from attribute editing back to the initial state).\n *\n * Use this to track or respond to changes in the editing workflow state.\n */\n readonly onEditingStepChange?: (newEditingStep: EditingStep) => void;\n}\n\n/** @group Editor */\nexport interface FormTemplateContext {\n /** The OpenLayers feature being edited. */\n feature: Feature;\n\n /** The layer containing the feature, or `undefined` if not available. */\n layer: Layer | undefined;\n}\n"],"names":["FeatureEditorImpl"],"mappings":";;AA4CO,MAAM,aAAA,GAAwCA;;"}
@@ -0,0 +1,39 @@
1
+ import { Layer, MapModel } from "@open-pioneer/map";
2
+ /**
3
+ * The context made available to implementations of the {@link FeatureEditorProps.resolveSelectionAvailability | resolveSelectionAvailability} property.
4
+ *
5
+ * @group Model
6
+ */
7
+ export interface SelectionAvailabilityContext {
8
+ /** The map used by the feature editor. */
9
+ mapModel: MapModel;
10
+ /** The layers the feature editor would use for the selection interaction. */
11
+ layers: Layer[];
12
+ }
13
+ /**
14
+ * The availability status for the selection button (available / unavailable).
15
+ *
16
+ * @group Model
17
+ */
18
+ export type SelectionAvailability = SelectionAvailable | SelectionUnavailable;
19
+ /**
20
+ * Selection is available.
21
+ *
22
+ * @group Model
23
+ */
24
+ export interface SelectionAvailable {
25
+ status: "available";
26
+ }
27
+ /**
28
+ * Selection is unavailable.
29
+ *
30
+ * @group Model
31
+ */
32
+ export interface SelectionUnavailable {
33
+ status: "unavailable";
34
+ /**
35
+ * The reason for being unavailable.
36
+ * This message will be presented to the user.
37
+ */
38
+ reason?: string;
39
+ }
package/i18n/de.yaml CHANGED
@@ -8,6 +8,11 @@ messages:
8
8
  resetButtonTooltip: Zeichnung zurücksetzen
9
9
  undoButtonTooltip: Rückgängig machen
10
10
  redoButtonTooltip: Wiederholen
11
+
12
+ selection:
13
+ defaultNotAvailableMessage: "Es können keine Geoobjekte ausgewählt werden."
14
+ noVisibleLayers: "In der Karte sind derzeit keine editierbaren Layer sichtbar."
15
+
11
16
  propertyEditor:
12
17
  defaultEditHeading: Geoobjekt bearbeiten
13
18
  defaultCreateHeading: Geoobjekt erstellen
@@ -26,11 +31,13 @@ messages:
26
31
  message: Sind Sie sicher, dass Sie dieses Geoobjekt löschen möchten?
27
32
  deleteButtonTitle: Geoobjekt löschen
28
33
  cancelButtonTitle: Abbrechen
34
+
29
35
  cancelConfirmationDialog:
30
36
  title: Änderungen verwerfen
31
37
  message: Sind Sie sicher, dass Sie Ihre Änderungen verwerfen möchten?
32
38
  confirmCancelButtonTitle: Änderungen verwerfen
33
39
  abortCancelButtonTitle: Weiter bearbeiten
40
+
34
41
  notifier:
35
42
  creationSuccess: Geoobjekt erstellt
36
43
  creationFailure: Das Geoobjekt konnte nicht erstellt werden.
@@ -38,6 +45,7 @@ messages:
38
45
  updateFailure: Das Geoobjekt konnte nicht aktualisiert werden.
39
46
  deletionSuccess: Geoobjekt gelöscht
40
47
  deletionFailure: Das Geoobjekt konnte nicht gelöscht werden.
48
+
41
49
  tooltips:
42
50
  drawingMessagePoint: Klicken Sie in die Karte, um den Punkt zu zeichnen.
43
51
  drawingMessageLineString: Klicken, um mit dem Zeichnen zu beginnen. <br></br> Doppelklicken, um die Zeichnung zu beenden.
package/i18n/en.yaml CHANGED
@@ -8,6 +8,11 @@ messages:
8
8
  resetButtonTooltip: Reset drawing
9
9
  undoButtonTooltip: Undo
10
10
  redoButtonTooltip: Redo
11
+
12
+ selection:
13
+ defaultNotAvailableMessage: "No features can be selected."
14
+ noVisibleLayers: "No editable layers are visible in the map."
15
+
11
16
  propertyEditor:
12
17
  defaultEditHeading: Edit Feature
13
18
  defaultCreateHeading: Create Feature
@@ -26,11 +31,13 @@ messages:
26
31
  message: Are you sure you want to delete this feature?
27
32
  deleteButtonTitle: Delete feature
28
33
  cancelButtonTitle: Cancel
34
+
29
35
  cancelConfirmationDialog:
30
36
  title: Discard changes
31
37
  message: Are you sure you want to discard your changes?
32
38
  confirmCancelButtonTitle: Discard changes
33
39
  abortCancelButtonTitle: Continue editing
40
+
34
41
  notifier:
35
42
  creationSuccess: Feature created
36
43
  creationFailure: The feature failed to be created.
@@ -38,6 +45,7 @@ messages:
38
45
  updateFailure: The feature failed to be updated.
39
46
  deletionSuccess: Feature deleted
40
47
  deletionFailure: The feature failed to be deleted.
48
+
41
49
  tooltips:
42
50
  drawingMessagePoint: Click on the map to draw the point.
43
51
  drawingMessageLineString: Click to start drawing. Double-click to finish drawing.
@@ -18,6 +18,7 @@ function FeatureEditor(props) {
18
18
  resolveFormTemplate,
19
19
  selectableLayers,
20
20
  snappableLayers = selectableLayers,
21
+ resolveSelectionAvailability,
21
22
  showActionBar = true,
22
23
  successNotifierDisplayDuration,
23
24
  failureNotifierDisplayDuration,
@@ -78,7 +79,11 @@ function FeatureEditor(props) {
78
79
  content = /* @__PURE__ */ jsx(
79
80
  ActionSelector,
80
81
  {
82
+ editingStep,
83
+ mapModel,
81
84
  templates,
85
+ selectableLayers,
86
+ resolveSelectionAvailability,
82
87
  showActionBar,
83
88
  onActionChange,
84
89
  drawingState
@@ -1 +1 @@
1
- {"version":3,"file":"FeatureEditor.js","sources":["FeatureEditor.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Box } from \"@chakra-ui/react\";\nimport { useMapModelValue } from \"@open-pioneer/map\";\nimport { useCommonComponentProps } from \"@open-pioneer/react-utils\";\nimport type { Type as GeometryType } from \"ol/geom/Geometry\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { type ReactElement, type ReactNode, useMemo } from \"react\";\nimport type { FeatureEditorProps } from \"../api/editor/editor\";\nimport { ActionSelector } from \"./components/action-selector/ActionSelector\";\nimport { PropertyEditor } from \"./components/property-editor/PropertyEditor\";\nimport { useEditingStep, useOnActionChange, useSnappingSources } from \"./editor/editorHooks\";\nimport { useEditingCallbacks } from \"./editor/useEditingCallbacks\";\nimport { TooltipMessages } from \"./geometry-editing/controller/EditingController\";\nimport { useGeometryEditing } from \"./geometry-editing/useGeometryEditing\";\n\nexport function FeatureEditor(props: FeatureEditorProps): ReactElement {\n const {\n map,\n templates,\n writer,\n resolveFormTemplate,\n selectableLayers,\n snappableLayers = selectableLayers,\n showActionBar = true,\n successNotifierDisplayDuration,\n failureNotifierDisplayDuration,\n onEditingStepChange,\n ...interactionOptions\n } = props;\n const { containerProps } = useCommonComponentProps(\"editor\", props);\n const mapModel = useMapModelValue(props);\n const intl = useIntl();\n\n const [editingStep, setEditingStep] = useEditingStep(onEditingStepChange);\n const snappingSources = useSnappingSources(mapModel, snappableLayers, templates);\n const onActionChange = useOnActionChange(mapModel, selectableLayers, templates, setEditingStep);\n\n const tooltipMessages = useMemo((): TooltipMessages => {\n return {\n getDrawingMessages() {\n return new Map<GeometryType, ReactNode>([\n [\"Point\", intl.formatRichMessage({ id: \"tooltips.drawingMessagePoint\" })],\n [\n \"LineString\",\n intl.formatRichMessage({ id: \"tooltips.drawingMessageLineString\" })\n ],\n [\"Polygon\", intl.formatRichMessage({ id: \"tooltips.drawingMessagePolygon\" })],\n [\"Circle\", intl.formatRichMessage({ id: \"tooltips.drawingMessageCircle\" })]\n ]);\n },\n getSelectionMessage() {\n return intl.formatRichMessage({ id: \"tooltips.selectionMessage\" });\n },\n getModificationMessages() {\n return new Map<string, ReactNode>([\n [\"Point\", intl.formatRichMessage({ id: \"tooltips.modificationMessagePoint\" })],\n [\"default\", intl.formatRichMessage({ id: \"tooltips.modificationMessage\" })]\n ]);\n }\n };\n }, [intl]);\n\n const drawingState = useGeometryEditing({\n map,\n editingStep,\n setEditingStep,\n snappingSources,\n tooltipMessages,\n ...interactionOptions\n });\n\n const editingCallbacks = useEditingCallbacks(\n mapModel,\n editingStep,\n writer,\n setEditingStep,\n successNotifierDisplayDuration,\n failureNotifierDisplayDuration\n );\n\n let content: ReactNode;\n switch (editingStep.id) {\n case \"initial\":\n case \"drawing\":\n case \"selection\":\n content = (\n <ActionSelector\n templates={templates}\n showActionBar={showActionBar}\n onActionChange={onActionChange}\n drawingState={drawingState}\n />\n );\n break;\n case \"creation\":\n case \"update\":\n content = (\n <PropertyEditor\n editingStep={editingStep}\n callbacks={editingCallbacks}\n templates={templates}\n resolveFormTemplate={resolveFormTemplate}\n />\n );\n break;\n }\n\n return (\n <Box {...containerProps} h=\"full\">\n {content}\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FeatureEditor.js","sources":["FeatureEditor.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Box } from \"@chakra-ui/react\";\nimport { useMapModelValue } from \"@open-pioneer/map\";\nimport { useCommonComponentProps } from \"@open-pioneer/react-utils\";\nimport type { Type as GeometryType } from \"ol/geom/Geometry\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { type ReactElement, type ReactNode, useMemo } from \"react\";\nimport type { FeatureEditorProps } from \"../api/editor/editor\";\nimport { ActionSelector } from \"./components/action-selector/ActionSelector\";\nimport { PropertyEditor } from \"./components/property-editor/PropertyEditor\";\nimport { useEditingStep, useOnActionChange, useSnappingSources } from \"./editor/editorHooks\";\nimport { useEditingCallbacks } from \"./editor/useEditingCallbacks\";\nimport { TooltipMessages } from \"./geometry-editing/controller/EditingController\";\nimport { useGeometryEditing } from \"./geometry-editing/useGeometryEditing\";\n\nexport function FeatureEditor(props: FeatureEditorProps): ReactElement {\n const {\n map,\n templates,\n writer,\n resolveFormTemplate,\n selectableLayers,\n snappableLayers = selectableLayers,\n resolveSelectionAvailability,\n showActionBar = true,\n successNotifierDisplayDuration,\n failureNotifierDisplayDuration,\n onEditingStepChange,\n ...interactionOptions\n } = props;\n const { containerProps } = useCommonComponentProps(\"editor\", props);\n const mapModel = useMapModelValue(props);\n const intl = useIntl();\n\n const [editingStep, setEditingStep] = useEditingStep(onEditingStepChange);\n const snappingSources = useSnappingSources(mapModel, snappableLayers, templates);\n const onActionChange = useOnActionChange(mapModel, selectableLayers, templates, setEditingStep);\n\n const tooltipMessages = useMemo((): TooltipMessages => {\n return {\n getDrawingMessages() {\n return new Map<GeometryType, ReactNode>([\n [\"Point\", intl.formatRichMessage({ id: \"tooltips.drawingMessagePoint\" })],\n [\n \"LineString\",\n intl.formatRichMessage({ id: \"tooltips.drawingMessageLineString\" })\n ],\n [\"Polygon\", intl.formatRichMessage({ id: \"tooltips.drawingMessagePolygon\" })],\n [\"Circle\", intl.formatRichMessage({ id: \"tooltips.drawingMessageCircle\" })]\n ]);\n },\n getSelectionMessage() {\n return intl.formatRichMessage({ id: \"tooltips.selectionMessage\" });\n },\n getModificationMessages() {\n return new Map<string, ReactNode>([\n [\"Point\", intl.formatRichMessage({ id: \"tooltips.modificationMessagePoint\" })],\n [\"default\", intl.formatRichMessage({ id: \"tooltips.modificationMessage\" })]\n ]);\n }\n };\n }, [intl]);\n\n const drawingState = useGeometryEditing({\n map,\n editingStep,\n setEditingStep,\n snappingSources,\n tooltipMessages,\n ...interactionOptions\n });\n\n const editingCallbacks = useEditingCallbacks(\n mapModel,\n editingStep,\n writer,\n setEditingStep,\n successNotifierDisplayDuration,\n failureNotifierDisplayDuration\n );\n\n let content: ReactNode;\n switch (editingStep.id) {\n case \"initial\":\n case \"drawing\":\n case \"selection\":\n content = (\n <ActionSelector\n editingStep={editingStep}\n mapModel={mapModel}\n templates={templates}\n selectableLayers={selectableLayers}\n resolveSelectionAvailability={resolveSelectionAvailability}\n showActionBar={showActionBar}\n onActionChange={onActionChange}\n drawingState={drawingState}\n />\n );\n break;\n case \"creation\":\n case \"update\":\n content = (\n <PropertyEditor\n editingStep={editingStep}\n callbacks={editingCallbacks}\n templates={templates}\n resolveFormTemplate={resolveFormTemplate}\n />\n );\n break;\n }\n\n return (\n <Box {...containerProps} h=\"full\">\n {content}\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -1,11 +1,18 @@
1
+ import { Layer, MapModel } from "@open-pioneer/map";
1
2
  import { type ReactElement } from "react";
3
+ import { FeatureEditorProps } from "../../../api/editor/editor";
4
+ import { EditingStep } from "../../../api/model/EditingStep";
2
5
  import type { FeatureTemplate } from "../../../api/model/FeatureTemplate";
3
6
  import { DrawingState } from "../../geometry-editing/useGeometryEditing";
4
7
  export interface ActionSelectorProps {
5
- readonly templates: FeatureTemplate[];
6
- readonly showActionBar: boolean;
7
- readonly drawingState: DrawingState;
8
- readonly onActionChange: (newAction: Action | undefined) => void;
8
+ mapModel: MapModel;
9
+ templates: FeatureTemplate[];
10
+ selectableLayers: Layer[] | undefined;
11
+ resolveSelectionAvailability: FeatureEditorProps["resolveSelectionAvailability"];
12
+ showActionBar: boolean;
13
+ editingStep: EditingStep;
14
+ drawingState: DrawingState;
15
+ onActionChange: (newAction: Action | undefined) => void;
9
16
  }
10
17
  export interface CreateAction {
11
18
  readonly mode: "create";
@@ -15,4 +22,4 @@ export interface UpdateAction {
15
22
  readonly mode: "update";
16
23
  }
17
24
  export type Action = CreateAction | UpdateAction;
18
- export declare function ActionSelector({ templates, showActionBar, drawingState, onActionChange }: ActionSelectorProps): ReactElement;
25
+ export declare function ActionSelector({ mapModel, templates, selectableLayers, resolveSelectionAvailability, showActionBar, editingStep, drawingState, onActionChange }: ActionSelectorProps): ReactElement;
@@ -2,17 +2,34 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { Flex, Box } from '@chakra-ui/react';
3
3
  import { useEvent, TitledSection } from '@open-pioneer/react-utils';
4
4
  import { useIntl } from '../../../_virtual/hooks.js';
5
- import { useState, useMemo } from 'react';
5
+ import { useEffectEvent, useEffect, useState, useMemo } from 'react';
6
+ import { useSelectionAvailability } from '../../editor/editorHooks.js';
6
7
  import { DrawingControls } from './DrawingControls.js';
7
8
  import { SelectButton } from './SelectButton.js';
8
9
  import { TemplateSelector } from './TemplateSelector.js';
9
10
 
10
11
  function ActionSelector({
12
+ mapModel,
11
13
  templates,
14
+ selectableLayers,
15
+ resolveSelectionAvailability,
12
16
  showActionBar,
17
+ editingStep,
13
18
  drawingState,
14
19
  onActionChange
15
20
  }) {
21
+ const selectionAvailability = useSelectionAvailability(
22
+ mapModel,
23
+ templates,
24
+ selectableLayers,
25
+ resolveSelectionAvailability
26
+ );
27
+ const onActionChangeEffect = useEffectEvent(onActionChange);
28
+ useEffect(() => {
29
+ if (editingStep.id === "selection" && selectionAvailability.status === "unavailable") {
30
+ onActionChangeEffect(void 0);
31
+ }
32
+ }, [editingStep.id, selectionAvailability.status]);
16
33
  const [selectButtonIsActive, setSelectButtonActive] = useState(false);
17
34
  const [selectedTemplate, setSelectedTemplate] = useState();
18
35
  const onButtonClick = useEvent(() => {
@@ -43,7 +60,15 @@ function ActionSelector({
43
60
  align: "stretch",
44
61
  overflowY: "auto",
45
62
  children: [
46
- /* @__PURE__ */ jsx(TitledSection, { title: editFeatureHeading, sectionHeadingProps: { size: "sm" }, children: /* @__PURE__ */ jsx(SelectButton, { isActive: selectButtonIsActive, onClick: onButtonClick }) }),
63
+ /* @__PURE__ */ jsx(TitledSection, { title: editFeatureHeading, sectionHeadingProps: { size: "sm" }, children: /* @__PURE__ */ jsx(
64
+ SelectButton,
65
+ {
66
+ isAvailable: selectionAvailability.status === "available",
67
+ notAvailableMessage: selectionAvailability.status === "unavailable" ? selectionAvailability.reason : void 0,
68
+ isActive: selectButtonIsActive,
69
+ onClick: onButtonClick
70
+ }
71
+ ) }),
47
72
  /* @__PURE__ */ jsxs(TitledSection, { title: createFeatureHeading, sectionHeadingProps: { size: "sm", mt: 3 }, children: [
48
73
  /* @__PURE__ */ jsx(Box, { flex: 1, overflowY: "auto", mb: 2, children: /* @__PURE__ */ jsx(
49
74
  TemplateSelector,
@@ -1 +1 @@
1
- {"version":3,"file":"ActionSelector.js","sources":["ActionSelector.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Box, Flex } from \"@chakra-ui/react\";\nimport { TitledSection, useEvent } from \"@open-pioneer/react-utils\";\nimport type { Type as GeometryType } from \"ol/geom/Geometry\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { useMemo, useState, type ReactElement } from \"react\";\nimport type { FeatureTemplate } from \"../../../api/model/FeatureTemplate\";\nimport { DrawingState } from \"../../geometry-editing/useGeometryEditing\";\nimport { DrawingControls } from \"./DrawingControls\";\nimport { SelectButton } from \"./SelectButton\";\nimport { TemplateSelector } from \"./TemplateSelector\";\n\nexport interface ActionSelectorProps {\n readonly templates: FeatureTemplate[];\n readonly showActionBar: boolean;\n readonly drawingState: DrawingState;\n readonly onActionChange: (newAction: Action | undefined) => void;\n}\n\nexport interface CreateAction {\n readonly mode: \"create\";\n readonly template: FeatureTemplate;\n}\n\nexport interface UpdateAction {\n readonly mode: \"update\";\n}\n\nexport type Action = CreateAction | UpdateAction;\n\nexport function ActionSelector({\n templates,\n showActionBar,\n drawingState,\n onActionChange\n}: ActionSelectorProps): ReactElement {\n const [selectButtonIsActive, setSelectButtonActive] = useState(false);\n const [selectedTemplate, setSelectedTemplate] = useState<FeatureTemplate>();\n\n const onButtonClick = useEvent(() => {\n setSelectButtonActive((active) => !active);\n setSelectedTemplate(undefined);\n onActionChange(!selectButtonIsActive ? { mode: \"update\" } : undefined);\n });\n\n const onTemplateClick = useEvent((template: FeatureTemplate) => {\n setSelectedTemplate((current) => (current !== template ? template : undefined));\n setSelectButtonActive(false);\n onActionChange(selectedTemplate !== template ? { mode: \"create\", template } : undefined);\n });\n\n const { formatMessage } = useIntl();\n\n const [editFeatureHeading, createFeatureHeading] = useMemo(\n () => [\n formatMessage({ id: \"actionSelector.editFeatureHeading\" }),\n formatMessage({ id: \"actionSelector.createFeatureHeading\" })\n ],\n [formatMessage]\n );\n\n return (\n <Flex\n className=\"editor__action-selector\"\n direction=\"column\"\n height=\"full\"\n rowGap={3}\n align=\"stretch\"\n overflowY=\"auto\"\n >\n <TitledSection title={editFeatureHeading} sectionHeadingProps={{ size: \"sm\" }}>\n <SelectButton isActive={selectButtonIsActive} onClick={onButtonClick} />\n </TitledSection>\n\n <TitledSection title={createFeatureHeading} sectionHeadingProps={{ size: \"sm\", mt: 3 }}>\n <Box flex={1} overflowY=\"auto\" mb={2}>\n <TemplateSelector\n templates={templates}\n selectedTemplate={selectedTemplate}\n onClick={onTemplateClick}\n />\n </Box>\n {showActionBar && shouldShowActionBar(selectedTemplate) && (\n <DrawingControls drawingState={drawingState} />\n )}\n </TitledSection>\n </Flex>\n );\n}\n\nfunction shouldShowActionBar(template: FeatureTemplate | undefined): boolean {\n return template != null && ACTION_GEOMETRY_TYPES.has(template.geometryType);\n}\n\nconst ACTION_GEOMETRY_TYPES = new Set<GeometryType>([\"Polygon\", \"LineString\"]);\n"],"names":[],"mappings":";;;;;;;;;AAgCO,SAAS,cAAA,CAAe;AAAA,EAC3B,SAAA;AAAA,EACA,aAAA;AAAA,EACA,YAAA;AAAA,EACA;AACJ,CAAA,EAAsC;AAClC,EAAA,MAAM,CAAC,oBAAA,EAAsB,qBAAqB,CAAA,GAAI,SAAS,KAAK,CAAA;AACpE,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAI,QAAA,EAA0B;AAE1E,EAAA,MAAM,aAAA,GAAgB,SAAS,MAAM;AACjC,IAAA,qBAAA,CAAsB,CAAC,MAAA,KAAW,CAAC,MAAM,CAAA;AACzC,IAAA,mBAAA,CAAoB,MAAS,CAAA;AAC7B,IAAA,cAAA,CAAe,CAAC,oBAAA,GAAuB,EAAE,IAAA,EAAM,QAAA,KAAa,MAAS,CAAA;AAAA,EACzE,CAAC,CAAA;AAED,EAAA,MAAM,eAAA,GAAkB,QAAA,CAAS,CAAC,QAAA,KAA8B;AAC5D,IAAA,mBAAA,CAAoB,CAAC,OAAA,KAAa,OAAA,KAAY,QAAA,GAAW,WAAW,MAAU,CAAA;AAC9E,IAAA,qBAAA,CAAsB,KAAK,CAAA;AAC3B,IAAA,cAAA,CAAe,qBAAqB,QAAA,GAAW,EAAE,MAAM,QAAA,EAAU,QAAA,KAAa,MAAS,CAAA;AAAA,EAC3F,CAAC,CAAA;AAED,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,OAAA,EAAQ;AAElC,EAAA,MAAM,CAAC,kBAAA,EAAoB,oBAAoB,CAAA,GAAI,OAAA;AAAA,IAC/C,MAAM;AAAA,MACF,aAAA,CAAc,EAAE,EAAA,EAAI,mCAAA,EAAqC,CAAA;AAAA,MACzD,aAAA,CAAc,EAAE,EAAA,EAAI,qCAAA,EAAuC;AAAA,KAC/D;AAAA,IACA,CAAC,aAAa;AAAA,GAClB;AAEA,EAAA,uBACI,IAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACG,SAAA,EAAU,yBAAA;AAAA,MACV,SAAA,EAAU,QAAA;AAAA,MACV,MAAA,EAAO,MAAA;AAAA,MACP,MAAA,EAAQ,CAAA;AAAA,MACR,KAAA,EAAM,SAAA;AAAA,MACN,SAAA,EAAU,MAAA;AAAA,MAEV,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,aAAA,EAAA,EAAc,KAAA,EAAO,kBAAA,EAAoB,mBAAA,EAAqB,EAAE,IAAA,EAAM,IAAA,EAAK,EACxE,QAAA,kBAAA,GAAA,CAAC,YAAA,EAAA,EAAa,QAAA,EAAU,oBAAA,EAAsB,OAAA,EAAS,eAAe,CAAA,EAC1E,CAAA;AAAA,wBAEA,IAAA,CAAC,aAAA,EAAA,EAAc,KAAA,EAAO,oBAAA,EAAsB,mBAAA,EAAqB,EAAE,IAAA,EAAM,IAAA,EAAM,EAAA,EAAI,CAAA,EAAE,EACjF,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,OAAI,IAAA,EAAM,CAAA,EAAG,SAAA,EAAU,MAAA,EAAO,IAAI,CAAA,EAC/B,QAAA,kBAAA,GAAA;AAAA,YAAC,gBAAA;AAAA,YAAA;AAAA,cACG,SAAA;AAAA,cACA,gBAAA;AAAA,cACA,OAAA,EAAS;AAAA;AAAA,WACb,EACJ,CAAA;AAAA,UACC,iBAAiB,mBAAA,CAAoB,gBAAgB,CAAA,oBAClD,GAAA,CAAC,mBAAgB,YAAA,EAA4B;AAAA,SAAA,EAErD;AAAA;AAAA;AAAA,GACJ;AAER;AAEA,SAAS,oBAAoB,QAAA,EAAgD;AACzE,EAAA,OAAO,QAAA,IAAY,IAAA,IAAQ,qBAAA,CAAsB,GAAA,CAAI,SAAS,YAAY,CAAA;AAC9E;AAEA,MAAM,wCAAwB,IAAI,GAAA,CAAkB,CAAC,SAAA,EAAW,YAAY,CAAC,CAAA;;;;"}
1
+ {"version":3,"file":"ActionSelector.js","sources":["ActionSelector.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Box, Flex } from \"@chakra-ui/react\";\nimport { Layer, MapModel } from \"@open-pioneer/map\";\nimport { TitledSection, useEvent } from \"@open-pioneer/react-utils\";\nimport type { Type as GeometryType } from \"ol/geom/Geometry\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { useEffect, useEffectEvent, useMemo, useState, type ReactElement } from \"react\";\nimport { FeatureEditorProps } from \"../../../api/editor/editor\";\nimport { EditingStep } from \"../../../api/model/EditingStep\";\nimport type { FeatureTemplate } from \"../../../api/model/FeatureTemplate\";\nimport { useSelectionAvailability } from \"../../editor/editorHooks\";\nimport { DrawingState } from \"../../geometry-editing/useGeometryEditing\";\nimport { DrawingControls } from \"./DrawingControls\";\nimport { SelectButton } from \"./SelectButton\";\nimport { TemplateSelector } from \"./TemplateSelector\";\n\n// TODO(refactor): Takes too many props, just to show or reset the availability of the select button.\nexport interface ActionSelectorProps {\n mapModel: MapModel;\n templates: FeatureTemplate[];\n selectableLayers: Layer[] | undefined;\n resolveSelectionAvailability: FeatureEditorProps[\"resolveSelectionAvailability\"];\n showActionBar: boolean;\n editingStep: EditingStep;\n drawingState: DrawingState;\n onActionChange: (newAction: Action | undefined) => void;\n}\n\nexport interface CreateAction {\n readonly mode: \"create\";\n readonly template: FeatureTemplate;\n}\n\nexport interface UpdateAction {\n readonly mode: \"update\";\n}\n\nexport type Action = CreateAction | UpdateAction;\n\nexport function ActionSelector({\n mapModel,\n templates,\n selectableLayers,\n resolveSelectionAvailability,\n showActionBar,\n editingStep,\n drawingState,\n onActionChange\n}: ActionSelectorProps): ReactElement {\n const selectionAvailability = useSelectionAvailability(\n mapModel,\n templates,\n selectableLayers,\n resolveSelectionAvailability\n );\n\n // Reset editing step \"initial\" when the selection becomes unavailable.\n // TODO(refactor): this should be initiated by the model; not the UI.\n const onActionChangeEffect = useEffectEvent(onActionChange);\n useEffect(() => {\n if (editingStep.id === \"selection\" && selectionAvailability.status === \"unavailable\") {\n onActionChangeEffect(undefined);\n }\n }, [editingStep.id, selectionAvailability.status]);\n\n const [selectButtonIsActive, setSelectButtonActive] = useState(false);\n const [selectedTemplate, setSelectedTemplate] = useState<FeatureTemplate>();\n\n const onButtonClick = useEvent(() => {\n setSelectButtonActive((active) => !active);\n setSelectedTemplate(undefined);\n onActionChange(!selectButtonIsActive ? { mode: \"update\" } : undefined);\n });\n\n const onTemplateClick = useEvent((template: FeatureTemplate) => {\n setSelectedTemplate((current) => (current !== template ? template : undefined));\n setSelectButtonActive(false);\n onActionChange(selectedTemplate !== template ? { mode: \"create\", template } : undefined);\n });\n\n const { formatMessage } = useIntl();\n\n const [editFeatureHeading, createFeatureHeading] = useMemo(\n () => [\n formatMessage({ id: \"actionSelector.editFeatureHeading\" }),\n formatMessage({ id: \"actionSelector.createFeatureHeading\" })\n ],\n [formatMessage]\n );\n\n return (\n <Flex\n className=\"editor__action-selector\"\n direction=\"column\"\n height=\"full\"\n rowGap={3}\n align=\"stretch\"\n overflowY=\"auto\"\n >\n <TitledSection title={editFeatureHeading} sectionHeadingProps={{ size: \"sm\" }}>\n <SelectButton\n isAvailable={selectionAvailability.status === \"available\"}\n notAvailableMessage={\n selectionAvailability.status === \"unavailable\"\n ? selectionAvailability.reason\n : undefined\n }\n isActive={selectButtonIsActive}\n onClick={onButtonClick}\n />\n </TitledSection>\n\n <TitledSection title={createFeatureHeading} sectionHeadingProps={{ size: \"sm\", mt: 3 }}>\n <Box flex={1} overflowY=\"auto\" mb={2}>\n <TemplateSelector\n templates={templates}\n selectedTemplate={selectedTemplate}\n onClick={onTemplateClick}\n />\n </Box>\n {showActionBar && shouldShowActionBar(selectedTemplate) && (\n <DrawingControls drawingState={drawingState} />\n )}\n </TitledSection>\n </Flex>\n );\n}\n\nfunction shouldShowActionBar(template: FeatureTemplate | undefined): boolean {\n return template != null && ACTION_GEOMETRY_TYPES.has(template.geometryType);\n}\n\nconst ACTION_GEOMETRY_TYPES = new Set<GeometryType>([\"Polygon\", \"LineString\"]);\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DrawingControls.js","sources":["DrawingControls.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { HStack, IconButton } from \"@chakra-ui/react\";\nimport { Tooltip } from \"@open-pioneer/chakra-snippets/tooltip\";\nimport { useReactiveSnapshot } from \"@open-pioneer/reactivity\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { useMemo, type ReactElement } from \"react\";\nimport { LuCheck, LuRedo, LuUndo, LuX } from \"react-icons/lu\";\nimport { type DrawingState } from \"../../geometry-editing/useGeometryEditing\";\n\nexport interface DrawingControlsProps {\n readonly drawingState: DrawingState;\n}\n\nexport function DrawingControls({ drawingState }: DrawingControlsProps): ReactElement {\n const capabilities = useReactiveSnapshot(\n () => ({\n canUndo: drawingState.canUndo,\n canRedo: drawingState.canRedo,\n canFinish: drawingState.canFinish,\n canReset: drawingState.canReset\n }),\n [drawingState]\n );\n\n const actions = useMemo(\n () => ({\n undo: () => drawingState.undo(),\n redo: () => drawingState.redo(),\n finish: () => drawingState.finish(),\n reset: () => drawingState.reset()\n }),\n [drawingState]\n );\n\n const { formatMessage } = useIntl();\n\n const tooltips = useMemo(\n () => ({\n finishButton: formatMessage({ id: \"actionSelector.finishButtonTooltip\" }),\n resetButton: formatMessage({ id: \"actionSelector.resetButtonTooltip\" }),\n undoButton: formatMessage({ id: \"actionSelector.undoButtonTooltip\" }),\n redoButton: formatMessage({ id: \"actionSelector.redoButtonTooltip\" })\n }),\n [formatMessage]\n );\n\n return (\n <HStack className=\"editor__drawing-controls\" justify=\"right\" gap={3} mb={\"4px\"}>\n <Tooltip content={tooltips.finishButton}>\n <IconButton\n aria-label={tooltips.finishButton}\n disabled={!capabilities.canFinish}\n onClick={actions.finish}\n >\n <LuCheck size={25} />\n </IconButton>\n </Tooltip>\n\n <Tooltip content={tooltips.resetButton}>\n <IconButton\n aria-label={tooltips.resetButton}\n disabled={!capabilities.canReset}\n onClick={actions.reset}\n >\n <LuX size={25} />\n </IconButton>\n </Tooltip>\n\n <Tooltip content={tooltips.undoButton}>\n <IconButton\n aria-label={tooltips.undoButton}\n disabled={!capabilities.canUndo}\n onClick={actions.undo}\n >\n <LuUndo size={25} />\n </IconButton>\n </Tooltip>\n\n <Tooltip content={tooltips.redoButton}>\n <IconButton\n aria-label={tooltips.redoButton}\n disabled={!capabilities.canRedo}\n onClick={actions.redo}\n >\n <LuRedo size={25} />\n </IconButton>\n </Tooltip>\n </HStack>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DrawingControls.js","sources":["DrawingControls.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { HStack, IconButton } from \"@chakra-ui/react\";\nimport { Tooltip } from \"@open-pioneer/chakra-snippets/tooltip\";\nimport { useReactiveSnapshot } from \"@open-pioneer/reactivity\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { useMemo, type ReactElement } from \"react\";\nimport { LuCheck, LuRedo, LuUndo, LuX } from \"react-icons/lu\";\nimport { type DrawingState } from \"../../geometry-editing/useGeometryEditing\";\n\nexport interface DrawingControlsProps {\n readonly drawingState: DrawingState;\n}\n\nexport function DrawingControls({ drawingState }: DrawingControlsProps): ReactElement {\n const capabilities = useReactiveSnapshot(\n () => ({\n canUndo: drawingState.canUndo,\n canRedo: drawingState.canRedo,\n canFinish: drawingState.canFinish,\n canReset: drawingState.canReset\n }),\n [drawingState]\n );\n\n const actions = useMemo(\n () => ({\n undo: () => drawingState.undo(),\n redo: () => drawingState.redo(),\n finish: () => drawingState.finish(),\n reset: () => drawingState.reset()\n }),\n [drawingState]\n );\n\n const { formatMessage } = useIntl();\n\n const tooltips = useMemo(\n () => ({\n finishButton: formatMessage({ id: \"actionSelector.finishButtonTooltip\" }),\n resetButton: formatMessage({ id: \"actionSelector.resetButtonTooltip\" }),\n undoButton: formatMessage({ id: \"actionSelector.undoButtonTooltip\" }),\n redoButton: formatMessage({ id: \"actionSelector.redoButtonTooltip\" })\n }),\n [formatMessage]\n );\n\n return (\n <HStack className=\"editor__drawing-controls\" justify=\"right\" gap={3} mb={\"4px\"}>\n <Tooltip content={tooltips.finishButton}>\n <IconButton\n aria-label={tooltips.finishButton}\n disabled={!capabilities.canFinish}\n onClick={actions.finish}\n >\n <LuCheck size={25} />\n </IconButton>\n </Tooltip>\n\n <Tooltip content={tooltips.resetButton}>\n <IconButton\n aria-label={tooltips.resetButton}\n disabled={!capabilities.canReset}\n onClick={actions.reset}\n >\n <LuX size={25} />\n </IconButton>\n </Tooltip>\n\n <Tooltip content={tooltips.undoButton}>\n <IconButton\n aria-label={tooltips.undoButton}\n disabled={!capabilities.canUndo}\n onClick={actions.undo}\n >\n <LuUndo size={25} />\n </IconButton>\n </Tooltip>\n\n <Tooltip content={tooltips.redoButton}>\n <IconButton\n aria-label={tooltips.redoButton}\n disabled={!capabilities.canRedo}\n onClick={actions.redo}\n >\n <LuRedo size={25} />\n </IconButton>\n </Tooltip>\n </HStack>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAeO,SAAS,eAAA,CAAgB,EAAE,YAAA,EAAa,EAAuC;AAClF,EAAA,MAAM,YAAA,GAAe,mBAAA;AAAA,IACjB,OAAO;AAAA,MACH,SAAS,YAAA,CAAa,OAAA;AAAA,MACtB,SAAS,YAAA,CAAa,OAAA;AAAA,MACtB,WAAW,YAAA,CAAa,SAAA;AAAA,MACxB,UAAU,YAAA,CAAa;AAAA,KAC3B,CAAA;AAAA,IACA,CAAC,YAAY;AAAA,GACjB;AAEA,EAAA,MAAM,OAAA,GAAU,OAAA;AAAA,IACZ,OAAO;AAAA,MACH,IAAA,EAAM,MAAM,YAAA,CAAa,IAAA,EAAK;AAAA,MAC9B,IAAA,EAAM,MAAM,YAAA,CAAa,IAAA,EAAK;AAAA,MAC9B,MAAA,EAAQ,MAAM,YAAA,CAAa,MAAA,EAAO;AAAA,MAClC,KAAA,EAAO,MAAM,YAAA,CAAa,KAAA;AAAM,KACpC,CAAA;AAAA,IACA,CAAC,YAAY;AAAA,GACjB;AAEA,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,OAAA,EAAQ;AAElC,EAAA,MAAM,QAAA,GAAW,OAAA;AAAA,IACb,OAAO;AAAA,MACH,YAAA,EAAc,aAAA,CAAc,EAAE,EAAA,EAAI,sCAAsC,CAAA;AAAA,MACxE,WAAA,EAAa,aAAA,CAAc,EAAE,EAAA,EAAI,qCAAqC,CAAA;AAAA,MACtE,UAAA,EAAY,aAAA,CAAc,EAAE,EAAA,EAAI,oCAAoC,CAAA;AAAA,MACpE,UAAA,EAAY,aAAA,CAAc,EAAE,EAAA,EAAI,oCAAoC;AAAA,KACxE,CAAA;AAAA,IACA,CAAC,aAAa;AAAA,GAClB;AAEA,EAAA,uBACI,IAAA,CAAC,UAAO,SAAA,EAAU,0BAAA,EAA2B,SAAQ,OAAA,EAAQ,GAAA,EAAK,CAAA,EAAG,EAAA,EAAI,KAAA,EACrE,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,OAAA,EAAS,QAAA,CAAS,YAAA,EACvB,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACG,cAAY,QAAA,CAAS,YAAA;AAAA,QACrB,QAAA,EAAU,CAAC,YAAA,CAAa,SAAA;AAAA,QACxB,SAAS,OAAA,CAAQ,MAAA;AAAA,QAEjB,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,IAAA,EAAM,EAAA,EAAI;AAAA;AAAA,KACvB,EACJ,CAAA;AAAA,oBAEA,GAAA,CAAC,OAAA,EAAA,EAAQ,OAAA,EAAS,QAAA,CAAS,WAAA,EACvB,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACG,cAAY,QAAA,CAAS,WAAA;AAAA,QACrB,QAAA,EAAU,CAAC,YAAA,CAAa,QAAA;AAAA,QACxB,SAAS,OAAA,CAAQ,KAAA;AAAA,QAEjB,QAAA,kBAAA,GAAA,CAAC,GAAA,EAAA,EAAI,IAAA,EAAM,EAAA,EAAI;AAAA;AAAA,KACnB,EACJ,CAAA;AAAA,oBAEA,GAAA,CAAC,OAAA,EAAA,EAAQ,OAAA,EAAS,QAAA,CAAS,UAAA,EACvB,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACG,cAAY,QAAA,CAAS,UAAA;AAAA,QACrB,QAAA,EAAU,CAAC,YAAA,CAAa,OAAA;AAAA,QACxB,SAAS,OAAA,CAAQ,IAAA;AAAA,QAEjB,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAO,IAAA,EAAM,EAAA,EAAI;AAAA;AAAA,KACtB,EACJ,CAAA;AAAA,oBAEA,GAAA,CAAC,OAAA,EAAA,EAAQ,OAAA,EAAS,QAAA,CAAS,UAAA,EACvB,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACG,cAAY,QAAA,CAAS,UAAA;AAAA,QACrB,QAAA,EAAU,CAAC,YAAA,CAAa,OAAA;AAAA,QACxB,SAAS,OAAA,CAAQ,IAAA;AAAA,QAEjB,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAO,IAAA,EAAM,EAAA,EAAI;AAAA;AAAA,KACtB,EACJ;AAAA,GAAA,EACJ,CAAA;AAER;;"}
@@ -1,7 +1,9 @@
1
- import type { ReactElement } from "react";
1
+ import { type ReactElement } from "react";
2
2
  interface SelectButtonProps {
3
- readonly isActive: boolean;
4
- readonly onClick: () => void;
3
+ isAvailable: boolean;
4
+ notAvailableMessage: string | undefined;
5
+ isActive: boolean;
6
+ onClick: () => void;
5
7
  }
6
- export declare function SelectButton({ isActive, onClick }: SelectButtonProps): ReactElement;
8
+ export declare function SelectButton(props: SelectButtonProps): ReactElement;
7
9
  export {};
@@ -1,11 +1,26 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { Toggle, Button } from '@chakra-ui/react';
3
+ import { Tooltip } from '@open-pioneer/chakra-snippets/tooltip';
3
4
  import { useIntl } from '../../../_virtual/hooks.js';
5
+ import { useMemo } from 'react';
4
6
  import { LuMousePointerClick } from 'react-icons/lu';
5
7
 
6
- function SelectButton({ isActive, onClick }) {
7
- const { formatMessage } = useIntl();
8
- return /* @__PURE__ */ jsx(Toggle.Root, { pressed: isActive, asChild: true, children: /* @__PURE__ */ jsxs(
8
+ function SelectButton(props) {
9
+ const {
10
+ isAvailable,
11
+ notAvailableMessage: notAvailableMessageProp,
12
+ isActive: isActiveProp,
13
+ onClick
14
+ } = props;
15
+ const intl = useIntl();
16
+ const isActive = isAvailable && isActiveProp;
17
+ const notAvailableMessage = useMemo(() => {
18
+ if (isAvailable) {
19
+ return void 0;
20
+ }
21
+ return notAvailableMessageProp ?? intl.formatMessage({ id: "selection.defaultNotAvailableMessage" });
22
+ }, [intl, isAvailable, notAvailableMessageProp]);
23
+ return /* @__PURE__ */ jsx(Tooltip, { disabled: notAvailableMessage == null, content: notAvailableMessage, children: /* @__PURE__ */ jsx(Toggle.Root, { disabled: !isAvailable, pressed: isActive, asChild: true, children: /* @__PURE__ */ jsxs(
9
24
  Button,
10
25
  {
11
26
  className: "editor__action-selector-select-button",
@@ -16,10 +31,10 @@ function SelectButton({ isActive, onClick }) {
16
31
  onClick,
17
32
  children: [
18
33
  /* @__PURE__ */ jsx(LuMousePointerClick, { "aria-hidden": "true" }),
19
- isActive ? formatMessage({ id: "actionSelector.selectButtonActiveTitle" }) : formatMessage({ id: "actionSelector.selectButtonTitle" })
34
+ isActive ? intl.formatMessage({ id: "actionSelector.selectButtonActiveTitle" }) : intl.formatMessage({ id: "actionSelector.selectButtonTitle" })
20
35
  ]
21
36
  }
22
- ) });
37
+ ) }) });
23
38
  }
24
39
 
25
40
  export { SelectButton };
@@ -1 +1 @@
1
- {"version":3,"file":"SelectButton.js","sources":["SelectButton.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Button, Toggle } from \"@chakra-ui/react\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport type { ReactElement } from \"react\";\nimport { LuMousePointerClick } from \"react-icons/lu\";\n\ninterface SelectButtonProps {\n readonly isActive: boolean;\n readonly onClick: () => void;\n}\n\nexport function SelectButton({ isActive, onClick }: SelectButtonProps): ReactElement {\n const { formatMessage } = useIntl();\n\n return (\n <Toggle.Root pressed={isActive} asChild>\n <Button\n className=\"editor__action-selector-select-button\"\n variant=\"outline\"\n _hover={{ bg: isActive ? \"colorPalette.700\" : \"colorPalette.subtle\" }}\n _pressed={{ bg: \"colorPalette.800\", color: \"colorPalette.contrast\" }}\n // Margin for focus outline\n marginX=\"4px\"\n onClick={onClick}\n >\n <LuMousePointerClick aria-hidden=\"true\" />\n {isActive\n ? formatMessage({ id: \"actionSelector.selectButtonActiveTitle\" })\n : formatMessage({ id: \"actionSelector.selectButtonTitle\" })}\n </Button>\n </Toggle.Root>\n );\n}\n"],"names":[],"mappings":";;;;;AAaO,SAAS,YAAA,CAAa,EAAE,QAAA,EAAU,OAAA,EAAQ,EAAoC;AACjF,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,OAAA,EAAQ;AAElC,EAAA,2BACK,MAAA,CAAO,IAAA,EAAP,EAAY,OAAA,EAAS,QAAA,EAAU,SAAO,IAAA,EACnC,QAAA,kBAAA,IAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACG,SAAA,EAAU,uCAAA;AAAA,MACV,OAAA,EAAQ,SAAA;AAAA,MACR,MAAA,EAAQ,EAAE,EAAA,EAAI,QAAA,GAAW,qBAAqB,qBAAA,EAAsB;AAAA,MACpE,QAAA,EAAU,EAAE,EAAA,EAAI,kBAAA,EAAoB,OAAO,uBAAA,EAAwB;AAAA,MAEnE,OAAA,EAAQ,KAAA;AAAA,MACR,OAAA;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,mBAAA,EAAA,EAAoB,eAAY,MAAA,EAAO,CAAA;AAAA,QACvC,QAAA,GACK,aAAA,CAAc,EAAE,EAAA,EAAI,wCAAA,EAA0C,CAAA,GAC9D,aAAA,CAAc,EAAE,EAAA,EAAI,kCAAA,EAAoC;AAAA;AAAA;AAAA,GAClE,EACJ,CAAA;AAER;;;;"}
1
+ {"version":3,"file":"SelectButton.js","sources":["SelectButton.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Button, Toggle } from \"@chakra-ui/react\";\nimport { Tooltip } from \"@open-pioneer/chakra-snippets/tooltip\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { useMemo, type ReactElement } from \"react\";\nimport { LuMousePointerClick } from \"react-icons/lu\";\n\ninterface SelectButtonProps {\n isAvailable: boolean;\n notAvailableMessage: string | undefined;\n\n isActive: boolean;\n onClick: () => void;\n}\n\nexport function SelectButton(props: SelectButtonProps): ReactElement {\n const {\n isAvailable,\n notAvailableMessage: notAvailableMessageProp,\n isActive: isActiveProp,\n onClick\n } = props;\n const intl = useIntl();\n\n const isActive = isAvailable && isActiveProp;\n\n const notAvailableMessage = useMemo(() => {\n if (isAvailable) {\n return undefined;\n }\n return (\n notAvailableMessageProp ??\n intl.formatMessage({ id: \"selection.defaultNotAvailableMessage\" })\n );\n }, [intl, isAvailable, notAvailableMessageProp]);\n\n return (\n <Tooltip disabled={notAvailableMessage == null} content={notAvailableMessage}>\n <Toggle.Root disabled={!isAvailable} pressed={isActive} asChild>\n <Button\n className=\"editor__action-selector-select-button\"\n variant=\"outline\"\n _hover={{ bg: isActive ? \"colorPalette.700\" : \"colorPalette.subtle\" }}\n _pressed={{ bg: \"colorPalette.800\", color: \"colorPalette.contrast\" }}\n // Margin for focus outline\n marginX=\"4px\"\n onClick={onClick}\n >\n <LuMousePointerClick aria-hidden=\"true\" />\n {isActive\n ? intl.formatMessage({ id: \"actionSelector.selectButtonActiveTitle\" })\n : intl.formatMessage({ id: \"actionSelector.selectButtonTitle\" })}\n </Button>\n </Toggle.Root>\n </Tooltip>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAiBO,SAAS,aAAa,KAAA,EAAwC;AACjE,EAAA,MAAM;AAAA,IACF,WAAA;AAAA,IACA,mBAAA,EAAqB,uBAAA;AAAA,IACrB,QAAA,EAAU,YAAA;AAAA,IACV;AAAA,GACJ,GAAI,KAAA;AACJ,EAAA,MAAM,OAAO,OAAA,EAAQ;AAErB,EAAA,MAAM,WAAW,WAAA,IAAe,YAAA;AAEhC,EAAA,MAAM,mBAAA,GAAsB,QAAQ,MAAM;AACtC,IAAA,IAAI,WAAA,EAAa;AACb,MAAA,OAAO,MAAA;AAAA,IACX;AACA,IAAA,OACI,2BACA,IAAA,CAAK,aAAA,CAAc,EAAE,EAAA,EAAI,wCAAwC,CAAA;AAAA,EAEzE,CAAA,EAAG,CAAC,IAAA,EAAM,WAAA,EAAa,uBAAuB,CAAC,CAAA;AAE/C,EAAA,2BACK,OAAA,EAAA,EAAQ,QAAA,EAAU,mBAAA,IAAuB,IAAA,EAAM,SAAS,mBAAA,EACrD,QAAA,kBAAA,GAAA,CAAC,MAAA,CAAO,IAAA,EAAP,EAAY,QAAA,EAAU,CAAC,aAAa,OAAA,EAAS,QAAA,EAAU,SAAO,IAAA,EAC3D,QAAA,kBAAA,IAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACG,SAAA,EAAU,uCAAA;AAAA,MACV,OAAA,EAAQ,SAAA;AAAA,MACR,MAAA,EAAQ,EAAE,EAAA,EAAI,QAAA,GAAW,qBAAqB,qBAAA,EAAsB;AAAA,MACpE,QAAA,EAAU,EAAE,EAAA,EAAI,kBAAA,EAAoB,OAAO,uBAAA,EAAwB;AAAA,MAEnE,OAAA,EAAQ,KAAA;AAAA,MACR,OAAA;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,mBAAA,EAAA,EAAoB,eAAY,MAAA,EAAO,CAAA;AAAA,QACvC,QAAA,GACK,IAAA,CAAK,aAAA,CAAc,EAAE,EAAA,EAAI,wCAAA,EAA0C,CAAA,GACnE,IAAA,CAAK,aAAA,CAAc,EAAE,EAAA,EAAI,oCAAoC;AAAA;AAAA;AAAA,KAE3E,CAAA,EACJ,CAAA;AAER;;"}