@opengeoweb/form-fields 14.0.0 → 14.1.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/index.esm.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { FormControl, FormHelperText, InputLabel, Select, RadioGroup, TextField, InputAdornment } from '@mui/material';
3
- import { useFormContext, useController, useForm, FormProvider } from 'react-hook-form';
3
+ import { useFormContext, useController, useForm, FormProvider, Controller } from 'react-hook-form';
4
4
  import { isEqual } from 'lodash';
5
5
  import { dateUtils } from '@opengeoweb/shared';
6
6
  import 'i18next';
7
7
  import { useTranslation } from 'react-i18next';
8
- import React from 'react';
8
+ import React, { useState } from 'react';
9
9
  import { DateTimePicker } from '@mui/x-date-pickers';
10
10
  import { CalendarToday } from '@opengeoweb/theme';
11
11
  import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
@@ -940,6 +940,131 @@ var ReactHookFormHiddenInput = function ReactHookFormHiddenInput(_ref) {
940
940
  }, props)) : null;
941
941
  };
942
942
 
943
+ function decimalToDegreesMinutes(value) {
944
+ var sign = value < 0 ? '-' : '';
945
+ var abs = Math.abs(value);
946
+ var degrees = Math.floor(abs);
947
+ var minutes = Math.round((abs - degrees) * 60);
948
+ // normalize rounding overflow (e.g. 59.9 min → +1 degree)
949
+ if (minutes === 60) {
950
+ abs += 1;
951
+ return decimalToDegreesMinutes(sign === '-' ? -abs : abs);
952
+ }
953
+ return "" + sign + degrees + "\xB0" + minutes.toString().padStart(2, '0') + "\u2032";
954
+ }
955
+ function degreesMinutesToDecimal(input) {
956
+ var raw = input.toString().trim();
957
+ var negative = raw.startsWith('-');
958
+ var digits = raw.replace(/[^0-9]/g, '');
959
+ var sign = negative ? -1 : 1;
960
+ if (digits.length <= 2) {
961
+ var _deg = parseInt(digits || '0', 10);
962
+ return sign * _deg;
963
+ }
964
+ // split into degrees + minutes
965
+ var deg = parseInt(digits.slice(0, digits.length - 2), 10);
966
+ var min = parseInt(digits.slice(-2), 10);
967
+ // normalize minutes overflow (e.g. 65 → 1°05′)
968
+ var extraDeg = Math.floor(min / 60);
969
+ min %= 60;
970
+ return sign * (deg + extraDeg + min / 60);
971
+ }
972
+ var DegreesMinutesField = function DegreesMinutesField(_ref) {
973
+ var name = _ref.name,
974
+ label = _ref.label,
975
+ _ref$disabled = _ref.disabled,
976
+ disabled = _ref$disabled === void 0 ? false : _ref$disabled,
977
+ _ref$isReadOnly = _ref.isReadOnly,
978
+ isReadOnly = _ref$isReadOnly === void 0 ? false : _ref$isReadOnly,
979
+ _ref$size = _ref.size,
980
+ size = _ref$size === void 0 ? 'small' : _ref$size,
981
+ _ref$isLongitude = _ref.isLongitude,
982
+ isLongitude = _ref$isLongitude === void 0 ? false : _ref$isLongitude,
983
+ t = _ref.t;
984
+ var _useFormContext = useFormContext(),
985
+ control = _useFormContext.control;
986
+ var _useState = useState(null),
987
+ localInput = _useState[0],
988
+ setLocalInput = _useState[1];
989
+ var _useState2 = useState(false),
990
+ isEditing = _useState2[0],
991
+ setIsEditing = _useState2[1];
992
+ return jsx(Controller, {
993
+ control: control,
994
+ name: name,
995
+ render: function render(_ref2) {
996
+ var _fieldState$error;
997
+ var field = _ref2.field,
998
+ fieldState = _ref2.fieldState;
999
+ var value = field.value,
1000
+ onChange = field.onChange;
1001
+ var handleChange = function handleChange(e) {
1002
+ setLocalInput(e.target.value);
1003
+ setIsEditing(true);
1004
+ };
1005
+ var handleBlur = function handleBlur() {
1006
+ if (localInput != null && localInput !== '-' && localInput !== '') {
1007
+ var input = localInput.replace(/[^\d-]/g, '');
1008
+ var negative = input.startsWith('-');
1009
+ var digits = input.replace(/[^0-9]/g, '');
1010
+ // Latitude: 4 digits (DDMM), Longitude: 5 (DDDMM)
1011
+ var maxLen = isLongitude ? 5 : 4;
1012
+ if (digits.length > maxLen) {
1013
+ digits = digits.slice(0, maxLen);
1014
+ }
1015
+ var normalized = negative ? "-" + digits : digits;
1016
+ try {
1017
+ var decimal = degreesMinutesToDecimal(normalized);
1018
+ onChange(decimal);
1019
+ } catch (_unused) {
1020
+ // Invalid, do not update form value
1021
+ }
1022
+ }
1023
+ setIsEditing(false);
1024
+ setLocalInput(null);
1025
+ };
1026
+ var displayValue;
1027
+ if (isEditing) {
1028
+ displayValue = localInput != null ? localInput : '';
1029
+ } else if (value != null) {
1030
+ displayValue = decimalToDegreesMinutes(value);
1031
+ } else {
1032
+ displayValue = '';
1033
+ }
1034
+ return jsx(TextField, {
1035
+ disabled: disabled,
1036
+ error: !!fieldState.error,
1037
+ helperText: (_fieldState$error = fieldState.error) == null ? void 0 : _fieldState$error.message,
1038
+ label: label,
1039
+ onBlur: handleBlur,
1040
+ onChange: handleChange,
1041
+ placeholder: isLongitude ? 'DDD°MM′' : 'DD°MM′',
1042
+ size: size,
1043
+ slotProps: {
1044
+ input: {
1045
+ readOnly: isReadOnly
1046
+ }
1047
+ },
1048
+ value: displayValue
1049
+ });
1050
+ },
1051
+ rules: {
1052
+ validate: function validate(value) {
1053
+ if (value == null || isNaN(value)) {
1054
+ return true;
1055
+ } // let empty pass
1056
+ if (!isLongitude && (value < -90 || value > 90)) {
1057
+ return t('form-fields-error-latitude-minutes');
1058
+ }
1059
+ if (isLongitude && (value < -180 || value > 180)) {
1060
+ return t('form-fields-error-longitude-minutes');
1061
+ }
1062
+ return true;
1063
+ }
1064
+ }
1065
+ });
1066
+ };
1067
+
943
1068
  // hidden input helpers, should not be part of send formdata
