@qoretechnologies/reqraft 0.10.60-pr.131.gfabf11e → 0.10.60-pr.131.gfcd7fef

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.
@@ -18,13 +18,6 @@ export interface INumberFormFieldProps extends Omit<IReqoreInputProps, 'value' |
18
18
  min_value?: number;
19
19
  max_value?: number;
20
20
  }
21
- /**
22
- * What a typed text is: the number it writes, or - when it writes none - the text as typed. A field of a
23
- * number type is typed freely (David's review of qorus#646): a template (`$local:x`) or an expression
24
- * (`@qty * 2`) is taken up by the field around it, and anything else is kept as typed for the form to flag
25
- * as not a number. `parseInt` cut `12abc` to 12 without a word.
26
- */
27
- export declare const typedNumber: (text: string, type: 'int' | 'float') => number | string;
28
21
  export declare const NumberFormField: ({ onChange, autoFocus, type, value, templates, min_value, max_value, ...rest }: INumberFormFieldProps) => import("react/jsx-runtime").JSX.Element;
29
22
  export default NumberFormField;
30
23
  //# sourceMappingURL=Number.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Number.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/fields/number/Number.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gDAAgD,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,mDAAmD,CAAC;AAQzF,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IACnG,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IAC5C,mFAAmF;IACnF,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAOD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,SAAU,MAAM,QAAQ,KAAK,GAAG,OAAO,KAAG,MAAM,GAAG,MAG1E,CAAC;AAEF,eAAO,MAAM,eAAe,mFASzB,qBAAqB,4CAuGvB,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"Number.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/fields/number/Number.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gDAAgD,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,mDAAmD,CAAC;AAQzF,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IACnG,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IAC5C,mFAAmF;IACnF,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,eAAe,mFASzB,qBAAqB,4CAgHvB,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -22,7 +22,7 @@ var __rest = (this && this.__rest) || function (s, e) {
22
22
  return t;
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.NumberFormField = exports.typedNumber = void 0;
25
+ exports.NumberFormField = void 0;
26
26
  var jsx_runtime_1 = require("react/jsx-runtime");
27
27
  var reqore_1 = require("@qoretechnologies/reqore");
28
28
  var react_1 = require("react");
@@ -31,27 +31,9 @@ var emittedValues_1 = require("../emittedValues");
31
31
  // Const-only usage at render time — the cycle (Number → TemplateField → Number)
32
32
  // is safe the same way Field → AutoFormField → Field is.
33
33
  var TemplateField_1 = require("../template/TemplateField");
34
- /** A whole number as typed: an optional sign and digits. */
35
- var WHOLE_NUMBER = /^[-+]?\d+$/;
36
- /** A decimal number as typed: an optional sign, digits with a point, and an exponent. */
37
- var DECIMAL_NUMBER = /^[-+]?(\d+\.?\d*|\.\d+)([eE][-+]?\d+)?$/;
38
- /**
39
- * What a typed text is: the number it writes, or - when it writes none - the text as typed. A field of a
40
- * number type is typed freely (David's review of qorus#646): a template (`$local:x`) or an expression
41
- * (`@qty * 2`) is taken up by the field around it, and anything else is kept as typed for the form to flag
42
- * as not a number. `parseInt` cut `12abc` to 12 without a word.
43
- */
44
- var typedNumber = function (text, type) {
45
- var trimmed = text.trim();
46
- return (type === 'int' ? WHOLE_NUMBER : DECIMAL_NUMBER).test(trimmed) ? Number(trimmed) : text;
47
- };
48
- exports.typedNumber = typedNumber;
49
34
  var NumberFormField = function (_a) {
50
35
  var onChange = _a.onChange, autoFocus = _a.autoFocus, _b = _a.type, type = _b === void 0 ? 'int' : _b, value = _a.value, templates = _a.templates, min_value = _a.min_value, max_value = _a.max_value, rest = __rest(_a, ["onChange", "autoFocus", "type", "value", "templates", "min_value", "max_value"]);
51
- // What is shown is what was typed; what the form gets is the number it writes, or the text when it writes
52
- // none. Showing the number instead dropped a decimal point mid-typing: `10.` is 10, and `10.9` became `109`.
53
- var _c = (0, react_1.useState)(value === undefined || value === null ? '' : String(value)), text = _c[0], setText = _c[1];
54
- var localValue = text === '' ? '' : (0, exports.typedNumber)(text, type);
36
+ var _c = (0, react_1.useState)(value !== null && value !== void 0 ? value : ''), localValue = _c[0], setLocalValue = _c[1];
55
37
  // The parent's echo of an emit still in flight is not a change: copying it
56
38
  // over the local value lost what was typed since. See `EmittedValues`.
57
39
  var emitted = (0, emittedValues_1.useEmittedValues)();
@@ -60,7 +42,7 @@ var NumberFormField = function (_a) {
60
42
  return;
61
43
  }
62
44
  if (value !== localValue) {
63
- setText(value === undefined || value === null ? '' : String(value));
45
+ setLocalValue(value !== null && value !== void 0 ? value : '');
64
46
  }
65
47
  }, [value]);
