@open-pioneer/feature-editing 1.3.0-dev.20260512095810 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/api/editor/context.d.ts +16 -18
- package/api/editor/context.js +3 -3
- package/api/editor/context.js.map +1 -1
- package/api/fields/standardFieldConfigs.d.ts +2 -1
- package/api/model/FeatureTemplate.d.ts +2 -2
- package/i18n/de.yaml +13 -0
- package/i18n/en.yaml +15 -2
- package/implementation/FeatureEditor.js +6 -6
- package/implementation/FeatureEditor.js.map +1 -1
- package/implementation/components/action-selector/SelectButton.js +1 -1
- package/implementation/components/action-selector/SelectButton.js.map +1 -1
- package/implementation/components/controls/NumberFieldControl.js +1 -0
- package/implementation/components/controls/NumberFieldControl.js.map +1 -1
- package/implementation/components/property-editor/CancelConfirmationDialog.d.ts +8 -0
- package/implementation/components/property-editor/CancelConfirmationDialog.js +51 -0
- package/implementation/components/property-editor/CancelConfirmationDialog.js.map +1 -0
- package/implementation/components/property-editor/PropertyEditor.d.ts +10 -5
- package/implementation/components/property-editor/PropertyEditor.js +99 -21
- package/implementation/components/property-editor/PropertyEditor.js.map +1 -1
- package/implementation/components/property-editor/PropertyField.js +30 -4
- package/implementation/components/property-editor/PropertyField.js.map +1 -1
- package/implementation/components/property-editor/PropertyForm.d.ts +4 -7
- package/implementation/components/property-editor/PropertyForm.js +49 -67
- package/implementation/components/property-editor/PropertyForm.js.map +1 -1
- package/implementation/context/PropertyFormContext.d.ts +26 -10
- package/implementation/context/PropertyFormContext.js +96 -21
- package/implementation/context/PropertyFormContext.js.map +1 -1
- package/implementation/context/usePropertyFormContext.d.ts +17 -2
- package/implementation/context/usePropertyFormContext.js +17 -3
- package/implementation/context/usePropertyFormContext.js.map +1 -1
- package/implementation/geometry-editing/controller/EditingController.js +3 -0
- package/implementation/geometry-editing/controller/EditingController.js.map +1 -1
- package/implementation/geometry-editing/interactions/BaseInteraction.js +2 -0
- package/implementation/geometry-editing/interactions/BaseInteraction.js.map +1 -1
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +13 -13
- package/implementation/context/PropertyFormContextProvider.d.ts +0 -9
- package/implementation/context/PropertyFormContextProvider.js +0 -18
- package/implementation/context/PropertyFormContextProvider.js.map +0 -1
package/CHANGELOG.md
CHANGED
package/api/editor/context.d.ts
CHANGED
|
@@ -4,18 +4,17 @@ import type { Feature } from "ol";
|
|
|
4
4
|
import type { ModificationStep } from "../model/EditingStep";
|
|
5
5
|
import type { FeatureTemplate } from "../model/FeatureTemplate";
|
|
6
6
|
/**
|
|
7
|
-
* React hook for accessing the property form context.
|
|
7
|
+
* React hook for accessing the property form context within a custom form.
|
|
8
8
|
*
|
|
9
|
-
* Provides access to the {@link
|
|
9
|
+
* Provides access to the {@link CustomFormContext} instance, which contains the current feature
|
|
10
10
|
* being edited and methods to read and update its properties. This hook must be called from within
|
|
11
|
-
* a component that is rendered inside a property form (
|
|
12
|
-
* {@link DynamicFormTemplate}).
|
|
11
|
+
* a component that is rendered inside a custom property form (see {@link DynamicFormTemplate}).
|
|
13
12
|
*
|
|
14
13
|
* The context also allows controlling the form's validity by setting the `isValid` property,
|
|
15
14
|
* which determines whether the save button is enabled.
|
|
16
15
|
*
|
|
17
|
-
* @returns The current {@link
|
|
18
|
-
* @throws Error if called outside of a property form context (e.g., when no feature is being
|
|
16
|
+
* @returns The current {@link CustomFormContext} instance.
|
|
17
|
+
* @throws Error if called outside of a custom property form context (e.g., when no feature is being
|
|
19
18
|
* edited).
|
|
20
19
|
*
|
|
21
20
|
* @example
|
|
@@ -23,37 +22,36 @@ import type { FeatureTemplate } from "../model/FeatureTemplate";
|
|
|
23
22
|
* import { useReactiveSnapshot, DISPATCH_SYNC } from "@open-pioneer/reactivity";
|
|
24
23
|
*
|
|
25
24
|
* function CustomForm() {
|
|
26
|
-
* const context =
|
|
27
|
-
* const name = useReactiveSnapshot(
|
|
25
|
+
* const context = useCustomFormContext();
|
|
26
|
+
* const name = useReactiveSnapshot(
|
|
27
|
+
* () => (context.properties.get("name") as string) ?? "",
|
|
28
|
+
* [context],
|
|
29
|
+
* DISPATCH_SYNC
|
|
30
|
+
* );
|
|
28
31
|
*
|
|
29
32
|
* useEffect(() => {
|
|
30
33
|
* context.isValid = name.length >= 1;
|
|
31
34
|
* }, [context, name]);
|
|
32
35
|
*
|
|
33
|
-
* return (
|
|
34
|
-
* <input
|
|
35
|
-
* value={name}
|
|
36
|
-
* onChange={(e) => context.properties.set("name", e.target.value)}
|
|
37
|
-
* />
|
|
38
|
-
* );
|
|
36
|
+
* return <Input value={name} onChange={(e) => context.properties.set("name", e.target.value)} />;
|
|
39
37
|
* }
|
|
40
38
|
* ```
|
|
41
39
|
*
|
|
42
40
|
* @group Editor
|
|
43
41
|
*/
|
|
44
|
-
export declare const
|
|
42
|
+
export declare const useCustomFormContext: () => CustomFormContext;
|
|
45
43
|
/**
|
|
46
44
|
* Context object providing access to feature properties and editing state during form editing.
|
|
47
45
|
*
|
|
48
|
-
*
|
|
46
|
+
* The context manages the state of a feature being edited, including its properties, validation
|
|
49
47
|
* status, and associated metadata. It provides reactive property management through a
|
|
50
48
|
* {@link ReactiveMap}, allowing components to automatically re-render when properties change.
|
|
51
49
|
*
|
|
52
|
-
* Access this context in custom forms using the {@link
|
|
50
|
+
* Access this context in custom forms using the {@link useCustomFormContext} hook.
|
|
53
51
|
*
|
|
54
52
|
* @group Editor
|
|
55
53
|
*/
|
|
56
|
-
export interface
|
|
54
|
+
export interface CustomFormContext {
|
|
57
55
|
/**
|
|
58
56
|
* The OpenLayers feature being edited.
|
|
59
57
|
*
|
package/api/editor/context.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCustomFormContext as useCustomFormContext$1 } from '../../implementation/context/usePropertyFormContext.js';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const useCustomFormContext = useCustomFormContext$1;
|
|
4
4
|
|
|
5
|
-
export {
|
|
5
|
+
export { useCustomFormContext };
|
|
6
6
|
//# sourceMappingURL=context.js.map
|
|
@@ -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\nimport type { ReactiveMap } from \"@conterra/reactivity-core\";\nimport type { Layer } from \"@open-pioneer/map\";\nimport type { useReactiveSnapshot } from \"@open-pioneer/reactivity\";\nimport type { Feature } from \"ol\";\nimport {
|
|
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\nimport type { ReactiveMap } from \"@conterra/reactivity-core\";\nimport type { Layer } from \"@open-pioneer/map\";\nimport type { useReactiveSnapshot } from \"@open-pioneer/reactivity\";\nimport type { Feature } from \"ol\";\nimport { useCustomFormContext as useCustomFormContextImpl } from \"../../implementation/context/usePropertyFormContext\";\nimport type { CreationStep, ModificationStep, UpdateStep } from \"../model/EditingStep\";\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":";;AA8CO,MAAM,oBAAA,GAAgDA;;;;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type FormatNumberOptions } from "@formatjs/intl";
|
|
1
2
|
import type { BaseFieldConfig } from "./BaseFieldConfig";
|
|
2
3
|
/**
|
|
3
4
|
* Configuration for a checkbox field.
|
|
@@ -87,7 +88,7 @@ export interface NumberFieldConfig extends BaseFieldConfig {
|
|
|
87
88
|
* `maximumFractionDigits`, `style`, `currency`, etc. Use this to control how the number
|
|
88
89
|
* is displayed to the user (e.g., as currency, percentage, or with specific decimal places).
|
|
89
90
|
*/
|
|
90
|
-
readonly formatOptions?:
|
|
91
|
+
readonly formatOptions?: FormatNumberOptions;
|
|
91
92
|
/**
|
|
92
93
|
* Increment/decrement step size when using stepper buttons.
|
|
93
94
|
*
|
|
@@ -95,7 +95,7 @@ export interface DeclarativeFormTemplate {
|
|
|
95
95
|
* component. Use this when you need advanced form layouts, custom validation logic, or
|
|
96
96
|
* specialized UI components that cannot be expressed through declarative field configurations.
|
|
97
97
|
*
|
|
98
|
-
* The render function should use {@link
|
|
98
|
+
* The render function should use {@link CustomFormContext} to read and update feature properties.
|
|
99
99
|
*
|
|
100
100
|
* @group Model
|
|
101
101
|
*/
|
|
@@ -113,7 +113,7 @@ export interface DynamicFormTemplate {
|
|
|
113
113
|
* Function that renders the custom form content.
|
|
114
114
|
*
|
|
115
115
|
* Should return a React element representing the form UI. The function can use React hooks
|
|
116
|
-
* and {@link
|
|
116
|
+
* and {@link CustomFormContext} to interact with feature properties.
|
|
117
117
|
*/
|
|
118
118
|
readonly renderForm: () => ReactNode;
|
|
119
119
|
}
|
package/i18n/de.yaml
CHANGED
|
@@ -3,6 +3,7 @@ messages:
|
|
|
3
3
|
editFeatureHeading: Geoobjekt bearbeiten
|
|
4
4
|
createFeatureHeading: Geoobjekt erstellen
|
|
5
5
|
selectButtonTitle: Auswählen
|
|
6
|
+
selectButtonActiveTitle: Auswählen beenden
|
|
6
7
|
finishButtonTooltip: Zeichnen beenden
|
|
7
8
|
resetButtonTooltip: Zeichnung zurücksetzen
|
|
8
9
|
undoButtonTooltip: Rückgängig machen
|
|
@@ -10,14 +11,26 @@ messages:
|
|
|
10
11
|
propertyEditor:
|
|
11
12
|
defaultEditHeading: Geoobjekt bearbeiten
|
|
12
13
|
defaultCreateHeading: Geoobjekt erstellen
|
|
14
|
+
requiredFieldHint: "Pflichtfeld"
|
|
13
15
|
saveButtonTitle: Speichern
|
|
14
16
|
cancelButtonTitle: Abbrechen
|
|
15
17
|
deleteButtonTooltip: Geoobjekt löschen
|
|
18
|
+
|
|
19
|
+
errors:
|
|
20
|
+
minAndMax: Der Wert muss zwischen {min} und {max} liegen.
|
|
21
|
+
minOnly: Der Wert muss mindestens {min} sein.
|
|
22
|
+
maxOnly: Der Wert darf höchstens {max} sein.
|
|
23
|
+
|
|
16
24
|
deleteConfirmationDialog:
|
|
17
25
|
title: Geoobjekt löschen
|
|
18
26
|
message: Sind Sie sicher, dass Sie dieses Geoobjekt löschen möchten?
|
|
19
27
|
deleteButtonTitle: Geoobjekt löschen
|
|
20
28
|
cancelButtonTitle: Abbrechen
|
|
29
|
+
cancelConfirmationDialog:
|
|
30
|
+
title: Änderungen verwerfen
|
|
31
|
+
message: Sind Sie sicher, dass Sie Ihre Änderungen verwerfen möchten?
|
|
32
|
+
confirmCancelButtonTitle: Änderungen verwerfen
|
|
33
|
+
abortCancelButtonTitle: Weiter bearbeiten
|
|
21
34
|
notifier:
|
|
22
35
|
creationSuccess: Geoobjekt erstellt
|
|
23
36
|
creationFailure: Das Geoobjekt konnte nicht erstellt werden.
|
package/i18n/en.yaml
CHANGED
|
@@ -3,6 +3,7 @@ messages:
|
|
|
3
3
|
editFeatureHeading: Edit Feature
|
|
4
4
|
createFeatureHeading: Create Feature
|
|
5
5
|
selectButtonTitle: Select
|
|
6
|
+
selectButtonActiveTitle: Cancel selection
|
|
6
7
|
finishButtonTooltip: Finish drawing
|
|
7
8
|
resetButtonTooltip: Reset drawing
|
|
8
9
|
undoButtonTooltip: Undo
|
|
@@ -10,14 +11,26 @@ messages:
|
|
|
10
11
|
propertyEditor:
|
|
11
12
|
defaultEditHeading: Edit Feature
|
|
12
13
|
defaultCreateHeading: Create Feature
|
|
14
|
+
requiredFieldHint: "Required field"
|
|
13
15
|
saveButtonTitle: Save
|
|
14
16
|
cancelButtonTitle: Cancel
|
|
15
|
-
deleteButtonTooltip: Delete
|
|
17
|
+
deleteButtonTooltip: Delete feature
|
|
18
|
+
|
|
19
|
+
errors:
|
|
20
|
+
minAndMax: The value must be between {min} and {max}.
|
|
21
|
+
minOnly: The value must be {min} or higher.
|
|
22
|
+
maxOnly: The value must be {max} or lower.
|
|
23
|
+
|
|
16
24
|
deleteConfirmationDialog:
|
|
17
25
|
title: Delete Feature
|
|
18
26
|
message: Are you sure you want to delete this feature?
|
|
19
|
-
deleteButtonTitle: Delete
|
|
27
|
+
deleteButtonTitle: Delete feature
|
|
20
28
|
cancelButtonTitle: Cancel
|
|
29
|
+
cancelConfirmationDialog:
|
|
30
|
+
title: Discard changes
|
|
31
|
+
message: Are you sure you want to discard your changes?
|
|
32
|
+
confirmCancelButtonTitle: Discard changes
|
|
33
|
+
abortCancelButtonTitle: Continue editing
|
|
21
34
|
notifier:
|
|
22
35
|
creationSuccess: Feature created
|
|
23
36
|
creationFailure: The feature failed to be created.
|
|
@@ -2,12 +2,10 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import { Box } from '@chakra-ui/react';
|
|
3
3
|
import { useMapModelValue } from '@open-pioneer/map';
|
|
4
4
|
import { useCommonComponentProps } from '@open-pioneer/react-utils';
|
|
5
|
-
import { useMemo } from 'react';
|
|
6
5
|
import { useIntl } from '../_virtual/hooks.js';
|
|
6
|
+
import { useMemo } from 'react';
|
|
7
7
|
import { ActionSelector } from './components/action-selector/ActionSelector.js';
|
|
8
|
-
import { PropertyForm } from './components/property-editor/PropertyForm.js';
|
|
9
8
|
import { PropertyEditor } from './components/property-editor/PropertyEditor.js';
|
|
10
|
-
import { PropertyFormContextProvider } from './context/PropertyFormContextProvider.js';
|
|
11
9
|
import { useEditingStep, useSnappingSources, useOnActionChange } from './editor/editorHooks.js';
|
|
12
10
|
import { useEditingCallbacks } from './editor/useEditingCallbacks.js';
|
|
13
11
|
import { useGeometryEditing } from './geometry-editing/useGeometryEditing.js';
|
|
@@ -89,13 +87,15 @@ function FeatureEditor(props) {
|
|
|
89
87
|
break;
|
|
90
88
|
case "creation":
|
|
91
89
|
case "update":
|
|
92
|
-
content = /* @__PURE__ */ jsx(
|
|
93
|
-
|
|
90
|
+
content = /* @__PURE__ */ jsx(
|
|
91
|
+
PropertyEditor,
|
|
94
92
|
{
|
|
93
|
+
editingStep,
|
|
94
|
+
callbacks: editingCallbacks,
|
|
95
95
|
templates,
|
|
96
96
|
resolveFormTemplate
|
|
97
97
|
}
|
|
98
|
-
)
|
|
98
|
+
);
|
|
99
99
|
break;
|
|
100
100
|
}
|
|
101
101
|
return /* @__PURE__ */ jsx(Box, { ...containerProps, h: "full", children: content });
|
|
@@ -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\nimport { Box } from \"@chakra-ui/react\";\nimport { useMapModelValue } from \"@open-pioneer/map\";\nimport { useCommonComponentProps } from \"@open-pioneer/react-utils\";\nimport
|
|
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\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\n"],"names":[],"mappings":"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"}
|
|
@@ -16,7 +16,7 @@ function SelectButton({ isActive, onClick }) {
|
|
|
16
16
|
onClick,
|
|
17
17
|
children: [
|
|
18
18
|
/* @__PURE__ */ jsx(LuMousePointerClick, { "aria-hidden": "true" }),
|
|
19
|
-
formatMessage({ id: "actionSelector.selectButtonTitle" })
|
|
19
|
+
isActive ? formatMessage({ id: "actionSelector.selectButtonActiveTitle" }) : formatMessage({ id: "actionSelector.selectButtonTitle" })
|
|
20
20
|
]
|
|
21
21
|
}
|
|
22
22
|
) });
|
|
@@ -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\nimport { Button, Toggle } from \"@chakra-ui/react\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { LuMousePointerClick } from \"react-icons/lu\";\nimport type { ReactElement } from \"react\";\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 {formatMessage({ id: \"actionSelector.selectButtonTitle\" })}\n </Button>\n </Toggle.Root>\n );\n}\n"],"names":[],"mappings":";;;;;AAYO,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,aAAA,CAAc,EAAE,EAAA,EAAI,kCAAA,EAAoC;AAAA;AAAA;AAAA,
|
|
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\nimport { Button, Toggle } from \"@chakra-ui/react\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { LuMousePointerClick } from \"react-icons/lu\";\nimport type { ReactElement } from \"react\";\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":";;;;;AAYO,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;;;;"}
|
|
@@ -32,6 +32,7 @@ function NumberFieldControl({
|
|
|
32
32
|
step: field.step,
|
|
33
33
|
formatOptions: field.formatOptions,
|
|
34
34
|
onValueChange,
|
|
35
|
+
clampValueOnBlur: false,
|
|
35
36
|
children: [
|
|
36
37
|
/* @__PURE__ */ jsx(NumberInput.Input, { ref: element, placeholder: field.placeholder ?? field.label }),
|
|
37
38
|
field.showSteppers && /* @__PURE__ */ jsx(NumberInput.Control, {})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberFieldControl.js","sources":["NumberFieldControl.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { NumberInput, type NumberInputValueChangeDetails } from \"@chakra-ui/react\";\nimport { useEvent } from \"@open-pioneer/react-utils\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { useCallback, useEffect, useRef, useState, type ReactElement } from \"react\";\nimport type { NumberFieldConfig } from \"../../../api/fields/standardFieldConfigs\";\n\ninterface NumberFieldControlProps {\n readonly value: number | undefined;\n readonly field: NumberFieldConfig;\n readonly onChange: (newNumber: number | undefined) => void;\n}\n\n// This component allows its value to be specified as a number. The value is still stored as a\n// string internally to allow non-numeric characters (such as '-', '.', or 'e') to be entered.\nexport function NumberFieldControl({\n value,\n field,\n onChange\n}: NumberFieldControlProps): ReactElement {\n const formatNumber = useFormatNumber(field.formatOptions);\n const [stringValue, setStringValue] = useState(() => formatNumber(value));\n const setNumericValue = useNumericState(value, field.formatOptions, setStringValue);\n\n const onValueChange = useEvent(({ value, valueAsNumber }: NumberInputValueChangeDetails) => {\n setStringValue(value);\n if (!isNaN(valueAsNumber)) {\n setNumericValue(valueAsNumber);\n onChange?.(valueAsNumber);\n } else if (value === \"\") {\n setNumericValue(undefined);\n onChange?.(undefined);\n }\n });\n\n const element = useRef<HTMLInputElement>(null);\n\n return (\n <NumberInput.Root\n value={stringValue ?? \"\"}\n min={field.min}\n max={field.max}\n step={field.step}\n formatOptions={field.formatOptions}\n onValueChange={onValueChange}\n >\n <NumberInput.Input ref={element} placeholder={field.placeholder ?? field.label} />\n {field.showSteppers && <NumberInput.Control />}\n </NumberInput.Root>\n );\n}\n\n// Additionally store the state as a number to be able to react to outside changes of 'value'.\nfunction useNumericState(\n value: number | undefined,\n formatOptions:
|
|
1
|
+
{"version":3,"file":"NumberFieldControl.js","sources":["NumberFieldControl.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { NumberInput, type NumberInputValueChangeDetails } from \"@chakra-ui/react\";\nimport { type FormatNumberOptions } from \"@formatjs/intl\";\nimport { useEvent } from \"@open-pioneer/react-utils\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { useCallback, useEffect, useRef, useState, type ReactElement } from \"react\";\nimport type { NumberFieldConfig } from \"../../../api/fields/standardFieldConfigs\";\n\ninterface NumberFieldControlProps {\n readonly value: number | undefined;\n readonly field: NumberFieldConfig;\n readonly onChange: (newNumber: number | undefined) => void;\n}\n\n// This component allows its value to be specified as a number. The value is still stored as a\n// string internally to allow non-numeric characters (such as '-', '.', or 'e') to be entered.\nexport function NumberFieldControl({\n value,\n field,\n onChange\n}: NumberFieldControlProps): ReactElement {\n const formatNumber = useFormatNumber(field.formatOptions);\n const [stringValue, setStringValue] = useState(() => formatNumber(value));\n const setNumericValue = useNumericState(value, field.formatOptions, setStringValue);\n\n const onValueChange = useEvent(({ value, valueAsNumber }: NumberInputValueChangeDetails) => {\n setStringValue(value);\n if (!isNaN(valueAsNumber)) {\n setNumericValue(valueAsNumber);\n onChange?.(valueAsNumber);\n } else if (value === \"\") {\n setNumericValue(undefined);\n onChange?.(undefined);\n }\n });\n\n const element = useRef<HTMLInputElement>(null);\n\n return (\n <NumberInput.Root\n value={stringValue ?? \"\"}\n min={field.min}\n max={field.max}\n step={field.step}\n formatOptions={field.formatOptions}\n onValueChange={onValueChange}\n clampValueOnBlur={false}\n >\n <NumberInput.Input ref={element} placeholder={field.placeholder ?? field.label} />\n {field.showSteppers && <NumberInput.Control />}\n </NumberInput.Root>\n );\n}\n\n// Additionally store the state as a number to be able to react to outside changes of 'value'.\nfunction useNumericState(\n value: number | undefined,\n formatOptions: FormatNumberOptions | undefined,\n setStringValue: (newValue: string | undefined) => void\n) {\n const [numericValue, setNumericValue] = useState(value);\n const formatNumber = useFormatNumber(formatOptions);\n\n useEffect(() => {\n if (value !== numericValue) {\n const stringValue = formatNumber(value);\n setStringValue(stringValue);\n setNumericValue(value);\n }\n }, [formatNumber, numericValue, setStringValue, value]);\n\n return setNumericValue;\n}\n\nfunction useFormatNumber(formatOptions: FormatNumberOptions | undefined) {\n const { formatNumber } = useIntl();\n\n return useCallback(\n (number: number | undefined) => {\n if (number != null) {\n return formatNumber(number, { maximumFractionDigits: 20, ...formatOptions });\n } else {\n return undefined;\n }\n },\n [formatNumber, formatOptions]\n );\n}\n"],"names":["value"],"mappings":";;;;;;AAiBO,SAAS,kBAAA,CAAmB;AAAA,EAC/B,KAAA;AAAA,EACA,KAAA;AAAA,EACA;AACJ,CAAA,EAA0C;AACtC,EAAA,MAAM,YAAA,GAAe,eAAA,CAAgB,KAAA,CAAM,aAAa,CAAA;AACxD,EAAA,MAAM,CAAC,aAAa,cAAc,CAAA,GAAI,SAAS,MAAM,YAAA,CAAa,KAAK,CAAC,CAAA;AACxE,EAAA,MAAM,eAAA,GAAkB,eAAA,CAAgB,KAAA,EAAO,KAAA,CAAM,eAAe,cAAc,CAAA;AAElF,EAAA,MAAM,gBAAgB,QAAA,CAAS,CAAC,EAAE,KAAA,EAAAA,MAAAA,EAAO,eAAc,KAAqC;AACxF,IAAA,cAAA,CAAeA,MAAK,CAAA;AACpB,IAAA,IAAI,CAAC,KAAA,CAAM,aAAa,CAAA,EAAG;AACvB,MAAA,eAAA,CAAgB,aAAa,CAAA;AAC7B,MAAA,QAAA,GAAW,aAAa,CAAA;AAAA,IAC5B,CAAA,MAAA,IAAWA,WAAU,EAAA,EAAI;AACrB,MAAA,eAAA,CAAgB,MAAS,CAAA;AACzB,MAAA,QAAA,GAAW,MAAS,CAAA;AAAA,IACxB;AAAA,EACJ,CAAC,CAAA;AAED,EAAA,MAAM,OAAA,GAAU,OAAyB,IAAI,CAAA;AAE7C,EAAA,uBACI,IAAA;AAAA,IAAC,WAAA,CAAY,IAAA;AAAA,IAAZ;AAAA,MACG,OAAO,WAAA,IAAe,EAAA;AAAA,MACtB,KAAK,KAAA,CAAM,GAAA;AAAA,MACX,KAAK,KAAA,CAAM,GAAA;AAAA,MACX,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,eAAe,KAAA,CAAM,aAAA;AAAA,MACrB,aAAA;AAAA,MACA,gBAAA,EAAkB,KAAA;AAAA,MAElB,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,WAAA,CAAY,OAAZ,EAAkB,GAAA,EAAK,SAAS,WAAA,EAAa,KAAA,CAAM,WAAA,IAAe,KAAA,CAAM,KAAA,EAAO,CAAA;AAAA,QAC/E,KAAA,CAAM,YAAA,oBAAgB,GAAA,CAAC,WAAA,CAAY,SAAZ,EAAoB;AAAA;AAAA;AAAA,GAChD;AAER;AAGA,SAAS,eAAA,CACL,KAAA,EACA,aAAA,EACA,cAAA,EACF;AACE,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAS,KAAK,CAAA;AACtD,EAAA,MAAM,YAAA,GAAe,gBAAgB,aAAa,CAAA;AAElD,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,IAAI,UAAU,YAAA,EAAc;AACxB,MAAA,MAAM,WAAA,GAAc,aAAa,KAAK,CAAA;AACtC,MAAA,cAAA,CAAe,WAAW,CAAA;AAC1B,MAAA,eAAA,CAAgB,KAAK,CAAA;AAAA,IACzB;AAAA,EACJ,GAAG,CAAC,YAAA,EAAc,YAAA,EAAc,cAAA,EAAgB,KAAK,CAAC,CAAA;AAEtD,EAAA,OAAO,eAAA;AACX;AAEA,SAAS,gBAAgB,aAAA,EAAgD;AACrE,EAAA,MAAM,EAAE,YAAA,EAAa,GAAI,OAAA,EAAQ;AAEjC,EAAA,OAAO,WAAA;AAAA,IACH,CAAC,MAAA,KAA+B;AAC5B,MAAA,IAAI,UAAU,IAAA,EAAM;AAChB,QAAA,OAAO,aAAa,MAAA,EAAQ,EAAE,uBAAuB,EAAA,EAAI,GAAG,eAAe,CAAA;AAAA,MAC/E,CAAA,MAAO;AACH,QAAA,OAAO,MAAA;AAAA,MACX;AAAA,IACJ,CAAA;AAAA,IACA,CAAC,cAAc,aAAa;AAAA,GAChC;AACJ;;;;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ReactElement } from "react";
|
|
2
|
+
interface CancelConfirmationDialogProps {
|
|
3
|
+
readonly isOpen: boolean;
|
|
4
|
+
readonly onConfirmCancel: () => void;
|
|
5
|
+
readonly onAbortCancel: () => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function CancelConfirmationDialog({ isOpen, onConfirmCancel, onAbortCancel }: CancelConfirmationDialogProps): ReactElement;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Dialog, Button } from '@chakra-ui/react';
|
|
3
|
+
import { useIntl } from '../../../_virtual/hooks.js';
|
|
4
|
+
import { useRef, useCallback, useMemo } from 'react';
|
|
5
|
+
|
|
6
|
+
function CancelConfirmationDialog({
|
|
7
|
+
isOpen,
|
|
8
|
+
onConfirmCancel,
|
|
9
|
+
onAbortCancel
|
|
10
|
+
}) {
|
|
11
|
+
const cancelButtonRef = useRef(null);
|
|
12
|
+
const initialFocusEl = useCallback(() => cancelButtonRef.current, []);
|
|
13
|
+
const { formatMessage } = useIntl();
|
|
14
|
+
const { title, message, confirmCancelButtonTitle, abortCancelButtonTitle } = useMemo(
|
|
15
|
+
() => ({
|
|
16
|
+
title: formatMessage({ id: "cancelConfirmationDialog.title" }),
|
|
17
|
+
message: formatMessage({ id: "cancelConfirmationDialog.message" }),
|
|
18
|
+
confirmCancelButtonTitle: formatMessage({
|
|
19
|
+
id: "cancelConfirmationDialog.confirmCancelButtonTitle"
|
|
20
|
+
}),
|
|
21
|
+
abortCancelButtonTitle: formatMessage({
|
|
22
|
+
id: "cancelConfirmationDialog.abortCancelButtonTitle"
|
|
23
|
+
})
|
|
24
|
+
}),
|
|
25
|
+
[formatMessage]
|
|
26
|
+
);
|
|
27
|
+
return /* @__PURE__ */ jsxs(Dialog.Root, { open: isOpen, initialFocusEl, role: "alertdialog", children: [
|
|
28
|
+
/* @__PURE__ */ jsx(Dialog.Backdrop, {}),
|
|
29
|
+
/* @__PURE__ */ jsx(Dialog.Positioner, { children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
|
|
30
|
+
/* @__PURE__ */ jsx(Dialog.Header, { fontSize: "large", fontWeight: "bold", children: title }),
|
|
31
|
+
/* @__PURE__ */ jsx(Dialog.Body, { children: message }),
|
|
32
|
+
/* @__PURE__ */ jsxs(Dialog.Footer, { children: [
|
|
33
|
+
/* @__PURE__ */ jsx(
|
|
34
|
+
Button,
|
|
35
|
+
{
|
|
36
|
+
bg: "red.solid",
|
|
37
|
+
color: "red.contrast",
|
|
38
|
+
_hover: { bg: "red.solid/90" },
|
|
39
|
+
loadingText: confirmCancelButtonTitle,
|
|
40
|
+
onClick: onConfirmCancel,
|
|
41
|
+
children: confirmCancelButtonTitle
|
|
42
|
+
}
|
|
43
|
+
),
|
|
44
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", ref: cancelButtonRef, onClick: onAbortCancel, children: abortCancelButtonTitle })
|
|
45
|
+
] })
|
|
46
|
+
] }) })
|
|
47
|
+
] });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { CancelConfirmationDialog };
|
|
51
|
+
//# sourceMappingURL=CancelConfirmationDialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CancelConfirmationDialog.js","sources":["CancelConfirmationDialog.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { Button, Dialog } from \"@chakra-ui/react\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { useCallback, useMemo, useRef, type ReactElement } from \"react\";\n\ninterface CancelConfirmationDialogProps {\n readonly isOpen: boolean;\n readonly onConfirmCancel: () => void;\n readonly onAbortCancel: () => void;\n}\n\nexport function CancelConfirmationDialog({\n isOpen,\n onConfirmCancel,\n onAbortCancel\n}: CancelConfirmationDialogProps): ReactElement {\n const cancelButtonRef = useRef<HTMLButtonElement>(null);\n const initialFocusEl = useCallback(() => cancelButtonRef.current, []);\n const { formatMessage } = useIntl();\n\n const { title, message, confirmCancelButtonTitle, abortCancelButtonTitle } = useMemo(\n () => ({\n title: formatMessage({ id: \"cancelConfirmationDialog.title\" }),\n message: formatMessage({ id: \"cancelConfirmationDialog.message\" }),\n confirmCancelButtonTitle: formatMessage({\n id: \"cancelConfirmationDialog.confirmCancelButtonTitle\"\n }),\n abortCancelButtonTitle: formatMessage({\n id: \"cancelConfirmationDialog.abortCancelButtonTitle\"\n })\n }),\n [formatMessage]\n );\n\n return (\n <Dialog.Root open={isOpen} initialFocusEl={initialFocusEl} role=\"alertdialog\">\n <Dialog.Backdrop />\n <Dialog.Positioner>\n <Dialog.Content>\n <Dialog.Header fontSize=\"large\" fontWeight=\"bold\">\n {title}\n </Dialog.Header>\n <Dialog.Body>{message}</Dialog.Body>\n <Dialog.Footer>\n <Button\n bg=\"red.solid\"\n color=\"red.contrast\"\n _hover={{ bg: \"red.solid/90\" }}\n loadingText={confirmCancelButtonTitle}\n onClick={onConfirmCancel}\n >\n {confirmCancelButtonTitle}\n </Button>\n <Button variant=\"outline\" ref={cancelButtonRef} onClick={onAbortCancel}>\n {abortCancelButtonTitle}\n </Button>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog.Positioner>\n </Dialog.Root>\n );\n}\n"],"names":[],"mappings":";;;;;AAYO,SAAS,wBAAA,CAAyB;AAAA,EACrC,MAAA;AAAA,EACA,eAAA;AAAA,EACA;AACJ,CAAA,EAAgD;AAC5C,EAAA,MAAM,eAAA,GAAkB,OAA0B,IAAI,CAAA;AACtD,EAAA,MAAM,iBAAiB,WAAA,CAAY,MAAM,eAAA,CAAgB,OAAA,EAAS,EAAE,CAAA;AACpE,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,OAAA,EAAQ;AAElC,EAAA,MAAM,EAAE,KAAA,EAAO,OAAA,EAAS,wBAAA,EAA0B,wBAAuB,GAAI,OAAA;AAAA,IACzE,OAAO;AAAA,MACH,KAAA,EAAO,aAAA,CAAc,EAAE,EAAA,EAAI,kCAAkC,CAAA;AAAA,MAC7D,OAAA,EAAS,aAAA,CAAc,EAAE,EAAA,EAAI,oCAAoC,CAAA;AAAA,MACjE,0BAA0B,aAAA,CAAc;AAAA,QACpC,EAAA,EAAI;AAAA,OACP,CAAA;AAAA,MACD,wBAAwB,aAAA,CAAc;AAAA,QAClC,EAAA,EAAI;AAAA,OACP;AAAA,KACL,CAAA;AAAA,IACA,CAAC,aAAa;AAAA,GAClB;AAEA,EAAA,uBACI,IAAA,CAAC,OAAO,IAAA,EAAP,EAAY,MAAM,MAAA,EAAQ,cAAA,EAAgC,MAAK,aAAA,EAC5D,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,MAAA,CAAO,UAAP,EAAgB,CAAA;AAAA,wBAChB,MAAA,CAAO,UAAA,EAAP,EACG,QAAA,kBAAA,IAAA,CAAC,MAAA,CAAO,SAAP,EACG,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,OAAO,MAAA,EAAP,EAAc,UAAS,OAAA,EAAQ,UAAA,EAAW,QACtC,QAAA,EAAA,KAAA,EACL,CAAA;AAAA,sBACA,GAAA,CAAC,MAAA,CAAO,IAAA,EAAP,EAAa,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,sBACtB,IAAA,CAAC,MAAA,CAAO,MAAA,EAAP,EACG,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACG,EAAA,EAAG,WAAA;AAAA,YACH,KAAA,EAAM,cAAA;AAAA,YACN,MAAA,EAAQ,EAAE,EAAA,EAAI,cAAA,EAAe;AAAA,YAC7B,WAAA,EAAa,wBAAA;AAAA,YACb,OAAA,EAAS,eAAA;AAAA,YAER,QAAA,EAAA;AAAA;AAAA,SACL;AAAA,wBACA,GAAA,CAAC,UAAO,OAAA,EAAQ,SAAA,EAAU,KAAK,eAAA,EAAiB,OAAA,EAAS,eACpD,QAAA,EAAA,sBAAA,EACL;AAAA,OAAA,EACJ;AAAA,KAAA,EACJ,CAAA,EACJ;AAAA,GAAA,EACJ,CAAA;AAER;;;;"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
}
|
|
5
|
-
export declare function PropertyEditor(
|
|
1
|
+
import { FeatureEditorProps } from "../../../api/editor/editor";
|
|
2
|
+
import { CreationStep, UpdateStep } from "../../../api/model/EditingStep";
|
|
3
|
+
import { FeatureTemplate } from "../../../api/model/FeatureTemplate";
|
|
4
|
+
import { EditingCallbacks } from "../../editor/useEditingCallbacks";
|
|
5
|
+
export declare function PropertyEditor(props: {
|
|
6
|
+
editingStep: CreationStep | UpdateStep;
|
|
7
|
+
callbacks: EditingCallbacks;
|
|
8
|
+
templates: FeatureTemplate[];
|
|
9
|
+
resolveFormTemplate: FeatureEditorProps["resolveFormTemplate"];
|
|
10
|
+
}): import("react/jsx-runtime").JSX.Element | undefined;
|
|
@@ -1,15 +1,84 @@
|
|
|
1
|
-
import { jsxs, Fragment
|
|
2
|
-
import {
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { Flex, useDisclosure } from '@chakra-ui/react';
|
|
3
3
|
import { useEvent } from '@open-pioneer/react-utils';
|
|
4
4
|
import { useReactiveSnapshot } from '@open-pioneer/reactivity';
|
|
5
|
-
import {
|
|
5
|
+
import { useMemo, useCallback } from 'react';
|
|
6
6
|
import { ButtonRow } from './ButtonRow.js';
|
|
7
|
+
import { CancelConfirmationDialog } from './CancelConfirmationDialog.js';
|
|
7
8
|
import { DeleteConfirmationDialog } from './DeleteConfirmationDialog.js';
|
|
9
|
+
import { DeclarativeFormContext, CustomFormContextImpl, FormContext } from '../../context/PropertyFormContext.js';
|
|
10
|
+
import { PropertyField } from './PropertyField.js';
|
|
11
|
+
import { PropertyForm } from './PropertyForm.js';
|
|
12
|
+
import { usePropertyFormContext } from '../../context/usePropertyFormContext.js';
|
|
8
13
|
|
|
9
|
-
function PropertyEditor(
|
|
14
|
+
function PropertyEditor(props) {
|
|
15
|
+
const { editingStep, callbacks, templates, resolveFormTemplate } = props;
|
|
16
|
+
const formTemplate = useFormTemplate(templates, resolveFormTemplate, editingStep);
|
|
17
|
+
const context = useMemo(() => {
|
|
18
|
+
if (!formTemplate) {
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
if (formTemplate.kind === "declarative") {
|
|
22
|
+
return new DeclarativeFormContext(editingStep, callbacks, formTemplate);
|
|
23
|
+
} else {
|
|
24
|
+
return new CustomFormContextImpl(editingStep, callbacks, formTemplate);
|
|
25
|
+
}
|
|
26
|
+
}, [formTemplate, editingStep, callbacks]);
|
|
27
|
+
return context && formTemplate && /* @__PURE__ */ jsx(FormContext, { value: context, children: /* @__PURE__ */ jsxs(
|
|
28
|
+
Flex,
|
|
29
|
+
{
|
|
30
|
+
className: "editor__property-editor",
|
|
31
|
+
direction: "column",
|
|
32
|
+
height: "full",
|
|
33
|
+
overflowY: "hidden",
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ jsx(PropertyForm, { children: formTemplate.kind === "dynamic" ? formTemplate.renderForm() : formTemplate.fields.map((field, index) => /* @__PURE__ */ jsx(PropertyField, { field }, index)) }),
|
|
36
|
+
/* @__PURE__ */ jsx(EditorControls, {})
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
) });
|
|
40
|
+
}
|
|
41
|
+
function useFormTemplate(templates, customResolver, editingStep) {
|
|
42
|
+
const defaultResolver = useDefaultFormTemplateResolver(templates);
|
|
43
|
+
const resolveFormTemplate = customResolver ?? defaultResolver;
|
|
44
|
+
const feature = editingStep.feature;
|
|
45
|
+
const layer = editingStep.id === "update" ? editingStep.layer : void 0;
|
|
46
|
+
const explicitTemplate = editingStep.id === "creation" ? editingStep.template : void 0;
|
|
47
|
+
return useMemo(() => {
|
|
48
|
+
if (explicitTemplate) {
|
|
49
|
+
return explicitTemplate;
|
|
50
|
+
} else if (editingStep.id === "update") {
|
|
51
|
+
return resolveFormTemplate({ feature, layer });
|
|
52
|
+
} else {
|
|
53
|
+
return void 0;
|
|
54
|
+
}
|
|
55
|
+
}, [explicitTemplate, editingStep.id, feature, layer, resolveFormTemplate]);
|
|
56
|
+
}
|
|
57
|
+
function useDefaultFormTemplateResolver(templates) {
|
|
58
|
+
return useCallback(
|
|
59
|
+
({ layer }) => {
|
|
60
|
+
if (layer?.id != null) {
|
|
61
|
+
return templates.find(({ layerId }) => layer.id === layerId);
|
|
62
|
+
} else {
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
[templates]
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function EditorControls() {
|
|
10
70
|
const context = usePropertyFormContext();
|
|
11
71
|
const canSave = useReactiveSnapshot(() => context.isValid, [context]);
|
|
12
|
-
const {
|
|
72
|
+
const {
|
|
73
|
+
open: deleteDialogIsOpen,
|
|
74
|
+
onOpen: openDeleteDialog,
|
|
75
|
+
onClose: closeDeleteDialog
|
|
76
|
+
} = useDisclosure();
|
|
77
|
+
const {
|
|
78
|
+
open: cancelDialogIsOpen,
|
|
79
|
+
onOpen: openCancelDialog,
|
|
80
|
+
onClose: closeCancelConfirmationDialog
|
|
81
|
+
} = useDisclosure();
|
|
13
82
|
const onSaveClick = useEvent(async () => {
|
|
14
83
|
const properties = context.getPropertiesAsObject();
|
|
15
84
|
context.feature.setProperties(properties);
|
|
@@ -17,28 +86,37 @@ function PropertyEditor({ children }) {
|
|
|
17
86
|
});
|
|
18
87
|
const onDeleteClick = useEvent(async () => {
|
|
19
88
|
await context.callbacks.onDelete();
|
|
20
|
-
|
|
89
|
+
closeDeleteDialog();
|
|
90
|
+
});
|
|
91
|
+
const onConfirmCancelClick = useEvent(() => {
|
|
92
|
+
context.callbacks.onCancel();
|
|
93
|
+
closeCancelConfirmationDialog();
|
|
21
94
|
});
|
|
22
95
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
23
|
-
/* @__PURE__ */
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
)
|
|
35
|
-
] }),
|
|
96
|
+
/* @__PURE__ */ jsx(
|
|
97
|
+
ButtonRow,
|
|
98
|
+
{
|
|
99
|
+
canSave,
|
|
100
|
+
showDeleteButton: context.mode === "update",
|
|
101
|
+
onSave: onSaveClick,
|
|
102
|
+
onDelete: openDeleteDialog,
|
|
103
|
+
onCancel: openCancelDialog
|
|
104
|
+
}
|
|
105
|
+
),
|
|
36
106
|
/* @__PURE__ */ jsx(
|
|
37
107
|
DeleteConfirmationDialog,
|
|
38
108
|
{
|
|
39
|
-
isOpen:
|
|
109
|
+
isOpen: deleteDialogIsOpen,
|
|
40
110
|
onDelete: onDeleteClick,
|
|
41
|
-
onCancel:
|
|
111
|
+
onCancel: closeDeleteDialog
|
|
112
|
+
}
|
|
113
|
+
),
|
|
114
|
+
/* @__PURE__ */ jsx(
|
|
115
|
+
CancelConfirmationDialog,
|
|
116
|
+
{
|
|
117
|
+
isOpen: cancelDialogIsOpen,
|
|
118
|
+
onConfirmCancel: onConfirmCancelClick,
|
|
119
|
+
onAbortCancel: closeCancelConfirmationDialog
|
|
42
120
|
}
|
|
43
121
|
)
|
|
44
122
|
] });
|