@huibo-ui/react-antd 1.0.10 → 1.0.12

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 (70) hide show
  1. package/LICENSE +21 -0
  2. package/lib/components/Affix.js +1 -1
  3. package/lib/components/Alert.js +1 -1
  4. package/lib/components/Avatar.js +4 -1
  5. package/lib/components/BackTop.js +1 -1
  6. package/lib/components/Badge.js +1 -1
  7. package/lib/components/Breadcrumb.js +4 -2
  8. package/lib/components/Button.js +3 -2
  9. package/lib/components/Card.d.ts +14 -1
  10. package/lib/components/Card.js +30 -3
  11. package/lib/components/Checkbox.d.ts +2 -0
  12. package/lib/components/Checkbox.js +14 -2
  13. package/lib/components/Collapse.d.ts +34 -4
  14. package/lib/components/Collapse.js +122 -4
  15. package/lib/components/Descriptions.js +3 -5
  16. package/lib/components/Divider.d.ts +8 -0
  17. package/lib/components/Divider.js +38 -3
  18. package/lib/components/Drawer.d.ts +7 -1
  19. package/lib/components/Drawer.js +167 -4
  20. package/lib/components/Dropdown.js +1 -1
  21. package/lib/components/FloatButton.js +1 -1
  22. package/lib/components/Form.d.ts +25 -14
  23. package/lib/components/Form.js +315 -92
  24. package/lib/components/Image.js +1 -1
  25. package/lib/components/Input.js +25 -4
  26. package/lib/components/InputNumber.d.ts +1 -1
  27. package/lib/components/InputNumber.js +15 -1
  28. package/lib/components/Layout.d.ts +1 -1
  29. package/lib/components/Layout.js +41 -16
  30. package/lib/components/Menu.js +55 -11
  31. package/lib/components/Modal.d.ts +4 -1
  32. package/lib/components/Modal.js +137 -20
  33. package/lib/components/PageHeader.js +1 -1
  34. package/lib/components/Pagination.js +1 -1
  35. package/lib/components/Popconfirm.js +1 -1
  36. package/lib/components/Popover.js +1 -1
  37. package/lib/components/Progress.js +1 -1
  38. package/lib/components/Radio.d.ts +5 -3
  39. package/lib/components/Radio.js +24 -13
  40. package/lib/components/Rate.js +1 -1
  41. package/lib/components/Result.js +1 -1
  42. package/lib/components/Segmented.d.ts +5 -3
  43. package/lib/components/Segmented.js +4 -1
  44. package/lib/components/Select.js +1 -1
  45. package/lib/components/Slider.d.ts +9 -2
  46. package/lib/components/Slider.js +8 -1
  47. package/lib/components/Space.d.ts +2 -1
  48. package/lib/components/Space.js +47 -4
  49. package/lib/components/Spin.js +1 -1
  50. package/lib/components/Statistic.d.ts +1 -0
  51. package/lib/components/Statistic.js +5 -1
  52. package/lib/components/Steps.js +4 -2
  53. package/lib/components/Switch.d.ts +6 -0
  54. package/lib/components/Switch.js +15 -1
  55. package/lib/components/Table.js +68 -12
  56. package/lib/components/Tabs.js +52 -27
  57. package/lib/components/Tag.d.ts +1 -0
  58. package/lib/components/Tag.js +16 -2
  59. package/lib/components/TimePicker.js +1 -1
  60. package/lib/components/Timeline.js +9 -1
  61. package/lib/components/Tooltip.js +1 -1
  62. package/lib/components/Tree.js +2 -4
  63. package/lib/components/TreeSelect.d.ts +2 -0
  64. package/lib/components/TreeSelect.js +19 -1
  65. package/lib/components/Typography.js +7 -3
  66. package/lib/components/Upload.js +1 -1
  67. package/lib/components/Watermark.js +21 -1
  68. package/lib/components/message.js +4 -3
  69. package/lib/components/notification.js +3 -1
  70. package/package.json +5 -6
