@hsu-react/ui 2.4.3 → 2.4.5

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.
@@ -6,5 +6,11 @@ interface IconProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpa
6
6
  onRef?: (ref?: React.RefObject<HTMLDivElement>) => void;
7
7
  fontSize?: number | string;
8
8
  }
9
- declare const Icon: React.FC<IconProps>;
9
+ /**
10
+ * `forwardRef` 不是可选的:`Tooltip` / `Dropdown` / `Popover` 都要往触发元素上挂 ref
11
+ * 才能定位浮层。作为普通函数组件时,把 Icon 直接塞进它们里面会让 React 报
12
+ * “Function components cannot be given refs”,浮层的定位也失去了锚点。
13
+ * 图标是这几个组件最常见的触发元素,所以这个坑几乎人人会踩。
14
+ */
15
+ declare const Icon: React.ForwardRefExoticComponent<Omit<IconProps, "ref"> & React.RefAttributes<HTMLSpanElement>>;
10
16
  export default Icon;
@@ -9,7 +9,7 @@ function _objectWithoutProperties(source, excluded) { if (source == null) return
9
9
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
10
10
  import { Icon as Iconify } from "@iconify/react";
11
11
  import * as AntdIcons from "@ant-design/icons";
12
- import React, { useEffect, useRef } from "react";
12
+ import React, { useCallback, useEffect, useRef } from "react";
13
13
  import classNames from "classnames";
14
14
  import styles from "./index.module.scss";
15
15
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -19,7 +19,13 @@ var getAntdIcon = function getAntdIcon(icon) {
19
19
  if (!/(?:Outlined|Filled|TwoTone)$/.test(icon)) return undefined;
20
20
  return AntdIcons[icon];
21
21
  };
22
- var Icon = function Icon(props) {
22
+ /**
23
+ * `forwardRef` 不是可选的:`Tooltip` / `Dropdown` / `Popover` 都要往触发元素上挂 ref
24
+ * 才能定位浮层。作为普通函数组件时,把 Icon 直接塞进它们里面会让 React 报
25
+ * “Function components cannot be given refs”,浮层的定位也失去了锚点。
26
+ * 图标是这几个组件最常见的触发元素,所以这个坑几乎人人会踩。
27
+ */
28
+ var Icon = /*#__PURE__*/React.forwardRef(function (props, forwardedRef) {
23
29
  var iconProps = props.iconProps,
24
30
  icon = props.icon,
25
31
  className = props.className,
@@ -29,6 +35,17 @@ var Icon = function Icon(props) {
29
35
  fontSize = props.fontSize,
30
36
  iconConfig = _objectWithoutProperties(props, _excluded);
31
37
  var ref = useRef(null);
38
+
39
+ // 内外两个 ref 都要喂:内部这个是 `onRef` 回调的载体(老接口,不能动),
40
+ // 外部那个是浮层组件用来定位的
41
+ var setRef = useCallback(function (node) {
42
+ ref.current = node;
43
+ if (typeof forwardedRef === "function") {
44
+ forwardedRef(node);
45
+ } else if (forwardedRef) {
46
+ forwardedRef.current = node;
47
+ }
48
+ }, [forwardedRef]);
32
49
  useEffect(function () {
33
50
  onRef === null || onRef === void 0 || onRef(ref);
34
51
  }, [onRef, ref]);
@@ -43,7 +60,7 @@ var Icon = function Icon(props) {
43
60
  return /*#__PURE__*/_jsx(AntdNamedIcon, _objectSpread(_objectSpread({}, iconConfig), {}, {
44
61
  className: classNames([styles.icon, className]),
45
62
  style: mergedStyle,
46
- ref: ref
63
+ ref: setRef
47
64
  }));
48
65
  }
49
66
 
@@ -51,7 +68,7 @@ var Icon = function Icon(props) {
51
68
  // the antd Icon's svg (whose viewBox would scale the inner svg down so much the icon becomes invisible).
52
69
  return /*#__PURE__*/_jsx("span", _objectSpread(_objectSpread({}, iconConfig), {}, {
53
70
  role: "img",
54
- ref: ref,
71
+ ref: setRef,
55
72
  className: classNames(["anticon", styles.icon, className]),
56
73
  style: mergedStyle,
57
74
  children: /*#__PURE__*/_jsx(Iconify, _objectSpread(_objectSpread({}, iconProps), {}, {
@@ -60,5 +77,6 @@ var Icon = function Icon(props) {
60
77
  height: "1em"
61
78
  }))
62
79
  }));
63
- };
80
+ });
81
+ Icon.displayName = "Icon";
64
82
  export default Icon;
@@ -12,4 +12,14 @@
12
12
  min-height: inherit;
13
13
  }
14
14
  }
15
+
16
+ // Same guard as Input: once the box is an .ant-input-affix-wrapper, `.ant-input` names
17
+ // the one-line <input> inside it, and an app sizing `.ant-input` like a bare input
18
+ // pushes that inner element out of the box and the text onto the bottom border.
19
+ // Full story in components/Input/index.module.scss.
20
+ &:global(.ant-input-affix-wrapper) > :global(.ant-input),
21
+ :global(.ant-input-affix-wrapper) > :global(.ant-input) {
22
+ height: auto;
23
+ min-height: 0px;
24
+ }
15
25
  }
@@ -19,4 +19,14 @@
19
19
  }
