@huibo-ui/react-antd 1.0.6 → 1.0.8

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.
@@ -101,7 +101,7 @@ export const Form = React.forwardRef(function Form(props, ref) {
101
101
  onValuesChange({ [prop]: info?.value }, {});
102
102
  };
103
103
  });
104
- return (_jsx(HbForm, { ref: refCallback, initialValues: initialValues, labelPosition: labelPosition, labelWidth: labelCol?.span ? `${(labelCol.span / 24) * 100}%` : '80px', style: style, className: className, children: children }));
104
+ return (_jsx(HbForm, { ref: refCallback, initialValues: initialValues, labelPosition: labelPosition, inline: layout === 'inline', labelWidth: labelCol?.span ? `${(labelCol.span / 24) * 100}%` : '80px', style: style, className: className, children: children }));
105
105
  });
106
106
  Form.useForm = function useForm() {
107
107
  const [f] = React.useState(createFormInstance);
@@ -1,13 +1,25 @@
1
1
  import React from 'react';
2
+ /**
3
+ * antd Menu 兼容层(自包含 React 渲染)。
4
+ *
5
+ * 为什么不用 core 的 hb-menu:
6
+ * 1. hb-menu 的 items 是数组,Stencil React 包装层首渲染时 items 常为 undefined → 走 <slot/>
7
+ * 渲染空;post-mount 赋值 el.items 也不可靠触发重渲染(scrm 异步加载 menuList 后
8
+ * 侧边菜单一直空白)。
9
+ * 2. hb-menu(Stencil)渲染 {item.icon} 时无法处理 React 元素图标(scrm 侧边菜单项
10
+ * 带 <img> 图标)。
11
+ * 故本层用纯 React 渲染(light DOM),items/图标/异步更新全部原生可用。
12
+ * 支持 antd mode(horizontal/vertical/inline)、selectedKeys、openKeys、onClick、子菜单展开。
13
+ */
2
14
  export interface MenuItemType {
3
15
  key: string;
4
- label: React.ReactNode;
16
+ label?: React.ReactNode;
5
17
  icon?: React.ReactNode;
6
18
  children?: MenuItemType[];
7
19
  disabled?: boolean;
20
+ [k: string]: any;
8
21
  }
9
22
  export interface MenuProps {
10
- [key: string]: any;
11
23
  items?: MenuItemType[];
12
24
  mode?: 'horizontal' | 'vertical' | 'inline';
13
25
  selectedKeys?: string[];
@@ -17,6 +29,7 @@ export interface MenuProps {
17
29
  onClick?: (info: {
18
30
  key: string;
19
31
  keyPath: string[];
32
+ item?: any;
20
33
  }) => void;
21
34
  onOpenChange?: (openKeys: string[]) => void;
22
35
  onSelect?: (info: {
@@ -26,15 +39,10 @@ export interface MenuProps {
26
39
  style?: React.CSSProperties;
27
40
  className?: string;
28
41
  children?: React.ReactNode;
42
+ [k: string]: any;
29
43
  }
30
- /**
31
- * antd Menu 兼容层。
32
- *
33
- * 关键:hb-menu 的 `items` 是数组,React 包装层(createReactComponent)只在
34
- * componentDidUpdate 时把数组同步为 DOM 属性,首渲染(componentDidLoad)时 items 为
35
- * undefined → hb-menu 走 <slot/> 分支渲染空。因此这里用 ref 在挂载时立即同步 items。
36
- */
37
44
  export declare function Menu(props: MenuProps): React.JSX.Element;
38
45
  export declare namespace Menu {
39
46
  var Item: any;
47
+ var SubMenu: any;
40
48
  }
@@ -1,32 +1,93 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
- import { HbMenu, HbMenuItem } from '@huibo-ui/react';
4
- /**
5
- * antd Menu 兼容层。
6
- *
7
- * 关键:hb-menu 的 `items` 是数组,React 包装层(createReactComponent)只在
8
- * componentDidUpdate 时把数组同步为 DOM 属性,首渲染(componentDidLoad)时 items 为
9
- * undefined → hb-menu 走 <slot/> 分支渲染空。因此这里用 ref 在挂载时立即同步 items。
10
- */
3
+ const PRIMARY = 'var(--hb-color-primary, #ff6700)';
11
4
  export function Menu(props) {
12
- const { items, mode, selectedKeys, openKeys, theme, inlineCollapsed, onClick, onOpenChange, style, className, children } = props;
13
- const refCallback = (el) => {
14
- // 立即把 items 数组同步到 CE(绕过 Stencil 只把 string/bool/number 设为 attribute 的限制)
15
- if (el && items) {
16
- el.items = items;
5
+ const { items, mode = 'vertical', selectedKeys, openKeys, theme = 'light', inlineCollapsed, onClick, onOpenChange, onSelect, style, className, children, } = props;
6
+ const horizontal = mode === 'horizontal';
7
+ const dark = theme === 'dark';
8
+ const [internalOpen, setInternalOpen] = React.useState(openKeys ?? []);
9
+ const openSet = openKeys !== undefined ? openKeys : internalOpen;
10
+ const selected = selectedKeys ?? [];
11
+ const collapsed = !!inlineCollapsed;
12
+ const fireClick = (item, keyPath) => {
13
+ if (item.disabled)
14
+ return;
15
+ const info = { key: item.key, keyPath: [item.key, ...keyPath], item };
16
+ onClick?.(info);
17
+ onSelect?.({ key: item.key, keyPath: info.keyPath });
18
+ };
19
+ const toggleSub = (key) => {
20
+ const next = openSet.includes(key) ? openSet.filter(k => k !== key) : [...openSet, key];
21
+ if (openKeys === undefined)
22
+ setInternalOpen(next);
23
+ onOpenChange?.(next);
24
+ };
25
+ const Icon = ({ icon }) => icon ? (_jsx("span", { style: { display: 'inline-flex', alignItems: 'center', marginRight: 8, width: 16, height: 16 }, children: icon })) : null;
26
+ const textColor = dark ? 'rgba(255,255,255,0.85)' : 'rgba(0,0,0,0.88)';
27
+ const disabledColor = dark ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.25)';
28
+ const renderItem = (item, keyPath) => {
29
+ const hasChildren = !!(item.children && item.children.length > 0);
30
+ const isSelected = selected.includes(item.key);
31
+ const isOpen = openSet.includes(item.key);
32
+ const labelColor = item.disabled ? disabledColor : isSelected ? PRIMARY : textColor;
33
+ if (hasChildren && !collapsed) {
34
+ return (_jsxs("li", { role: "menuitem", style: { listStyle: 'none' }, children: [_jsxs("div", { onClick: () => !item.disabled && toggleSub(item.key), style: {
35
+ display: 'flex',
36
+ alignItems: 'center',
37
+ height: 40,
38
+ padding: collapsed ? 0 : '0 16px',
39
+ cursor: item.disabled ? 'not-allowed' : 'pointer',
40
+ color: labelColor,
41
+ 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));
17
43
  }
44
+ // 叶子项
45
+ const baseStyle = {
46
+ display: 'flex',
47
+ alignItems: 'center',
48
+ height: horizontal ? 56 : 40,
49
+ padding: horizontal ? '0 20px' : collapsed ? 0 : '0 16px',
50
+ cursor: item.disabled ? 'not-allowed' : 'pointer',
51
+ color: labelColor,
52
+ background: isSelected ? (dark ? PRIMARY : 'rgba(255,103,0,0.06)') : 'transparent',
53
+ borderBottom: horizontal && isSelected ? `2px solid ${PRIMARY}` : 'none',
54
+ whiteSpace: 'nowrap',
55
+ listStyle: 'none',
56
+ };
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));
18
58
  };
19
- return (_jsx(HbMenu, { ref: refCallback, items: items, mode: (mode === 'inline' ? 'vertical' : mode), selectedKeys: selectedKeys, openKeys: openKeys, theme: theme, collapsed: inlineCollapsed, style: style, className: className, onHbSelect: (e) => {
20
- const key = e?.detail;
21
- if (onClick)
22
- onClick({ key, keyPath: [key] });
23
- if (props.onSelect)
24
- props.onSelect({ key, keyPath: [key] });
25
- }, onHbOpenChange: (e) => {
26
- if (onOpenChange)
27
- onOpenChange(e?.detail || []);
28
- }, children: children }));
59
+ const list = items && items.length > 0 ? items : [];
60
+ return (_jsxs("ul", { role: "menu", className: className, style: {
61
+ listStyle: 'none',
62
+ margin: 0,
63
+ padding: inlineCollapsed || collapsed ? 0 : horizontal ? 0 : '8px 0',
64
+ display: horizontal ? 'flex' : 'block',
65
+ flexDirection: horizontal ? 'row' : 'column',
66
+ alignItems: horizontal ? 'stretch' : 'stretch',
67
+ background: dark ? '#001529' : '#fff',
68
+ color: textColor,
69
+ borderBottom: horizontal ? `1px solid ${dark ? '#1f1f1f' : '#f0f0f0'}` : 'none',
70
+ minWidth: horizontal ? 0 : collapsed ? 0 : 200,
71
+ height: horizontal ? 56 : '100%',
72
+ boxSizing: 'border-box',
73
+ userSelect: 'none',
74
+ ...style,
75
+ }, children: [list.map(it => renderItem(it, [])), children] }));
29
76
  }
30
- Menu.Item = function MenuItem({ children, key: itemKey, disabled, onClick }) {
31
- return React.createElement(HbMenuItem, { itemKey, disabled, onClick }, children);
77
+ /** Menu.Item —— antd4 声明式占位(scrm items prop,这里仅兜底直接渲染) */
78
+ Menu.Item = function MenuItem({ children, disabled, onClick }) {
79
+ return (_jsx("li", { role: "menuitem", onClick: e => !disabled && onClick?.(e), style: {
80
+ listStyle: 'none',
81
+ display: 'flex',
82
+ alignItems: 'center',
83
+ height: 40,
84
+ padding: '0 16px',
85
+ cursor: disabled ? 'not-allowed' : 'pointer',
86
+ color: disabled ? 'rgba(0,0,0,0.25)' : 'rgba(0,0,0,0.88)',
87
+ whiteSpace: 'nowrap',
88
+ }, children: children }));
89
+ };
90
+ Menu.SubMenu = function SubMenu({ children, title }) {
91
+ // 简化:直接展开渲染(scrm 用 items,此分支少用)
92
+ return (_jsxs("li", { role: "menuitem", style: { listStyle: 'none' }, children: [_jsx("div", { style: { padding: '0 16px', height: 40, display: 'flex', alignItems: 'center', color: 'rgba(0,0,0,0.88)' }, children: title }), _jsx("ul", { role: "menu", style: { listStyle: 'none', margin: 0, padding: 0 }, children: children })] }));
32
93
  };
@@ -1,11 +1,20 @@
1
1
  import React from 'react';
2
+ /**
3
+ * antd Tabs 兼容层(自包含 React 渲染)。
4
+ *
5
+ * 为什么不用 core 的 hb-tabs:hb-tabs 的 items[].content 只接受 string/HTML,
6
+ * 不渲染 React 节点(scrm 权益管理 Tabs items 的 children 是复杂 React 内容 → 空白);
7
+ * 且 items 数组 post-mount 同步不可靠(同 hb-menu 的坑)。故本层纯 React 渲染:
8
+ * items 的 children/label 全部原生,activeKey 受控 + 非受控,type=line/card,tabPosition。
9
+ */
2
10
  export interface TabItemType {
3
11
  key: string;
4
- label: React.ReactNode;
12
+ label?: React.ReactNode;
5
13
  children?: React.ReactNode;
6
14
  disabled?: boolean;
15
+ [k: string]: any;
7
16
  }
8
- export declare function Tabs(props: {
17
+ export interface TabsProps {
9
18
  items?: TabItemType[];
10
19
  activeKey?: string;
11
20
  defaultActiveKey?: string;
@@ -13,9 +22,13 @@ export declare function Tabs(props: {
13
22
  type?: 'line' | 'card';
14
23
  size?: 'small' | 'middle' | 'large';
15
24
  tabPosition?: 'top' | 'bottom' | 'left' | 'right';
16
- children?: React.ReactNode;
25
+ destroyInactiveTabPane?: boolean;
17
26
  style?: React.CSSProperties;
18
- }): React.JSX.Element;
27
+ className?: string;
28
+ children?: React.ReactNode;
29
+ [k: string]: any;
30
+ }
31
+ export declare function Tabs(props: TabsProps): React.JSX.Element;
19
32
  export declare namespace Tabs {
20
33
  var TabPane: any;
21
34
  }
@@ -1,7 +1,67 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
- import { HbTabs, HbTabPane } from '@huibo-ui/react';
3
+ const PRIMARY = 'var(--hb-color-primary, #ff6700)';
4
4
  export function Tabs(props) {
5
- return _jsx(HbTabs, { items: props.items, modelValue: props.activeKey, type: props.type, size: props.size === 'middle' ? 'default' : props.size, tabPosition: props.tabPosition, onHbTabChange: (e) => props.onChange?.(e.detail), style: props.style, children: props.children });
5
+ const { items, activeKey, defaultActiveKey, onChange, type = 'line', size = 'middle', tabPosition = 'top', destroyInactiveTabPane = false, style, className, children, } = props;
6
+ const list = items && items.length > 0 ? items : [];
7
+ const [internal, setInternal] = React.useState(activeKey ?? defaultActiveKey ?? list[0]?.key ?? '');
8
+ const current = activeKey !== undefined ? activeKey : internal;
9
+ const select = (key, disabled) => {
10
+ if (disabled)
11
+ return;
12
+ if (activeKey === undefined)
13
+ setInternal(key);
14
+ onChange?.(key);
15
+ };
16
+ const vertical = tabPosition === 'left' || tabPosition === 'right';
17
+ const card = type === 'card';
18
+ const fontSize = size === 'large' ? 16 : size === 'small' ? 13 : 14;
19
+ const tabPadding = size === 'large' ? '16px 24px' : size === 'small' ? '8px 16px' : '12px 16px';
20
+ const tabBar = (_jsx("div", { style: {
21
+ display: 'flex',
22
+ flexDirection: vertical ? 'column' : 'row',
23
+ borderBottom: vertical || card ? 'none' : '1px solid #f0f0f0',
24
+ [vertical ? 'borderRight' : 'borderBottom']: vertical ? '1px solid #f0f0f0' : undefined,
25
+ gap: vertical ? 0 : 4,
26
+ overflowX: vertical ? 'visible' : 'auto',
27
+ scrollbarWidth: 'thin',
28
+ }, children: list.map(it => {
29
+ const active = it.key === current;
30
+ return (_jsxs("div", { role: "tab", "aria-selected": active, onClick: () => select(it.key, it.disabled), style: {
31
+ padding: tabPadding,
32
+ cursor: it.disabled ? 'not-allowed' : 'pointer',
33
+ color: it.disabled ? 'rgba(0,0,0,0.25)' : active ? PRIMARY : 'rgba(0,0,0,0.88)',
34
+ fontWeight: active ? 500 : 400,
35
+ fontSize,
36
+ whiteSpace: 'nowrap',
37
+ position: 'relative',
38
+ background: card ? (active ? '#fff' : 'transparent') : 'transparent',
39
+ border: card ? '1px solid #f0f0f0' : 'none',
40
+ [vertical ? 'borderBottom' : 'borderBottom']: card ? (active ? '1px solid #fff' : '1px solid #f0f0f0') : 'none',
41
+ marginBottom: card && !vertical ? '-1px' : undefined,
42
+ borderRadius: card ? (vertical ? '4px 0 0 4px' : '4px 4px 0 0') : undefined,
43
+ transition: 'color .2s',
44
+ }, children: [it.label, active && !card && (_jsx("span", { style: {
45
+ position: 'absolute',
46
+ [vertical ? 'right' : 'bottom']: 0,
47
+ [vertical ? 'top' : 'left']: vertical ? 0 : 0,
48
+ [vertical ? 'width' : 'height']: '2px',
49
+ [vertical ? 'height' : 'width']: '100%',
50
+ background: PRIMARY,
51
+ } }))] }, it.key));
52
+ }) }));
53
+ const activeItem = list.find(it => it.key === current);
54
+ const panels = (_jsxs("div", { style: { flex: 1, minWidth: 0, padding: vertical ? '0 16px' : '16px 0' }, children: [destroyInactiveTabPane
55
+ ? activeItem?.children
56
+ : list.map(it => (_jsx("div", { style: { display: it.key === current ? 'block' : 'none' }, children: it.children }, it.key))), !items || items.length === 0 ? children : null] }));
57
+ return (_jsxs("div", { className: className, style: {
58
+ display: 'flex',
59
+ flexDirection: vertical ? 'row' : 'column',
60
+ [tabPosition === 'bottom' ? 'flexDirection' : '']: tabPosition === 'bottom' ? 'column-reverse' : undefined,
61
+ ...style,
62
+ }, children: [tabPosition === 'bottom' ? panels : tabBar, tabPosition === 'bottom' ? tabBar : panels] }));
6
63
  }
7
- Tabs.TabPane = function TabPane(props) { return React.createElement(HbTabPane, { key: props.tabKey || props.key, label: props.tab }, props.children); };
64
+ /** Tabs.TabPane —— antd4 声明式占位(scrm items,这里仅兜底) */
65
+ Tabs.TabPane = function TabPane(_props) {
66
+ return null;
67
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huibo-ui/react-antd",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "antd 兼容层 — 接收 antd 原生 props,内部转换为 huibo-ui,实现丝滑平替",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",