66
48
  (0, react_use_1.useDebounce)(function () {
@@ -70,13 +52,18 @@ var NumberFormField = function (_a) {
70
52
  }
71
53
  }, 100, [localValue, onChange]);
72
54
  var handleChange = (0, react_1.useCallback)(function (rawValue) {
73
- setText(String(rawValue));
74
- }, []);
55
+ if (rawValue === '' || rawValue === '-' || rawValue === '-.') {
56
+ setLocalValue(rawValue);
57
+ return;
58
+ }
59
+ var parsed = type === 'int' ? parseInt(rawValue, 10) : parseFloat(rawValue);
60
+ setLocalValue(isNaN(parsed) ? rawValue : parsed);
61
+ }, [type]);
75
62
  var handleInputChange = (0, react_1.useCallback)(function (event) {
76
63
  handleChange(event.target.value);
77
64
  }, [handleChange]);
78
65
  var handleResetClick = (0, react_1.useCallback)(function () {
79
- setText('0');
66
+ setLocalValue(0);
80
67
  emitted.record(0);
81
68
  onChange === null || onChange === void 0 ? void 0 : onChange(0);
82
69
  }, [onChange]);
@@ -88,22 +75,16 @@ var NumberFormField = function (_a) {
88
75
  }
89
76
  : undefined;
90
77
  }, [autoFocus]);
91
- var handleItemSelect = (0, react_1.useCallback)(function (item) { return setText(String(item.value)); }, []);
92
- // The schema's bounds are the form's to check (a text input has no min or max); they are named in the
93
- // props only to keep them off the DOM node.
94
- void min_value;
95
- void max_value;
78
+ var handleItemSelect = (0, react_1.useCallback)(function (item) { return setLocalValue(item.value); }, []);
79
+ // The schema's bounds under the names the input uses. Omitted entirely when the schema
80
+ // declares none, so an undeclared bound stays undeclared rather than becoming `min={0}`.
81
+ var bounds = (0, react_1.useMemo)(function () { return (__assign(__assign({}, (typeof min_value === 'number' ? { min: min_value } : {})), (typeof max_value === 'number' ? { max: max_value } : {}))); }, [min_value, max_value]);
96
82
  // IDE `NumberField` parity: with templates the input is wrapped in a
97
83
  // focus-opened dropdown of the template values.
98
84
  if (templates === null || templates === void 0 ? void 0 : templates.items) {
99
- return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreDropdown, __assign({}, rest, { component: reqore_1.ReqoreInput, fluid: true, icon: 'MoneyDollarCircleLine', items: templates === null || templates === void 0 ? void 0 : templates.items, filterable: true, value: text, onItemSelect: handleItemSelect, onChange: handleInputChange,
100
- // typed freely: a number input drops what is not part of a number, so a template or an expression
101
- // could not be typed; the keyboard is still a numeric one
102
- type: 'text', inputMode: type === 'int' ? 'numeric' : 'decimal', onClearClick: handleResetClick, focusRules: focusRules }, TemplateField_1.TemplatesListProps)));
85
+ return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreDropdown, __assign({}, rest, { component: reqore_1.ReqoreInput, fluid: true, icon: 'MoneyDollarCircleLine', items: templates === null || templates === void 0 ? void 0 : templates.items, filterable: true, value: localValue, onItemSelect: handleItemSelect, onChange: handleInputChange, type: 'number', step: type === 'int' ? 1 : 0.1 }, bounds, { onClearClick: handleResetClick, focusRules: focusRules }, TemplateField_1.TemplatesListProps)));
103
86
  }