944
1069
  var HIDDEN_INPUT_HELPER_IS_DRAFT = 'IS_DRAFT';
945
1070
  var useDraftFormHelpers = function useDraftFormHelpers(isDefaultDraft) {
@@ -983,4 +1108,4 @@ var useDraftFormHelpers = function useDraftFormHelpers(isDefaultDraft) {
983
1108
  };
984
1109
  };
985
1110
 
986
- export { FORM_FIELDS_NAMESPACE, HIDDEN_INPUT_HELPER_IS_DRAFT, ReactHookKeyboardDateTimePicker as ReactHookFormDateTime, ReactHookFormFormControl, ReactHookFormHiddenInput, ReactHookFormNumberField, ReactHookFormProviderWrapper as ReactHookFormProvider, ReactHookFormRadioGroup, ReactHookFormSelect, ReactHookFormTextField, countIntersectionPoints, defaultFormOptions, errorMessages, formFieldsTranslations, getDateValue, getDeepProperty, getFormattedValueForDatePicker, hasIntersectionWithFIR, hasMulitpleIntersections, hasValidGeometry, isEmpty, isGeometryDirty, isInteger, isLatitude, isLongitude, isMaximumOneDrawing, isNonOrBothCoordinates, isValidGeoJsonCoordinates, isValidMax, isValidMin, isXHoursAfter, isXHoursBefore, useDraftFormHelpers };
1111
+ export { DegreesMinutesField, FORM_FIELDS_NAMESPACE, HIDDEN_INPUT_HELPER_IS_DRAFT, ReactHookKeyboardDateTimePicker as ReactHookFormDateTime, ReactHookFormFormControl, ReactHookFormHiddenInput, ReactHookFormNumberField, ReactHookFormProviderWrapper as ReactHookFormProvider, ReactHookFormRadioGroup, ReactHookFormSelect, ReactHookFormTextField, countIntersectionPoints, defaultFormOptions, errorMessages, formFieldsTranslations, getDateValue, getDeepProperty, getFormattedValueForDatePicker, hasIntersectionWithFIR, hasMulitpleIntersections, hasValidGeometry, isEmpty, isGeometryDirty, isInteger, isLatitude, isLongitude, isMaximumOneDrawing, isNonOrBothCoordinates, isValidGeoJsonCoordinates, isValidMax, isValidMin, isXHoursAfter, isXHoursBefore, useDraftFormHelpers };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeoweb/form-fields",
3
- "version": "14.0.0",
3
+ "version": "14.1.0",
4
4
  "description": "GeoWeb form-fields library for the opengeoweb project",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -8,11 +8,11 @@
