@contentful/field-editor-number 1.1.7 → 1.2.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 +6 -0
- package/dist/NumberEditor.styles.d.ts +6 -0
- package/dist/field-editor-number.cjs.development.js +193 -69
- package/dist/field-editor-number.cjs.development.js.map +1 -1
- package/dist/field-editor-number.cjs.production.min.js +1 -1
- package/dist/field-editor-number.cjs.production.min.js.map +1 -1
- package/dist/field-editor-number.esm.js +194 -70
- package/dist/field-editor-number.esm.js.map +1 -1
- package/dist/parseNumber.d.ts +2 -4
- package/dist/utils.d.ts +9 -0
- package/package.json +3 -5
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.2.0](https://github.com/contentful/field-editors/compare/@contentful/field-editor-number@1.1.7...@contentful/field-editor-number@1.2.0) (2022-08-22)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- refactoring of Number field-editor [BAU-722] ([#1203](https://github.com/contentful/field-editors/issues/1203)) ([d93698e](https://github.com/contentful/field-editors/commit/d93698e730e0eb7c83378bfaad4566a70aba23f6))
|
|
11
|
+
|
|
6
12
|
## [1.1.7](https://github.com/contentful/field-editors/compare/@contentful/field-editor-number@1.1.6...@contentful/field-editor-number@1.1.7) (2022-07-29)
|
|
7
13
|
|
|
8
14
|
**Note:** Version bump only for package @contentful/field-editor-number
|
|
@@ -5,64 +5,97 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
6
6
|
|
|
7
7
|
var React = require('react');
|
|
8
|
-
var fieldEditorShared = require('@contentful/field-editor-shared');
|
|
9
|
-
var isEmpty = _interopDefault(require('lodash/isEmpty'));
|
|
10
8
|
var f36Components = require('@contentful/f36-components');
|
|
9
|
+
var f36Icons = require('@contentful/f36-icons');
|
|
10
|
+
var fieldEditorShared = require('@contentful/field-editor-shared');
|
|
11
|
+
var tokens = _interopDefault(require('@contentful/f36-tokens'));
|
|
12
|
+
var emotion = require('emotion');
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
14
|
+
var styles = {
|
|
15
|
+
container: /*#__PURE__*/emotion.css({
|
|
16
|
+
position: 'relative'
|
|
17
|
+
}),
|
|
18
|
+
controlsWrapper: /*#__PURE__*/emotion.css({
|
|
19
|
+
position: 'absolute',
|
|
20
|
+
top: '1px',
|
|
21
|
+
right: '1px',
|
|
22
|
+
width: tokens.spacingL,
|
|
23
|
+
height: 'calc(100% - 2px)',
|
|
24
|
+
display: 'flex',
|
|
25
|
+
flexDirection: 'column'
|
|
26
|
+
}),
|
|
27
|
+
control: /*#__PURE__*/emotion.css({
|
|
28
|
+
display: 'flex',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
justifyContent: 'center',
|
|
31
|
+
minHeight: 0,
|
|
32
|
+
cursor: 'pointer',
|
|
33
|
+
padding: 0,
|
|
34
|
+
margin: 0,
|
|
35
|
+
outline: 'none',
|
|
36
|
+
border: "0 solid " + tokens.gray300,
|
|
37
|
+
background: 'none',
|
|
38
|
+
borderLeftWidth: '1px',
|
|
39
|
+
'&:first-of-type': {
|
|
40
|
+
borderTopRightRadius: tokens.borderRadiusMedium
|
|
41
|
+
},
|
|
42
|
+
'&:last-of-type': {
|
|
43
|
+
borderTopWidth: '1px',
|
|
44
|
+
borderBottomRightRadius: tokens.borderRadiusMedium
|
|
45
|
+
},
|
|
46
|
+
svg: {
|
|
47
|
+
fill: tokens.gray600
|
|
48
|
+
},
|
|
49
|
+
'&:hover': {
|
|
50
|
+
backgroundColor: tokens.gray200
|
|
51
|
+
},
|
|
52
|
+
'&:active': {
|
|
53
|
+
backgroundColor: tokens.gray300
|
|
54
|
+
}
|
|
55
|
+
}),
|
|
56
|
+
input: /*#__PURE__*/emotion.css({
|
|
57
|
+
paddingRight: tokens.spacingXl
|
|
58
|
+
})
|
|
59
|
+
};
|
|
41
60
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
value: floatVal
|
|
46
|
-
};
|
|
61
|
+
function parseNumber(value, type) {
|
|
62
|
+
if (Number.isNaN(+value)) {
|
|
63
|
+
return;
|
|
47
64
|
}
|
|
48
65
|
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
66
|
+
return type === 'Integer' ? parseInt(value, 10) : parseFloat(value);
|
|
67
|
+
}
|
|
68
|
+
var FLOAT_REGEX = /^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]*)?$/;
|
|
69
|
+
var INT_REGEX = /^[+-]?([0-9]*)$/;
|
|
70
|
+
function isNumberInputValueValid(value, type) {
|
|
71
|
+
var regex = type === 'Integer' ? INT_REGEX : FLOAT_REGEX;
|
|
72
|
+
return regex.test(value);
|
|
53
73
|
}
|
|
54
74
|
|
|
55
|
-
function getRangeFromField(field) {
|
|
75
|
+
var getRangeFromField = function getRangeFromField(field) {
|
|
56
76
|
var validations = field.validations || [];
|
|
57
77
|
var result = validations.find(function (validation) {
|
|
58
78
|
return validation.range;
|
|
59
79
|
});
|
|
60
80
|
return result ? result.range : {};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function valueToString(value) {
|
|
81
|
+
};
|
|
82
|
+
var valueToString = function valueToString(value) {
|
|
64
83
|
return value === undefined ? '' : String(value);
|
|
65
|
-
}
|
|
84
|
+
};
|
|
85
|
+
var countDecimals = function countDecimals(number) {
|
|
86
|
+
var _number$toString$spli, _number$toString$spli2;
|
|
87
|
+
|
|
88
|
+
return (_number$toString$spli = (_number$toString$spli2 = number.toString().split('.')[1]) == null ? void 0 : _number$toString$spli2.length) != null ? _number$toString$spli : 0;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
var StepChangeType;
|
|
92
|
+
|
|
93
|
+
(function (StepChangeType) {
|
|
94
|
+
StepChangeType["Increment"] = "increment";
|
|
95
|
+
StepChangeType["Decrement"] = "decrement";
|
|
96
|
+
})(StepChangeType || (StepChangeType = {}));
|
|
97
|
+
|
|
98
|
+
var NUMBER_STEP = 1;
|
|
66
99
|
|
|
67
100
|
function InnerNumberEditor(_ref) {
|
|
68
101
|
var disabled = _ref.disabled,
|
|
@@ -70,46 +103,137 @@ function InnerNumberEditor(_ref) {
|
|
|
70
103
|
field = _ref.field,
|
|
71
104
|
setValue = _ref.setValue,
|
|
72
105
|
sdkValue = _ref.value;
|
|
73
|
-
var previousValue = React.useRef(valueToString(sdkValue));
|
|
74
106
|
|
|
75
107
|
var _React$useState = React.useState(valueToString(sdkValue)),
|
|
76
108
|
inputValue = _React$useState[0],
|
|
77
109
|
setInputValue = _React$useState[1];
|
|
78
110
|
|
|
79
111
|
var range = getRangeFromField(field);
|
|
112
|
+
var inputRef = React.useRef(null);
|
|
80
113
|
React.useEffect(function () {
|
|
81
|
-
|
|
114
|
+
var stringSdkValue = valueToString(sdkValue); // Update the input value if the SDK value (numeric) changes
|
|
115
|
+
|
|
116
|
+
if (stringSdkValue !== inputValue) {
|
|
117
|
+
setInputValue(stringSdkValue);
|
|
118
|
+
} // eslint-disable-next-line react-hooks/exhaustive-deps -- we want to trigger it only when sdkValue has changed
|
|
119
|
+
|
|
82
120
|
}, [sdkValue]);
|
|
83
|
-
React.useEffect(function () {
|
|
84
|
-
var stringifiedSdkValue = valueToString(sdkValue); // Update the input value (string) if the SDK value (numeric) changes
|
|
85
121
|
|
|
86
|
-
|
|
87
|
-
|
|
122
|
+
var updateExternalValue = function updateExternalValue(value) {
|
|
123
|
+
if (sdkValue !== value) {
|
|
124
|
+
setValue(value);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
var changeValueByStep = function changeValueByStep(type) {
|
|
129
|
+
var currentValue = Number.isNaN(+inputValue) ? 0 : +inputValue;
|
|
130
|
+
var nextValue = type === StepChangeType.Increment ? currentValue + NUMBER_STEP : currentValue - NUMBER_STEP; // Floating point numbers cannot represent all decimals precisely in binary.
|
|
131
|
+
// This can lead to unexpected results, such as 0.1 + 0.2 = 0.30000000000000004.
|
|
132
|
+
// See more details: https://floating-point-gui.de/
|
|
133
|
+
|
|
134
|
+
nextValue = +nextValue.toFixed(countDecimals(currentValue));
|
|
135
|
+
setInputValue(valueToString(nextValue));
|
|
136
|
+
setValue(nextValue);
|
|
137
|
+
}; // Keeps focus on the input
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
var handleControlPointerDown = function handleControlPointerDown(event) {
|
|
141
|
+
var _inputRef$current;
|
|
142
|
+
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
(_inputRef$current = inputRef.current) == null ? void 0 : _inputRef$current.focus();
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
var handleKeyDown = function handleKeyDown(event) {
|
|
148
|
+
var keyToFnMap = {
|
|
149
|
+
ArrowUp: function ArrowUp() {
|
|
150
|
+
return changeValueByStep(StepChangeType.Increment);
|
|
151
|
+
},
|
|
152
|
+
ArrowDown: function ArrowDown() {
|
|
153
|
+
return changeValueByStep(StepChangeType.Decrement);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
var fn = keyToFnMap[event.key];
|
|
157
|
+
|
|
158
|
+
if (fn) {
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
fn();
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
var handleInputChange = function handleInputChange(e) {
|
|
165
|
+
var value = e.target.value;
|
|
166
|
+
|
|
167
|
+
if (!value) {
|
|
168
|
+
setInputValue(value);
|
|
169
|
+
updateExternalValue(undefined);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!isNumberInputValueValid(value, field.type)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
setInputValue(value);
|
|
178
|
+
var parsedNumber = parseNumber(value, field.type);
|
|
179
|
+
field.setInvalid(parsedNumber === undefined);
|
|
180
|
+
|
|
181
|
+
if (parsedNumber !== undefined) {
|
|
182
|
+
updateExternalValue(parsedNumber);
|
|
88
183
|
}
|
|
89
|
-
}
|
|
184
|
+
};
|
|
185
|
+
|
|
90
186
|
return React.createElement("div", {
|
|
91
|
-
"data-test-id": "number-editor"
|
|
92
|
-
|
|
187
|
+
"data-test-id": "number-editor",
|
|
188
|
+
className: styles.container
|
|
189
|
+
}, React.createElement(f36Components.TextInput // With type="number" react doesn't call onChange for certain inputs, for example if you type `e`
|
|
190
|
+
// so we use "text" instead and fully rely on our own validation.
|
|
191
|
+
// See more details: https://github.com/facebook/react/issues/6556
|
|
192
|
+
, {
|
|
193
|
+
// With type="number" react doesn't call onChange for certain inputs, for example if you type `e`
|
|
194
|
+
// so we use "text" instead and fully rely on our own validation.
|
|
195
|
+
// See more details: https://github.com/facebook/react/issues/6556
|
|
196
|
+
type: "text",
|
|
93
197
|
testId: "number-editor-input",
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
198
|
+
className: styles.input,
|
|
199
|
+
min: range.min,
|
|
200
|
+
max: range.max,
|
|
97
201
|
isRequired: field.required,
|
|
98
202
|
isInvalid: errors.length > 0,
|
|
99
203
|
isDisabled: disabled,
|
|
100
204
|
value: inputValue,
|
|
101
|
-
|
|
102
|
-
onChange:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
205
|
+
ref: inputRef,
|
|
206
|
+
onChange: handleInputChange,
|
|
207
|
+
onKeyDown: handleKeyDown,
|
|
208
|
+
// The same role that input type="number" has
|
|
209
|
+
// See more details: https://www.digitala11y.com/spinbutton-role/
|
|
210
|
+
role: "spinbutton",
|
|
211
|
+
"aria-valuenow": sdkValue != null ? sdkValue : 0,
|
|
212
|
+
"aria-valuetext": inputValue,
|
|
213
|
+
"aria-valuemin": range.min,
|
|
214
|
+
"aria-valuemax": range.max
|
|
215
|
+
}), !disabled && React.createElement("div", {
|
|
216
|
+
className: styles.controlsWrapper,
|
|
217
|
+
"aria-hidden": "true"
|
|
218
|
+
}, React.createElement("button", {
|
|
219
|
+
tabIndex: -1,
|
|
220
|
+
className: styles.control,
|
|
221
|
+
onClick: function onClick() {
|
|
222
|
+
return changeValueByStep(StepChangeType.Increment);
|
|
223
|
+
},
|
|
224
|
+
onPointerDown: handleControlPointerDown
|
|
225
|
+
}, React.createElement(f36Icons.ArrowUpTrimmedIcon, {
|
|
226
|
+
size: "medium"
|
|
227
|
+
})), React.createElement("button", {
|
|
228
|
+
tabIndex: -1,
|
|
229
|
+
className: styles.control,
|
|
230
|
+
onClick: function onClick() {
|
|
231
|
+
return changeValueByStep(StepChangeType.Decrement);
|
|
232
|
+
},
|
|
233
|
+
onPointerDown: handleControlPointerDown
|
|
234
|
+
}, React.createElement(f36Icons.ArrowDownTrimmedIcon, {
|
|
235
|
+
size: "medium"
|
|
236
|
+
}))));
|
|
113
237
|
}
|
|
114
238
|
|
|
115
239
|
function NumberEditor(props) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-editor-number.cjs.development.js","sources":["../src/parseNumber.ts","../src/NumberEditor.tsx"],"sourcesContent":["import isEmpty from 'lodash/isEmpty';\n\nexport function parseNumber(\n value: string,\n type: string\n): {\n isValid: boolean;\n value: number | undefined;\n} {\n // This has saner semantics than parseFloat.\n // For values with chars in them, it gives\n // us NaN unlike parseFloat\n const floatVal = +value;\n const hasDot = value.includes('.');\n const hasFractional = /^(?:\\+|-)?\\d+\\.\\d+$/.test(value);\n\n if (isEmpty(value)) {\n return {\n isValid: true,\n value: undefined,\n };\n }\n\n if (isNaN(floatVal)) {\n return {\n isValid: false,\n value: undefined,\n };\n }\n\n if (type === 'Integer' && hasDot) {\n const intVal = parseInt(value, 10);\n\n return {\n isValid: false,\n value: intVal,\n };\n }\n\n if (hasDot && !hasFractional) {\n return {\n isValid: false,\n value: floatVal,\n };\n }\n\n return {\n isValid: true,\n value: floatVal,\n };\n}\n","import * as React from 'react';\nimport {\n FieldAPI,\n FieldConnector,\n FieldConnectorChildProps,\n} from '@contentful/field-editor-shared';\nimport { parseNumber } from './parseNumber';\n\nimport { TextInput } from '@contentful/f36-components';\n\nexport interface NumberEditorProps {\n /**\n * is the field disabled initially\n */\n isInitiallyDisabled: boolean;\n\n /**\n * sdk.field\n */\n field: FieldAPI;\n}\n\ntype RangeValidation = { min?: number; max?: number };\n\nfunction getRangeFromField(field: FieldAPI): RangeValidation {\n const validations = field.validations || [];\n const result = validations.find((validation) => (validation as any).range) as\n | { range: RangeValidation }\n | undefined;\n return result ? result.range : {};\n}\n\nfunction valueToString(value: InnerNumberEditorProps['value']) {\n return value === undefined ? '' : String(value);\n}\n\ntype InnerNumberEditorProps = Pick<\n FieldConnectorChildProps<number>,\n 'disabled' | 'errors' | 'setValue' | 'value'\n> & {\n field: NumberEditorProps['field'];\n};\n\nfunction InnerNumberEditor({\n disabled,\n errors,\n field,\n setValue,\n value: sdkValue,\n}: InnerNumberEditorProps) {\n const previousValue = React.useRef(valueToString(sdkValue));\n const [inputValue, setInputValue] = React.useState(valueToString(sdkValue));\n const range = getRangeFromField(field);\n\n React.useEffect(() => {\n previousValue.current = valueToString(sdkValue);\n }, [sdkValue]);\n\n React.useEffect(() => {\n const stringifiedSdkValue = valueToString(sdkValue);\n // Update the input value (string) if the SDK value (numeric) changes\n if (stringifiedSdkValue !== previousValue.current && stringifiedSdkValue !== inputValue) {\n setInputValue(stringifiedSdkValue);\n }\n }, [inputValue, sdkValue]);\n\n return (\n <div data-test-id=\"number-editor\">\n <TextInput\n testId=\"number-editor-input\"\n min={range.min !== undefined ? String(range.min) : ''}\n max={range.max !== undefined ? String(range.max) : ''}\n step={field.type === 'Integer' ? '1' : ''}\n isRequired={field.required}\n isInvalid={errors.length > 0}\n isDisabled={disabled}\n value={inputValue}\n type=\"number\"\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => {\n const parseResult = parseNumber(e.target.value, field.type);\n field.setInvalid(!parseResult.isValid);\n\n if (parseResult.isValid) {\n setValue(parseResult.value);\n }\n\n setInputValue(e.target.value);\n }}\n />\n </div>\n );\n}\n\nexport function NumberEditor(props: NumberEditorProps) {\n const { field } = props;\n\n return (\n <FieldConnector<number> field={field} isInitiallyDisabled={props.isInitiallyDisabled}>\n {({\n value,\n errors,\n disabled,\n setValue,\n }: Pick<FieldConnectorChildProps<number>, 'disabled' | 'errors' | 'setValue' | 'value'>) => (\n <InnerNumberEditor\n disabled={disabled}\n errors={errors}\n field={field}\n setValue={setValue}\n value={value}\n />\n )}\n </FieldConnector>\n );\n}\n\nNumberEditor.defaultProps = {\n isInitiallyDisabled: true,\n};\n"],"names":["parseNumber","value","type","floatVal","hasDot","includes","hasFractional","test","isEmpty","isValid","undefined","isNaN","intVal","parseInt","getRangeFromField","field","validations","result","find","validation","range","valueToString","String","InnerNumberEditor","disabled","errors","setValue","sdkValue","previousValue","React","inputValue","setInputValue","current","stringifiedSdkValue","TextInput","testId","min","max","step","isRequired","required","isInvalid","length","isDisabled","onChange","e","parseResult","target","setInvalid","NumberEditor","props","FieldConnector","isInitiallyDisabled","defaultProps"],"mappings":";;;;;;;;;;;SAEgBA,YACdC,OACAC;AAKA;AACA;AACA;AACA,MAAMC,QAAQ,GAAG,CAACF,KAAlB;AACA,MAAMG,MAAM,GAAGH,KAAK,CAACI,QAAN,CAAe,GAAf,CAAf;AACA,MAAMC,aAAa,GAAG,sBAAsBC,IAAtB,CAA2BN,KAA3B,CAAtB;;AAEA,MAAIO,OAAO,CAACP,KAAD,CAAX,EAAoB;AAClB,WAAO;AACLQ,MAAAA,OAAO,EAAE,IADJ;AAELR,MAAAA,KAAK,EAAES;AAFF,KAAP;AAID;;AAED,MAAIC,KAAK,CAACR,QAAD,CAAT,EAAqB;AACnB,WAAO;AACLM,MAAAA,OAAO,EAAE,KADJ;AAELR,MAAAA,KAAK,EAAES;AAFF,KAAP;AAID;;AAED,MAAIR,IAAI,KAAK,SAAT,IAAsBE,MAA1B,EAAkC;AAChC,QAAMQ,MAAM,GAAGC,QAAQ,CAACZ,KAAD,EAAQ,EAAR,CAAvB;AAEA,WAAO;AACLQ,MAAAA,OAAO,EAAE,KADJ;AAELR,MAAAA,KAAK,EAAEW;AAFF,KAAP;AAID;;AAED,MAAIR,MAAM,IAAI,CAACE,aAAf,EAA8B;AAC5B,WAAO;AACLG,MAAAA,OAAO,EAAE,KADJ;AAELR,MAAAA,KAAK,EAAEE;AAFF,KAAP;AAID;;AAED,SAAO;AACLM,IAAAA,OAAO,EAAE,IADJ;AAELR,IAAAA,KAAK,EAAEE;AAFF,GAAP;AAID;;AC1BD,SAASW,iBAAT,CAA2BC,KAA3B;AACE,MAAMC,WAAW,GAAGD,KAAK,CAACC,WAAN,IAAqB,EAAzC;AACA,MAAMC,MAAM,GAAGD,WAAW,CAACE,IAAZ,CAAiB,UAACC,UAAD;AAAA,WAAiBA,UAAkB,CAACC,KAApC;AAAA,GAAjB,CAAf;AAGA,SAAOH,MAAM,GAAGA,MAAM,CAACG,KAAV,GAAkB,EAA/B;AACD;;AAED,SAASC,aAAT,CAAuBpB,KAAvB;AACE,SAAOA,KAAK,KAAKS,SAAV,GAAsB,EAAtB,GAA2BY,MAAM,CAACrB,KAAD,CAAxC;AACD;;AASD,SAASsB,iBAAT;MACEC,gBAAAA;MACAC,cAAAA;MACAV,aAAAA;MACAW,gBAAAA;MACOC,gBAAP1B;AAEA,MAAM2B,aAAa,GAAGC,YAAA,CAAaR,aAAa,CAACM,QAAD,CAA1B,CAAtB;;AACA,wBAAoCE,cAAA,CAAeR,aAAa,CAACM,QAAD,CAA5B,CAApC;AAAA,MAAOG,UAAP;AAAA,MAAmBC,aAAnB;;AACA,MAAMX,KAAK,GAAGN,iBAAiB,CAACC,KAAD,CAA/B;AAEAc,EAAAA,eAAA,CAAgB;AACdD,IAAAA,aAAa,CAACI,OAAd,GAAwBX,aAAa,CAACM,QAAD,CAArC;AACD,GAFD,EAEG,CAACA,QAAD,CAFH;AAIAE,EAAAA,eAAA,CAAgB;AACd,QAAMI,mBAAmB,GAAGZ,aAAa,CAACM,QAAD,CAAzC;;AAEA,QAAIM,mBAAmB,KAAKL,aAAa,CAACI,OAAtC,IAAiDC,mBAAmB,KAAKH,UAA7E,EAAyF;AACvFC,MAAAA,aAAa,CAACE,mBAAD,CAAb;AACD;AACF,GAND,EAMG,CAACH,UAAD,EAAaH,QAAb,CANH;AAQA,SACEE,mBAAA,MAAA;oBAAkB;GAAlB,EACEA,mBAAA,CAACK,uBAAD;AACEC,IAAAA,MAAM,EAAC;AACPC,IAAAA,GAAG,EAAEhB,KAAK,CAACgB,GAAN,KAAc1B,SAAd,GAA0BY,MAAM,CAACF,KAAK,CAACgB,GAAP,CAAhC,GAA8C;AACnDC,IAAAA,GAAG,EAAEjB,KAAK,CAACiB,GAAN,KAAc3B,SAAd,GAA0BY,MAAM,CAACF,KAAK,CAACiB,GAAP,CAAhC,GAA8C;AACnDC,IAAAA,IAAI,EAAEvB,KAAK,CAACb,IAAN,KAAe,SAAf,GAA2B,GAA3B,GAAiC;AACvCqC,IAAAA,UAAU,EAAExB,KAAK,CAACyB;AAClBC,IAAAA,SAAS,EAAEhB,MAAM,CAACiB,MAAP,GAAgB;AAC3BC,IAAAA,UAAU,EAAEnB;AACZvB,IAAAA,KAAK,EAAE6B;AACP5B,IAAAA,IAAI,EAAC;AACL0C,IAAAA,QAAQ,EAAE,kBAACC,CAAD;AACR,UAAMC,WAAW,GAAG9C,WAAW,CAAC6C,CAAC,CAACE,MAAF,CAAS9C,KAAV,EAAiBc,KAAK,CAACb,IAAvB,CAA/B;AACAa,MAAAA,KAAK,CAACiC,UAAN,CAAiB,CAACF,WAAW,CAACrC,OAA9B;;AAEA,UAAIqC,WAAW,CAACrC,OAAhB,EAAyB;AACvBiB,QAAAA,QAAQ,CAACoB,WAAW,CAAC7C,KAAb,CAAR;AACD;;AAED8B,MAAAA,aAAa,CAACc,CAAC,CAACE,MAAF,CAAS9C,KAAV,CAAb;AACD;GAnBH,CADF,CADF;AAyBD;;AAED,SAAgBgD,aAAaC;AAC3B,MAAQnC,KAAR,GAAkBmC,KAAlB,CAAQnC,KAAR;AAEA,SACEc,mBAAA,CAACsB,gCAAD;AAAwBpC,IAAAA,KAAK,EAAEA;AAAOqC,IAAAA,mBAAmB,EAAEF,KAAK,CAACE;GAAjE,EACG;AAAA,QACCnD,KADD,SACCA,KADD;AAAA,QAECwB,MAFD,SAECA,MAFD;AAAA,QAGCD,QAHD,SAGCA,QAHD;AAAA,QAICE,QAJD,SAICA,QAJD;AAAA,WAMCG,mBAAA,CAACN,iBAAD;AACEC,MAAAA,QAAQ,EAAEA;AACVC,MAAAA,MAAM,EAAEA;AACRV,MAAAA,KAAK,EAAEA;AACPW,MAAAA,QAAQ,EAAEA;AACVzB,MAAAA,KAAK,EAAEA;KALT,CAND;AAAA,GADH,CADF;AAkBD;AAEDgD,YAAY,CAACI,YAAb,GAA4B;AAC1BD,EAAAA,mBAAmB,EAAE;AADK,CAA5B;;;;"}
|
|
1
|
+
{"version":3,"file":"field-editor-number.cjs.development.js","sources":["../src/NumberEditor.styles.ts","../src/parseNumber.ts","../src/utils.ts","../src/NumberEditor.tsx"],"sourcesContent":["import tokens from '@contentful/f36-tokens';\nimport { css } from 'emotion';\n\nexport const styles = {\n container: css({\n position: 'relative',\n }),\n controlsWrapper: css({\n position: 'absolute',\n top: '1px',\n right: '1px',\n width: tokens.spacingL,\n height: 'calc(100% - 2px)',\n display: 'flex',\n flexDirection: 'column',\n }),\n control: css({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: 0,\n cursor: 'pointer',\n padding: 0,\n margin: 0,\n outline: 'none',\n border: `0 solid ${tokens.gray300}`,\n background: 'none',\n borderLeftWidth: '1px',\n\n '&:first-of-type': {\n borderTopRightRadius: tokens.borderRadiusMedium,\n },\n\n '&:last-of-type': {\n borderTopWidth: '1px',\n borderBottomRightRadius: tokens.borderRadiusMedium,\n },\n\n svg: {\n fill: tokens.gray600,\n },\n\n '&:hover': {\n backgroundColor: tokens.gray200,\n },\n\n '&:active': {\n backgroundColor: tokens.gray300,\n },\n }),\n input: css({\n paddingRight: tokens.spacingXl,\n }),\n};\n","export function parseNumber(value: string, type: string) {\n if (Number.isNaN(+value)) {\n return;\n }\n\n return type === 'Integer' ? parseInt(value, 10) : parseFloat(value);\n}\n\nconst FLOAT_REGEX = /^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]*)?$/;\nconst INT_REGEX = /^[+-]?([0-9]*)$/;\n\nexport function isNumberInputValueValid(value: string, type: string) {\n const regex = type === 'Integer' ? INT_REGEX : FLOAT_REGEX;\n\n return regex.test(value);\n}\n","import { FieldAPI } from '@contentful/field-editor-shared';\n\ntype RangeValidation = { min?: number; max?: number };\n\nexport const getRangeFromField = (field: FieldAPI): RangeValidation => {\n const validations = field.validations || [];\n const result = validations.find((validation) => (validation as any).range) as\n | { range: RangeValidation }\n | undefined;\n return result ? result.range : {};\n};\n\nexport const valueToString = (value: number | null | undefined) => {\n return value === undefined ? '' : String(value);\n};\n\nexport const countDecimals = (number: number) => {\n return number.toString().split('.')[1]?.length ?? 0;\n};\n","import * as React from 'react';\n\nimport { TextInput } from '@contentful/f36-components';\nimport { ArrowUpTrimmedIcon, ArrowDownTrimmedIcon } from '@contentful/f36-icons';\nimport {\n FieldAPI,\n FieldConnector,\n FieldConnectorChildProps,\n} from '@contentful/field-editor-shared';\n\nimport { styles } from './NumberEditor.styles';\nimport { isNumberInputValueValid, parseNumber } from './parseNumber';\nimport { getRangeFromField, valueToString, countDecimals } from './utils';\n\nexport interface NumberEditorProps {\n /**\n * is the field disabled initially\n */\n isInitiallyDisabled: boolean;\n\n /**\n * sdk.field\n */\n field: FieldAPI;\n}\n\ntype InnerNumberEditorProps = Pick<\n FieldConnectorChildProps<number>,\n 'disabled' | 'errors' | 'setValue' | 'value'\n> & {\n field: NumberEditorProps['field'];\n};\n\nenum StepChangeType {\n Increment = 'increment',\n Decrement = 'decrement',\n}\n\nconst NUMBER_STEP = 1;\n\nfunction InnerNumberEditor({\n disabled,\n errors,\n field,\n setValue,\n value: sdkValue,\n}: InnerNumberEditorProps) {\n const [inputValue, setInputValue] = React.useState(valueToString(sdkValue));\n const range = getRangeFromField(field);\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n const stringSdkValue = valueToString(sdkValue);\n // Update the input value if the SDK value (numeric) changes\n if (stringSdkValue !== inputValue) {\n setInputValue(stringSdkValue);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- we want to trigger it only when sdkValue has changed\n }, [sdkValue]);\n\n const updateExternalValue = (value: number | undefined) => {\n if (sdkValue !== value) {\n setValue(value);\n }\n };\n\n const changeValueByStep = (type: StepChangeType) => {\n const currentValue = Number.isNaN(+inputValue) ? 0 : +inputValue;\n let nextValue =\n type === StepChangeType.Increment ? currentValue + NUMBER_STEP : currentValue - NUMBER_STEP;\n // Floating point numbers cannot represent all decimals precisely in binary.\n // This can lead to unexpected results, such as 0.1 + 0.2 = 0.30000000000000004.\n // See more details: https://floating-point-gui.de/\n nextValue = +nextValue.toFixed(countDecimals(currentValue));\n\n setInputValue(valueToString(nextValue));\n setValue(nextValue);\n };\n\n // Keeps focus on the input\n const handleControlPointerDown: React.PointerEventHandler<HTMLButtonElement> = (event) => {\n event.preventDefault();\n inputRef.current?.focus();\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<any>) => {\n const keyToFnMap: {\n [key: string]: () => void;\n } = {\n ArrowUp: () => changeValueByStep(StepChangeType.Increment),\n ArrowDown: () => changeValueByStep(StepChangeType.Decrement),\n };\n\n const fn = keyToFnMap[event.key];\n if (fn) {\n event.preventDefault();\n fn();\n }\n };\n\n const handleInputChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {\n const value = e.target.value;\n if (!value) {\n setInputValue(value);\n updateExternalValue(undefined);\n return;\n }\n\n if (!isNumberInputValueValid(value, field.type)) {\n return;\n }\n\n setInputValue(value);\n\n const parsedNumber = parseNumber(value, field.type);\n field.setInvalid(parsedNumber === undefined);\n if (parsedNumber !== undefined) {\n updateExternalValue(parsedNumber);\n }\n };\n\n return (\n <div data-test-id=\"number-editor\" className={styles.container}>\n <TextInput\n // With type=\"number\" react doesn't call onChange for certain inputs, for example if you type `e`\n // so we use \"text\" instead and fully rely on our own validation.\n // See more details: https://github.com/facebook/react/issues/6556\n type=\"text\"\n testId=\"number-editor-input\"\n className={styles.input}\n min={range.min}\n max={range.max}\n isRequired={field.required}\n isInvalid={errors.length > 0}\n isDisabled={disabled}\n value={inputValue}\n ref={inputRef}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n // The same role that input type=\"number\" has\n // See more details: https://www.digitala11y.com/spinbutton-role/\n role=\"spinbutton\"\n aria-valuenow={sdkValue ?? 0}\n aria-valuetext={inputValue}\n aria-valuemin={range.min}\n aria-valuemax={range.max}\n />\n {/**\n * We hide this controls from screen readers and keyboard focus.\n * For those purposes we have a keyboard handler. The same way native input number works.\n */}\n {!disabled && (\n <div className={styles.controlsWrapper} aria-hidden=\"true\">\n <button\n tabIndex={-1}\n className={styles.control}\n onClick={() => changeValueByStep(StepChangeType.Increment)}\n onPointerDown={handleControlPointerDown}>\n <ArrowUpTrimmedIcon size=\"medium\" />\n </button>\n <button\n tabIndex={-1}\n className={styles.control}\n onClick={() => changeValueByStep(StepChangeType.Decrement)}\n onPointerDown={handleControlPointerDown}>\n <ArrowDownTrimmedIcon size=\"medium\" />\n </button>\n </div>\n )}\n </div>\n );\n}\n\nexport function NumberEditor(props: NumberEditorProps) {\n const { field } = props;\n\n return (\n <FieldConnector<number> field={field} isInitiallyDisabled={props.isInitiallyDisabled}>\n {({\n value,\n errors,\n disabled,\n setValue,\n }: Pick<FieldConnectorChildProps<number>, 'disabled' | 'errors' | 'setValue' | 'value'>) => (\n <InnerNumberEditor\n disabled={disabled}\n errors={errors}\n field={field}\n setValue={setValue}\n value={value}\n />\n )}\n </FieldConnector>\n );\n}\n\nNumberEditor.defaultProps = {\n isInitiallyDisabled: true,\n};\n"],"names":["styles","container","css","position","controlsWrapper","top","right","width","tokens","spacingL","height","display","flexDirection","control","alignItems","justifyContent","minHeight","cursor","padding","margin","outline","border","gray300","background","borderLeftWidth","borderTopRightRadius","borderRadiusMedium","borderTopWidth","borderBottomRightRadius","svg","fill","gray600","backgroundColor","gray200","input","paddingRight","spacingXl","parseNumber","value","type","Number","isNaN","parseInt","parseFloat","FLOAT_REGEX","INT_REGEX","isNumberInputValueValid","regex","test","getRangeFromField","field","validations","result","find","validation","range","valueToString","undefined","String","countDecimals","number","toString","split","length","StepChangeType","NUMBER_STEP","InnerNumberEditor","disabled","errors","setValue","sdkValue","React","inputValue","setInputValue","inputRef","stringSdkValue","updateExternalValue","changeValueByStep","currentValue","nextValue","Increment","toFixed","handleControlPointerDown","event","preventDefault","current","focus","handleKeyDown","keyToFnMap","ArrowUp","ArrowDown","Decrement","fn","key","handleInputChange","e","target","parsedNumber","setInvalid","className","TextInput","testId","min","max","isRequired","required","isInvalid","isDisabled","ref","onChange","onKeyDown","role","tabIndex","onClick","onPointerDown","ArrowUpTrimmedIcon","size","ArrowDownTrimmedIcon","NumberEditor","props","FieldConnector","isInitiallyDisabled","defaultProps"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t=require("react"),i=require("@contentful/field-editor-shared"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t,n=require("react"),r=require("@contentful/f36-components"),i=require("@contentful/f36-icons"),o=require("@contentful/field-editor-shared"),a=(e=require("@contentful/f36-tokens"))&&"object"==typeof e&&"default"in e?e.default:e,u=require("emotion"),l={container:u.css({position:"relative"}),controlsWrapper:u.css({position:"absolute",top:"1px",right:"1px",width:a.spacingL,height:"calc(100% - 2px)",display:"flex",flexDirection:"column"}),control:u.css({display:"flex",alignItems:"center",justifyContent:"center",minHeight:0,cursor:"pointer",padding:0,margin:0,outline:"none",border:"0 solid "+a.gray300,background:"none",borderLeftWidth:"1px","&:first-of-type":{borderTopRightRadius:a.borderRadiusMedium},"&:last-of-type":{borderTopWidth:"1px",borderBottomRightRadius:a.borderRadiusMedium},svg:{fill:a.gray600},"&:hover":{backgroundColor:a.gray200},"&:active":{backgroundColor:a.gray300}}),input:u.css({paddingRight:a.spacingXl})},c=/^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]*)?$/,s=/^[+-]?([0-9]*)$/,d=function(e){return void 0===e?"":String(e)};function f(e){var o=e.disabled,a=e.errors,u=e.field,f=e.setValue,m=e.value,p=n.useState(d(m)),v=p[0],b=p[1],g=function(e){var t=(e.validations||[]).find((function(e){return e.range}));return t?t.range:{}}(u),x=n.useRef(null);n.useEffect((function(){var e=d(m);e!==v&&b(e)}),[m]);var y=function(e){m!==e&&f(e)},I=function(e){var n,r,i,o=Number.isNaN(+v)?0:+v,a=e===t.Increment?o+1:o-1;a=+a.toFixed((n=o,null!=(r=null==(i=n.toString().split(".")[1])?void 0:i.length)?r:0)),b(d(a)),f(a)},h=function(e){var t;e.preventDefault(),null==(t=x.current)||t.focus()};return n.createElement("div",{"data-test-id":"number-editor",className:l.container},n.createElement(r.TextInput,{type:"text",testId:"number-editor-input",className:l.input,min:g.min,max:g.max,isRequired:u.required,isInvalid:a.length>0,isDisabled:o,value:v,ref:x,onChange:function(e){var t=e.target.value;if(!t)return b(t),void y(void 0);if(function(e,t){return("Integer"===t?s:c).test(e)}(t,u.type)){b(t);var n=function(e,t){if(!Number.isNaN(+e))return"Integer"===t?parseInt(e,10):parseFloat(e)}(t,u.type);u.setInvalid(void 0===n),void 0!==n&&y(n)}},onKeyDown:function(e){var n={ArrowUp:function(){return I(t.Increment)},ArrowDown:function(){return I(t.Decrement)}}[e.key];n&&(e.preventDefault(),n())},role:"spinbutton","aria-valuenow":null!=m?m:0,"aria-valuetext":v,"aria-valuemin":g.min,"aria-valuemax":g.max}),!o&&n.createElement("div",{className:l.controlsWrapper,"aria-hidden":"true"},n.createElement("button",{tabIndex:-1,className:l.control,onClick:function(){return I(t.Increment)},onPointerDown:h},n.createElement(i.ArrowUpTrimmedIcon,{size:"medium"})),n.createElement("button",{tabIndex:-1,className:l.control,onClick:function(){return I(t.Decrement)},onPointerDown:h},n.createElement(i.ArrowDownTrimmedIcon,{size:"medium"}))))}function m(e){var t=e.field;return n.createElement(o.FieldConnector,{field:t,isInitiallyDisabled:e.isInitiallyDisabled},(function(e){return n.createElement(f,{disabled:e.disabled,errors:e.errors,field:t,setValue:e.setValue,value:e.value})}))}!function(e){e.Increment="increment",e.Decrement="decrement"}(t||(t={})),m.defaultProps={isInitiallyDisabled:!0},exports.NumberEditor=m;
|
|
2
2
|
//# sourceMappingURL=field-editor-number.cjs.production.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-editor-number.cjs.production.min.js","sources":["../src/NumberEditor.tsx","../src/parseNumber.ts"],"sourcesContent":["import * as React from 'react';\nimport {\n FieldAPI,\n FieldConnector,\n FieldConnectorChildProps,\n} from '@contentful/field-editor-shared';\nimport { parseNumber } from './parseNumber';\n\nimport { TextInput } from '@contentful/f36-components';\n\nexport interface NumberEditorProps {\n /**\n * is the field disabled initially\n */\n isInitiallyDisabled: boolean;\n\n /**\n * sdk.field\n */\n field: FieldAPI;\n}\n\ntype RangeValidation = { min?: number; max?: number };\n\nfunction getRangeFromField(field: FieldAPI): RangeValidation {\n const validations = field.validations || [];\n const result = validations.find((validation) => (validation as any).range) as\n | { range: RangeValidation }\n | undefined;\n return result ? result.range : {};\n}\n\nfunction valueToString(value: InnerNumberEditorProps['value']) {\n return value === undefined ? '' : String(value);\n}\n\ntype InnerNumberEditorProps = Pick<\n FieldConnectorChildProps<number>,\n 'disabled' | 'errors' | 'setValue' | 'value'\n> & {\n field: NumberEditorProps['field'];\n};\n\nfunction InnerNumberEditor({\n disabled,\n errors,\n field,\n setValue,\n value: sdkValue,\n}: InnerNumberEditorProps) {\n const previousValue = React.useRef(valueToString(sdkValue));\n const [inputValue, setInputValue] = React.useState(valueToString(sdkValue));\n const range = getRangeFromField(field);\n\n React.useEffect(() => {\n previousValue.current = valueToString(sdkValue);\n }, [sdkValue]);\n\n React.useEffect(() => {\n const stringifiedSdkValue = valueToString(sdkValue);\n // Update the input value (string) if the SDK value (numeric) changes\n if (stringifiedSdkValue !== previousValue.current && stringifiedSdkValue !== inputValue) {\n setInputValue(stringifiedSdkValue);\n }\n }, [inputValue, sdkValue]);\n\n return (\n <div data-test-id=\"number-editor\">\n <TextInput\n testId=\"number-editor-input\"\n min={range.min !== undefined ? String(range.min) : ''}\n max={range.max !== undefined ? String(range.max) : ''}\n step={field.type === 'Integer' ? '1' : ''}\n isRequired={field.required}\n isInvalid={errors.length > 0}\n isDisabled={disabled}\n value={inputValue}\n type=\"number\"\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => {\n const parseResult = parseNumber(e.target.value, field.type);\n field.setInvalid(!parseResult.isValid);\n\n if (parseResult.isValid) {\n setValue(parseResult.value);\n }\n\n setInputValue(e.target.value);\n }}\n />\n </div>\n );\n}\n\nexport function NumberEditor(props: NumberEditorProps) {\n const { field } = props;\n\n return (\n <FieldConnector<number> field={field} isInitiallyDisabled={props.isInitiallyDisabled}>\n {({\n value,\n errors,\n disabled,\n setValue,\n }: Pick<FieldConnectorChildProps<number>, 'disabled' | 'errors' | 'setValue' | 'value'>) => (\n <InnerNumberEditor\n disabled={disabled}\n errors={errors}\n field={field}\n setValue={setValue}\n value={value}\n />\n )}\n </FieldConnector>\n );\n}\n\nNumberEditor.defaultProps = {\n isInitiallyDisabled: true,\n};\n","import isEmpty from 'lodash/isEmpty';\n\nexport function parseNumber(\n value: string,\n type: string\n): {\n isValid: boolean;\n value: number | undefined;\n} {\n // This has saner semantics than parseFloat.\n // For values with chars in them, it gives\n // us NaN unlike parseFloat\n const floatVal = +value;\n const hasDot = value.includes('.');\n const hasFractional = /^(?:\\+|-)?\\d+\\.\\d+$/.test(value);\n\n if (isEmpty(value)) {\n return {\n isValid: true,\n value: undefined,\n };\n }\n\n if (isNaN(floatVal)) {\n return {\n isValid: false,\n value: undefined,\n };\n }\n\n if (type === 'Integer' && hasDot) {\n const intVal = parseInt(value, 10);\n\n return {\n isValid: false,\n value: intVal,\n };\n }\n\n if (hasDot && !hasFractional) {\n return {\n isValid: false,\n value: floatVal,\n };\n }\n\n return {\n isValid: true,\n value: floatVal,\n };\n}\n"],"names":["valueToString","value","undefined","String","InnerNumberEditor","disabled","errors","field","setValue","sdkValue","previousValue","React","inputValue","setInputValue","range","result","validations","find","validation","getRangeFromField","current","stringifiedSdkValue","TextInput","testId","min","max","step","type","isRequired","required","isInvalid","length","isDisabled","onChange","e","floatVal","hasDot","hasFractional","parseResult","target","includes","test","isEmpty","isValid","isNaN","parseInt","setInvalid","NumberEditor","props","FieldConnector","isInitiallyDisabled","defaultProps"],"mappings":"iQAgCA,SAASA,EAAcC,eACJC,IAAVD,EAAsB,GAAKE,OAAOF,GAU3C,SAASG,SACPC,IAAAA,SACAC,IAAAA,OACAC,IAAAA,MACAC,IAAAA,SACOC,IAAPR,MAEMS,EAAgBC,SAAaX,EAAcS,MACbE,WAAeX,EAAcS,IAA1DG,OAAYC,OACbC,EA5BR,SAA2BP,OAEnBQ,GADcR,EAAMS,aAAe,IACdC,MAAK,SAACC,UAAgBA,EAAmBJ,gBAG7DC,EAASA,EAAOD,MAAQ,GAuBjBK,CAAkBZ,UAEhCI,aAAgB,WACdD,EAAcU,QAAUpB,EAAcS,KACrC,CAACA,IAEJE,aAAgB,eACRU,EAAsBrB,EAAcS,GAEtCY,IAAwBX,EAAcU,SAAWC,IAAwBT,GAC3EC,EAAcQ,KAEf,CAACT,EAAYH,IAGdE,sCAAkB,iBAChBA,gBAACW,aACCC,OAAO,sBACPC,SAAmBtB,IAAdY,EAAMU,IAAoBrB,OAAOW,EAAMU,KAAO,GACnDC,SAAmBvB,IAAdY,EAAMW,IAAoBtB,OAAOW,EAAMW,KAAO,GACnDC,KAAqB,YAAfnB,EAAMoB,KAAqB,IAAM,GACvCC,WAAYrB,EAAMsB,SAClBC,UAAWxB,EAAOyB,OAAS,EAC3BC,WAAY3B,EACZJ,MAAOW,EACPe,KAAK,SACLM,SAAU,SAACC,OC3EjBjC,EACA0B,EAQMQ,EACAC,EACAC,EDiEQC,GC3EdX,ED2EwDpB,EAAMoB,KCnExDQ,IATNlC,ED4EwCiC,EAAEK,OAAOtC,OClE3CmC,EAASnC,EAAMuC,SAAS,KACxBH,EAAgB,sBAAsBI,KAAKxC,GAE7CyC,EAAQzC,GACH,CACL0C,SAAS,EACT1C,WAAOC,GAIP0C,MAAMT,GACD,CACLQ,SAAS,EACT1C,WAAOC,GAIE,YAATyB,GAAsBS,EAGjB,CACLO,SAAS,EACT1C,MAJa4C,SAAS5C,EAAO,KAQ7BmC,IAAWC,EACN,CACLM,SAAS,EACT1C,MAAOkC,GAIJ,CACLQ,SAAS,EACT1C,MAAOkC,IDgCD5B,EAAMuC,YAAYR,EAAYK,SAE1BL,EAAYK,SACdnC,EAAS8B,EAAYrC,OAGvBY,EAAcqB,EAAEK,OAAOtC,oBAOjB8C,EAAaC,OACnBzC,EAAUyC,EAAVzC,aAGNI,gBAACsC,kBAAuB1C,MAAOA,EAAO2C,oBAAqBF,EAAME,sBAC9D,mBAMCvC,gBAACP,GACCC,WAJFA,SAKEC,SANFA,OAOEC,MAAOA,EACPC,WANFA,SAOEP,QAVFA,WAiBR8C,EAAaI,aAAe,CAC1BD,qBAAqB"}
|
|
1
|
+
{"version":3,"file":"field-editor-number.cjs.production.min.js","sources":["../src/NumberEditor.tsx","../src/NumberEditor.styles.ts","../src/parseNumber.ts","../src/utils.ts"],"sourcesContent":["import * as React from 'react';\n\nimport { TextInput } from '@contentful/f36-components';\nimport { ArrowUpTrimmedIcon, ArrowDownTrimmedIcon } from '@contentful/f36-icons';\nimport {\n FieldAPI,\n FieldConnector,\n FieldConnectorChildProps,\n} from '@contentful/field-editor-shared';\n\nimport { styles } from './NumberEditor.styles';\nimport { isNumberInputValueValid, parseNumber } from './parseNumber';\nimport { getRangeFromField, valueToString, countDecimals } from './utils';\n\nexport interface NumberEditorProps {\n /**\n * is the field disabled initially\n */\n isInitiallyDisabled: boolean;\n\n /**\n * sdk.field\n */\n field: FieldAPI;\n}\n\ntype InnerNumberEditorProps = Pick<\n FieldConnectorChildProps<number>,\n 'disabled' | 'errors' | 'setValue' | 'value'\n> & {\n field: NumberEditorProps['field'];\n};\n\nenum StepChangeType {\n Increment = 'increment',\n Decrement = 'decrement',\n}\n\nconst NUMBER_STEP = 1;\n\nfunction InnerNumberEditor({\n disabled,\n errors,\n field,\n setValue,\n value: sdkValue,\n}: InnerNumberEditorProps) {\n const [inputValue, setInputValue] = React.useState(valueToString(sdkValue));\n const range = getRangeFromField(field);\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n const stringSdkValue = valueToString(sdkValue);\n // Update the input value if the SDK value (numeric) changes\n if (stringSdkValue !== inputValue) {\n setInputValue(stringSdkValue);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- we want to trigger it only when sdkValue has changed\n }, [sdkValue]);\n\n const updateExternalValue = (value: number | undefined) => {\n if (sdkValue !== value) {\n setValue(value);\n }\n };\n\n const changeValueByStep = (type: StepChangeType) => {\n const currentValue = Number.isNaN(+inputValue) ? 0 : +inputValue;\n let nextValue =\n type === StepChangeType.Increment ? currentValue + NUMBER_STEP : currentValue - NUMBER_STEP;\n // Floating point numbers cannot represent all decimals precisely in binary.\n // This can lead to unexpected results, such as 0.1 + 0.2 = 0.30000000000000004.\n // See more details: https://floating-point-gui.de/\n nextValue = +nextValue.toFixed(countDecimals(currentValue));\n\n setInputValue(valueToString(nextValue));\n setValue(nextValue);\n };\n\n // Keeps focus on the input\n const handleControlPointerDown: React.PointerEventHandler<HTMLButtonElement> = (event) => {\n event.preventDefault();\n inputRef.current?.focus();\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<any>) => {\n const keyToFnMap: {\n [key: string]: () => void;\n } = {\n ArrowUp: () => changeValueByStep(StepChangeType.Increment),\n ArrowDown: () => changeValueByStep(StepChangeType.Decrement),\n };\n\n const fn = keyToFnMap[event.key];\n if (fn) {\n event.preventDefault();\n fn();\n }\n };\n\n const handleInputChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {\n const value = e.target.value;\n if (!value) {\n setInputValue(value);\n updateExternalValue(undefined);\n return;\n }\n\n if (!isNumberInputValueValid(value, field.type)) {\n return;\n }\n\n setInputValue(value);\n\n const parsedNumber = parseNumber(value, field.type);\n field.setInvalid(parsedNumber === undefined);\n if (parsedNumber !== undefined) {\n updateExternalValue(parsedNumber);\n }\n };\n\n return (\n <div data-test-id=\"number-editor\" className={styles.container}>\n <TextInput\n // With type=\"number\" react doesn't call onChange for certain inputs, for example if you type `e`\n // so we use \"text\" instead and fully rely on our own validation.\n // See more details: https://github.com/facebook/react/issues/6556\n type=\"text\"\n testId=\"number-editor-input\"\n className={styles.input}\n min={range.min}\n max={range.max}\n isRequired={field.required}\n isInvalid={errors.length > 0}\n isDisabled={disabled}\n value={inputValue}\n ref={inputRef}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n // The same role that input type=\"number\" has\n // See more details: https://www.digitala11y.com/spinbutton-role/\n role=\"spinbutton\"\n aria-valuenow={sdkValue ?? 0}\n aria-valuetext={inputValue}\n aria-valuemin={range.min}\n aria-valuemax={range.max}\n />\n {/**\n * We hide this controls from screen readers and keyboard focus.\n * For those purposes we have a keyboard handler. The same way native input number works.\n */}\n {!disabled && (\n <div className={styles.controlsWrapper} aria-hidden=\"true\">\n <button\n tabIndex={-1}\n className={styles.control}\n onClick={() => changeValueByStep(StepChangeType.Increment)}\n onPointerDown={handleControlPointerDown}>\n <ArrowUpTrimmedIcon size=\"medium\" />\n </button>\n <button\n tabIndex={-1}\n className={styles.control}\n onClick={() => changeValueByStep(StepChangeType.Decrement)}\n onPointerDown={handleControlPointerDown}>\n <ArrowDownTrimmedIcon size=\"medium\" />\n </button>\n </div>\n )}\n </div>\n );\n}\n\nexport function NumberEditor(props: NumberEditorProps) {\n const { field } = props;\n\n return (\n <FieldConnector<number> field={field} isInitiallyDisabled={props.isInitiallyDisabled}>\n {({\n value,\n errors,\n disabled,\n setValue,\n }: Pick<FieldConnectorChildProps<number>, 'disabled' | 'errors' | 'setValue' | 'value'>) => (\n <InnerNumberEditor\n disabled={disabled}\n errors={errors}\n field={field}\n setValue={setValue}\n value={value}\n />\n )}\n </FieldConnector>\n );\n}\n\nNumberEditor.defaultProps = {\n isInitiallyDisabled: true,\n};\n","import tokens from '@contentful/f36-tokens';\nimport { css } from 'emotion';\n\nexport const styles = {\n container: css({\n position: 'relative',\n }),\n controlsWrapper: css({\n position: 'absolute',\n top: '1px',\n right: '1px',\n width: tokens.spacingL,\n height: 'calc(100% - 2px)',\n display: 'flex',\n flexDirection: 'column',\n }),\n control: css({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: 0,\n cursor: 'pointer',\n padding: 0,\n margin: 0,\n outline: 'none',\n border: `0 solid ${tokens.gray300}`,\n background: 'none',\n borderLeftWidth: '1px',\n\n '&:first-of-type': {\n borderTopRightRadius: tokens.borderRadiusMedium,\n },\n\n '&:last-of-type': {\n borderTopWidth: '1px',\n borderBottomRightRadius: tokens.borderRadiusMedium,\n },\n\n svg: {\n fill: tokens.gray600,\n },\n\n '&:hover': {\n backgroundColor: tokens.gray200,\n },\n\n '&:active': {\n backgroundColor: tokens.gray300,\n },\n }),\n input: css({\n paddingRight: tokens.spacingXl,\n }),\n};\n","export function parseNumber(value: string, type: string) {\n if (Number.isNaN(+value)) {\n return;\n }\n\n return type === 'Integer' ? parseInt(value, 10) : parseFloat(value);\n}\n\nconst FLOAT_REGEX = /^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]*)?$/;\nconst INT_REGEX = /^[+-]?([0-9]*)$/;\n\nexport function isNumberInputValueValid(value: string, type: string) {\n const regex = type === 'Integer' ? INT_REGEX : FLOAT_REGEX;\n\n return regex.test(value);\n}\n","import { FieldAPI } from '@contentful/field-editor-shared';\n\ntype RangeValidation = { min?: number; max?: number };\n\nexport const getRangeFromField = (field: FieldAPI): RangeValidation => {\n const validations = field.validations || [];\n const result = validations.find((validation) => (validation as any).range) as\n | { range: RangeValidation }\n | undefined;\n return result ? result.range : {};\n};\n\nexport const valueToString = (value: number | null | undefined) => {\n return value === undefined ? '' : String(value);\n};\n\nexport const countDecimals = (number: number) => {\n return number.toString().split('.')[1]?.length ?? 0;\n};\n"],"names":["StepChangeType","styles","container","css","position","controlsWrapper","top","right","width","tokens","spacingL","height","display","flexDirection","control","alignItems","justifyContent","minHeight","cursor","padding","margin","outline","border","gray300","background","borderLeftWidth","borderTopRightRadius","borderRadiusMedium","borderTopWidth","borderBottomRightRadius","svg","fill","gray600","backgroundColor","gray200","input","paddingRight","spacingXl","FLOAT_REGEX","INT_REGEX","valueToString","value","undefined","String","InnerNumberEditor","disabled","errors","field","setValue","sdkValue","React","inputValue","setInputValue","range","result","validations","find","validation","getRangeFromField","inputRef","stringSdkValue","updateExternalValue","changeValueByStep","type","number","currentValue","Number","isNaN","nextValue","Increment","toFixed","toString","split","_number$toString$spli2","length","handleControlPointerDown","event","preventDefault","current","focus","className","TextInput","testId","min","max","isRequired","required","isInvalid","isDisabled","ref","onChange","e","target","test","isNumberInputValueValid","parsedNumber","parseInt","parseFloat","parseNumber","setInvalid","onKeyDown","fn","ArrowUp","ArrowDown","Decrement","key","role","tabIndex","onClick","onPointerDown","ArrowUpTrimmedIcon","size","ArrowDownTrimmedIcon","NumberEditor","props","FieldConnector","isInitiallyDisabled","defaultProps"],"mappings":"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"}
|
|
@@ -1,62 +1,95 @@
|
|
|
1
|
-
import { createElement,
|
|
2
|
-
import { FieldConnector } from '@contentful/field-editor-shared';
|
|
3
|
-
import isEmpty from 'lodash-es/isEmpty';
|
|
1
|
+
import { createElement, useState, useRef, useEffect } from 'react';
|
|
4
2
|
import { TextInput } from '@contentful/f36-components';
|
|
3
|
+
import { ArrowUpTrimmedIcon, ArrowDownTrimmedIcon } from '@contentful/f36-icons';
|
|
4
|
+
import { FieldConnector } from '@contentful/field-editor-shared';
|
|
5
|
+
import tokens from '@contentful/f36-tokens';
|
|
6
|
+
import { css } from 'emotion';
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
8
|
+
var styles = {
|
|
9
|
+
container: /*#__PURE__*/css({
|
|
10
|
+
position: 'relative'
|
|
11
|
+
}),
|
|
12
|
+
controlsWrapper: /*#__PURE__*/css({
|
|
13
|
+
position: 'absolute',
|
|
14
|
+
top: '1px',
|
|
15
|
+
right: '1px',
|
|
16
|
+
width: tokens.spacingL,
|
|
17
|
+
height: 'calc(100% - 2px)',
|
|
18
|
+
display: 'flex',
|
|
19
|
+
flexDirection: 'column'
|
|
20
|
+
}),
|
|
21
|
+
control: /*#__PURE__*/css({
|
|
22
|
+
display: 'flex',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
justifyContent: 'center',
|
|
25
|
+
minHeight: 0,
|
|
26
|
+
cursor: 'pointer',
|
|
27
|
+
padding: 0,
|
|
28
|
+
margin: 0,
|
|
29
|
+
outline: 'none',
|
|
30
|
+
border: "0 solid " + tokens.gray300,
|
|
31
|
+
background: 'none',
|
|
32
|
+
borderLeftWidth: '1px',
|
|
33
|
+
'&:first-of-type': {
|
|
34
|
+
borderTopRightRadius: tokens.borderRadiusMedium
|
|
35
|
+
},
|
|
36
|
+
'&:last-of-type': {
|
|
37
|
+
borderTopWidth: '1px',
|
|
38
|
+
borderBottomRightRadius: tokens.borderRadiusMedium
|
|
39
|
+
},
|
|
40
|
+
svg: {
|
|
41
|
+
fill: tokens.gray600
|
|
42
|
+
},
|
|
43
|
+
'&:hover': {
|
|
44
|
+
backgroundColor: tokens.gray200
|
|
45
|
+
},
|
|
46
|
+
'&:active': {
|
|
47
|
+
backgroundColor: tokens.gray300
|
|
48
|
+
}
|
|
49
|
+
}),
|
|
50
|
+
input: /*#__PURE__*/css({
|
|
51
|
+
paddingRight: tokens.spacingXl
|
|
52
|
+
})
|
|
53
|
+
};
|
|
35
54
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
value: floatVal
|
|
40
|
-
};
|
|
55
|
+
function parseNumber(value, type) {
|
|
56
|
+
if (Number.isNaN(+value)) {
|
|
57
|
+
return;
|
|
41
58
|
}
|
|
42
59
|
|
|
43
|
-
return
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
60
|
+
return type === 'Integer' ? parseInt(value, 10) : parseFloat(value);
|
|
61
|
+
}
|
|
62
|
+
var FLOAT_REGEX = /^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]*)?$/;
|
|
63
|
+
var INT_REGEX = /^[+-]?([0-9]*)$/;
|
|
64
|
+
function isNumberInputValueValid(value, type) {
|
|
65
|
+
var regex = type === 'Integer' ? INT_REGEX : FLOAT_REGEX;
|
|
66
|
+
return regex.test(value);
|
|
47
67
|
}
|
|
48
68
|
|
|
49
|
-
function getRangeFromField(field) {
|
|
69
|
+
var getRangeFromField = function getRangeFromField(field) {
|
|
50
70
|
var validations = field.validations || [];
|
|
51
71
|
var result = validations.find(function (validation) {
|
|
52
72
|
return validation.range;
|
|
53
73
|
});
|
|
54
74
|
return result ? result.range : {};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function valueToString(value) {
|
|
75
|
+
};
|
|
76
|
+
var valueToString = function valueToString(value) {
|
|
58
77
|
return value === undefined ? '' : String(value);
|
|
59
|
-
}
|
|
78
|
+
};
|
|
79
|
+
var countDecimals = function countDecimals(number) {
|
|
80
|
+
var _number$toString$spli, _number$toString$spli2;
|
|
81
|
+
|
|
82
|
+
return (_number$toString$spli = (_number$toString$spli2 = number.toString().split('.')[1]) == null ? void 0 : _number$toString$spli2.length) != null ? _number$toString$spli : 0;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
var StepChangeType;
|
|
86
|
+
|
|
87
|
+
(function (StepChangeType) {
|
|
88
|
+
StepChangeType["Increment"] = "increment";
|
|
89
|
+
StepChangeType["Decrement"] = "decrement";
|
|
90
|
+
})(StepChangeType || (StepChangeType = {}));
|
|
91
|
+
|
|
92
|
+
var NUMBER_STEP = 1;
|
|
60
93
|
|
|
61
94
|
function InnerNumberEditor(_ref) {
|
|
62
95
|
var disabled = _ref.disabled,
|
|
@@ -64,46 +97,137 @@ function InnerNumberEditor(_ref) {
|
|
|
64
97
|
field = _ref.field,
|
|
65
98
|
setValue = _ref.setValue,
|
|
66
99
|
sdkValue = _ref.value;
|
|
67
|
-
var previousValue = useRef(valueToString(sdkValue));
|
|
68
100
|
|
|
69
101
|
var _React$useState = useState(valueToString(sdkValue)),
|
|
70
102
|
inputValue = _React$useState[0],
|
|
71
103
|
setInputValue = _React$useState[1];
|
|
72
104
|
|
|
73
105
|
var range = getRangeFromField(field);
|
|
106
|
+
var inputRef = useRef(null);
|
|
74
107
|
useEffect(function () {
|
|
75
|
-
|
|
108
|
+
var stringSdkValue = valueToString(sdkValue); // Update the input value if the SDK value (numeric) changes
|
|
109
|
+
|
|
110
|
+
if (stringSdkValue !== inputValue) {
|
|
111
|
+
setInputValue(stringSdkValue);
|
|
112
|
+
} // eslint-disable-next-line react-hooks/exhaustive-deps -- we want to trigger it only when sdkValue has changed
|
|
113
|
+
|
|
76
114
|
}, [sdkValue]);
|
|
77
|
-
useEffect(function () {
|
|
78
|
-
var stringifiedSdkValue = valueToString(sdkValue); // Update the input value (string) if the SDK value (numeric) changes
|
|
79
115
|
|
|
80
|
-
|
|
81
|
-
|
|
116
|
+
var updateExternalValue = function updateExternalValue(value) {
|
|
117
|
+
if (sdkValue !== value) {
|
|
118
|
+
setValue(value);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
var changeValueByStep = function changeValueByStep(type) {
|
|
123
|
+
var currentValue = Number.isNaN(+inputValue) ? 0 : +inputValue;
|
|
124
|
+
var nextValue = type === StepChangeType.Increment ? currentValue + NUMBER_STEP : currentValue - NUMBER_STEP; // Floating point numbers cannot represent all decimals precisely in binary.
|
|
125
|
+
// This can lead to unexpected results, such as 0.1 + 0.2 = 0.30000000000000004.
|
|
126
|
+
// See more details: https://floating-point-gui.de/
|
|
127
|
+
|
|
128
|
+
nextValue = +nextValue.toFixed(countDecimals(currentValue));
|
|
129
|
+
setInputValue(valueToString(nextValue));
|
|
130
|
+
setValue(nextValue);
|
|
131
|
+
}; // Keeps focus on the input
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
var handleControlPointerDown = function handleControlPointerDown(event) {
|
|
135
|
+
var _inputRef$current;
|
|
136
|
+
|
|
137
|
+
event.preventDefault();
|
|
138
|
+
(_inputRef$current = inputRef.current) == null ? void 0 : _inputRef$current.focus();
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
var handleKeyDown = function handleKeyDown(event) {
|
|
142
|
+
var keyToFnMap = {
|
|
143
|
+
ArrowUp: function ArrowUp() {
|
|
144
|
+
return changeValueByStep(StepChangeType.Increment);
|
|
145
|
+
},
|
|
146
|
+
ArrowDown: function ArrowDown() {
|
|
147
|
+
return changeValueByStep(StepChangeType.Decrement);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
var fn = keyToFnMap[event.key];
|
|
151
|
+
|
|
152
|
+
if (fn) {
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
fn();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
var handleInputChange = function handleInputChange(e) {
|
|
159
|
+
var value = e.target.value;
|
|
160
|
+
|
|
161
|
+
if (!value) {
|
|
162
|
+
setInputValue(value);
|
|
163
|
+
updateExternalValue(undefined);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!isNumberInputValueValid(value, field.type)) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
setInputValue(value);
|
|
172
|
+
var parsedNumber = parseNumber(value, field.type);
|
|
173
|
+
field.setInvalid(parsedNumber === undefined);
|
|
174
|
+
|
|
175
|
+
if (parsedNumber !== undefined) {
|
|
176
|
+
updateExternalValue(parsedNumber);
|
|
82
177
|
}
|
|
83
|
-
}
|
|
178
|
+
};
|
|
179
|
+
|
|
84
180
|
return createElement("div", {
|
|
85
|
-
"data-test-id": "number-editor"
|
|
86
|
-
|
|
181
|
+
"data-test-id": "number-editor",
|
|
182
|
+
className: styles.container
|
|
183
|
+
}, createElement(TextInput // With type="number" react doesn't call onChange for certain inputs, for example if you type `e`
|
|
184
|
+
// so we use "text" instead and fully rely on our own validation.
|
|
185
|
+
// See more details: https://github.com/facebook/react/issues/6556
|
|
186
|
+
, {
|
|
187
|
+
// With type="number" react doesn't call onChange for certain inputs, for example if you type `e`
|
|
188
|
+
// so we use "text" instead and fully rely on our own validation.
|
|
189
|
+
// See more details: https://github.com/facebook/react/issues/6556
|
|
190
|
+
type: "text",
|
|
87
191
|
testId: "number-editor-input",
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
192
|
+
className: styles.input,
|
|
193
|
+
min: range.min,
|
|
194
|
+
max: range.max,
|
|
91
195
|
isRequired: field.required,
|
|
92
196
|
isInvalid: errors.length > 0,
|
|
93
197
|
isDisabled: disabled,
|
|
94
198
|
value: inputValue,
|
|
95
|
-
|
|
96
|
-
onChange:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
199
|
+
ref: inputRef,
|
|
200
|
+
onChange: handleInputChange,
|
|
201
|
+
onKeyDown: handleKeyDown,
|
|
202
|
+
// The same role that input type="number" has
|
|
203
|
+
// See more details: https://www.digitala11y.com/spinbutton-role/
|
|
204
|
+
role: "spinbutton",
|
|
205
|
+
"aria-valuenow": sdkValue != null ? sdkValue : 0,
|
|
206
|
+
"aria-valuetext": inputValue,
|
|
207
|
+
"aria-valuemin": range.min,
|
|
208
|
+
"aria-valuemax": range.max
|
|
209
|
+
}), !disabled && createElement("div", {
|
|
210
|
+
className: styles.controlsWrapper,
|
|
211
|
+
"aria-hidden": "true"
|
|
212
|
+
}, createElement("button", {
|
|
213
|
+
tabIndex: -1,
|
|
214
|
+
className: styles.control,
|
|
215
|
+
onClick: function onClick() {
|
|
216
|
+
return changeValueByStep(StepChangeType.Increment);
|
|
217
|
+
},
|
|
218
|
+
onPointerDown: handleControlPointerDown
|
|
219
|
+
}, createElement(ArrowUpTrimmedIcon, {
|
|
220
|
+
size: "medium"
|
|
221
|
+
})), createElement("button", {
|
|
222
|
+
tabIndex: -1,
|
|
223
|
+
className: styles.control,
|
|
224
|
+
onClick: function onClick() {
|
|
225
|
+
return changeValueByStep(StepChangeType.Decrement);
|
|
226
|
+
},
|
|
227
|
+
onPointerDown: handleControlPointerDown
|
|
228
|
+
}, createElement(ArrowDownTrimmedIcon, {
|
|
229
|
+
size: "medium"
|
|
230
|
+
}))));
|
|
107
231
|
}
|
|
108
232
|
|
|
109
233
|
function NumberEditor(props) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-editor-number.esm.js","sources":["../src/parseNumber.ts","../src/NumberEditor.tsx"],"sourcesContent":["import isEmpty from 'lodash/isEmpty';\n\nexport function parseNumber(\n value: string,\n type: string\n): {\n isValid: boolean;\n value: number | undefined;\n} {\n // This has saner semantics than parseFloat.\n // For values with chars in them, it gives\n // us NaN unlike parseFloat\n const floatVal = +value;\n const hasDot = value.includes('.');\n const hasFractional = /^(?:\\+|-)?\\d+\\.\\d+$/.test(value);\n\n if (isEmpty(value)) {\n return {\n isValid: true,\n value: undefined,\n };\n }\n\n if (isNaN(floatVal)) {\n return {\n isValid: false,\n value: undefined,\n };\n }\n\n if (type === 'Integer' && hasDot) {\n const intVal = parseInt(value, 10);\n\n return {\n isValid: false,\n value: intVal,\n };\n }\n\n if (hasDot && !hasFractional) {\n return {\n isValid: false,\n value: floatVal,\n };\n }\n\n return {\n isValid: true,\n value: floatVal,\n };\n}\n","import * as React from 'react';\nimport {\n FieldAPI,\n FieldConnector,\n FieldConnectorChildProps,\n} from '@contentful/field-editor-shared';\nimport { parseNumber } from './parseNumber';\n\nimport { TextInput } from '@contentful/f36-components';\n\nexport interface NumberEditorProps {\n /**\n * is the field disabled initially\n */\n isInitiallyDisabled: boolean;\n\n /**\n * sdk.field\n */\n field: FieldAPI;\n}\n\ntype RangeValidation = { min?: number; max?: number };\n\nfunction getRangeFromField(field: FieldAPI): RangeValidation {\n const validations = field.validations || [];\n const result = validations.find((validation) => (validation as any).range) as\n | { range: RangeValidation }\n | undefined;\n return result ? result.range : {};\n}\n\nfunction valueToString(value: InnerNumberEditorProps['value']) {\n return value === undefined ? '' : String(value);\n}\n\ntype InnerNumberEditorProps = Pick<\n FieldConnectorChildProps<number>,\n 'disabled' | 'errors' | 'setValue' | 'value'\n> & {\n field: NumberEditorProps['field'];\n};\n\nfunction InnerNumberEditor({\n disabled,\n errors,\n field,\n setValue,\n value: sdkValue,\n}: InnerNumberEditorProps) {\n const previousValue = React.useRef(valueToString(sdkValue));\n const [inputValue, setInputValue] = React.useState(valueToString(sdkValue));\n const range = getRangeFromField(field);\n\n React.useEffect(() => {\n previousValue.current = valueToString(sdkValue);\n }, [sdkValue]);\n\n React.useEffect(() => {\n const stringifiedSdkValue = valueToString(sdkValue);\n // Update the input value (string) if the SDK value (numeric) changes\n if (stringifiedSdkValue !== previousValue.current && stringifiedSdkValue !== inputValue) {\n setInputValue(stringifiedSdkValue);\n }\n }, [inputValue, sdkValue]);\n\n return (\n <div data-test-id=\"number-editor\">\n <TextInput\n testId=\"number-editor-input\"\n min={range.min !== undefined ? String(range.min) : ''}\n max={range.max !== undefined ? String(range.max) : ''}\n step={field.type === 'Integer' ? '1' : ''}\n isRequired={field.required}\n isInvalid={errors.length > 0}\n isDisabled={disabled}\n value={inputValue}\n type=\"number\"\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => {\n const parseResult = parseNumber(e.target.value, field.type);\n field.setInvalid(!parseResult.isValid);\n\n if (parseResult.isValid) {\n setValue(parseResult.value);\n }\n\n setInputValue(e.target.value);\n }}\n />\n </div>\n );\n}\n\nexport function NumberEditor(props: NumberEditorProps) {\n const { field } = props;\n\n return (\n <FieldConnector<number> field={field} isInitiallyDisabled={props.isInitiallyDisabled}>\n {({\n value,\n errors,\n disabled,\n setValue,\n }: Pick<FieldConnectorChildProps<number>, 'disabled' | 'errors' | 'setValue' | 'value'>) => (\n <InnerNumberEditor\n disabled={disabled}\n errors={errors}\n field={field}\n setValue={setValue}\n value={value}\n />\n )}\n </FieldConnector>\n );\n}\n\nNumberEditor.defaultProps = {\n isInitiallyDisabled: true,\n};\n"],"names":["parseNumber","value","type","floatVal","hasDot","includes","hasFractional","test","isEmpty","isValid","undefined","isNaN","intVal","parseInt","getRangeFromField","field","validations","result","find","validation","range","valueToString","String","InnerNumberEditor","disabled","errors","setValue","sdkValue","previousValue","React","inputValue","setInputValue","current","stringifiedSdkValue","TextInput","testId","min","max","step","isRequired","required","isInvalid","length","isDisabled","onChange","e","parseResult","target","setInvalid","NumberEditor","props","FieldConnector","isInitiallyDisabled","defaultProps"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"field-editor-number.esm.js","sources":["../src/NumberEditor.styles.ts","../src/parseNumber.ts","../src/utils.ts","../src/NumberEditor.tsx"],"sourcesContent":["import tokens from '@contentful/f36-tokens';\nimport { css } from 'emotion';\n\nexport const styles = {\n container: css({\n position: 'relative',\n }),\n controlsWrapper: css({\n position: 'absolute',\n top: '1px',\n right: '1px',\n width: tokens.spacingL,\n height: 'calc(100% - 2px)',\n display: 'flex',\n flexDirection: 'column',\n }),\n control: css({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: 0,\n cursor: 'pointer',\n padding: 0,\n margin: 0,\n outline: 'none',\n border: `0 solid ${tokens.gray300}`,\n background: 'none',\n borderLeftWidth: '1px',\n\n '&:first-of-type': {\n borderTopRightRadius: tokens.borderRadiusMedium,\n },\n\n '&:last-of-type': {\n borderTopWidth: '1px',\n borderBottomRightRadius: tokens.borderRadiusMedium,\n },\n\n svg: {\n fill: tokens.gray600,\n },\n\n '&:hover': {\n backgroundColor: tokens.gray200,\n },\n\n '&:active': {\n backgroundColor: tokens.gray300,\n },\n }),\n input: css({\n paddingRight: tokens.spacingXl,\n }),\n};\n","export function parseNumber(value: string, type: string) {\n if (Number.isNaN(+value)) {\n return;\n }\n\n return type === 'Integer' ? parseInt(value, 10) : parseFloat(value);\n}\n\nconst FLOAT_REGEX = /^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]*)?$/;\nconst INT_REGEX = /^[+-]?([0-9]*)$/;\n\nexport function isNumberInputValueValid(value: string, type: string) {\n const regex = type === 'Integer' ? INT_REGEX : FLOAT_REGEX;\n\n return regex.test(value);\n}\n","import { FieldAPI } from '@contentful/field-editor-shared';\n\ntype RangeValidation = { min?: number; max?: number };\n\nexport const getRangeFromField = (field: FieldAPI): RangeValidation => {\n const validations = field.validations || [];\n const result = validations.find((validation) => (validation as any).range) as\n | { range: RangeValidation }\n | undefined;\n return result ? result.range : {};\n};\n\nexport const valueToString = (value: number | null | undefined) => {\n return value === undefined ? '' : String(value);\n};\n\nexport const countDecimals = (number: number) => {\n return number.toString().split('.')[1]?.length ?? 0;\n};\n","import * as React from 'react';\n\nimport { TextInput } from '@contentful/f36-components';\nimport { ArrowUpTrimmedIcon, ArrowDownTrimmedIcon } from '@contentful/f36-icons';\nimport {\n FieldAPI,\n FieldConnector,\n FieldConnectorChildProps,\n} from '@contentful/field-editor-shared';\n\nimport { styles } from './NumberEditor.styles';\nimport { isNumberInputValueValid, parseNumber } from './parseNumber';\nimport { getRangeFromField, valueToString, countDecimals } from './utils';\n\nexport interface NumberEditorProps {\n /**\n * is the field disabled initially\n */\n isInitiallyDisabled: boolean;\n\n /**\n * sdk.field\n */\n field: FieldAPI;\n}\n\ntype InnerNumberEditorProps = Pick<\n FieldConnectorChildProps<number>,\n 'disabled' | 'errors' | 'setValue' | 'value'\n> & {\n field: NumberEditorProps['field'];\n};\n\nenum StepChangeType {\n Increment = 'increment',\n Decrement = 'decrement',\n}\n\nconst NUMBER_STEP = 1;\n\nfunction InnerNumberEditor({\n disabled,\n errors,\n field,\n setValue,\n value: sdkValue,\n}: InnerNumberEditorProps) {\n const [inputValue, setInputValue] = React.useState(valueToString(sdkValue));\n const range = getRangeFromField(field);\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n React.useEffect(() => {\n const stringSdkValue = valueToString(sdkValue);\n // Update the input value if the SDK value (numeric) changes\n if (stringSdkValue !== inputValue) {\n setInputValue(stringSdkValue);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- we want to trigger it only when sdkValue has changed\n }, [sdkValue]);\n\n const updateExternalValue = (value: number | undefined) => {\n if (sdkValue !== value) {\n setValue(value);\n }\n };\n\n const changeValueByStep = (type: StepChangeType) => {\n const currentValue = Number.isNaN(+inputValue) ? 0 : +inputValue;\n let nextValue =\n type === StepChangeType.Increment ? currentValue + NUMBER_STEP : currentValue - NUMBER_STEP;\n // Floating point numbers cannot represent all decimals precisely in binary.\n // This can lead to unexpected results, such as 0.1 + 0.2 = 0.30000000000000004.\n // See more details: https://floating-point-gui.de/\n nextValue = +nextValue.toFixed(countDecimals(currentValue));\n\n setInputValue(valueToString(nextValue));\n setValue(nextValue);\n };\n\n // Keeps focus on the input\n const handleControlPointerDown: React.PointerEventHandler<HTMLButtonElement> = (event) => {\n event.preventDefault();\n inputRef.current?.focus();\n };\n\n const handleKeyDown = (event: React.KeyboardEvent<any>) => {\n const keyToFnMap: {\n [key: string]: () => void;\n } = {\n ArrowUp: () => changeValueByStep(StepChangeType.Increment),\n ArrowDown: () => changeValueByStep(StepChangeType.Decrement),\n };\n\n const fn = keyToFnMap[event.key];\n if (fn) {\n event.preventDefault();\n fn();\n }\n };\n\n const handleInputChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {\n const value = e.target.value;\n if (!value) {\n setInputValue(value);\n updateExternalValue(undefined);\n return;\n }\n\n if (!isNumberInputValueValid(value, field.type)) {\n return;\n }\n\n setInputValue(value);\n\n const parsedNumber = parseNumber(value, field.type);\n field.setInvalid(parsedNumber === undefined);\n if (parsedNumber !== undefined) {\n updateExternalValue(parsedNumber);\n }\n };\n\n return (\n <div data-test-id=\"number-editor\" className={styles.container}>\n <TextInput\n // With type=\"number\" react doesn't call onChange for certain inputs, for example if you type `e`\n // so we use \"text\" instead and fully rely on our own validation.\n // See more details: https://github.com/facebook/react/issues/6556\n type=\"text\"\n testId=\"number-editor-input\"\n className={styles.input}\n min={range.min}\n max={range.max}\n isRequired={field.required}\n isInvalid={errors.length > 0}\n isDisabled={disabled}\n value={inputValue}\n ref={inputRef}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n // The same role that input type=\"number\" has\n // See more details: https://www.digitala11y.com/spinbutton-role/\n role=\"spinbutton\"\n aria-valuenow={sdkValue ?? 0}\n aria-valuetext={inputValue}\n aria-valuemin={range.min}\n aria-valuemax={range.max}\n />\n {/**\n * We hide this controls from screen readers and keyboard focus.\n * For those purposes we have a keyboard handler. The same way native input number works.\n */}\n {!disabled && (\n <div className={styles.controlsWrapper} aria-hidden=\"true\">\n <button\n tabIndex={-1}\n className={styles.control}\n onClick={() => changeValueByStep(StepChangeType.Increment)}\n onPointerDown={handleControlPointerDown}>\n <ArrowUpTrimmedIcon size=\"medium\" />\n </button>\n <button\n tabIndex={-1}\n className={styles.control}\n onClick={() => changeValueByStep(StepChangeType.Decrement)}\n onPointerDown={handleControlPointerDown}>\n <ArrowDownTrimmedIcon size=\"medium\" />\n </button>\n </div>\n )}\n </div>\n );\n}\n\nexport function NumberEditor(props: NumberEditorProps) {\n const { field } = props;\n\n return (\n <FieldConnector<number> field={field} isInitiallyDisabled={props.isInitiallyDisabled}>\n {({\n value,\n errors,\n disabled,\n setValue,\n }: Pick<FieldConnectorChildProps<number>, 'disabled' | 'errors' | 'setValue' | 'value'>) => (\n <InnerNumberEditor\n disabled={disabled}\n errors={errors}\n field={field}\n setValue={setValue}\n value={value}\n />\n )}\n </FieldConnector>\n );\n}\n\nNumberEditor.defaultProps = {\n isInitiallyDisabled: true,\n};\n"],"names":["styles","container","css","position","controlsWrapper","top","right","width","tokens","spacingL","height","display","flexDirection","control","alignItems","justifyContent","minHeight","cursor","padding","margin","outline","border","gray300","background","borderLeftWidth","borderTopRightRadius","borderRadiusMedium","borderTopWidth","borderBottomRightRadius","svg","fill","gray600","backgroundColor","gray200","input","paddingRight","spacingXl","parseNumber","value","type","Number","isNaN","parseInt","parseFloat","FLOAT_REGEX","INT_REGEX","isNumberInputValueValid","regex","test","getRangeFromField","field","validations","result","find","validation","range","valueToString","undefined","String","countDecimals","number","toString","split","length","StepChangeType","NUMBER_STEP","InnerNumberEditor","disabled","errors","setValue","sdkValue","React","inputValue","setInputValue","inputRef","stringSdkValue","updateExternalValue","changeValueByStep","currentValue","nextValue","Increment","toFixed","handleControlPointerDown","event","preventDefault","current","focus","handleKeyDown","keyToFnMap","ArrowUp","ArrowDown","Decrement","fn","key","handleInputChange","e","target","parsedNumber","setInvalid","className","TextInput","testId","min","max","isRequired","required","isInvalid","isDisabled","ref","onChange","onKeyDown","role","tabIndex","onClick","onPointerDown","ArrowUpTrimmedIcon","size","ArrowDownTrimmedIcon","NumberEditor","props","FieldConnector","isInitiallyDisabled","defaultProps"],"mappings":"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"}
|
package/dist/parseNumber.d.ts
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
export declare function parseNumber(value: string, type: string):
|
|
2
|
-
|
|
3
|
-
value: number | undefined;
|
|
4
|
-
};
|
|
1
|
+
export declare function parseNumber(value: string, type: string): number | undefined;
|
|
2
|
+
export declare function isNumberInputValueValid(value: string, type: string): boolean;
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FieldAPI } from '@contentful/field-editor-shared';
|
|
2
|
+
declare type RangeValidation = {
|
|
3
|
+
min?: number;
|
|
4
|
+
max?: number;
|
|
5
|
+
};
|
|
6
|
+
export declare const getRangeFromField: (field: FieldAPI) => RangeValidation;
|
|
7
|
+
export declare const valueToString: (value: number | null | undefined) => string;
|
|
8
|
+
export declare const countDecimals: (number: number) => number;
|
|
9
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/field-editor-number",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/field-editor-number.esm.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -24,9 +24,7 @@
|
|
|
24
24
|
"@contentful/f36-components": "^4.0.27",
|
|
25
25
|
"@contentful/f36-tokens": "^4.0.0",
|
|
26
26
|
"@contentful/field-editor-shared": "^1.1.3",
|
|
27
|
-
"emotion": "^10.0.17"
|
|
28
|
-
"lodash": "^4.17.15",
|
|
29
|
-
"lodash-es": "^4.17.15"
|
|
27
|
+
"emotion": "^10.0.17"
|
|
30
28
|
},
|
|
31
29
|
"devDependencies": {
|
|
32
30
|
"@contentful/field-editor-test-utils": "^1.2.2",
|
|
@@ -47,5 +45,5 @@
|
|
|
47
45
|
}
|
|
48
46
|
}
|
|
49
47
|
},
|
|
50
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "68f60f962e6228f551180c3be78988a70d972a51"
|
|
51
49
|
}
|