@hsu-react/ui 2.5.2 → 2.5.4
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/es/components/Chart/Bubble/index.js +7 -2
- package/es/components/Chart/Common/index.js +7 -2
- package/es/components/Chart/Heatmap/index.js +7 -2
- package/es/components/Chart/Pie/Pie3D/index.js +8 -2
- package/es/components/Chart/Pie/index.js +7 -2
- package/es/components/Chat/ChatInput/index.js +7 -2
- package/es/components/Icon/index.js +8 -2
- package/es/components/Input/Number/index.js +28 -21
- package/es/components/Input/Password/index.js +44 -36
- package/es/components/Input/Search/index.js +44 -36
- package/es/components/Input/TextArea/index.js +64 -42
- package/es/components/Input/index.js +40 -34
- package/es/components/Select/_hooks/useSelectInputOptions.js +8 -2
- package/es/components/Slider/index.js +24 -15
- package/es/components/Upload/_hooks/useUploadFileList.js +8 -2
- package/es/components/Upload/_hooks/useUploadOperations.js +8 -2
- package/es/hooks/useLatestRef.d.ts +24 -0
- package/es/hooks/useLatestRef.js +29 -0
- package/es/layout/Menu/index.js +24 -6
- package/es/layout/Menu/index.module.scss +53 -10
- package/lib/components/Chart/Bubble/index.js +6 -2
- package/lib/components/Chart/Common/index.js +6 -2
- package/lib/components/Chart/Heatmap/index.js +6 -2
- package/lib/components/Chart/Pie/Pie3D/index.js +7 -2
- package/lib/components/Chart/Pie/index.js +6 -2
- package/lib/components/Chat/ChatInput/index.js +6 -2
- package/lib/components/Icon/index.js +7 -2
- package/lib/components/Input/Number/index.js +27 -18
- package/lib/components/Input/Password/index.js +40 -26
- package/lib/components/Input/Search/index.js +42 -26
- package/lib/components/Input/TextArea/index.js +54 -26
- package/lib/components/Input/index.js +37 -24
- package/lib/components/Select/_hooks/useSelectInputOptions.js +7 -2
- package/lib/components/Slider/index.js +23 -11
- package/lib/components/Upload/_hooks/useUploadFileList.js +7 -2
- package/lib/components/Upload/_hooks/useUploadOperations.js +7 -2
- package/lib/hooks/useLatestRef.d.ts +24 -0
- package/lib/hooks/useLatestRef.js +35 -0
- package/lib/layout/Menu/index.js +24 -6
- package/lib/layout/Menu/index.module.scss +53 -10
- package/package.json +9 -3
- package/src/__tests__/setup.ts +26 -0
- package/src/components/Chart/Bubble/index.tsx +7 -2
- package/src/components/Chart/Common/index.tsx +7 -2
- package/src/components/Chart/Heatmap/index.tsx +7 -2
- package/src/components/Chart/Pie/Pie3D/index.tsx +7 -2
- package/src/components/Chart/Pie/index.tsx +7 -2
- package/src/components/Chat/ChatInput/index.tsx +7 -2
- package/src/components/Icon/index.tsx +7 -2
- package/src/components/Input/Number/index.tsx +25 -23
- package/src/components/Input/Password/index.tsx +37 -26
- package/src/components/Input/Search/index.tsx +39 -26
- package/src/components/Input/TextArea/index.test.tsx +208 -0
- package/src/components/Input/TextArea/index.tsx +56 -30
- package/src/components/Input/index.test.tsx +189 -0
- package/src/components/Input/index.tsx +36 -29
- package/src/components/Select/_hooks/useSelectInputOptions.ts +6 -9
- package/src/components/Slider/index.tsx +22 -12
- package/src/components/Upload/_hooks/useUploadFileList.ts +6 -2
- package/src/components/Upload/_hooks/useUploadOperations.ts +7 -2
- package/src/hooks/useLatestRef.ts +31 -0
- package/src/layout/Menu/index.md +11 -0
- package/src/layout/Menu/index.module.scss +53 -10
- package/src/layout/Menu/index.tsx +24 -7
|
@@ -8,6 +8,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
8
8
|
var _useContainerReady = _interopRequireDefault(require("../_hooks/useContainerReady"));
|
|
9
9
|
var _indexModule = _interopRequireDefault(require("../index.module.scss"));
|
|
10
10
|
var echarts = _interopRequireWildcard(require("echarts"));
|
|
11
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
11
12
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
13
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
14
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -41,7 +42,10 @@ const Common = props => {
|
|
|
41
42
|
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
42
43
|
chartInstanceRef.current?.resize();
|
|
43
44
|
}, []);
|
|
45
|
+
const onChartRef = (0, _useLatestRef.useLatestRef)(onChart);
|
|
44
46
|
|
|
47
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
48
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
45
49
|
// Initialize the chart
|
|
46
50
|
(0, _react.useEffect)(() => {
|
|
47
51
|
if (!chartRef.current || !containerReady) return;
|
|
@@ -57,7 +61,7 @@ const Common = props => {
|
|
|
57
61
|
chart.setOption(chartOption, true);
|
|
58
62
|
|
|
59
63
|
// Chart-ready callback (can be used for legend auto-scroll, etc.)
|
|
60
|
-
|
|
64
|
+
onChartRef.current?.(chart);
|
|
61
65
|
|
|
62
66
|
// Add resize listener
|
|
63
67
|
window.addEventListener("resize", handleResize);
|
|
@@ -72,7 +76,7 @@ const Common = props => {
|
|
|
72
76
|
return () => {
|
|
73
77
|
window.removeEventListener("resize", handleResize);
|
|
74
78
|
};
|
|
75
|
-
}, [chartOption, handleResize,
|
|
79
|
+
}, [chartOption, handleResize, onChartRef, containerReady]);
|
|
76
80
|
|
|
77
81
|
// Clean up resources on component unmount
|
|
78
82
|
(0, _react.useEffect)(() => {
|
|
@@ -11,6 +11,7 @@ var echarts = _interopRequireWildcard(require("echarts"));
|
|
|
11
11
|
var _heatmap = require("../_utils/heatmap");
|
|
12
12
|
var _chartTheme = require("../_utils/chartTheme");
|
|
13
13
|
var _useIsDark = _interopRequireDefault(require("../../../hooks/useIsDark"));
|
|
14
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
14
15
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
17
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -191,7 +192,10 @@ const Heatmap = props => {
|
|
|
191
192
|
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
192
193
|
chartInstanceRef.current?.resize();
|
|
193
194
|
}, []);
|
|
195
|
+
const onChartRef = (0, _useLatestRef.useLatestRef)(onChart);
|
|
194
196
|
|
|
197
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
198
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
195
199
|
// Initialize the chart
|
|
196
200
|
(0, _react.useEffect)(() => {
|
|
197
201
|
if (!chartRef.current || !containerReady) return;
|
|
@@ -202,7 +206,7 @@ const Heatmap = props => {
|
|
|
202
206
|
chart = echarts.init(chartRef.current);
|
|
203
207
|
chartInstanceRef.current = chart;
|
|
204
208
|
// Call the onChart callback on first initialization
|
|
205
|
-
|
|
209
|
+
onChartRef.current?.(chart);
|
|
206
210
|
}
|
|
207
211
|
|
|
208
212
|
// Apply the option
|
|
@@ -229,7 +233,7 @@ const Heatmap = props => {
|
|
|
229
233
|
chartInstanceRef.current?.off("click", onClick);
|
|
230
234
|
}
|
|
231
235
|
};
|
|
232
|
-
}, [chartOption, handleResize,
|
|
236
|
+
}, [chartOption, handleResize, onChartRef, onClick, containerReady]);
|
|
233
237
|
|
|
234
238
|
// Clean up resources when the component unmounts
|
|
235
239
|
(0, _react.useEffect)(() => {
|
|
@@ -11,6 +11,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
11
11
|
var _hooks = require("./_hooks");
|
|
12
12
|
var _option = require("./_utils/option");
|
|
13
13
|
var _indexModule = _interopRequireDefault(require("../../index.module.scss"));
|
|
14
|
+
var _useLatestRef = require("../../../../hooks/useLatestRef");
|
|
14
15
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
17
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -84,13 +85,17 @@ const ChartPie3D = props => {
|
|
|
84
85
|
yOffset,
|
|
85
86
|
autoRotate
|
|
86
87
|
});
|
|
88
|
+
const onChartRef = (0, _useLatestRef.useLatestRef)(onChart);
|
|
89
|
+
|
|
90
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
91
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
87
92
|
(0, _react.useEffect)(() => {
|
|
88
93
|
if (!chartRef.current || !containerReady) return;
|
|
89
94
|
let chart = chartInstanceRef.current;
|
|
90
95
|
if (!chart) {
|
|
91
96
|
chart = echarts.init(chartRef.current);
|
|
92
97
|
chartInstanceRef.current = chart;
|
|
93
|
-
|
|
98
|
+
onChartRef.current?.(chart);
|
|
94
99
|
}
|
|
95
100
|
chart.setOption(chartOption, true);
|
|
96
101
|
optionRef.current = chartOption;
|
|
@@ -114,7 +119,7 @@ const ChartPie3D = props => {
|
|
|
114
119
|
}
|
|
115
120
|
}
|
|
116
121
|
};
|
|
117
|
-
}, [chartOption, handleResize, handleMouseOver, handleGlobalOut,
|
|
122
|
+
}, [chartOption, handleResize, handleMouseOver, handleGlobalOut, onChartRef, onClick, containerReady]);
|
|
118
123
|
(0, _react.useEffect)(() => {
|
|
119
124
|
return () => {
|
|
120
125
|
if (resizeObserverRef.current) {
|
|
@@ -10,6 +10,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
10
10
|
var _chartUtils = require("../chartUtils");
|
|
11
11
|
var _indexModule = _interopRequireDefault(require("../index.module.scss"));
|
|
12
12
|
var _Pie3D = _interopRequireDefault(require("./Pie3D"));
|
|
13
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
13
14
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
15
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
16
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -200,7 +201,10 @@ const ChartPie = props => {
|
|
|
200
201
|
if (!el || el.clientWidth === 0 || el.clientHeight === 0) return;
|
|
201
202
|
chartInstanceRef.current?.resize();
|
|
202
203
|
}, []);
|
|
204
|
+
const onChartRef = (0, _useLatestRef.useLatestRef)(onChart);
|
|
203
205
|
|
|
206
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
207
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
204
208
|
// Initialize the chart
|
|
205
209
|
(0, _react.useEffect)(() => {
|
|
206
210
|
if (!chartRef.current || !containerReady) return;
|
|
@@ -211,7 +215,7 @@ const ChartPie = props => {
|
|
|
211
215
|
chart = echarts.init(chartRef.current);
|
|
212
216
|
chartInstanceRef.current = chart;
|
|
213
217
|
// Call the onChart callback on first initialization
|
|
214
|
-
|
|
218
|
+
onChartRef.current?.(chart);
|
|
215
219
|
}
|
|
216
220
|
|
|
217
221
|
// Apply the option
|
|
@@ -259,7 +263,7 @@ const ChartPie = props => {
|
|
|
259
263
|
legendScrollRef.current = null;
|
|
260
264
|
}
|
|
261
265
|
};
|
|
262
|
-
}, [chartOption, handleResize,
|
|
266
|
+
}, [chartOption, handleResize, onChartRef, onClick, enableLegendAutoScroll, seriesData, legendVisibleCount, legendScrollInterval, containerReady]);
|
|
263
267
|
|
|
264
268
|
// Clean up resources when the component unmounts
|
|
265
269
|
(0, _react.useEffect)(() => {
|
|
@@ -12,6 +12,7 @@ var _hooks = require("./_hooks");
|
|
|
12
12
|
var _components = require("./_components");
|
|
13
13
|
var _classnames = _interopRequireDefault(require("classnames"));
|
|
14
14
|
var _Button = _interopRequireDefault(require("../../Button"));
|
|
15
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
15
16
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
16
17
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
17
18
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -61,7 +62,10 @@ const ChatInput = props => {
|
|
|
61
62
|
const isControlled = externalAgentsState !== undefined;
|
|
62
63
|
// Use external state or internal state
|
|
63
64
|
const agentsState = isControlled ? externalAgentsState : internalAgentsState;
|
|
65
|
+
const onAgentsChangeRef = (0, _useLatestRef.useLatestRef)(onAgentsChange);
|
|
64
66
|
|
|
67
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
68
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
65
69
|
// When the model switches, sync the agents state (only in uncontrolled mode)
|
|
66
70
|
(0, _react.useEffect)(() => {
|
|
67
71
|
if (isControlled) return; // In controlled mode, state is managed externally
|
|
@@ -69,9 +73,9 @@ const ChatInput = props => {
|
|
|
69
73
|
const currentModel = modelConfig?.modelList.find(m => m.value === modelConfig?.modelType);
|
|
70
74
|
if (currentModel?.agents && currentModel.agents.length > 0) {
|
|
71
75
|
setInternalAgentsState(currentModel.agents);
|
|
72
|
-
|
|
76
|
+
onAgentsChangeRef.current?.(currentModel.agents);
|
|
73
77
|
}
|
|
74
|
-
}, [modelConfig?.modelType, modelConfig?.modelList,
|
|
78
|
+
}, [modelConfig?.modelType, modelConfig?.modelList, onAgentsChangeRef, isControlled]);
|
|
75
79
|
|
|
76
80
|
// Based on the currently selected model, filter the feature configs supported by that model
|
|
77
81
|
const currentModel = modelConfig?.modelList.find(m => m.value === modelConfig?.modelType);
|
|
@@ -9,6 +9,7 @@ var AntdIcons = _interopRequireWildcard(require("@ant-design/icons"));
|
|
|
9
9
|
var _react2 = _interopRequireWildcard(require("react"));
|
|
10
10
|
var _classnames = _interopRequireDefault(require("classnames"));
|
|
11
11
|
var _indexModule = _interopRequireDefault(require("./index.module.scss"));
|
|
12
|
+
var _useLatestRef = require("../../hooks/useLatestRef");
|
|
12
13
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
15
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -48,9 +49,13 @@ const Icon = /*#__PURE__*/_react2.default.forwardRef((props, forwardedRef) => {
|
|
|
48
49
|
forwardedRef.current = node;
|
|
49
50
|
}
|
|
50
51
|
}, [forwardedRef]);
|
|
52
|
+
const onRefRef = (0, _useLatestRef.useLatestRef)(onRef);
|
|
53
|
+
|
|
54
|
+
// 回调 prop 不进依赖数组:消费方传内联箭头时每次渲染都是新引用,effect 会跟着
|
|
55
|
+
// 重跑并再调一次回调 —— 回调里 setState 就是死循环(详见 Input/TextArea 的说明)
|
|
51
56
|
(0, _react2.useEffect)(() => {
|
|
52
|
-
|
|
53
|
-
}, [
|
|
57
|
+
onRefRef.current?.(ref);
|
|
58
|
+
}, [onRefRef, ref]);
|
|
54
59
|
const mergedStyle = {
|
|
55
60
|
color,
|
|
56
61
|
fontSize: typeof fontSize === "number" ? `${fontSize}px` : fontSize,
|
|
@@ -9,7 +9,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
9
9
|
var _icons = require("@ant-design/icons");
|
|
10
10
|
var _classnames = _interopRequireDefault(require("classnames"));
|
|
11
11
|
var _indexModule = _interopRequireDefault(require("./index.module.scss"));
|
|
12
|
-
var
|
|
12
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
13
13
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
15
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -32,20 +32,26 @@ const InputNumber = props => {
|
|
|
32
32
|
// `getRef` keeps handing consumers an `HTMLInputElement` exactly as before.
|
|
33
33
|
const ref = (0, _react.useRef)(null);
|
|
34
34
|
const wrapperRef = (0, _react.useRef)(null);
|
|
35
|
+
const getRefRef = (0, _useLatestRef.useLatestRef)(getRef);
|
|
35
36
|
|
|
36
37
|
// On initialization, prefer value, then fall back to defaultValue
|
|
37
38
|
const initialValue = value !== undefined ? typeof value === "number" ? `${value}` : value?.toString() ?? "" : defaultValue !== undefined ? typeof defaultValue === "number" ? `${defaultValue}` : defaultValue?.toString() ?? "" : "";
|
|
38
39
|
const [_value, setValue] = (0, _react.useState)(initialValue);
|
|
39
|
-
const [lastValue, setLastValue] = (0, _react.useState)(initialValue);
|
|
40
40
|
const prevValueRef = (0, _react.useRef)(undefined);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
// 最近一次通知出去的文本,代替原来那个用 state 记账的写法
|
|
42
|
+
const notifiedRef = (0, _react.useRef)(initialValue);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* `onChange` 是「用户改了输入」这个**事件**的通知,不是从 state 推导出来的结果,
|
|
46
|
+
* 所以在事件处理里发,不在 effect 里发。详见 `TextArea/index.tsx` 里那段说明——
|
|
47
|
+
* 旧写法(依赖数组里放 `onChange`、体内又调它、用 state 记「通知过没有」)
|
|
48
|
+
* 会让传内联箭头的消费方死循环。
|
|
49
|
+
*/
|
|
50
|
+
const notify = next => {
|
|
51
|
+
if (next === notifiedRef.current) return;
|
|
52
|
+
notifiedRef.current = next;
|
|
53
|
+
onChange?.(next);
|
|
54
|
+
};
|
|
49
55
|
(0, _react.useEffect)(() => {
|
|
50
56
|
// Update internal state only when the external value prop actually changes
|
|
51
57
|
if (prevValueRef.current !== value) {
|
|
@@ -62,30 +68,32 @@ const InputNumber = props => {
|
|
|
62
68
|
// 表示的是同一个数,此时以用户正在敲的那份文本为准。
|
|
63
69
|
const sameNumber = value !== undefined && value !== null && _value !== "" && Number(value) === Number(_value);
|
|
64
70
|
if (sameNumber) {
|
|
65
|
-
//
|
|
66
|
-
|
|
71
|
+
// 只同步「已通知过的值」,不动正在编辑的文本
|
|
72
|
+
notifiedRef.current = _value;
|
|
67
73
|
} else if (value !== undefined && value !== null) {
|
|
68
74
|
const newValue = typeof value === "number" ? `${value}` : value?.toString();
|
|
75
|
+
// 外部把值改了,之前通知过什么就不作数了,重新以外部值为准
|
|
76
|
+
notifiedRef.current = newValue;
|
|
69
77
|
setValue(newValue);
|
|
70
|
-
setLastValue(newValue);
|
|
71
78
|
} else {
|
|
72
79
|
// Clear only on initialization or when explicitly set to undefined/null externally
|
|
80
|
+
notifiedRef.current = "";
|
|
73
81
|
setValue("");
|
|
74
|
-
setLastValue("");
|
|
75
82
|
}
|
|
76
83
|
}
|
|
77
84
|
// `_value` 是刻意不进依赖的:这个 effect 只该在**外部** value 变化时跑,
|
|
78
85
|
// 把它加进来会让每次键入都重跑一遍,等于绕过上面那个 prevValueRef 判断
|
|
79
86
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
80
87
|
}, [value]);
|
|
88
|
+
|
|
89
|
+
// 只在挂载时把 ref 交出去。`getRef` 进依赖数组同样会被内联箭头带着每次渲染重跑
|
|
81
90
|
(0, _react.useEffect)(() => {
|
|
82
|
-
|
|
83
|
-
}, [
|
|
91
|
+
getRefRef.current?.(ref.current);
|
|
92
|
+
}, [getRefRef]);
|
|
84
93
|
const handleClear = e => {
|
|
85
94
|
e.stopPropagation();
|
|
86
95
|
setValue("");
|
|
87
|
-
|
|
88
|
-
onChange?.("");
|
|
96
|
+
notify("");
|
|
89
97
|
};
|
|
90
98
|
const showClear = allowClear && _value !== "" && _value !== undefined && !disabled;
|
|
91
99
|
const input = /*#__PURE__*/(0, _jsxRuntime.jsx)(_antd.InputNumber, {
|
|
@@ -94,6 +102,7 @@ const InputNumber = props => {
|
|
|
94
102
|
onChange: e => {
|
|
95
103
|
const newValue = e === null || e === undefined ? "" : (typeof e === "number" ? e : e || "").toString();
|
|
96
104
|
setValue(newValue);
|
|
105
|
+
notify(newValue);
|
|
97
106
|
},
|
|
98
107
|
className: (0, _classnames.default)(_indexModule.default.antdInput, className),
|
|
99
108
|
controls: false,
|
|
@@ -8,6 +8,7 @@ var _Password = _interopRequireDefault(require("antd/es/input/Password"));
|
|
|
8
8
|
var _react = _interopRequireWildcard(require("react"));
|
|
9
9
|
var _classnames = _interopRequireDefault(require("classnames"));
|
|
10
10
|
var _indexModule = _interopRequireDefault(require("./index.module.scss"));
|
|
11
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
11
12
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
13
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
13
14
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
@@ -21,51 +22,64 @@ const Password = props => {
|
|
|
21
22
|
className,
|
|
22
23
|
...inputConfig
|
|
23
24
|
} = props;
|
|
24
|
-
const [isComposing, setComposing] = (0, _react.useState)(false);
|
|
25
25
|
const ref = (0, _react.useRef)(null);
|
|
26
|
+
const getRefRef = (0, _useLatestRef.useLatestRef)(getRef);
|
|
27
|
+
// 输入法组字期间不往外通知。放 ref 不放 state:它只决定「要不要发通知」,
|
|
28
|
+
// 不参与渲染,而且必须在同一次事件里立刻生效
|
|
29
|
+
const composingRef = (0, _react.useRef)(false);
|
|
26
30
|
|
|
27
31
|
// On initialization, prefer value, then fall back to defaultValue
|
|
28
32
|
const initialValue = value !== undefined ? value?.toString() ?? "" : defaultValue !== undefined ? defaultValue?.toString() ?? "" : "";
|
|
29
33
|
const [_value, setValue] = (0, _react.useState)(initialValue);
|
|
30
|
-
const [lastValue, setLastValue] = (0, _react.useState)(initialValue);
|
|
31
34
|
const prevValueRef = (0, _react.useRef)(undefined);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
// 最近一次通知出去的原文。只用来去掉 compositionend 与 input 两个事件的重复通知
|
|
36
|
+
const notifiedRef = (0, _react.useRef)(initialValue);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* `onChange` 是「用户改了输入」这个**事件**的通知,不是从 state 推导出来的结果,
|
|
40
|
+
* 所以在事件处理里发,不在 effect 里发。详见 `TextArea/index.tsx` 里那段说明——
|
|
41
|
+
* 旧写法(依赖数组里放 `onChange`、体内又调它、用 state 记「通知过没有」)
|
|
42
|
+
* 会让传内联箭头的消费方死循环。
|
|
43
|
+
*/
|
|
44
|
+
const notify = next => {
|
|
45
|
+
if (next === notifiedRef.current) return;
|
|
46
|
+
notifiedRef.current = next;
|
|
47
|
+
// 全是空白等同于空
|
|
48
|
+
onChange?.(next.trim() === "" ? "" : next);
|
|
49
|
+
};
|
|
40
50
|
(0, _react.useEffect)(() => {
|
|
41
51
|
// Update internal state only when the external value prop actually changes
|
|
42
52
|
if (prevValueRef.current !== value) {
|
|
43
53
|
prevValueRef.current = value;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
setValue("");
|
|
50
|
-
setLastValue("");
|
|
51
|
-
}
|
|
54
|
+
|
|
55
|
+
// 外部把值改了,之前通知过什么就不作数了,重新以外部值为准
|
|
56
|
+
const next = value !== undefined ? value?.toString() ?? "" : "";
|
|
57
|
+
notifiedRef.current = next;
|
|
58
|
+
setValue(next);
|
|
52
59
|
}
|
|
53
60
|
}, [value]);
|
|
61
|
+
|
|
62
|
+
// 只在挂载时把 ref 交出去。`getRef` 进依赖数组同样会被内联箭头带着每次渲染重跑
|
|
54
63
|
(0, _react.useEffect)(() => {
|
|
55
|
-
|
|
56
|
-
}, [
|
|
64
|
+
getRefRef.current?.(ref.current);
|
|
65
|
+
}, [getRefRef]);
|
|
57
66
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Password.default, {
|
|
58
67
|
className: (0, _classnames.default)(_indexModule.default.antdInput, className),
|
|
59
68
|
ref: ref,
|
|
60
|
-
onCompositionStart: () =>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
onCompositionStart: () => {
|
|
70
|
+
composingRef.current = true;
|
|
71
|
+
},
|
|
72
|
+
onCompositionEnd: e => {
|
|
73
|
+
composingRef.current = false;
|
|
74
|
+
// 组完字的那一下自己发通知。浏览器之间 compositionend 与 input 的先后
|
|
75
|
+
// 不一致,`notify` 里按原文去重,两种顺序都只会发出一次
|
|
76
|
+
notify(e.currentTarget.value);
|
|
65
77
|
},
|
|
66
78
|
value: _value,
|
|
67
79
|
onChange: e => {
|
|
68
|
-
|
|
80
|
+
const next = e.target.value;
|
|
81
|
+
setValue(next);
|
|
82
|
+
if (!composingRef.current) notify(next);
|
|
69
83
|
},
|
|
70
84
|
...inputConfig
|
|
71
85
|
});
|
|
@@ -8,6 +8,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
8
8
|
var _Search = _interopRequireDefault(require("antd/es/input/Search"));
|
|
9
9
|
var _classnames = _interopRequireDefault(require("classnames"));
|
|
10
10
|
var _indexModule = _interopRequireDefault(require("./index.module.scss"));
|
|
11
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
11
12
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
13
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
14
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
@@ -21,50 +22,65 @@ const Search = props => {
|
|
|
21
22
|
className,
|
|
22
23
|
...inputConfig
|
|
23
24
|
} = props;
|
|
24
|
-
const [isComposing, setComposing] = (0, _react.useState)(false);
|
|
25
25
|
const ref = (0, _react.useRef)(null);
|
|
26
|
+
const getRefRef = (0, _useLatestRef.useLatestRef)(getRef);
|
|
27
|
+
// 输入法组字期间不往外通知。放 ref 不放 state:它只决定「要不要发通知」,
|
|
28
|
+
// 不参与渲染,而且必须在同一次事件里立刻生效
|
|
29
|
+
const composingRef = (0, _react.useRef)(false);
|
|
26
30
|
|
|
27
31
|
// On initialization, prefer value, then fall back to defaultValue
|
|
28
32
|
const initialValue = value !== undefined ? value?.toString() ?? "" : defaultValue !== undefined ? defaultValue?.toString() ?? "" : "";
|
|
29
33
|
const [_value, setValue] = (0, _react.useState)(initialValue);
|
|
30
|
-
const [lastValue, setLastValue] = (0, _react.useState)(initialValue);
|
|
31
34
|
const prevValueRef = (0, _react.useRef)(undefined);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
// 最近一次通知出去的原文。只用来去掉 compositionend 与 input 两个事件的重复通知
|
|
36
|
+
const notifiedRef = (0, _react.useRef)(initialValue);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* `onChange` 是「用户改了输入」这个**事件**的通知,不是从 state 推导出来的结果,
|
|
40
|
+
* 所以在事件处理里发,不在 effect 里发。详见 `TextArea/index.tsx` 里那段说明——
|
|
41
|
+
* 旧写法(依赖数组里放 `onChange`、体内又调它、用 state 记「通知过没有」)
|
|
42
|
+
* 会让传内联箭头的消费方死循环。
|
|
43
|
+
*/
|
|
44
|
+
const notify = next => {
|
|
45
|
+
if (next === notifiedRef.current) return;
|
|
46
|
+
notifiedRef.current = next;
|
|
47
|
+
// 全是空白等同于空
|
|
48
|
+
onChange?.(next.trim() === "" ? "" : next);
|
|
49
|
+
};
|
|
40
50
|
(0, _react.useEffect)(() => {
|
|
41
51
|
// Update internal state only when the external value prop actually changes
|
|
42
52
|
if (prevValueRef.current !== value) {
|
|
43
53
|
prevValueRef.current = value;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
setValue("");
|
|
50
|
-
setLastValue("");
|
|
51
|
-
}
|
|
54
|
+
|
|
55
|
+
// 外部把值改了,之前通知过什么就不作数了,重新以外部值为准
|
|
56
|
+
const next = value !== undefined ? value?.toString() ?? "" : "";
|
|
57
|
+
notifiedRef.current = next;
|
|
58
|
+
setValue(next);
|
|
52
59
|
}
|
|
53
60
|
}, [value]);
|
|
61
|
+
|
|
62
|
+
// 只在挂载时把 ref 交出去。`getRef` 进依赖数组同样会被内联箭头带着每次渲染重跑
|
|
54
63
|
(0, _react.useEffect)(() => {
|
|
55
|
-
|
|
56
|
-
}, [
|
|
64
|
+
getRefRef.current?.(ref.current);
|
|
65
|
+
}, [getRefRef]);
|
|
57
66
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Search.default, {
|
|
58
67
|
ref: ref,
|
|
59
68
|
className: (0, _classnames.default)(_indexModule.default.antdInput, className),
|
|
60
|
-
onCompositionStart: () =>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
onCompositionStart: () => {
|
|
70
|
+
composingRef.current = true;
|
|
71
|
+
},
|
|
72
|
+
onCompositionEnd: e => {
|
|
73
|
+
composingRef.current = false;
|
|
74
|
+
// 组完字的那一下自己发通知。浏览器之间 compositionend 与 input 的先后
|
|
75
|
+
// 不一致,`notify` 里按原文去重,两种顺序都只会发出一次
|
|
76
|
+
notify(e.currentTarget.value);
|
|
65
77
|
},
|
|
66
78
|
value: _value,
|
|
67
|
-
onChange: e =>
|
|
79
|
+
onChange: e => {
|
|
80
|
+
const next = e.target.value;
|
|
81
|
+
setValue(next);
|
|
82
|
+
if (!composingRef.current) notify(next);
|
|
83
|
+
},
|
|
68
84
|
...inputConfig
|
|
69
85
|
});
|
|
70
86
|
};
|
|
@@ -8,6 +8,7 @@ var _TextArea = _interopRequireDefault(require("antd/es/input/TextArea"));
|
|
|
8
8
|
var _react = _interopRequireWildcard(require("react"));
|
|
9
9
|
var _classnames = _interopRequireDefault(require("classnames"));
|
|
10
10
|
var _indexModule = _interopRequireDefault(require("./index.module.scss"));
|
|
11
|
+
var _useLatestRef = require("../../../hooks/useLatestRef");
|
|
11
12
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
13
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
13
14
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
@@ -33,41 +34,63 @@ const TextArea = props => {
|
|
|
33
34
|
style,
|
|
34
35
|
...inputConfig
|
|
35
36
|
} = props;
|
|
36
|
-
const [isComposing, setComposing] = (0, _react.useState)(false);
|
|
37
37
|
const ref = (0, _react.useRef)(null);
|
|
38
38
|
const [focused, setFocused] = (0, _react.useState)(false);
|
|
39
39
|
|
|
40
40
|
// On initialization, prefer value, then fall back to defaultValue
|
|
41
41
|
const initialValue = value !== undefined ? value?.toString() ?? "" : defaultValue !== undefined ? defaultValue?.toString() ?? "" : "";
|
|
42
42
|
const [_value, setValue] = (0, _react.useState)(initialValue);
|
|
43
|
-
const [lastValue, setLastValue] = (0, _react.useState)(initialValue);
|
|
44
43
|
const [hasError, setHasError] = (0, _react.useState)(false);
|
|
45
44
|
const prevValueRef = (0, _react.useRef)(undefined);
|
|
46
|
-
(0,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
const getRefRef = (0, _useLatestRef.useLatestRef)(getRef);
|
|
46
|
+
|
|
47
|
+
// 输入法组字期间不往外通知。放 ref 不放 state:它只影响「要不要发通知」,
|
|
48
|
+
// 不参与渲染,而且必须在同一次事件里立刻生效
|
|
49
|
+
const composingRef = (0, _react.useRef)(false);
|
|
50
|
+
// 最近一次通知出去的原文。只用来去掉 compositionend 与 input 两个事件的重复通知
|
|
51
|
+
const notifiedRef = (0, _react.useRef)(initialValue);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* `onChange` 是「用户改了输入」这个**事件**的通知,不是从 state 推导出来的结果,
|
|
55
|
+
* 所以它在事件处理里发,不在 effect 里发。
|
|
56
|
+
*
|
|
57
|
+
* 早先是一个 effect 在发:依赖数组里放着 `onChange`,effect 体内又调它,
|
|
58
|
+
* 还用一个 `lastValue` state 记「这个值通知过没有」。两处都是错的——
|
|
59
|
+
* 1. 消费方传内联箭头(`onChange={(v) => setX(v)}`,React 里最常见的写法)时
|
|
60
|
+
* 每次渲染都是新引用,effect 每次渲染都重跑;
|
|
61
|
+
* 2. 「通知过没有」的记账放在 React state 里就**不可靠**:父级用 MobX /
|
|
62
|
+
* `useSyncExternalStore` 这类外部 store 回写 `value` 时,React 会以更高的
|
|
63
|
+
* 同步优先级重渲一次,effect 里排的那次 `setLastValue` 会被这一趟**跳过**,
|
|
64
|
+
* 于是 effect 读到过期的记账、判定「还没通知」,再发一次 → 父级再回写 →
|
|
65
|
+
* 死循环,React 抛 `Maximum update depth exceeded`,输入框被上层
|
|
66
|
+
* ErrorBoundary 整块摘掉。
|
|
67
|
+
*
|
|
68
|
+
* 现在 `onChange` 的引用稳不稳定完全不影响正确性:永远调到最新的那个回调,
|
|
69
|
+
* 且只在真的有新文本时调一次。
|
|
70
|
+
*/
|
|
71
|
+
const notify = next => {
|
|
72
|
+
if (next === notifiedRef.current) return;
|
|
73
|
+
notifiedRef.current = next;
|
|
74
|
+
// 全是空白等同于空:外部拿到的是 "",但输入框里用户敲的空格照旧留着
|
|
75
|
+
onChange?.(next.trim() === "" ? "" : next);
|
|
76
|
+
};
|
|
54
77
|
(0, _react.useEffect)(() => {
|
|
55
78
|
// Update internal state only when the external value prop actually changes
|
|
56
79
|
if (prevValueRef.current !== value) {
|
|
57
80
|
prevValueRef.current = value;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
setValue("");
|
|
64
|
-
setLastValue("");
|
|
65
|
-
}
|
|
81
|
+
|
|
82
|
+
// 外部把值改了,之前通知过什么就不作数了,重新以外部值为准
|
|
83
|
+
const next = value !== undefined ? value?.toString() ?? "" : "";
|
|
84
|
+
notifiedRef.current = next;
|
|
85
|
+
setValue(next);
|
|
66
86
|
}
|
|
67
87
|
}, [value]);
|
|
88
|
+
|
|
89
|
+
// 只在挂载时把 ref 交出去。`getRef` 进依赖数组同样会被内联箭头带着每次渲染重跑,
|
|
90
|
+
// 消费方在里面 setState 就是一个死循环
|
|
68
91
|
(0, _react.useEffect)(() => {
|
|
69
|
-
|
|
70
|
-
}, [
|
|
92
|
+
getRefRef.current?.(ref.current);
|
|
93
|
+
}, [getRefRef]);
|
|
71
94
|
(0, _react.useEffect)(() => {
|
|
72
95
|
// Use a MutationObserver to watch for error state changes
|
|
73
96
|
const checkError = () => {
|
|
@@ -106,18 +129,23 @@ const TextArea = props => {
|
|
|
106
129
|
style: style,
|
|
107
130
|
children: [prefix, /*#__PURE__*/(0, _jsxRuntime.jsx)(_TextArea.default, {
|
|
108
131
|
ref: ref,
|
|
109
|
-
onCompositionStart: () =>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
132
|
+
onCompositionStart: () => {
|
|
133
|
+
composingRef.current = true;
|
|
134
|
+
},
|
|
135
|
+
onCompositionEnd: e => {
|
|
136
|
+
composingRef.current = false;
|
|
137
|
+
// 组完字的那一下自己发通知。浏览器之间 compositionend 与 input 的先后
|
|
138
|
+
// 不一致,`notify` 里按原文去重,两种顺序都只会发出一次
|
|
139
|
+
notify(e.currentTarget.value);
|
|
114
140
|
},
|
|
115
141
|
value: _value,
|
|
116
142
|
autoSize: disabled && text ? autoSize || {
|
|
117
143
|
minRows: 1
|
|
118
144
|
} : autoSize,
|
|
119
145
|
onChange: e => {
|
|
120
|
-
|
|
146
|
+
const next = e.target.value;
|
|
147
|
+
setValue(next);
|
|
148
|
+
if (!composingRef.current) notify(next);
|
|
121
149
|
},
|
|
122
150
|
onFocus: e => {
|
|
123
151
|
setFocused(true);
|