@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
@@ -2,17 +2,23 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  const PRIMARY = 'var(--hb-color-primary, #ff6700)';
4
4
  export function Menu(props) {
5
- const { items, mode = 'vertical', selectedKeys, openKeys, theme = 'light', inlineCollapsed, onClick, onOpenChange, onSelect, style, className, children, } = props;
5
+ const { items, mode = 'vertical', selectedKeys, openKeys, theme = 'light', inlineCollapsed, onClick, onOpenChange, onSelect, style, className, children } = props;
6
6
  const horizontal = mode === 'horizontal';
7
7
  const dark = theme === 'dark';
8
8
  const [internalOpen, setInternalOpen] = React.useState(openKeys ?? []);
9
9
  const openSet = openKeys !== undefined ? openKeys : internalOpen;
10
- const selected = selectedKeys ?? [];
10
+ // 非受控选中:selectedKeys 未传时,点击菜单项应自动高亮选中(对齐 antd 默认行为)。
11
+ // 旧实现 selected 恒为 [](selectedKeys ?? []),点击后无选中视觉反馈。
12
+ const [internalSelected, setInternalSelected] = React.useState([]);
13
+ const selected = selectedKeys !== undefined ? selectedKeys : internalSelected;
11
14
  const collapsed = !!inlineCollapsed;
12
15
  const fireClick = (item, keyPath) => {
13
16
  if (item.disabled)
14
17
  return;
15
18
  const info = { key: item.key, keyPath: [item.key, ...keyPath], item };
19
+ // 非受控模式:内部更新选中态,触发高亮重渲染
20
+ if (selectedKeys === undefined)
21
+ setInternalSelected([item.key]);
16
22
  onClick?.(info);
17
23
  onSelect?.({ key: item.key, keyPath: info.keyPath });
18
24
  };
@@ -22,24 +28,26 @@ export function Menu(props) {
22
28
  setInternalOpen(next);
23
29
  onOpenChange?.(next);
24
30
  };
25
- const Icon = ({ icon }) => icon ? (_jsx("span", { style: { display: 'inline-flex', alignItems: 'center', marginRight: 8, width: 16, height: 16 }, children: icon })) : null;
31
+ const Icon = ({ icon }) => icon ? _jsx("span", { style: { display: 'inline-flex', alignItems: 'center', marginRight: 8, width: 16, height: 16 }, children: icon }) : null;
26
32
  const textColor = dark ? 'rgba(255,255,255,0.85)' : 'rgba(0,0,0,0.88)';
27
33
  const disabledColor = dark ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.25)';