20
20
  }
21
21
  }
22
+
23
+ // Same guard as Input: once the box is an .ant-input-affix-wrapper, `.ant-input` names
24
+ // the one-line <input> inside it, and an app sizing `.ant-input` like a bare input
25
+ // pushes that inner element out of the box and the text onto the bottom border.
26
+ // Full story in components/Input/index.module.scss.
27
+ &:global(.ant-input-affix-wrapper) > :global(.ant-input),
28
+ :global(.ant-input-affix-wrapper) > :global(.ant-input) {
29
+ height: auto;
30
+ min-height: 0px;
31
+ }
22
32
  }
@@ -19,4 +19,23 @@
19
19
  min-height: inherit;
20
20
  }
21
21
  }
22
+
23
+ // Keep the inner <input> one line tall, whatever the app says about `.ant-input`.
24
+ //
25
+ // `.ant-input` is two different elements depending on the shape: on a bare antd input
26
+ // it is the bordered box itself, but as soon as there is a clear button, a prefix or a
27
+ // suffix — and this component turns allowClear on by default, so there almost always
28
+ // is one — the box becomes `.ant-input-affix-wrapper` and `.ant-input` moves to the
29
+ // <input> inside it. Apps carry the bare-input habit over and write
30
+ // `.some-form .ant-input { height: 32px }` meaning the box; that height lands on the
31
+ // inner input instead, which is then taller than the ~22px the wrapper's padding
32
+ // leaves it, hangs out of the bottom, and drags the text down onto the border.
33
+ //
34
+ // Three classes, so it outweighs the two-class selectors apps typically write; an app
35
+ // that really means the inner input can still say so with one more level.
36
+ &:global(.ant-input-affix-wrapper) > :global(.ant-input),
37
+ :global(.ant-input-affix-wrapper) > :global(.ant-input) {
38
+ height: auto;
39
+ min-height: 0px;
40
+ }
22
41
  }
@@ -6,5 +6,11 @@ interface IconProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpa
6
6
  onRef?: (ref?: React.RefObject<HTMLDivElement>) => void;
7
7
  fontSize?: number | string;
8
8
  }
9
- declare const Icon: React.FC<IconProps>;
9
+ /**
10
+ * `forwardRef` 不是可选的:`Tooltip` / `Dropdown` / `Popover` 都要往触发元素上挂 ref
11
+ * 才能定位浮层。作为普通函数组件时,把 Icon 直接塞进它们里面会让 React 报
12
+ * “Function components cannot be given refs”,浮层的定位也失去了锚点。
13
+ * 图标是这几个组件最常见的触发元素,所以这个坑几乎人人会踩。
14
+ */
15
+ declare const Icon: React.ForwardRefExoticComponent<Omit<IconProps, "ref"> & React.RefAttributes<HTMLSpanElement>>;
10
16
  export default Icon;
