@byline/admin 3.20.4 → 3.21.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.
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
import type { CounterField, DecimalField, FieldComponentSlots, FloatField, IntegerField } from '@byline/core';
|
|
9
|
+
type NumericalValue = string | number | null;
|
|
9
10
|
export declare const NumericalField: ({ field, value, defaultValue, onChange, id, path, components, }: {
|
|
10
11
|
field: IntegerField | FloatField | DecimalField | CounterField;
|
|
11
12
|
value?: string | number | null;
|
|
12
13
|
defaultValue?: string | number | null;
|
|
13
|
-
onChange?: (value:
|
|
14
|
+
onChange?: (value: NumericalValue) => void;
|
|
14
15
|
id?: string;
|
|
15
16
|
path?: string;
|
|
16
17
|
/** Optional UI component slot overrides from the admin config. */
|
|
17
18
|
components?: FieldComponentSlots;
|
|
18
19
|
}) => import("react").JSX.Element;
|
|
20
|
+
export {};
|
|
@@ -1,13 +1,39 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { normalizeNumericValue } from "@byline/core";
|
|
2
4
|
import { Input } from "@byline/ui/react";
|
|
3
5
|
import { useFieldError, useFieldValue } from "../../forms/form-context.js";
|
|
6
|
+
const COMPLETE_NUMERIC_RE = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
4
7
|
const NumericalField = ({ field, value, defaultValue, onChange, id, path, components })=>{
|
|
5
8
|
const fieldPath = path ?? field.name;
|
|
6
9
|
const fieldError = useFieldError(fieldPath);
|
|
7
10
|
const fieldValue = useFieldValue(fieldPath);
|
|
8
|
-
const incomingValue = value
|
|
11
|
+
const incomingValue = void 0 !== value ? value : void 0 !== fieldValue ? fieldValue : defaultValue ?? null;
|
|
9
12
|
const htmlId = id ?? fieldPath;
|
|
10
|
-
const
|
|
13
|
+
const canonicalDisplay = null == incomingValue ? '' : String(incomingValue);
|
|
14
|
+
const [displayValue, setDisplayValue] = useState(canonicalDisplay);
|
|
15
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
16
|
+
useEffect(()=>{
|
|
17
|
+
if (!isEditing) setDisplayValue(canonicalDisplay);
|
|
18
|
+
}, [
|
|
19
|
+
canonicalDisplay,
|
|
20
|
+
isEditing
|
|
21
|
+
]);
|
|
22
|
+
const canonicalize = (nextValue)=>{
|
|
23
|
+
if (null == nextValue || 'string' == typeof nextValue && '' === nextValue.trim()) return null;
|
|
24
|
+
if ('counter' === field.type) return;
|
|
25
|
+
if ('string' == typeof nextValue && !COMPLETE_NUMERIC_RE.test(nextValue.trim())) return;
|
|
26
|
+
try {
|
|
27
|
+
return normalizeNumericValue(field.type, nextValue, fieldPath);
|
|
28
|
+
} catch {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const commit = (nextValue)=>{
|
|
33
|
+
const canonical = canonicalize(nextValue);
|
|
34
|
+
if (void 0 !== canonical) onChange?.(canonical);
|
|
35
|
+
return canonical;
|
|
36
|
+
};
|
|
11
37
|
const slots = components;
|
|
12
38
|
const CustomLabel = slots?.Label;
|
|
13
39
|
const CustomHelpText = slots?.HelpText;
|
|
@@ -35,12 +61,14 @@ const NumericalField = ({ field, value, defaultValue, onChange, id, path, compon
|
|
|
35
61
|
const renderInput = ()=>{
|
|
36
62
|
if (CustomField) return /*#__PURE__*/ jsx(CustomField, {
|
|
37
63
|
...slotBaseProps,
|
|
38
|
-
onChange: (
|
|
64
|
+
onChange: (nextValue)=>commit(nextValue),
|
|
39
65
|
defaultValue: defaultValue,
|
|
40
66
|
placeholder: field.placeholder
|
|
41
67
|
});
|
|
42
68
|
return /*#__PURE__*/ jsx(Input, {
|
|
43
|
-
type: "
|
|
69
|
+
type: "text",
|
|
70
|
+
inputMode: 'integer' === field.type || 'counter' === field.type ? 'numeric' : 'decimal',
|
|
71
|
+
step: 'integer' === field.type || 'counter' === field.type ? 1 : 'any',
|
|
44
72
|
id: htmlId,
|
|
45
73
|
name: field.name,
|
|
46
74
|
label: suppressInputLabel ? void 0 : field.label,
|
|
@@ -48,7 +76,16 @@ const NumericalField = ({ field, value, defaultValue, onChange, id, path, compon
|
|
|
48
76
|
readOnly: field.readOnly,
|
|
49
77
|
helpText: suppressInputHelpText ? void 0 : field.helpText,
|
|
50
78
|
value: displayValue,
|
|
51
|
-
|
|
79
|
+
onFocus: ()=>setIsEditing(true),
|
|
80
|
+
onChange: (e)=>{
|
|
81
|
+
setDisplayValue(e.target.value);
|
|
82
|
+
commit(e.target.value);
|
|
83
|
+
},
|
|
84
|
+
onBlur: ()=>{
|
|
85
|
+
setIsEditing(false);
|
|
86
|
+
const canonical = commit(displayValue);
|
|
87
|
+
setDisplayValue(void 0 === canonical ? canonicalDisplay : canonical?.toString() ?? '');
|
|
88
|
+
},
|
|
52
89
|
error: null != fieldError,
|
|
53
90
|
errorText: fieldError
|
|
54
91
|
});
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@byline/admin",
|
|
3
3
|
"private": false,
|
|
4
4
|
"license": "MPL-2.0",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.21.0",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.9.0"
|
|
8
8
|
},
|
|
@@ -155,10 +155,10 @@
|
|
|
155
155
|
"react-diff-viewer-continued": "^4.2.2",
|
|
156
156
|
"uuid": "^14.0.1",
|
|
157
157
|
"zod": "^4.4.3",
|
|
158
|
-
"@byline/auth": "3.
|
|
159
|
-
"@byline/
|
|
160
|
-
"@byline/
|
|
161
|
-
"@byline/
|
|
158
|
+
"@byline/auth": "3.21.0",
|
|
159
|
+
"@byline/ui": "3.21.0",
|
|
160
|
+
"@byline/i18n": "3.21.0",
|
|
161
|
+
"@byline/core": "3.21.0"
|
|
162
162
|
},
|
|
163
163
|
"peerDependencies": {
|
|
164
164
|
"react": "^19.0.0",
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { useEffect, useState } from 'react'
|
|
10
|
+
|
|
9
11
|
import type {
|
|
10
12
|
CounterField,
|
|
11
13
|
DecimalField,
|
|
@@ -14,10 +16,15 @@ import type {
|
|
|
14
16
|
FloatField,
|
|
15
17
|
IntegerField,
|
|
16
18
|
} from '@byline/core'
|
|
19
|
+
import { normalizeNumericValue } from '@byline/core'
|
|
17
20
|
import { Input } from '@byline/ui/react'
|
|
18
21
|
|
|
19
22
|
import { useFieldError, useFieldValue } from '../../forms/form-context'
|
|
20
23
|
|
|
24
|
+
type NumericalValue = string | number | null
|
|
25
|
+
|
|
26
|
+
const COMPLETE_NUMERIC_RE = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/
|
|
27
|
+
|
|
21
28
|
export const NumericalField = ({
|
|
22
29
|
field,
|
|
23
30
|
value,
|
|
@@ -30,7 +37,7 @@ export const NumericalField = ({
|
|
|
30
37
|
field: IntegerField | FloatField | DecimalField | CounterField
|
|
31
38
|
value?: string | number | null
|
|
32
39
|
defaultValue?: string | number | null
|
|
33
|
-
onChange?: (value:
|
|
40
|
+
onChange?: (value: NumericalValue) => void
|
|
34
41
|
id?: string
|
|
35
42
|
path?: string
|
|
36
43
|
/** Optional UI component slot overrides from the admin config. */
|
|
@@ -38,11 +45,36 @@ export const NumericalField = ({
|
|
|
38
45
|
}) => {
|
|
39
46
|
const fieldPath = path ?? field.name
|
|
40
47
|
const fieldError = useFieldError(fieldPath)
|
|
41
|
-
const fieldValue = useFieldValue<
|
|
42
|
-
const incomingValue =
|
|
48
|
+
const fieldValue = useFieldValue<NumericalValue | undefined>(fieldPath)
|
|
49
|
+
const incomingValue =
|
|
50
|
+
value !== undefined ? value : fieldValue !== undefined ? fieldValue : (defaultValue ?? null)
|
|
43
51
|
const htmlId = id ?? fieldPath
|
|
44
|
-
const
|
|
45
|
-
|
|
52
|
+
const canonicalDisplay = incomingValue == null ? '' : String(incomingValue)
|
|
53
|
+
const [displayValue, setDisplayValue] = useState(canonicalDisplay)
|
|
54
|
+
const [isEditing, setIsEditing] = useState(false)
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (!isEditing) setDisplayValue(canonicalDisplay)
|
|
58
|
+
}, [canonicalDisplay, isEditing])
|
|
59
|
+
|
|
60
|
+
const canonicalize = (nextValue: NumericalValue): NumericalValue | undefined => {
|
|
61
|
+
if (nextValue == null || (typeof nextValue === 'string' && nextValue.trim() === '')) return null
|
|
62
|
+
if (field.type === 'counter') return undefined
|
|
63
|
+
if (typeof nextValue === 'string' && !COMPLETE_NUMERIC_RE.test(nextValue.trim())) {
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
return normalizeNumericValue(field.type, nextValue, fieldPath)
|
|
68
|
+
} catch {
|
|
69
|
+
return undefined
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const commit = (nextValue: NumericalValue) => {
|
|
74
|
+
const canonical = canonicalize(nextValue)
|
|
75
|
+
if (canonical !== undefined) onChange?.(canonical)
|
|
76
|
+
return canonical
|
|
77
|
+
}
|
|
46
78
|
|
|
47
79
|
// Custom component slots (from admin config)
|
|
48
80
|
const slots = components
|
|
@@ -79,7 +111,7 @@ export const NumericalField = ({
|
|
|
79
111
|
return (
|
|
80
112
|
<CustomField
|
|
81
113
|
{...slotBaseProps}
|
|
82
|
-
onChange={(
|
|
114
|
+
onChange={(nextValue: NumericalValue) => commit(nextValue)}
|
|
83
115
|
defaultValue={defaultValue}
|
|
84
116
|
placeholder={field.placeholder}
|
|
85
117
|
/>
|
|
@@ -87,7 +119,9 @@ export const NumericalField = ({
|
|
|
87
119
|
}
|
|
88
120
|
return (
|
|
89
121
|
<Input
|
|
90
|
-
type="
|
|
122
|
+
type="text"
|
|
123
|
+
inputMode={field.type === 'integer' || field.type === 'counter' ? 'numeric' : 'decimal'}
|
|
124
|
+
step={field.type === 'integer' || field.type === 'counter' ? 1 : 'any'}
|
|
91
125
|
id={htmlId}
|
|
92
126
|
name={field.name}
|
|
93
127
|
label={suppressInputLabel ? undefined : field.label}
|
|
@@ -95,7 +129,18 @@ export const NumericalField = ({
|
|
|
95
129
|
readOnly={field.readOnly}
|
|
96
130
|
helpText={suppressInputHelpText ? undefined : field.helpText}
|
|
97
131
|
value={displayValue}
|
|
98
|
-
|
|
132
|
+
onFocus={() => setIsEditing(true)}
|
|
133
|
+
onChange={(e) => {
|
|
134
|
+
setDisplayValue(e.target.value)
|
|
135
|
+
commit(e.target.value)
|
|
136
|
+
}}
|
|
137
|
+
onBlur={() => {
|
|
138
|
+
setIsEditing(false)
|
|
139
|
+
const canonical = commit(displayValue)
|
|
140
|
+
setDisplayValue(
|
|
141
|
+
canonical === undefined ? canonicalDisplay : (canonical?.toString() ?? '')
|
|
142
|
+
)
|
|
143
|
+
}}
|
|
99
144
|
error={fieldError != null}
|
|
100
145
|
errorText={fieldError}
|
|
101
146
|
/>
|