104
- return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreInput, __assign({ fluid: true, icon: 'MoneyDollarCircleLine', value: text, onChange: handleInputChange,
105
- // typed freely, on a numeric keyboard (see above)
106
- type: 'text', inputMode: type === 'int' ? 'numeric' : 'decimal', onClearClick: handleResetClick, focusRules: focusRules }, rest)));
87
+ return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreInput, __assign({ fluid: true, icon: 'MoneyDollarCircleLine', value: localValue, onChange: handleInputChange, type: 'number', step: type === 'int' ? 1 : 0.1, onClearClick: handleResetClick, focusRules: focusRules }, rest, bounds)));
107
88
  };
108
89
  exports.NumberFormField = NumberFormField;
109
90
  exports.default = exports.NumberFormField;
@@ -1 +1 @@
1
- {"version":3,"file":"Number.js","sourceRoot":"","sources":["../../../../../src/components/form/fields/number/Number.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Number.js","sourceRoot":"","sources":["../../../../../src/components/form/fields/number/Number.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qoretechnologies/reqraft",
3
- "version": "0.10.60-pr.131.gfabf11e",
3
+ "version": "0.10.60-pr.131.gfcd7fef",
4
4
  "description": "ReQraft is a collection of React components and hooks that are used across Qore Technologies' products made using the ReQore component library from Qore.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -575,7 +575,7 @@ export const Timeout: Story = {
575
575
 
576
576
  const input = await canvas.findByDisplayValue('45');
577
577
  await expect(input).toBeInTheDocument();
578
- await expect(input).toHaveAttribute('inputmode', 'numeric');
578
+ await expect(input).toHaveAttribute('type', 'number');
579
579
  await expect(canvas.getByText('seconds')).toBeInTheDocument();
580
580
  await expect(canvas.queryByText('Unknown type!')).not.toBeInTheDocument();
581
581
  },
@@ -51,14 +51,12 @@ export const Integer: Story = {
51
51
  const input = canvas.getByLabelText('Number');
52
52
 
53
53
  await expect(input).toBeInTheDocument();
54
- await expect(input).toHaveValue('42');
55
- // typed freely, on a numeric keyboard
56
- await expect(input).toHaveAttribute('type', 'text');
57
- await expect(input).toHaveAttribute('inputmode', 'numeric');
54
+ await expect(input).toHaveValue(42);
55
+ await expect(input).toHaveAttribute('type', 'number');
58
56
 
59
57
  await userEvent.clear(input);
60
58
  await userEvent.type(input, '10');
61
- await expect(input).toHaveValue('10');
59
+ await expect(input).toHaveValue(10);
62
60
  await waitFor(() => expect(args.onChange).toHaveBeenLastCalledWith(10), { timeout: 500 });
63
61
  },
64
62
  };
@@ -72,7 +70,7 @@ export const Float: Story = {
72
70
  docs: {
73
71
  description: {
74
72
  story:
75
- 'Renders the Number field in float mode with 3.14 pre-populated — the input asks for a decimal keyboard and accepts decimals.',
73
+ 'Renders the Number field in float mode with 3.14 pre-populated — the input uses a 0.1 step and accepts decimals.',
76
74
  },
77
75
  },
78
76
  },
@@ -80,12 +78,12 @@ export const Float: Story = {
80
78
  const canvas = within(canvasElement);
81
79
  const input = canvas.getByLabelText('Number');
82
80
 
83
- await expect(input).toHaveValue('3.14');
84
- await expect(input).toHaveAttribute('inputmode', 'decimal');
81
+ await expect(input).toHaveValue(3.14);
82
+ await expect(input).toHaveAttribute('step', '0.1');
85
83
 
86
84
  await userEvent.clear(input);
87
85
  await userEvent.type(input, '10.9');
88
- await expect(input).toHaveValue('10.9');
86
+ await expect(input).toHaveValue(10.9);
89
87
  await waitFor(() => expect(args.onChange).toHaveBeenLastCalledWith(10.9), { timeout: 500 });
90
88
  },
91
89
  };