@@ -19,7 +19,13 @@ const getAntdIcon = icon => {
19
19
  if (!/(?:Outlined|Filled|TwoTone)$/.test(icon)) return undefined;
20
20
  return AntdIcons[icon];
21
21
  };
22
- const Icon = props => {
22
+ /**
23
+ * `forwardRef` 不是可选的:`Tooltip` / `Dropdown` / `Popover` 都要往触发元素上挂 ref
24
+ * 才能定位浮层。作为普通函数组件时,把 Icon 直接塞进它们里面会让 React 报
25
+ * “Function components cannot be given refs”,浮层的定位也失去了锚点。
26
+ * 图标是这几个组件最常见的触发元素,所以这个坑几乎人人会踩。
27
+ */
28
+ const Icon = /*#__PURE__*/_react2.default.forwardRef((props, forwardedRef) => {
23
29
  const {
24
30
  iconProps,
25
31
  icon,
@@ -31,6 +37,17 @@ const Icon = props => {
31
37
  ...iconConfig
32
38
  } = props;
33
39
  const ref = (0, _react2.useRef)(null);
40
+
41
+ // 内外两个 ref 都要喂:内部这个是 `onRef` 回调的载体(老接口,不能动),
42
+ // 外部那个是浮层组件用来定位的
43
+ const setRef = (0, _react2.useCallback)(node => {
44
+ ref.current = node;
45
+ if (typeof forwardedRef === "function") {
46
+ forwardedRef(node);
47
+ } else if (forwardedRef) {
48
+ forwardedRef.current = node;
49
+ }
50
+ }, [forwardedRef]);
34
51
  (0, _react2.useEffect)(() => {
35
52
  onRef?.(ref);
36
53
  }, [onRef, ref]);
@@ -47,7 +64,7 @@ const Icon = props => {
47
64
  ...iconConfig,
48
65
  className: (0, _classnames.default)([_indexModule.default.icon, className]),
49
66
  style: mergedStyle,
50
- ref: ref
67
+ ref: setRef
51
68
  });
52
69
  }
53
70
 
@@ -56,7 +73,7 @@ const Icon = props => {
56
73
  return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
57
74
  ...iconConfig,
58
75
  role: "img",
59
- ref: ref,
76
+ ref: setRef,
60
77
  className: (0, _classnames.default)(["anticon", _indexModule.default.icon, className]),
61
78
  style: mergedStyle,
62
79
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_react.Icon, {
@@ -66,5 +83,6 @@ const Icon = props => {
66
83
  height: "1em"
67
84
  })
68
85
  });
69
- };
86
+ });
87
+ Icon.displayName = "Icon";
70
88
  var _default = exports.default = Icon;
@@ -12,4 +12,14 @@
12
12
  min-height: inherit;
13
13
  }
14
14
  }
15
+
16
+ // Same guard as Input: once the box is an .ant-input-affix-wrapper, `.ant-input` names
17
+ // the one-line <input> inside it, and an app sizing `.ant-input` like a bare input
18
+ // pushes that inner element out of the box and the text onto the bottom border.
19
+ // Full story in components/Input/index.module.scss.
20
+ &:global(.ant-input-affix-wrapper) > :global(.ant-input),
21
+ :global(.ant-input-affix-wrapper) > :global(.ant-input) {
22
+ height: auto;
23
+ min-height: 0px;
24
+ }
15
25
  }
@@ -19,4 +19,14 @@
19
19
  }
20
20
  }
21
21
  }
