antd-mobile 5.40.0 → 5.41.0-alpha.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.
Files changed (58) hide show
  1. package/2x/bundle/antd-mobile.cjs.development.js +150 -16
  2. package/2x/bundle/antd-mobile.cjs.js +6 -6
  3. package/2x/bundle/antd-mobile.es.development.js +150 -16
  4. package/2x/bundle/antd-mobile.es.js +3983 -3914
  5. package/2x/bundle/antd-mobile.umd.development.js +150 -16
  6. package/2x/bundle/antd-mobile.umd.js +7 -7
  7. package/2x/bundle/style.css +4 -1
  8. package/2x/cjs/components/date-picker/date-picker-week-utils.js +2 -1
  9. package/2x/cjs/components/ellipsis/ellipsis.d.ts +3 -0
  10. package/2x/cjs/components/ellipsis/ellipsis.js +10 -2
  11. package/2x/cjs/components/swiper/swiper.js +1 -1
  12. package/2x/cjs/components/tabs/tabs.js +45 -13
  13. package/2x/cjs/components/virtual-input/virtual-input.css +4 -1
  14. package/2x/cjs/components/virtual-input/virtual-input.d.ts +6 -0
  15. package/2x/cjs/components/virtual-input/virtual-input.js +105 -8
  16. package/2x/es/components/date-picker/date-picker-week-utils.js +2 -1
  17. package/2x/es/components/ellipsis/ellipsis.d.ts +3 -0
  18. package/2x/es/components/ellipsis/ellipsis.js +10 -2
  19. package/2x/es/components/swiper/swiper.js +1 -1
  20. package/2x/es/components/tabs/tabs.js +44 -12
  21. package/2x/es/components/virtual-input/virtual-input.css +4 -1
  22. package/2x/es/components/virtual-input/virtual-input.d.ts +6 -0
  23. package/2x/es/components/virtual-input/virtual-input.js +105 -8
  24. package/2x/package.json +3 -3
  25. package/bundle/antd-mobile.cjs.development.js +150 -16
  26. package/bundle/antd-mobile.cjs.js +6 -6
  27. package/bundle/antd-mobile.compatible.umd.js +1 -1
  28. package/bundle/antd-mobile.es.development.js +150 -16
  29. package/bundle/antd-mobile.es.js +3983 -3914
  30. package/bundle/antd-mobile.umd.development.js +150 -16
  31. package/bundle/antd-mobile.umd.js +7 -7
  32. package/bundle/style.css +1 -1
  33. package/cjs/components/date-picker/date-picker-week-utils.js +2 -1
  34. package/cjs/components/ellipsis/ellipsis.d.ts +3 -0
  35. package/cjs/components/ellipsis/ellipsis.js +10 -2
  36. package/cjs/components/swiper/swiper.js +1 -1
  37. package/cjs/components/tabs/tabs.js +45 -13
  38. package/cjs/components/virtual-input/virtual-input.css +3 -1
  39. package/cjs/components/virtual-input/virtual-input.d.ts +6 -0
  40. package/cjs/components/virtual-input/virtual-input.js +105 -8
  41. package/es/components/date-picker/date-picker-week-utils.js +2 -1
  42. package/es/components/ellipsis/ellipsis.d.ts +3 -0
  43. package/es/components/ellipsis/ellipsis.js +10 -2
  44. package/es/components/swiper/swiper.js +1 -1
  45. package/es/components/tabs/tabs.js +44 -12
  46. package/es/components/virtual-input/virtual-input.css +3 -1
  47. package/es/components/virtual-input/virtual-input.d.ts +6 -0
  48. package/es/components/virtual-input/virtual-input.js +105 -8
  49. package/package.json +3 -3
  50. package/umd/antd-mobile.js +1 -1
  51. package/2x/cjs/components/ellipsis/~ellipsis.d.ts +0 -15
  52. package/2x/cjs/components/ellipsis/~ellipsis.js +0 -161
  53. package/2x/es/components/ellipsis/~ellipsis.d.ts +0 -15
  54. package/2x/es/components/ellipsis/~ellipsis.js +0 -151
  55. package/cjs/components/ellipsis/~ellipsis.d.ts +0 -15
  56. package/cjs/components/ellipsis/~ellipsis.js +0 -161
  57. package/es/components/ellipsis/~ellipsis.d.ts +0 -15
  58. package/es/components/ellipsis/~ellipsis.js +0 -151
@@ -8,7 +8,10 @@ import { mergeProp, mergeProps } from '../../utils/with-default-props';
8
8
  import { useConfig } from '../config-provider';
9
9
  const classPrefix = 'adm-virtual-input';
10
10
  const defaultProps = {
11
- defaultValue: ''
11
+ defaultValue: '',
12
+ cursor: {
13
+ movable: false
14
+ }
12
15
  };