@@ -1,6 +1,169 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { HbDrawer } from '@huibo-ui/react';
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ /**
4
+ * antd Drawer 兼容层(纯 div 实现)。
5
+ *
6
+ * 不再走 <HbDrawer>(Shadow DOM slot):抽屉内容进 slot 后,<Form>/<Descriptions> 等
7
+ * 嵌套布局受 ::slotted 干扰。纯 div 自渲染 mask + 四方向滑入容器,children 作为真实 DOM,
8
+ * 嵌套复杂内容布局稳定。动画复刻 hb-drawer 的四方向 slide(@keyframes 内联,不依赖 hb-*)。
9
+ */
10
+ // @keyframes 只需注入一次(与 Modal 共享 head,独立命名避免与 Modal 冲突)。
11
+ let drawerMotionInjected = false;
12
+ function injectDrawerMotion() {
13
+ if (drawerMotionInjected || typeof document === 'undefined')
14
+ return;
15
+ drawerMotionInjected = true;
16
+ const style = document.createElement('style');
17
+ style.setAttribute('data-hb-drawer-motion', '');
18
+ style.textContent = `
19
+ @keyframes hb-drawer2-overlay-in { from { opacity: 0; } to { opacity: 1; } }
20
+ @keyframes hb-drawer2-overlay-out { from { opacity: 1; } to { opacity: 0; } }
21
+ @keyframes hb-drawer2-in-ltr { from { transform: translateX(-100%); } to { transform: translateX(0); } }
22
+ @keyframes hb-drawer2-out-ltr { from { transform: translateX(0); } to { transform: translateX(-100%); } }
23
+ @keyframes hb-drawer2-in-rtl { from { transform: translateX(100%); } to { transform: translateX(0); } }
24
+ @keyframes hb-drawer2-out-rtl { from { transform: translateX(0); } to { transform: translateX(100%); } }
25
+ @keyframes hb-drawer2-in-ttb { from { transform: translateY(-100%); } to { transform: translateY(0); } }
26
+ @keyframes hb-drawer2-out-ttb { from { transform: translateY(0); } to { transform: translateY(-100%); } }
27
+ @keyframes hb-drawer2-in-btt { from { transform: translateY(100%); } to { transform: translateY(0); } }
28
+ @keyframes hb-drawer2-out-btt { from { transform: translateY(0); } to { transform: translateY(100%); } }
29
+ `;
30
+ document.head.appendChild(style);
31
+ }
32
+ const CloseIcon = () => (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 1024 1024", fill: "currentColor", "aria-hidden": "true", children: _jsx("path", { d: "M563.8 512l262.5-312.9c14.4-17.2.5-43.1-21.3-43.1H704.5c-7.9 0-15.4 3.5-20.5 9.5L512 417.8 339.9 165.5c-5.1-6-12.6-9.5-20.5-9.5H198.7c-21.8 0-35.7 25.9-21.3 43.1L439.9 512 177.4 824.9c-14.4 17.2-.5 43.1 21.3 43.1h120.5c7.9 0 15.4-3.5 20.5-9.5L512 606.2l172.1 252.3c5.1 6 12.6 9.5 20.5 9.5h120.5c21.8 0 35.7-25.9 21.3-43.1L563.8 512z" }) }));
33
+ const placementToDir = { left: 'ltr', right: 'rtl', top: 'ttb', bottom: 'btt' };
3
34
  export function Drawer(props) {
4
- const dirMap = { left: 'ltr', right: 'rtl', top: 'ttb', bottom: 'btt' };
5
- return _jsx(HbDrawer, { modelValue: props.open ?? false, title: typeof props.title === 'string' ? props.title : '', direction: dirMap[props.placement || 'right'], size: props.width ? String(props.width) : undefined, closeOnClickModal: props.maskClosable ?? true, onHbClose: () => props.onClose?.(), children: props.children });
35
+ const { open, title, placement = 'right', width, height, onClose, maskClosable = true, keyboard = true, destroyOnClose, children, footer, style, className, closable = true, } = props;
36
+ injectDrawerMotion();
37
+ const dir = placementToDir[placement];
38
+ const isHorizontal = placement === 'left' || placement === 'right';
39
+ const resolvedSize = isHorizontal
40
+ ? width != null
41
+ ? typeof width === 'number'
42
+ ? `${width}px`
43
+ : String(width)
44
+ : '378px'
45
+ : height != null
46
+ ? typeof height === 'number'
47
+ ? `${height}px`
48
+ : String(height)
49
+ : '378px';
50
+ // 离场动画:open 由 true→false 时播放 0.3s 出场再卸载。
51
+ // 关键:显示条件 = open || leaving;两者都为 false 时返回 null(完全卸载),
52
+ // 否则 open=false 后 drawer 仍 fixed 显示,导致遮罩挡住下层、X/Esc「看似」关不掉。
53
+ const [leaving, setLeaving] = React.useState(false);
54
+ const prevOpen = React.useRef(!!open);
55
+ React.useEffect(() => {
56
+ if (open && !prevOpen.current) {
57
+ setLeaving(false);
58
+ }
59
+ else if (!open && prevOpen.current) {
60
+ setLeaving(true);
61
+ const t = setTimeout(() => setLeaving(false), 300);
62
+ return () => clearTimeout(t);
63
+ }
64
+ prevOpen.current = !!open;
65
+ }, [open]);
66
+ // Esc 关闭(必须在所有 hooks 内,不能被下面的 return null 跳过 → 否则违反 Rules of Hooks)
67
+ React.useEffect(() => {
68
+ if (!open || !keyboard)
69
+ return;
70
+ const onKey = (e) => {
71
+ if (e.key === 'Escape')
72
+ onClose?.();
73
+ };
74
+ window.addEventListener('keydown', onKey);
75
+ return () => window.removeEventListener('keydown', onKey);
76
+ }, [open, keyboard, onClose]);
77
+ // body 滚动锁定:弹层显示时锁住 body 滚动,避免关闭时页面因滚动条显隐而 reflow 闪烁。
78
+ React.useEffect(() => {
79
+ if (!open)
80
+ return;
81
+ const body = document.body;
82
+ const prevOverflow = body.style.overflow;
83
+ const prevPaddingRight = body.style.paddingRight;
84
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
85
+ if (scrollbarWidth > 0)
86
+ body.style.paddingRight = `${scrollbarWidth}px`;
87
+ body.style.overflow = 'hidden';
88
+ return () => {
89
+ body.style.overflow = prevOverflow;
90
+ body.style.paddingRight = prevPaddingRight;
91
+ };
92
+ }, [open]);
93
+ // 不显示(关闭且离场动画结束)→ 完全不渲染(必须在所有 hooks 之后)
94
+ if (!open && !leaving)
95
+ return null;
96
+ // 容器定位(四方向)
97
+ const posStyle = { position: 'fixed' };
98
+ if (dir === 'ltr') {
99
+ posStyle.top = 0;
100
+ posStyle.left = 0;
101
+ posStyle.bottom = 0;
102
+ posStyle.width = resolvedSize;
103
+ posStyle.maxWidth = '100vw';
104
+ }
105
+ if (dir === 'rtl') {
106
+ posStyle.top = 0;
107
+ posStyle.right = 0;
108
+ posStyle.bottom = 0;
109
+ posStyle.width = resolvedSize;
110
+ posStyle.maxWidth = '100vw';
111
+ }
112
+ if (dir === 'ttb') {
113
+ posStyle.left = 0;
114
+ posStyle.right = 0;
115
+ posStyle.top = 0;
116
+ posStyle.height = resolvedSize;
117
+ posStyle.maxHeight = '100vh';
118
+ }
119
+ if (dir === 'btt') {
120
+ posStyle.left = 0;
121
+ posStyle.right = 0;
122
+ posStyle.bottom = 0;
123
+ posStyle.height = resolvedSize;
124
+ posStyle.maxHeight = '100vh';
125
+ }
126
+ return (_jsxs("div", { style: { position: 'fixed', inset: 0, zIndex: 1000, pointerEvents: 'none' }, children: [_jsx("div", { onClick: () => maskClosable && onClose?.(), style: {
127
+ position: 'fixed',
128
+ inset: 0,
129
+ background: 'var(--hb-color-bg-mask, rgba(0,0,0,0.45))',
130
+ pointerEvents: 'auto',
131
+ animation: `${leaving ? 'hb-drawer2-overlay-out' : 'hb-drawer2-overlay-in'} 0.3s ease both`,
132
+ } }), _jsxs("div", { style: {
133
+ ...posStyle,
134
+ background: 'var(--hb-color-bg-elevated, #fff)',
135
+ boxShadow: '-6px 0 16px 0 rgba(0,0,0,0.08), -3px 0 6px -4px rgba(0,0,0,0.12), -9px 0 28px 8px rgba(0,0,0,0.05)',
136
+ pointerEvents: 'auto',
137
+ display: 'flex',
138
+ flexDirection: 'column',
139
+ zIndex: 1050,
140
+ animation: `${leaving ? `hb-drawer2-out-${dir}` : `hb-drawer2-in-${dir}`} 0.3s cubic-bezier(0.16, 1, 0.3, 1) both`,
141
+ ...style,
142
+ }, className: className, children: [title !== undefined && (_jsxs("div", { style: {
143
+ display: 'flex',
144
+ alignItems: 'center',
145
+ justifyContent: 'space-between',
146
+ padding: '16px 24px',
147
+ borderBottom: '1px solid var(--hb-color-border-secondary, #f0f0f0)',
148
+ }, children: [_jsx("div", { style: { fontSize: 16, fontWeight: 600, color: 'var(--hb-color-text, rgba(0,0,0,0.88))' }, children: title }), closable && (_jsx("button", { type: "button", onClick: () => onClose?.(), "aria-label": "close", style: {
149
+ display: 'flex',
150
+ alignItems: 'center',
151
+ justifyContent: 'center',
152
+ width: 28,
153
+ height: 28,
154
+ border: 'none',
155
+ background: 'none',
156
+ color: 'var(--hb-color-text-secondary, rgba(0,0,0,0.45))',
157
+ cursor: 'pointer',
158
+ borderRadius: 6,
159
+ padding: 0,
160
+ transition: 'all 0.2s',
161
+ }, onMouseEnter: e => (e.currentTarget.style.background = 'var(--hb-color-fill-tertiary, #fafafa)'), onMouseLeave: e => (e.currentTarget.style.background = 'none'), children: _jsx(CloseIcon, {}) }))] })), _jsx("div", { style: { flex: 1, padding: 24, overflowY: 'auto' }, children: children }), footer !== null && footer !== undefined && (_jsx("div", { style: {
162
+ display: 'flex',
163
+ alignItems: 'center',
164
+ justifyContent: 'flex-end',
165
+ gap: 8,
166
+ padding: '8px 24px',
167
+ borderTop: '1px solid var(--hb-color-border-secondary, #f0f0f0)',
168
+ }, children: footer }))] })] }));
6
169
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { HbDropdown } from '@huibo-ui/react';
3
3
  export function Dropdown(props) {
4
- return _jsx(HbDropdown, { items: props.menu?.items || [], trigger: (props.trigger?.[0] || 'hover'), placement: props.placement, children: props.children });
4
+ return (_jsx(HbDropdown, { items: props.menu?.items || [], trigger: (props.trigger?.[0] || 'hover'), placement: props.placement, children: props.children }));
5
5
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { HbFloatButton } from '@huibo-ui/react';
3
3
  export function FloatButton(props) {
4
- return _jsx(HbFloatButton, { type: props.type, icon: props.icon, shape: props.shape, onHbClick: () => props.onClick?.(), children: props.children });
4
+ return (_jsx(HbFloatButton, { type: props.type, icon: props.icon, shape: props.shape, onHbClick: () => props.onClick?.(), children: props.children }));
5
5
  }
@@ -1,18 +1,18 @@
1
1
  import React from 'react';
2
2
  /**
3
- * antd Form 兼容层(同步化版)。
3
+ * antd Form 兼容层(纯 div + React 受控实现)。
4
4
  *
5
- * 核心改造:hb-form @Method 是异步(Promise),但 antd FormInstance 的
6
- * getFieldsValue / resetFields / setFieldsValue 是同步。scrm 等业务大量
7
- * `formRef.current.getFieldsValue()` 同步取值,异步会拿到 Promise 而崩。
5
+ * 不再走 <HbForm>/<HbFormItem>(Shadow DOM slot):
6
+ * 1) hb-form-item labelCol/wrapperCol 栅格、labelAlign、必填星号等布局受 Shadow DOM
7
+ * 限制,弹窗/卡片内嵌表单极易错位;
8
+ * 2) hb-form 内部靠 querySelector('hb-input,...') 取子控件 modelValue,在纯 React 子树
9
+ * (兼容层组件已是标准 antd 签名 value/onChange)下完全多余且脆弱。
8
10
  *
9
- * 解法:instance 内部维护同步 `values` 缓存,hbChange(onValuesChange) 实时更新;
10
- * - 读方法同步返回缓存
11
- * - 写方法同步更新缓存 + 异步调 hb-form @Method 落到子控件
12
- * - validateFields / validate 保持异步(antd 本身也返回 Promise)
11
+ * 新方案对齐 antd 的 rc-field-form 思路:Form 持有 values/errors React state,
12
+ * FormItem 通过 cloneElement 给唯一子控件注入 value/onChange/onChange(标准 antd 签名),
13
+ * 子控件作为真实 DOM 参与 flex 文档流。校验在 React 层跑,validateFields 返回 values。
13
14
  *
14
- * Form forwardRef:`<Form ref={formRef}>` 拿到同步 FormInstance
15
- * useForm 与 ref 共享同一份 createFormInstance 逻辑。
15
+ * FormInstance 保持 antd 语义:getFieldsValue/setFieldsValue/validateFields/resetFields
16
16
  */
17
17
  export interface FormInstance {
18
18
  getFieldValue: (name: string) => any;
@@ -24,11 +24,22 @@ export interface FormInstance {
24
24
  resetFields: (nameList?: string[]) => void;
25
25
  submit: () => void;
26
26
  scrollToField: (name: string) => void;
27
- getFieldsError: () => any[];
27
+ getFieldsError: () => Array<{
28
+ name: string;
29
+ errors: string[];
30
+ }>;
28
31
  getFieldError: (name: string) => string[];
29
- /** scrm 兼容:handleReset = resetFields */
32
+ /** 内部:注册字段(FormItem 挂载时调用) */
33
+ __registerField?: (name: string, field: FieldControl) => () => void;
34
+ /** scrm 兼容 */
30
35
  handleReset?: () => void;
31
36
  }
37
+ interface FieldControl {
38
+ setValue: (value: any) => void;
39
+ validate: () => Promise<string[]>;
40
+ setErrors: (errors: string[]) => void;
41
+ props: FormItemProps;
42
+ }
32
43
  export interface FormProps {
33
44
  form?: FormInstance;
34
45
  initialValues?: Record<string, any>;
@@ -49,10 +60,11 @@ export interface FormProps {
49
60
  children?: React.ReactNode;
50
61
  style?: React.CSSProperties;
51
62
  className?: string;
63
+ name?: string;
52
64
  [key: string]: any;
53
65
  }
54
66
  type FormType = React.ForwardRefExoticComponent<FormProps & React.RefAttributes<FormInstance>> & {
55
- Item: any;
67
+ Item: React.ComponentType<FormItemProps>;
56
68
  useForm: () => [FormInstance];
57
69
  useWatch: (namePath: string | string[], form?: FormInstance) => any;
58
70
  List: any;
@@ -75,7 +87,6 @@ export interface FormItemProps {
75
87
  };
76
88
  colon?: boolean;
77
89
  tooltip?: React.ReactNode;
78
- /** antd noStyle:不额外包裹。hb-form-item 无对应概念,这里仅消费该 prop 不报错 */
79
90
  noStyle?: boolean;
80
91
  style?: React.CSSProperties;
81
92
  className?: string;