1 | import { useIsomorphicLayoutEffect } from 'ahooks';
|
2 | import { CloseCircleFill } from 'antd-mobile-icons';
|
3 | import classNames from 'classnames';
|
4 | import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
5 | import { withNativeProps } from '../../utils/native-props';
|
6 | import { usePropsValue } from '../../utils/use-props-value';
|
7 | import { mergeProp, mergeProps } from '../../utils/with-default-props';
|
8 | import { useConfig } from '../config-provider';
|
9 | const classPrefix = 'adm-virtual-input';
|
10 | const defaultProps = {
|
11 | defaultValue: ''
|
12 | };
|
13 | export const VirtualInput = forwardRef((props, ref) => {
|
14 | const {
|
15 | locale,
|
16 | input: componentConfig = {}
|
17 | } = useConfig();
|
18 | const mergedProps = mergeProps(defaultProps, componentConfig, props);
|
19 | const [value, setValue] = usePropsValue(mergedProps);
|
20 | const rootRef = useRef(null);
|
21 | const contentRef = useRef(null);
|
22 | const [hasFocus, setHasFocus] = useState(false);
|
23 | const clearIcon = mergeProp(React.createElement(CloseCircleFill, null), componentConfig.clearIcon, props.clearIcon);
|
24 | function scrollToEnd() {
|
25 | const root = rootRef.current;
|
26 | if (!root) return;
|
27 | if (document.activeElement !== root) {
|
28 | return;
|
29 | }
|
30 | const content = contentRef.current;
|
31 | if (!content) return;
|
32 | content.scrollLeft = content.clientWidth;
|
33 | }
|
34 | useIsomorphicLayoutEffect(() => {
|
35 | scrollToEnd();
|
36 | }, [value]);
|
37 | useEffect(() => {
|
38 | if (hasFocus) {
|
39 | scrollToEnd();
|
40 | }
|
41 | }, [hasFocus]);
|
42 | useImperativeHandle(ref, () => ({
|
43 | focus: () => {
|
44 | var _a;
|
45 | (_a = rootRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
46 | },
|
47 | blur: () => {
|
48 | var _a;
|
49 | (_a = rootRef.current) === null || _a === void 0 ? void 0 : _a.blur();
|
50 | }
|
51 | }));
|
52 | function onFocus() {
|
53 | var _a;
|
54 | setHasFocus(true);
|
55 | (_a = mergedProps.onFocus) === null || _a === void 0 ? void 0 : _a.call(mergedProps);
|
56 | }
|
57 | function onBlur() {
|
58 | var _a;
|
59 | setHasFocus(false);
|
60 | (_a = mergedProps.onBlur) === null || _a === void 0 ? void 0 : _a.call(mergedProps);
|
61 | }
|
62 | const keyboard = mergedProps.keyboard;
|
63 | const keyboardElement = keyboard && React.cloneElement(keyboard, {
|
64 | onInput: v => {
|
65 | var _a, _b;
|
66 | setValue(value + v);
|
67 | (_b = (_a = keyboard.props).onInput) === null || _b === void 0 ? void 0 : _b.call(_a, v);
|
68 | },
|
69 | onDelete: () => {
|
70 | var _a, _b;
|
71 | setValue(value.slice(0, -1));
|
72 | (_b = (_a = keyboard.props).onDelete) === null || _b === void 0 ? void 0 : _b.call(_a);
|
73 | },
|
74 | visible: hasFocus,
|
75 | onClose: () => {
|
76 | var _a, _b, _c, _d;
|
77 | const activeElement = document.activeElement;
|
78 |
|
79 |
|
80 | if (activeElement && ((_a = rootRef.current) === null || _a === void 0 ? void 0 : _a.contains(activeElement))) {
|
81 | activeElement.blur();
|
82 | } else {
|
83 | (_b = rootRef.current) === null || _b === void 0 ? void 0 : _b.blur();
|
84 | }
|
85 | (_d = (_c = keyboard.props).onClose) === null || _d === void 0 ? void 0 : _d.call(_c);
|
86 | },
|
87 | getContainer: null
|
88 | });
|
89 | return withNativeProps(mergedProps, React.createElement("div", {
|
90 | ref: rootRef,
|
91 | className: classNames(classPrefix, {
|
92 | [`${classPrefix}-disabled`]: mergedProps.disabled
|
93 | }),
|
94 | tabIndex: mergedProps.disabled ? undefined : 0,
|
95 | role: 'textbox',
|
96 | onFocus: onFocus,
|
97 | onBlur: onBlur,
|
98 | onClick: mergedProps.onClick
|
99 | }, React.createElement("div", {
|
100 | className: `${classPrefix}-content`,
|
101 | ref: contentRef,
|
102 | "aria-disabled": mergedProps.disabled,
|
103 | "aria-label": mergedProps.placeholder
|
104 | }, value, React.createElement("div", {
|
105 | className: `${classPrefix}-caret-container`
|
106 | }, hasFocus && React.createElement("div", {
|
107 | className: `${classPrefix}-caret`
|
108 | }))), mergedProps.clearable && !!value && hasFocus && React.createElement("div", {
|
109 | className: `${classPrefix}-clear`,
|
110 | onClick: e => {
|
111 | var _a;
|
112 | e.stopPropagation();
|
113 | setValue('');
|
114 | (_a = mergedProps.onClear) === null || _a === void 0 ? void 0 : _a.call(mergedProps);
|
115 | },
|
116 | role: 'button',
|
117 | "aria-label": locale.Input.clear
|
118 | }, clearIcon), [undefined, null, ''].includes(value) && React.createElement("div", {
|
119 | className: `${classPrefix}-placeholder`
|
120 | }, mergedProps.placeholder), keyboardElement));
|
121 | }); |
\ | No newline at end of file |