13
16
  export const VirtualInput = forwardRef((props, ref) => {
14
17
  const {
@@ -21,6 +24,13 @@ export const VirtualInput = forwardRef((props, ref) => {
21
24
  const contentRef = useRef(null);
22
25
  const [hasFocus, setHasFocus] = useState(false);
23
26
  const [caretPosition, setCaretPosition] = useState(value.length); // 光标位置,从 0 开始,如值是 2 则表示光标在顺序下标为 2 的数字之前
27
+ const keyboardDataRef = useRef({}); // 临时记录虚拟键盘输入,在下次更新时用于判断光标位置如何调整
28
+ const touchDataRef = useRef(); // 记录上一次 touch 时的坐标位置
29
+ const charRef = useRef(null); // 第一个字符的 DOM
30
+ const charWidthRef = useRef(0); // 单个字符宽度
31
+ const caretRef = useRef(null); // 光标的 DOM
32
+ const [isCaretDragging, setIsCaretDragging] = useState(false);
33
+ const touchMoveTimeoutRef = useRef();
24
34
  const clearIcon = mergeProp(React.createElement(CloseCircleFill, null), componentConfig.clearIcon, props.clearIcon);
25
35
  function scrollToEnd() {
26
36
  const root = rootRef.current;
@@ -32,6 +42,24 @@ export const VirtualInput = forwardRef((props, ref) => {
32
42
  if (!content) return;
33
43
  content.scrollLeft = content.clientWidth;
34
44
  }
45
+ useEffect(() => {
46
+ // 记录单个字符的宽度,用于光标移动时的计算
47
+ if (charRef.current) {
48
+ charWidthRef.current = charRef.current.getBoundingClientRect().width;
49
+ }
50
+ }, [value]);
51
+ useEffect(() => {
52
+ // 经过外部受控逻辑后,再调整光标位置,如果受控逻辑改动了值则光标放到最后
53
+ if (value === keyboardDataRef.current.newValue) {
54
+ if (keyboardDataRef.current.mode === 'input') {
55
+ setCaretPosition(c => c + 1);
56
+ } else if (keyboardDataRef.current.mode === 'delete') {
57
+ setCaretPosition(c => c - 1);
58
+ }
59
+ } else {
60
+ setCaretPosition(value.length);
61
+ }
62
+ }, [value]);
35
63
  useIsomorphicLayoutEffect(() => {
36
64
  scrollToEnd();
37
65
  }, [value]);
@@ -65,16 +93,24 @@ export const VirtualInput = forwardRef((props, ref) => {
65
93
  onInput: v => {
66
94
  var _a, _b;
67
95
  const newValue = value.substring(0, caretPosition) + v + value.substring(caretPosition);
96
+ // 临时记录,用于后续光标位置
97
+ keyboardDataRef.current = {
98
+ newValue,
99
+ mode: 'input'
100
+ };
68
101
  setValue(newValue);
69
- setCaretPosition(c => c + 1);
70
102
  (_b = (_a = keyboard.props).onInput) === null || _b === void 0 ? void 0 : _b.call(_a, v);
71
103
  },
72
104
  onDelete: () => {
73
105
  var _a, _b;
74
106
  if (caretPosition === 0) return;
75
107
  const newValue = value.substring(0, caretPosition - 1) + value.substring(caretPosition);
108
+ // 临时记录,用于后续光标位置
109
+ keyboardDataRef.current = {
110
+ newValue,
111
+ mode: 'delete'
112
+ };
76
113
  setValue(newValue);
77
- setCaretPosition(caretPosition - 1);
78
114
  (_b = (_a = keyboard.props).onDelete) === null || _b === void 0 ? void 0 : _b.call(_a);
79
115
  },
80
116
  visible: hasFocus,
@@ -94,23 +130,80 @@ export const VirtualInput = forwardRef((props, ref) => {
94
130
  });
95
131
  // 点击输入框时,将光标置于最后
96
132
  const setCaretPositionToEnd = () => {
97
- setCaretPosition(value.length);
133
+ var _a, _b;
134
+ if (caretPosition !== value.length) {
135
+ setCaretPosition(value.length);
136
+ (_b = (_a = mergedProps.cursor) === null || _a === void 0 ? void 0 : _a.onMove) === null || _b === void 0 ? void 0 : _b.call(_a, value.length);
137
+ }
98
138
  };
99
139
  // 点击单个字符时,根据点击位置置于字符前或后
100
140
  const changeCaretPosition = index => e => {
141
+ var _a, _b, _c;
142
+ if (mergedProps.disabled || !((_a = mergedProps.cursor) === null || _a === void 0 ? void 0 : _a.movable)) return;
101
143
  e.stopPropagation();
102
144
  const rect = e.target.getBoundingClientRect();
103
145
  const midX = rect.left + rect.width / 2;
104
146
  const clickX = e.clientX;
105
147
  // 点击区域是否偏右
106
148
  const isRight = clickX > midX;
107
- setCaretPosition(isRight ? index + 1 : index);
149
+ const newCaretPosition = isRight ? index + 1 : index;
150
+ setCaretPosition(newCaretPosition);
151
+ (_c = (_b = mergedProps.cursor) === null || _b === void 0 ? void 0 : _b.onMove) === null || _c === void 0 ? void 0 : _c.call(_b, newCaretPosition);
152
+ };
153
+ // 在光标附近 touchmove 时也可以调整光标位置
154
+ const handleTouchStart = e => {
155
+ var _a;
156
+ if (mergedProps.disabled || !((_a = mergedProps.cursor) === null || _a === void 0 ? void 0 : _a.movable)) return;
157
+ if (!caretRef.current) return;
158
+ const touch = e.touches[0];
159
+ const caretRect = caretRef.current.getBoundingClientRect();
160
+ const distance = Math.abs(touch.clientX - (caretRect.left + caretRect.width / 2));
161
+ if (distance < 20) {
162
+ // 20px 阈值可调整
163
+ touchDataRef.current = {
164
+ startX: touch.clientX,
165
+ startCaretPosition: caretPosition
166
+ };
167
+ } else {
168
+ touchDataRef.current = null;
169
+ }
170
+ };
171
+ const handleTouchMove = e => {
172
+ var _a, _b, _c;
173
+ if (!touchDataRef.current || !((_a = mergedProps.cursor) === null || _a === void 0 ? void 0 : _a.movable)) return;
174
+ setIsCaretDragging(true);
175
+ const touch = e.touches[0];
176
+ const deltaX = touch.clientX - touchDataRef.current.startX;
177
+ const charWidth = charWidthRef.current;
178
+ const moveChars = Math.round(deltaX / charWidth);
179
+ let newCaretPosition = touchDataRef.current.startCaretPosition + moveChars;
180
+ // 边界处理
181
+ newCaretPosition = Math.max(0, Math.min(newCaretPosition, value.length));
182
+ setCaretPosition(newCaretPosition);
183
+ (_c = (_b = mergedProps.cursor) === null || _b === void 0 ? void 0 : _b.onMove) === null || _c === void 0 ? void 0 : _c.call(_b, newCaretPosition);
184
+ // 防止 touchend 不触发
185
+ if (touchMoveTimeoutRef.current) {
186
+ clearTimeout(touchMoveTimeoutRef.current);
187
+ }
188
+ touchMoveTimeoutRef.current = setTimeout(() => {
189
+ setIsCaretDragging(false);
190
+ touchMoveTimeoutRef.current = null;
191
+ }, 500);
192
+ };
193
+ const handleTouchEnd = () => {
194
+ touchDataRef.current = null;
195
+ setIsCaretDragging(false);
196
+ if (touchMoveTimeoutRef.current) {
197
+ clearTimeout(touchMoveTimeoutRef.current);
198
+ touchMoveTimeoutRef.current = null;
199
+ }
108
200
  };
109
201
  const chars = (value + '').split('');
110
202
  return withNativeProps(mergedProps, React.createElement("div", {
111
203
  ref: rootRef,
112
204
  className: classNames(classPrefix, {
113
- [`${classPrefix}-disabled`]: mergedProps.disabled
205
+ [`${classPrefix}-disabled`]: mergedProps.disabled,
206
+ [`${classPrefix}-caret-dragging`]: isCaretDragging
114
207
  }),
115
208
  tabIndex: mergedProps.disabled ? undefined : 0,
116
209
  role: 'textbox',
@@ -122,13 +215,18 @@ export const VirtualInput = forwardRef((props, ref) => {
122
215
  ref: contentRef,
123
216
  "aria-disabled": mergedProps.disabled,
124
217
  "aria-label": mergedProps.placeholder,
125
- onClick: setCaretPositionToEnd
218
+ onClick: setCaretPositionToEnd,
219
+ onTouchStart: handleTouchStart,
220
+ onTouchMove: handleTouchMove,
221
+ onTouchEnd: handleTouchEnd
126
222
  }, chars.slice(0, caretPosition).map((i, index) => React.createElement("span", {
223
+ ref: index === 0 ? charRef : undefined,
127
224
  key: index,
128
225
  onClick: changeCaretPosition(index)
129
226
  }, i)), React.createElement("div", {
130
227
  className: `${classPrefix}-caret-container`
131
228
  }, hasFocus && React.createElement("div", {
229
+ ref: caretRef,
132
230
  className: `${classPrefix}-caret`
133
231
  })), chars.slice(caretPosition).map((i, index) => React.createElement("span", {
134
232
  key: index,
@@ -139,7 +237,6 @@ export const VirtualInput = forwardRef((props, ref) => {
139
237
  var _a;
140
238
  e.stopPropagation();
141
239
  setValue('');
142
- setCaretPosition(0);
143
240
  (_a = mergedProps.onClear) === null || _a === void 0 ? void 0 : _a.call(mergedProps);
144
241
  },
145
242
  role: 'button',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antd-mobile",
3
- "version": "5.40.0",
3
+ "version": "5.41.0-alpha.0",
4
4
  "homepage": "https://github.com/ant-design/ant-design-mobile#readme",
5
5
  "bugs": {
6
6
  "url": "https://github.com/ant-design/ant-design-mobile/issues"
@@ -53,8 +53,8 @@
53
53
  "use-sync-external-store": "^1.2.0"
54
54
  },
55
55
  "peerDependencies": {
56
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
57
- "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
56
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
57
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
58
58
  },
59
59
  "GravityCDN": "./umd/antd-mobile.js",
60
60
  "size-limit": [