28
34
  const renderItem = (item, keyPath) => {
29
35
  const hasChildren = !!(item.children && item.children.length > 0);
30
36
  const isSelected = selected.includes(item.key);
31
37
  const isOpen = openSet.includes(item.key);
32
- const labelColor = item.disabled ? disabledColor : isSelected ? PRIMARY : textColor;
33
38
  if (hasChildren && !collapsed) {
34
- return (_jsxs("li", { role: "menuitem", style: { listStyle: 'none' }, children: [_jsxs("div", { onClick: () => !item.disabled && toggleSub(item.key), style: {
39
+ return (_jsxs("li", { role: "menuitem", style: { listStyle: 'none' }, children: [_jsxs("div", { onClick: () => !item.disabled && toggleSub(item.key), className: `hb-menu__item-li${isSelected ? ' hb-menu__item-li--selected' : ''}`, style: {
35
40
  display: 'flex',
36
41
  alignItems: 'center',
37
42
  height: 40,
38
43
  padding: collapsed ? 0 : '0 16px',
39
44
  cursor: item.disabled ? 'not-allowed' : 'pointer',
40
- color: labelColor,
45
+ color: item.disabled ? disabledColor : undefined,
41
46
  whiteSpace: 'nowrap',
42
- }, children: [_jsx(Icon, { icon: item.icon }), _jsx("span", { style: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis' }, children: item.label }), _jsx("span", { style: { display: 'inline-block', transition: 'transform .2s', transform: isOpen ? 'rotate(90deg)' : 'none', fontSize: 10, marginLeft: 8 }, children: "\u25B6" })] }), isOpen && (_jsx("ul", { role: "menu", style: { listStyle: 'none', margin: 0, padding: 0 }, children: item.children.map(c => renderItem(c, [item.key, ...keyPath])) }))] }, item.key));
47
+ boxSizing: 'border-box',
48
+ borderRadius: 6,
49
+ margin: '2px 8px',
50
+ }, children: [_jsx(Icon, { icon: item.icon }), _jsx("span", { style: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis' }, children: item.label }), _jsx("span", { style: { display: 'inline-block', transition: 'transform .2s', transform: isOpen ? 'rotate(90deg)' : 'none', fontSize: 10, marginLeft: 8 }, children: "\u25B6" })] }), _jsx("div", { className: `hb-menu__submenu${isOpen ? ' hb-menu__submenu--open' : ''}`, children: _jsx("div", { children: _jsx("ul", { role: "menu", style: { listStyle: 'none', margin: 0, padding: 0 }, children: item.children.map(c => renderItem(c, [item.key, ...keyPath])) }) }) })] }, item.key));
43
51
  }
44
52
  // 叶子项
45
53
  const baseStyle = {
@@ -48,16 +56,50 @@ export function Menu(props) {
48
56
  height: horizontal ? 56 : 40,
49
57
  padding: horizontal ? '0 20px' : collapsed ? 0 : '0 16px',
50
58
  cursor: item.disabled ? 'not-allowed' : 'pointer',
51
- color: labelColor,
52
- background: isSelected ? (dark ? PRIMARY : 'rgba(255,103,0,0.06)') : 'transparent',
59
+ // color/background 不在 inline 设:交给 .hb-menu__item-li(--selected) class 控制,
60
+ // 否则 inline 优先级高于 class,选中态的文字色/背景色会被覆盖。
61
+ color: item.disabled ? disabledColor : undefined,
53
62
  borderBottom: horizontal && isSelected ? `2px solid ${PRIMARY}` : 'none',
54
63
  whiteSpace: 'nowrap',
55
64
  listStyle: 'none',
65
+ boxSizing: 'border-box',
66
+ borderRadius: horizontal ? 0 : 6,
67
+ margin: horizontal ? 0 : '2px 8px',
56
68
  };
57
- return (_jsxs("li", { role: "menuitem", onClick: () => fireClick(item, keyPath), style: baseStyle, children: [_jsx(Icon, { icon: item.icon }), _jsx("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis' }, children: item.label })] }, item.key));
69
+ return (_jsxs("li", { role: "menuitem", onClick: () => fireClick(item, keyPath), className: `hb-menu__item-li${isSelected ? ' hb-menu__item-li--selected' : ''}`, style: baseStyle, children: [_jsx(Icon, { icon: item.icon }), _jsx("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis' }, children: item.label })] }, item.key));
58
70
  };
59
71
  const list = items && items.length > 0 ? items : [];
60
- return (_jsxs("ul", { role: "menu", className: className, style: {
72
+ // 唯一类名前缀,避免多实例样式冲突(hover/选中指示条需 CSS 伪类,inline style 做不到)。
73
+ const scope = React.useMemo(() => `hb-menu-${Math.random().toString(36).slice(2, 9)}`, []);
74
+ const selectedBg = dark ? 'rgba(255,255,255,0.08)' : 'rgba(22,119,255,0.06)';
75
+ const hoverBg = dark ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.04)';
76
+ // hover/选中指示条样式:CSS 伪类(:hover / ::after)无法用 inline style 表达,
77
+ // 注入到 document.head(注意 <ul> 内放 <style> 会被浏览器按内容模型忽略/移位,故必须放 head)。
78
+ // 带scope类名隔离,组件卸载时移除。
79
+ React.useEffect(() => {
80
+ if (typeof document === 'undefined')
81
+ return;
82
+ const styleEl = document.createElement('style');
83
+ styleEl.dataset.hbMenuScope = scope;
84
+ styleEl.textContent = `
85
+ .${scope} .hb-menu__item-li { position: relative; transition: background 0.2s, color 0.2s; color: ${textColor}; }
86
+ .${scope} .hb-menu__item-li:hover { background: ${hoverBg}; }
87
+ .${scope} .hb-menu__item-li--selected { background: ${selectedBg}; color: ${PRIMARY}; }
88
+ .${scope} .hb-menu__item-li--selected:hover { background: ${selectedBg}; }
89
+ .${scope}.hb-menu--inline .hb-menu__item-li--selected::after,
90
+ .${scope}.hb-menu--vertical .hb-menu__item-li--selected::after {
91
+ content: ''; position: absolute; right: 0; top: 0; bottom: 0; width: 3px; background: ${PRIMARY};
92
+ }
93
+ .${scope} .hb-menu__submenu { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.3s cubic-bezier(0.645,0.045,0.355,1); overflow: hidden; }
94
+ .${scope} .hb-menu__submenu--open { grid-template-rows: 1fr; }
95
+ .${scope} .hb-menu__submenu > div { min-height: 0; }
96
+ `;
97
+ document.head.appendChild(styleEl);
98
+ return () => {
99
+ styleEl.remove();
100
+ };
101
+ }, [scope, dark, hoverBg, selectedBg]);
102
+ return (_jsxs("ul", { role: "menu", className: `${scope} hb-menu--${mode} ${className || ''}`, style: {
61
103
  listStyle: 'none',
62
104
  margin: 0,
63
105
  padding: inlineCollapsed || collapsed ? 0 : horizontal ? 0 : '8px 0',
@@ -67,6 +109,8 @@ export function Menu(props) {
67
109
  background: dark ? '#001529' : '#fff',
68
110
  color: textColor,
69
111
  borderBottom: horizontal ? `1px solid ${dark ? '#1f1f1f' : '#f0f0f0'}` : 'none',
112
+ // 对齐 antd:inline 菜单无右边框(旧实现可能带边框线,这里显式 none)
113
+ borderRight: 'none',
70
114
  minWidth: horizontal ? 0 : collapsed ? 0 : 200,
71
115
  height: horizontal ? 56 : '100%',
72
116
  boxSizing: 'border-box',
@@ -15,8 +15,11 @@ export interface ModalProps {
15
15
  footer?: React.ReactNode | null;
16
16
  children?: React.ReactNode;
17
17
  style?: React.CSSProperties;
18
+ className?: string;
19
+ centered?: boolean;
20
+ closable?: boolean;
18
21
  }
19
- export declare function Modal(props: ModalProps): React.JSX.Element;
22
+ export declare function Modal(props: ModalProps): React.JSX.Element | null;
20
23
  export declare namespace Modal {
21
24
  var confirm: any;
22
25
  var info: any;
@@ -1,16 +1,141 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
- import { HbDialog } from '@huibo-ui/react';
3
+ /**
4
+ * antd Modal 兼容层(纯 div 实现)。
5
+ *
6
+ * 不再走 <HbDialog>(Shadow DOM slot):弹窗 body/footer 内容进 slot 后,<Form>/<Descriptions>
7
+ * 等嵌套布局会受 ::slotted 干扰,且 hb-dialog 的 footer 受限(不支持任意 ReactNode)。
8
+ * 纯 div 自渲染 mask + 居中容器 + header/body/footer,children 作为真实 DOM,嵌套表单布局稳定。
9
+ * 动画复刻 hb-dialog 的 fade+zoom(@keyframes 内联,不依赖 hb-*)。
10
+ *
11
+ * 命令式 API(confirm/info/warning/success/error)与 useModal 保留不变。
12
+ */
13
+ // @keyframes 只需注入一次;用模块级标志避免重复插入。
14
+ let motionInjected = false;
15
+ function injectMotion() {
16
+ if (motionInjected || typeof document === 'undefined')
17
+ return;
18
+ motionInjected = true;
19
+ const style = document.createElement('style');
20
+ style.setAttribute('data-hb-modal-motion', '');
21
+ style.textContent = `
22
+ @keyframes hb-modal-overlay-in { from { opacity: 0; } to { opacity: 1; } }
23
+ @keyframes hb-modal-overlay-out { from { opacity: 1; } to { opacity: 0; } }
24
+ @keyframes hb-modal-in {
25
+ from { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
26
+ to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
27
+ }
28
+ @keyframes hb-modal-out {
29
+ from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
30
+ to { opacity: 0; transform: translate(-50%, -50%) scale(0.95); }
31
+ }
32
+ `;
33
+ document.head.appendChild(style);
34
+ }
35
+ const CloseIcon = () => (_jsx("svg", { width: "14", height: "14", 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" }) }));
4
36
  export function Modal(props) {
5
- const { open, title, width, onCancel, onOk, confirmLoading, okText, cancelText, maskClosable = true, keyboard = true, destroyOnClose, footer, children } = props;
6
- // 默认 footer:OK/Cancel 按钮(对齐 antd 默认行为)。footer=null 时无底部。
7
- const defaultFooter = (_jsxs("div", { style: { textAlign: 'right', marginTop: 16, display: 'flex', justifyContent: 'flex-end', gap: 8 }, children: [_jsx("button", { type: "button", onClick: (e) => onCancel?.(e), style: { padding: '4px 15px', height: 32, borderRadius: 6, border: '1px solid #d9d9d9', background: '#fff', cursor: 'pointer' }, children: cancelText || '取消' }), _jsx("button", { type: "button", disabled: confirmLoading, onClick: (e) => onOk?.(e), style: { padding: '4px 15px', height: 32, borderRadius: 6, border: 'none', background: 'var(--hb-color-primary, #ff6700)', color: '#fff', cursor: 'pointer' }, children: confirmLoading ? '加载中...' : (okText || '确定') })] }));
37
+ const { open, title, width, onCancel, onOk, confirmLoading, okText, cancelText, maskClosable = true, keyboard = true, destroyOnClose, footer, children, style, className, centered = true, closable = true, } = props;
38
+ injectMotion();
39
+ // 离场动画:open true→false 时保留 DOM 0.3s 出场再卸载。
40
+ const [leaving, setLeaving] = React.useState(false);
41
+ const prevOpen = React.useRef(!!open);
42
+ React.useEffect(() => {
43
+ if (open && !prevOpen.current) {
44
+ setLeaving(false);
45
+ }
46
+ else if (!open && prevOpen.current) {
47
+ setLeaving(true);
48
+ const t = setTimeout(() => setLeaving(false), 300);
49
+ return () => clearTimeout(t);
50
+ }
51
+ prevOpen.current = !!open;
52
+ }, [open]);
53
+ // Esc 关闭
54
+ React.useEffect(() => {
55
+ if (!open || !keyboard)
56
+ return;
57
+ const onKey = (e) => {
58
+ if (e.key === 'Escape')
59
+ onCancel?.({});
60
+ };
61
+ window.addEventListener('keydown', onKey);
62
+ return () => window.removeEventListener('keydown', onKey);
63
+ }, [open, keyboard, onCancel]);
64
+ // body 滚动锁定:弹层显示时锁住 body 滚动,避免关闭时页面因滚动条显隐而 reflow 闪烁。
65
+ // 用引用计数支持多个弹层叠加;compensate scrollbar 宽度避免横向跳动。
66
+ React.useEffect(() => {
67
+ if (!open)
68
+ return;
69
+ const body = document.body;
70
+ const prevOverflow = body.style.overflow;
71
+ const prevPaddingRight = body.style.paddingRight;
72
+ const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
73
+ if (scrollbarWidth > 0)
74
+ body.style.paddingRight = `${scrollbarWidth}px`;
75
+ body.style.overflow = 'hidden';
76
+ return () => {
77
+ body.style.overflow = prevOverflow;
78
+ body.style.paddingRight = prevPaddingRight;
79
+ };
80
+ }, [open]);
81
+ // 显示条件 = open || leaving;都为 false 时完全卸载,
82
+ // 否则 open=false 后 modal/遮罩仍 fixed 显示,挡住下层。
83
+ if (!open && !leaving)
84
+ return null;
85
+ const defaultFooter = (_jsxs("div", { style: { textAlign: 'right', display: 'flex', justifyContent: 'flex-end', gap: 8 }, children: [_jsx("button", { type: "button", onClick: e => onCancel?.(e), style: { padding: '4px 15px', height: 32, borderRadius: 6, border: '1px solid #d9d9d9', background: '#fff', cursor: 'pointer', fontSize: 14 }, children: cancelText || '取消' }), _jsx("button", { type: "button", disabled: confirmLoading, onClick: e => onOk?.(e), style: { padding: '4px 15px', height: 32, borderRadius: 6, border: 'none', background: 'var(--hb-color-primary, #ff6700)', color: '#fff', cursor: 'pointer', fontSize: 14 }, children: confirmLoading ? '加载中...' : okText || '确定' })] }));
8
86
  const resolvedFooter = footer === undefined ? defaultFooter : footer;
9
- // antd width 默认 520(number);hb-dialog width string,number 必须补 px 否则是非法 CSS 被忽略
10
- const resolvedWidth = width == null ? undefined : typeof width === 'number' ? `${width}px` : String(width);
11
- const wrapClassName = props.wrapClassName;
12
- return (_jsxs(HbDialog, { modelValue: open || false, title: typeof title === 'string' ? title : '', width: resolvedWidth, closeOnClickModal: maskClosable, closeOnPressEscape: keyboard, destroyOnClose: destroyOnClose, showClose: true, style: props.style, className: [props.className, wrapClassName].filter(Boolean).join(' ') || undefined, onHbClose: () => { if (onCancel)
13
- onCancel({}); }, children: [children, resolvedFooter !== null && resolvedFooter !== undefined ? resolvedFooter : null] }));
87
+ const resolvedWidth = width == null ? 520 : typeof width === 'number' ? `${width}px` : String(width);
88
+ return (_jsxs("div", { style: { position: 'fixed', inset: 0, zIndex: 1000, pointerEvents: 'none' }, children: [_jsx("div", { onClick: () => maskClosable && onCancel?.({}), style: {
89
+ position: 'fixed',
90
+ inset: 0,
91
+ background: 'var(--hb-color-bg-mask, rgba(0,0,0,0.45))',
92
+ pointerEvents: 'auto',
93
+ animation: `${leaving ? 'hb-modal-overlay-out' : 'hb-modal-overlay-in'} 0.3s ease both`,
94
+ } }), _jsxs("div", { style: {
95
+ position: 'fixed',
96
+ top: centered ? '50%' : 100,
97
+ left: '50%',
98
+ width: resolvedWidth,
99
+ maxWidth: 'calc(100vw - 32px)',
100
+ background: 'var(--hb-color-bg-elevated, #fff)',
101
+ borderRadius: 8,
102
+ boxShadow: '0 6px 16px 0 rgba(0,0,0,0.08), 0 3px 6px -4px rgba(0,0,0,0.12), 0 9px 28px 8px rgba(0,0,0,0.05)',
103
+ pointerEvents: 'auto',
104
+ display: 'flex',
105
+ flexDirection: 'column',
106
+ maxHeight: 'calc(100vh - 32px)',
107
+ overflow: 'hidden',
108
+ zIndex: 1050,
109
+ transform: centered ? 'translate(-50%, -50%)' : 'translateX(-50%)',
110
+ animation: `${leaving ? 'hb-modal-out' : 'hb-modal-in'} 0.3s cubic-bezier(0.16, 1, 0.3, 1) both`,
111
+ ...style,
112
+ }, className: className, children: [(title !== undefined || closable) && (_jsxs("div", { style: {
113
+ display: 'flex',
114
+ alignItems: 'center',
115
+ justifyContent: 'space-between',
116
+ padding: '16px 24px',
117
+ borderBottom: '1px solid var(--hb-color-border-secondary, #f0f0f0)',
118
+ }, 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: e => onCancel?.(e), "aria-label": "close", style: {
119
+ display: 'inline-flex',
120
+ alignItems: 'center',
121
+ justifyContent: 'center',
122
+ width: 22,
123
+ height: 22,
124
+ border: 'none',
125
+ background: 'none',
126
+ color: 'var(--hb-color-text-secondary, rgba(0,0,0,0.45))',
127
+ cursor: 'pointer',
128
+ borderRadius: '50%',
129
+ padding: 0,
130
+ transition: 'all 0.2s',
131
+ }, 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', color: 'var(--hb-color-text, rgba(0,0,0,0.88))', fontSize: 14 }, children: children }), resolvedFooter !== null && resolvedFooter !== undefined && (_jsx("div", { style: {
132
+ display: 'flex',
133
+ alignItems: 'center',
134
+ justifyContent: 'flex-end',
135
+ gap: 8,
136
+ padding: '8px 24px',
137
+ borderTop: '1px solid var(--hb-color-border-secondary, #f0f0f0)',
138
+ }, children: resolvedFooter }))] })] }));
14
139
  }
15
140
  function imperative(opts) {
16
141
  const div = document.createElement('div');
@@ -21,7 +146,6 @@ function imperative(opts) {
21
146
  return;
22
147
  destroyed = true;
23
148
  if (!root) {
24
- // createRoot 尚未就绪(import 未完成),直接移除容器
25
149
  div.remove();
26
150
  return;
27
151
  }
@@ -29,13 +153,12 @@ function imperative(opts) {
29
153
  setTimeout(() => {
30
154
  root.unmount();
31
155
  div.remove();
32
- }, 200);
156
+ }, 300);
33
157
  };
34
158
  let root;
35
- // 动态拿到 createRoot
36
159
  import('react-dom/client').then(({ createRoot }) => {
37
160
  root = createRoot(div);
38
- const showCancel = opts.okCancel !== false; // 默认 confirm 两按钮,其余单按钮
161
+ const showCancel = opts.okCancel !== false;
39
162
  root.render(React.createElement(Modal, {
40
163
  open: true,
41
164
  title: opts.title,
@@ -53,7 +176,7 @@ function imperative(opts) {
53
176
  destroy();
54
177
  }
55
178
  },
56
- footer: null, // hb-dialog footer 受限,交由 content 自行渲染按钮或使用默认
179
+ footer: null,
57
180
  }, opts.content));
58
181
  });
59
182
  return { destroy };
@@ -63,11 +186,6 @@ Modal.info = ((p) => imperative({ ...p, type: 'info', okCancel: false }));
63
186
  Modal.warning = ((p) => imperative({ ...p, type: 'warning', okCancel: false }));
64
187
  Modal.success = ((p) => imperative({ ...p, type: 'success', okCancel: false }));
65
188
  Modal.error = ((p) => imperative({ ...p, type: 'error', okCancel: false }));
66
- /**
67
- * useModal —— 返回 [modal, contextHolder]。
68
- * contextHolder 需渲染在组件树里(占位用,当前实现命令式调用走 body 挂载,holder 可不渲染内容)。
69
- * modal 实例提供 confirm/info/warning/success/error,与静态方法同义。
70
- */
71
189
  Modal.useModal = function useModal() {
72
190
  const api = React.useMemo(() => ({
73
191
  confirm: (p) => imperative({ ...p, type: 'confirm', okCancel: true }),
@@ -76,7 +194,6 @@ Modal.useModal = function useModal() {
76
194
  success: (p) => imperative({ ...p, type: 'success', okCancel: false }),
77
195
  error: (p) => imperative({ ...p, type: 'error', okCancel: false }),
78
196
  }), []);
79
- // holder:命令式调用自己挂 body,这里返回空占位以满足 antd 的解构用法
80
197
  const holder = React.createElement(React.Fragment, null);
81
198
  return [api, holder];
82
199
  };
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { HbPageHeader } from '@huibo-ui/react';
3
3
  export function PageHeader(props) {
4
- return _jsx(HbPageHeader, { title: typeof props.title === 'string' ? props.title : '', subtitle: typeof props.subTitle === 'string' ? props.subTitle : '', extra: props.extra, showBack: !!props.onBack });
4
+ return (_jsx(HbPageHeader, { title: typeof props.title === 'string' ? props.title : '', subtitle: typeof props.subTitle === 'string' ? props.subTitle : '', extra: props.extra, showBack: !!props.onBack }));
5
5
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { HbPagination } from '@huibo-ui/react';
3
3
  export function Pagination(props) {
4
- return _jsx(HbPagination, { "current-page": props.current ?? 1, "page-size": props.pageSize ?? 10, total: props.total ?? 0, "show-page-size": props.showSizeChanger, "page-sizes": props.pageSizeOptions?.map(Number), onHbCurrentChange: (e) => props.onChange?.(e.detail, props.pageSize ?? 10), style: props.style });
4
+ return (_jsx(HbPagination, { "current-page": props.current ?? 1, "page-size": props.pageSize ?? 10, total: props.total ?? 0, "show-page-size": props.showSizeChanger, "page-sizes": props.pageSizeOptions?.map(Number), onHbCurrentChange: (e) => props.onChange?.(e.detail, props.pageSize ?? 10), style: props.style }));
5
5
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { HbPopconfirm } from '@huibo-ui/react';
3
3
  export function Popconfirm(props) {
4
- return _jsx(HbPopconfirm, { title: typeof props.title === 'string' ? props.title : '', confirmText: props.okText, cancelText: props.cancelText, position: (props.placement || 'top'), onHbConfirm: () => props.onConfirm?.(), onHbCancel: () => props.onCancel?.(), children: props.children });
4
+ return (_jsx(HbPopconfirm, { title: typeof props.title === 'string' ? props.title : '', confirmText: props.okText, cancelText: props.cancelText, position: (props.placement || 'top'), onHbConfirm: () => props.onConfirm?.(), onHbCancel: () => props.onCancel?.(), children: props.children }));
5
5
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { HbPopover } from '@huibo-ui/react';
3
3
  export function Popover(props) {
4
- return _jsx(HbPopover, { content: props.content, title: typeof props.title === 'string' ? props.title : '', position: (props.placement || 'top'), trigger: (props.trigger || 'hover'), children: props.children });
4
+ return (_jsx(HbPopover, { content: props.content, title: typeof props.title === 'string' ? props.title : '', position: (props.placement || 'top'), trigger: (props.trigger || 'hover'), children: props.children }));
5
5
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { HbProgress } from '@huibo-ui/react';
3
3
  export function Progress(props) {
4
- return _jsx(HbProgress, { percentage: props.percent ?? 0, type: props.type, status: props.status, showText: props.showInfo ?? true, strokeColor: props.strokeColor, strokeWidth: props.strokeWidth, style: props.style });
4
+ return (_jsx(HbProgress, { percentage: props.percent ?? 0, type: props.type, status: props.status, showText: props.showInfo ?? true, strokeColor: props.strokeColor, strokeWidth: props.strokeWidth, style: props.style }));
5
5
  }
@@ -2,9 +2,10 @@ import React from 'react';
2
2
  export declare function Radio(props: {
3
3
  value?: any;
4
4
  checked?: boolean;
5
- onChange?: (e: any) => void;
6
5
  disabled?: boolean;
7
6
  children?: React.ReactNode;
7
+ onChange?: (e: any) => void;
8
+ style?: React.CSSProperties;
8
9
  }): React.JSX.Element;
9
10
  export declare namespace Radio {
10
11
  var Group: any;
@@ -12,12 +13,13 @@ export declare namespace Radio {
12
13
  }
13
14
  export declare function RadioGroup(props: {
14
15
  value?: any;
16
+ defaultValue?: any;
15
17
  onChange?: (value: any) => void;
16
- options?: {
18
+ options?: Array<{
17
19
  value: any;
18
20
  label: React.ReactNode;
19
21
  disabled?: boolean;
20
- }[];
22
+ }>;
21
23
  disabled?: boolean;
22
24
  children?: React.ReactNode;
23
25
  style?: React.CSSProperties;
@@ -1,23 +1,34 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
2
3
  import { HbRadio } from '@huibo-ui/react';
4
+ const GroupContext = React.createContext(null);
3
5
  export function Radio(props) {
4
- return _jsx(HbRadio, { value: props.value, checked: props.checked, disabled: props.disabled, label: props.children, onHbChange: (e) => props.onChange?.({ target: { value: e.detail } }), children: props.children });
6
+ const ctx = React.useContext(GroupContext);
7
+ const isChecked = ctx ? ctx.value === props.value : props.checked;
8
+ const disabled = ctx?.disabled || props.disabled;
9
+ return (_jsx(HbRadio, { value: props.value, checked: isChecked ?? false, disabled: disabled, label: props.children, onHbChange: () => {
10
+ if (ctx)
11
+ ctx.onChange(props.value);
12
+ props.onChange?.({ target: { value: props.value, checked: true } });
13
+ }, style: props.style, children: props.children }));
5
14
  }
6
- // RadioGroup:huibo-ui 无独立 hb-radio-group 组件,用 div + 多个 hb-radio 实现
7
15
  export function RadioGroup(props) {
8
- const { value, onChange, options, disabled } = props;
9
- if (options) {
10
- return (_jsx("div", { style: props.style, children: options.map((opt, i) => (_jsx(HbRadio, { value: opt.value, checked: value === opt.value, disabled: disabled || opt.disabled, label: opt.label, onHbChange: (e) => onChange?.(e.detail) }, i))) }));
11
- }
12
- // children 模式:透传(用户自己用 <Radio> 子元素)
13
- return _jsx("div", { style: props.style, "data-value": value, children: props.children });
16
+ const { value: ctrlValue, defaultValue, onChange, options, disabled, children, style } = props;
17
+ const isControlled = ctrlValue !== undefined;
18
+ const [inner, setInner] = React.useState(defaultValue);
19
+ const selected = isControlled ? ctrlValue : inner;
20
+ const handleChange = React.useCallback((val) => {
21
+ if (!isControlled)
22
+ setInner(val);
23
+ onChange?.(val);
24
+ }, [isControlled, onChange]);
25
+ const ctx = React.useMemo(() => ({ value: selected, disabled, onChange: handleChange }), [selected, disabled, handleChange]);
26
+ return (_jsx(GroupContext.Provider, { value: ctx, children: _jsx("div", { style: style, children: options
27
+ ? options.map((opt, i) => (_jsx(HbRadio, { value: opt.value, checked: selected === opt.value, disabled: disabled || opt.disabled, label: opt.label, onHbChange: () => handleChange(opt.value) }, i)))
28
+ : children }) }));
14
29
  }
15
- // 挂上 antd 静态子组件(Radio.Group / Radio.Button)
16
30
  Radio.Group = RadioGroup;
17
- /**
18
- * Radio.Button —— 样式变体,huibo hb-radio 无 button 形态;
19
- * 这里复用 Radio 渲染(功能可用,视觉非按钮)。
20
- */
31
+ /** Radio.Button —— 按钮形态变体。hb-radio 无 button 形态,复用 Radio(功能可用)。 */
21
32
  Radio.Button = function RadioButton(props) {
22
33
  return _jsx(Radio, { ...props });
23
34
  };
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { HbRate } from '@huibo-ui/react';
3
3
  export function Rate(props) {
4
- return _jsx(HbRate, { modelValue: props.value ?? 0, max: props.count ?? 5, allowHalf: props.allowHalf, disabled: props.disabled, onHbChange: (e) => props.onChange?.(e.detail), style: props.style });
4
+ return (_jsx(HbRate, { modelValue: props.value ?? 0, max: props.count ?? 5, allowHalf: props.allowHalf, disabled: props.disabled, onHbChange: (e) => props.onChange?.(e.detail), style: props.style }));
5
5
  }
@@ -1,5 +1,5 @@
1
1
  import { jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { HbResult } from '@huibo-ui/react';
3
3
  export function Result(props) {
4
- return _jsxs(HbResult, { status: props.status, title: typeof props.title === 'string' ? props.title : '', subTitle: typeof props.subTitle === 'string' ? props.subTitle : '', children: [props.extra, props.children] });
4
+ return (_jsxs(HbResult, { status: props.status, title: typeof props.title === 'string' ? props.title : '', subTitle: typeof props.subTitle === 'string' ? props.subTitle : '', children: [props.extra, props.children] }));
5
5
  }
@@ -1,10 +1,12 @@
1
1
  import React from 'react';
2
2
  export declare function Segmented(props: {
3
- options?: {
3
+ options?: Array<string | {
4
4
  value: string;
5
- label: React.ReactNode;
6
- }[];
5
+ label?: React.ReactNode;
6
+ disabled?: boolean;
7
+ }>;
7
8
  value?: string;
9
+ defaultValue?: string;
8
10
  onChange?: (value: string) => void;
9
11
  block?: boolean;
10
12
  size?: 'small' | 'middle' | 'large';
@@ -1,5 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { HbSegmented } from '@huibo-ui/react';
3
3
  export function Segmented(props) {
4
- return _jsx(HbSegmented, { options: props.options, modelValue: props.value, block: props.block, size: props.size === 'middle' ? 'default' : props.size, onHbChange: (e) => props.onChange?.(e.detail) });
4
+ // antd 允许 options 为字符串数组(['每日','每周'])或对象数组;hb-segmented 只认 {value,label}
5
+ // 规范化:字符串 → { value: s, label: s },对象保持(label 缺省时用 value 兜底)。
6
+ const normalized = props.options?.map(o => typeof o === 'string' ? { value: o, label: o } : { value: String(o?.value), label: (o?.label ?? String(o?.value)), disabled: o?.disabled });
7
+ return (_jsx(HbSegmented, { options: normalized, modelValue: props.value ?? props.defaultValue, block: props.block, size: props.size === 'middle' ? 'default' : props.size, onHbChange: (e) => props.onChange?.(e.detail) }));
5
8
  }
@@ -27,7 +27,7 @@ function SelectOptionComponent(_props) {
27
27
  return null;
28
28
  }
29
29
  export function Select(props) {
30
- const { value, onChange, options, mode, placeholder, disabled, size, allowClear, showSearch, virtual, style, children, } = props;
30
+ const { value, onChange, options, mode, placeholder, disabled, size, allowClear, showSearch, virtual, style, children } = props;
31
31
  // antd maxTagCount:多选时折叠超出数量的 tag。映射到 hb-select 的 collapseTags + maxCollapseTags
32
32
  const maxTagCount = props.maxTagCount;
33
33
  // antd fieldNames:自定义 option 字段名(如 {label:'nickName', value:'id'})。
@@ -1,11 +1,18 @@
1
1
  import React from 'react';
2
+ /**
3
+ * antd Slider 兼容层。
4
+ * 修复:旧实现 modelValue={props.value ?? 0} 没处理 defaultValue,导致 demo 的
5
+ * defaultValue={30} 不生效、滑块初始永远在 0。同时补上 range 双滑块支持。
6
+ */
2
7
  export declare function Slider(props: {
3
- value?: number;
4
- onChange?: (value: number) => void;
8
+ value?: number | number[];
9
+ defaultValue?: number | number[];
10
+ onChange?: (value: number | number[]) => void;
5
11
  min?: number;
6
12
  max?: number;
7
13
  step?: number;
8
14
  disabled?: boolean;
15
+ range?: boolean;
9
16
  vertical?: boolean;
10
17
  style?: React.CSSProperties;
11
18
  }): React.JSX.Element;
@@ -1,5 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { HbSlider } from '@huibo-ui/react';
3
+ /**
4
+ * antd Slider 兼容层。
5
+ * 修复:旧实现 modelValue={props.value ?? 0} 没处理 defaultValue,导致 demo 的
6
+ * defaultValue={30} 不生效、滑块初始永远在 0。同时补上 range 双滑块支持。
7
+ */
3
8
  export function Slider(props) {
4
- return _jsx(HbSlider, { modelValue: props.value ?? 0, min: props.min ?? 0, max: props.max ?? 100, step: props.step ?? 1, disabled: props.disabled, vertical: props.vertical, onHbChange: (e) => props.onChange?.(e.detail), style: props.style });
9
+ const isRange = props.range || Array.isArray(props.value) || Array.isArray(props.defaultValue);
10
+ const modelValue = props.value ?? props.defaultValue ?? (isRange ? [0, 100] : 0);
11
+ return (_jsx(HbSlider, { modelValue: modelValue, min: props.min ?? 0, max: props.max ?? 100, step: props.step ?? 1, disabled: props.disabled, range: isRange, vertical: props.vertical, onHbChange: (e) => props.onChange?.(e.detail), style: props.style }));
5
12
  }
@@ -1,11 +1,12 @@
1
1
  import React from 'react';
2
2
  export declare function Space(props: {
3
- size?: 'small' | 'middle' | 'large' | number;
3
+ size?: 'small' | 'middle' | 'large' | number | [number, number];
4
4
  direction?: 'horizontal' | 'vertical';
5
5
  align?: 'start' | 'center' | 'end';
6
6
  wrap?: boolean;
7
7
  children?: React.ReactNode;
8
8
  style?: React.CSSProperties;
9
+ className?: string;
9
10
  }): React.JSX.Element;
10
11
  export declare namespace Space {
11
12
  var Compact: any;
@@ -1,12 +1,55 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { HbSpace } from '@huibo-ui/react';
2
+ /**
3
+ * antd Space 兼容层(纯 div 实现)。
4
+ *
5
+ * 不再走 <HbSpace>(Shadow DOM slot):hb-space 的 ::slotted(*) 会把所有子元素强制
6
+ * display:inline-block + flex-shrink:0,导致塞进来的 <Select>/<DatePicker>/<Input>
7
+ * 宽度锁死、搜索栏等 flex 容器布局错乱。纯 div + flex + gap 是 antd 的真实做法,
8
+ * 子元素作为真实 DOM 参与 flex 流,宽度/伸缩行为与 antd 一致。
9
+ *
10
+ * size 映射 antd:small=8px / middle=16px / large=24px / number=自定义。
11
+ */
12
+ const SIZE_MAP = {
13
+ small: 8,
14
+ middle: 16,
15
+ large: 24,
16
+ };
3
17
  export function Space(props) {
4
- return _jsx(HbSpace, { size: props.size === 'middle' ? 'default' : props.size, direction: props.direction, align: props.align, wrap: props.wrap, style: props.style, children: props.children });
18
+ const { size = 'small', direction = 'horizontal', align, wrap, children, style, className } = props;
19
+ // antd gutter 支持 [horizontal, vertical] 数组;单值时两向同值。
20
+ let rowGap;
21
+ let columnGap;
22
+ if (Array.isArray(size)) {
23
+ rowGap = size[0];
24
+ columnGap = size[1];
25
+ }
26
+ else if (typeof size === 'number') {
27
+ rowGap = size;
28
+ columnGap = size;
29
+ }
30
+ else {
31
+ const v = SIZE_MAP[size] ?? 8;
32
+ rowGap = v;
33
+ columnGap = v;
34
+ }
35
+ const isHorizontal = direction === 'horizontal';
36
+ return (_jsx("div", { style: {
37
+ display: 'flex',
38
+ flexDirection: isHorizontal ? 'row' : 'column',
39
+ flexWrap: wrap ? 'wrap' : 'nowrap',
40
+ rowGap,
41
+ columnGap,
42
+ alignItems: align === 'start' ? 'flex-start' : align === 'end' ? 'flex-end' : align === 'center' ? 'center' : undefined,
43
+ ...style,
44
+ }, className: className, children: children }));
5
45
  }
6
46
  /**
7
47
  * Space.Compact —— 紧凑布局(antd 子组件)。huibo 无对应形态,
8
- * 这里直接透传 children(间距为 0)。
48
+ * 直接 inline-flex 透传 children,子元素间无 gap(紧凑)。
9
49
  */
10
50
  Space.Compact = function Compact(props) {
11
- return _jsx("div", { style: { display: 'inline-flex', ...props.style }, children: props.children });
51
+ return (_jsx("div", { style: {
52
+ display: props.block ? 'flex' : 'inline-flex',
53
+ ...props.style,
54
+ }, className: props.className, children: props.children }));
12
55
  };