@@ -106,7 +104,7 @@ export const Empty: Story = {
106
104
  const input = canvas.getByLabelText('Number');
107
105
 
108
106
  await expect(input).toBeInTheDocument();
109
- await expect(input).toHaveValue('');
107
+ await expect(input).toHaveValue(null);
110
108
  },
111
109
  };
112
110
 
@@ -129,31 +127,6 @@ export const Disabled: Story = {
129
127
  const input = canvas.getByLabelText('Number');
130
128
 
131
129
  await expect(input).toBeDisabled();
132
- await expect(input).toHaveValue('99');
133
- },
134
- };
135
-
136
- export const TypedFreely: Story = {
137
- args: {
138
- value: 7,
139
- type: 'int',
140
- },
141
- parameters: {
142
- docs: {
143
- description: {
144
- story:
145
- 'Renders the Number field in integer mode after typing what is not a number: 12abc stays as typed (the form flags it, it is not cut to 12), and a template ($local:quantity) or an expression (@qty * 2) is passed on as typed for the field around it to take up.',
146
- },
147
- },
148
- },
149
- async play({ args, canvasElement }) {
150
- const canvas = within(canvasElement);
151
- const input = canvas.getByLabelText('Number');
152
- for (const text of ['12abc', '@qty * 2', '$local:quantity']) {
153
- await userEvent.clear(input);
154
- await userEvent.type(input, text);
155
- await expect(input).toHaveValue(text);
156
- await waitFor(() => expect(args.onChange).toHaveBeenLastCalledWith(text), { timeout: 500 });
157
- }
130
+ await expect(input).toHaveValue(99);
158
131
  },
159
132
  };
@@ -27,22 +27,6 @@ export interface INumberFormFieldProps extends Omit<IReqoreInputProps, 'value' |
27
27
  max_value?: number;
28
28
  }
29
29
 