8
8
  "url": "git@gitlab.com:opengeoweb/opengeoweb.git"
9
9
  },
10
10
  "dependencies": {
11
- "@opengeoweb/theme": "14.0.0",
11
+ "@opengeoweb/theme": "14.1.0",
12
12
  "react-hook-form": "^7.50.1",
13
13
  "@mui/x-date-pickers": "^8.11.2",
14
14
  "lodash": "^4.17.21",
15
- "@opengeoweb/shared": "14.0.0",
15
+ "@opengeoweb/shared": "14.1.0",
16
16
  "react-i18next": "^15.1.1",
17
17
  "@mui/material": "^7.0.1",
18
18
  "i18next": "^25.0.1",
@@ -0,0 +1,15 @@
1
+ import type { TFunction } from 'i18next';
2
+ import React from 'react';
3
+ export declare function decimalToDegreesMinutes(value: number): string;
4
+ export declare function degreesMinutesToDecimal(input: string | number): number;
5
+ interface Props {
6
+ name: string;
7
+ label: string;
8
+ disabled?: boolean;
9
+ isReadOnly?: boolean;
10
+ size?: 'small' | 'medium';
11
+ isLongitude?: boolean;
12
+ t: TFunction;
13
+ }
14
+ export declare const DegreesMinutesField: React.FC<Props>;
15
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -7,6 +7,7 @@ export { default as ReactHookFormDateTime } from './ReactHookFormDateTime';
7
7
  export * from './ReactHookFormDateTime';
8
8
  export { default as ReactHookFormProvider } from './ReactHookFormProvider';
9
9
  export { default as ReactHookFormHiddenInput } from './ReactHookFormHiddenInput';
10
+ export { DegreesMinutesField } from './DegreesMinutesField';
10
11
  export { defaultFormOptions } from './ReactHookFormProvider';
11
12
  export { errorMessages, isMaximumOneDrawing, hasValidGeometry, isGeometryDirty, isValidGeoJsonCoordinates, hasIntersectionWithFIR, countIntersectionPoints, isValidMax, isValidMin, isInteger, hasMulitpleIntersections, isLatitude, isLongitude, isNonOrBothCoordinates, isEmpty, isXHoursAfter, isXHoursBefore, } from './utils';
12
13
  export { getDeepProperty } from './formUtils';