22
+
23
+ // Same guard as Input: once the box is an .ant-input-affix-wrapper, `.ant-input` names
24
+ // the one-line <input> inside it, and an app sizing `.ant-input` like a bare input
25
+ // pushes that inner element out of the box and the text onto the bottom border.
26
+ // Full story in components/Input/index.module.scss.
27
+ &:global(.ant-input-affix-wrapper) > :global(.ant-input),
28
+ :global(.ant-input-affix-wrapper) > :global(.ant-input) {
29
+ height: auto;
30
+ min-height: 0px;
31
+ }
22
32
  }
@@ -19,4 +19,23 @@
19
19
  min-height: inherit;
20
20
  }
21
21
  }
22
+
23
+ // Keep the inner <input> one line tall, whatever the app says about `.ant-input`.
24
+ //
25
+ // `.ant-input` is two different elements depending on the shape: on a bare antd input
26
+ // it is the bordered box itself, but as soon as there is a clear button, a prefix or a
27
+ // suffix — and this component turns allowClear on by default, so there almost always
28
+ // is one — the box becomes `.ant-input-affix-wrapper` and `.ant-input` moves to the
29
+ // <input> inside it. Apps carry the bare-input habit over and write
30
+ // `.some-form .ant-input { height: 32px }` meaning the box; that height lands on the
31
+ // inner input instead, which is then taller than the ~22px the wrapper's padding
32
+ // leaves it, hangs out of the bottom, and drags the text down onto the border.
33
+ //
34
+ // Three classes, so it outweighs the two-class selectors apps typically write; an app
35
+ // that really means the inner input can still say so with one more level.
36
+ &:global(.ant-input-affix-wrapper) > :global(.ant-input),
37
+ :global(.ant-input-affix-wrapper) > :global(.ant-input) {
38
+ height: auto;
39
+ min-height: 0px;
40
+ }
22
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hsu-react/ui",
3
- "version": "2.4.3",
3
+ "version": "2.4.5",
4
4
  "description": "一套基于 React + Ant Design 的中后台业务组件库",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -5,7 +5,7 @@ import {
5
5
  } from "@iconify/react";
6
6
 
7
7
  import * as AntdIcons from "@ant-design/icons";
8
- import React, { useEffect, useRef } from "react";
8
+ import React, { useCallback, useEffect, useRef } from "react";
9
9
  import classNames from "classnames";
10
10
  import styles from "./index.module.scss";
11
11
 
@@ -38,7 +38,13 @@ interface IconProps
38
38
  fontSize?: number | string;
39
39
  }
40
40
 
41
- const Icon: React.FC<IconProps> = (props) => {
41
+ /**
42
+ * `forwardRef` 不是可选的:`Tooltip` / `Dropdown` / `Popover` 都要往触发元素上挂 ref
43
+ * 才能定位浮层。作为普通函数组件时,把 Icon 直接塞进它们里面会让 React 报
44
+ * “Function components cannot be given refs”,浮层的定位也失去了锚点。
45
+ * 图标是这几个组件最常见的触发元素,所以这个坑几乎人人会踩。
46
+ */
47
+ const Icon = React.forwardRef<HTMLSpanElement, IconProps>((props, forwardedRef) => {
42
48
  const {
43
49
  iconProps,
44
50
  icon,
@@ -51,6 +57,22 @@ const Icon: React.FC<IconProps> = (props) => {
51
57
  } = props;
52
58
  const ref = useRef<HTMLDivElement>(null);
53
59
 
60
+ // 内外两个 ref 都要喂:内部这个是 `onRef` 回调的载体(老接口,不能动),
61
+ // 外部那个是浮层组件用来定位的
62
+ const setRef = useCallback(
63
+ (node: HTMLSpanElement | null) => {
64
+ (ref as React.MutableRefObject<HTMLDivElement | null>).current =
65
+ node as unknown as HTMLDivElement | null;
66
+
67
+ if (typeof forwardedRef === "function") {
68
+ forwardedRef(node);
69
+ } else if (forwardedRef) {
70
+ forwardedRef.current = node;
71
+ }
72
+ },
73
+ [forwardedRef]
74
+ );
75
+
54
76
  useEffect(() => {
55
77
  onRef?.(ref);
56
78
  }, [onRef, ref]);
@@ -69,7 +91,7 @@ const Icon: React.FC<IconProps> = (props) => {
69
91
  {...iconConfig}
70
92
  className={classNames([styles.icon, className])}
71
93
  style={mergedStyle}
72
- ref={ref as unknown as React.Ref<HTMLSpanElement>}
94
+ ref={setRef}
73
95
  />
74
96
  );
75
97
  }
@@ -80,13 +102,15 @@ const Icon: React.FC<IconProps> = (props) => {
80
102
  <span
81
103
  {...iconConfig}
82
104
  role="img"
83
- ref={ref as unknown as React.Ref<HTMLSpanElement>}
105
+ ref={setRef}
84
106
  className={classNames(["anticon", styles.icon, className])}
85
107
  style={mergedStyle}
86
108
  >
87
109
  <Iconify {...iconProps} icon={icon} width="1em" height="1em" />
88
110
  </span>
89
111
  );
90
- };
112
+ });
113
+
114
+ Icon.displayName = "Icon";
91
115
 