30
- /** A whole number as typed: an optional sign and digits. */
31
- const WHOLE_NUMBER = /^[-+]?\d+$/;
32
- /** A decimal number as typed: an optional sign, digits with a point, and an exponent. */
33
- const DECIMAL_NUMBER = /^[-+]?(\d+\.?\d*|\.\d+)([eE][-+]?\d+)?$/;
34
-
35
- /**
36
- * What a typed text is: the number it writes, or - when it writes none - the text as typed. A field of a
37
- * number type is typed freely (David's review of qorus#646): a template (`$local:x`) or an expression
38
- * (`@qty * 2`) is taken up by the field around it, and anything else is kept as typed for the form to flag
39
- * as not a number. `parseInt` cut `12abc` to 12 without a word.
40
- */
41
- export const typedNumber = (text: string, type: 'int' | 'float'): number | string => {
42
- const trimmed = text.trim();
43
- return (type === 'int' ? WHOLE_NUMBER : DECIMAL_NUMBER).test(trimmed) ? Number(trimmed) : text;
44
- };
45
-
46
30
  export const NumberFormField = ({
47
31
  onChange,
48
32
  autoFocus,
@@ -53,10 +37,7 @@ export const NumberFormField = ({
53
37
  max_value,
54
38
  ...rest
55
39
  }: INumberFormFieldProps) => {
56
- // What is shown is what was typed; what the form gets is the number it writes, or the text when it writes
57
- // none. Showing the number instead dropped a decimal point mid-typing: `10.` is 10, and `10.9` became `109`.
58
- const [text, setText] = useState<string>(value === undefined || value === null ? '' : String(value));
59
- const localValue = text === '' ? '' : typedNumber(text, type);
40
+ const [localValue, setLocalValue] = useState<number | string>(value ?? '');
60
41
  // The parent's echo of an emit still in flight is not a change: copying it
61
42
  // over the local value lost what was typed since. See `EmittedValues`.
62
43
  const emitted = useEmittedValues<number | string | undefined>();
@@ -66,7 +47,7 @@ export const NumberFormField = ({
66
47
  return;
67
48
  }
68
49
  if (value !== localValue) {
69
- setText(value === undefined || value === null ? '' : String(value));
50
+ setLocalValue(value ?? '');
70
51
  }
71
52
  }, [value]);
72
53
 
@@ -81,9 +62,17 @@ export const NumberFormField = ({
81
62
  [localValue, onChange]
82
63
  );
83
64
 
84
- const handleChange = useCallback((rawValue: number | string): void => {
85
- setText(String(rawValue));
86
- }, []);
65
+ const handleChange = useCallback(
66
+ (rawValue: number | string): void => {
67
+ if (rawValue === '' || rawValue === '-' || rawValue === '-.') {
68
+ setLocalValue(rawValue);
69
+ return;
70
+ }
71
+ const parsed = type === 'int' ? parseInt(rawValue as string, 10) : parseFloat(rawValue as string);
72
+ setLocalValue(isNaN(parsed) ? rawValue : parsed);
73
+ },
74
+ [type]
75
+ );
87
76
 
88
77
  const handleInputChange = useCallback(
89
78
  (event: ChangeEvent<HTMLInputElement>): void => {
@@ -93,7 +82,7 @@ export const NumberFormField = ({
93
82
  );
94
83
 
95
84
  const handleResetClick = useCallback((): void => {
96
- setText('0');
85
+ setLocalValue(0);
97
86
  emitted.record(0);
98
87
  onChange?.(0);
99
88
  }, [onChange]);
@@ -109,12 +98,17 @@ export const NumberFormField = ({
109
98
  [autoFocus]
110
99
  );
111
100
 
112
- const handleItemSelect = useCallback((item) => setText(String(item.value)), []);
101
+ const handleItemSelect = useCallback((item) => setLocalValue(item.value), []);
113
102
 
114
- // The schema's bounds are the form's to check (a text input has no min or max); they are named in the
115
- // props only to keep them off the DOM node.
116
- void min_value;
117
- void max_value;
103
+ // The schema's bounds under the names the input uses. Omitted entirely when the schema
104
+ // declares none, so an undeclared bound stays undeclared rather than becoming `min={0}`.
105
+ const bounds = useMemo(
106
+ () => ({
107
+ ...(typeof min_value === 'number' ? { min: min_value } : {}),
108
+ ...(typeof max_value === 'number' ? { max: max_value } : {}),
109
+ }),
110
+ [min_value, max_value]
111
+ );
118
112
 
119
113
  // IDE `NumberField` parity: with templates the input is wrapped in a
120
114
  // focus-opened dropdown of the template values.
@@ -127,13 +121,12 @@ export const NumberFormField = ({
127
121
  icon='MoneyDollarCircleLine'
128
122
  items={templates?.items}
129
123
  filterable
130
- value={text}
124
+ value={localValue}
131
125
  onItemSelect={handleItemSelect}
132
126
  onChange={handleInputChange}
133
- // typed freely: a number input drops what is not part of a number, so a template or an expression
134
- // could not be typed; the keyboard is still a numeric one
135
- type='text'
136
- inputMode={type === 'int' ? 'numeric' : 'decimal'}
127
+ type='number'
128
+ step={type === 'int' ? 1 : 0.1}
129
+ {...bounds}
137
130
  onClearClick={handleResetClick}
138
131
  focusRules={focusRules}
139
132
  {...TemplatesListProps}
@@ -145,14 +138,14 @@ export const NumberFormField = ({
145
138
  <ReqoreInput
146
139
  fluid
147
140
  icon='MoneyDollarCircleLine'
148
- value={text}
141
+ value={localValue}
149
142
  onChange={handleInputChange}
150
- // typed freely, on a numeric keyboard (see above)
151
- type='text'
152
- inputMode={type === 'int' ? 'numeric' : 'decimal'}
143
+ type='number'
144
+ step={type === 'int' ? 1 : 0.1}
153
145
  onClearClick={handleResetClick}
154
146
  focusRules={focusRules}
155
147
  {...rest}
148
+ {...bounds}
156
149
  />
157
150
  );
158
151
  };