92
116
  export default Icon;
@@ -12,4 +12,14 @@
12
12
  min-height: inherit;
13
13
  }
14
14
  }
15
+
16
+ // Same guard as Input: once the box is an .ant-input-affix-wrapper, `.ant-input` names
17
+ // the one-line <input> inside it, and an app sizing `.ant-input` like a bare input
18
+ // pushes that inner element out of the box and the text onto the bottom border.
19
+ // Full story in components/Input/index.module.scss.
20
+ &:global(.ant-input-affix-wrapper) > :global(.ant-input),
21
+ :global(.ant-input-affix-wrapper) > :global(.ant-input) {
22
+ height: auto;
23
+ min-height: 0px;
24
+ }
15
25
  }
@@ -19,4 +19,14 @@
19
19
  }
20
20
  }
21
21
  }
22
+
23
+ // Same guard as Input: once the box is an .ant-input-affix-wrapper, `.ant-input` names
24
+ // the one-line <input> inside it, and an app sizing `.ant-input` like a bare input
25
+ // pushes that inner element out of the box and the text onto the bottom border.
26
+ // Full story in components/Input/index.module.scss.
27
+ &:global(.ant-input-affix-wrapper) > :global(.ant-input),
28
+ :global(.ant-input-affix-wrapper) > :global(.ant-input) {
29
+ height: auto;
30
+ min-height: 0px;
31
+ }
22
32
  }
@@ -19,4 +19,23 @@
19
19
  min-height: inherit;
20
20
  }
21
21
  }
22
+
23
+ // Keep the inner <input> one line tall, whatever the app says about `.ant-input`.
24
+ //
25
+ // `.ant-input` is two different elements depending on the shape: on a bare antd input
26
+ // it is the bordered box itself, but as soon as there is a clear button, a prefix or a
27
+ // suffix — and this component turns allowClear on by default, so there almost always
28
+ // is one — the box becomes `.ant-input-affix-wrapper` and `.ant-input` moves to the
29
+ // <input> inside it. Apps carry the bare-input habit over and write
30
+ // `.some-form .ant-input { height: 32px }` meaning the box; that height lands on the
31
+ // inner input instead, which is then taller than the ~22px the wrapper's padding
32
+ // leaves it, hangs out of the bottom, and drags the text down onto the border.
33
+ //
34
+ // Three classes, so it outweighs the two-class selectors apps typically write; an app
35
+ // that really means the inner input can still say so with one more level.
36
+ &:global(.ant-input-affix-wrapper) > :global(.ant-input),
37
+ :global(.ant-input-affix-wrapper) > :global(.ant-input) {
38
+ height: auto;
39
+ min-height: 0px;
40
+ }
22
41
  }