@huibo-ui/react-antd 1.0.5 → 1.0.7
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.
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
export
|
|
2
|
+
export interface CascaderProps {
|
|
3
3
|
options?: any[];
|
|
4
4
|
value?: any;
|
|
5
|
-
onChange?: (value: any) => void;
|
|
5
|
+
onChange?: (value: any, selectOptions?: any) => void;
|
|
6
6
|
multiple?: boolean;
|
|
7
7
|
placeholder?: string;
|
|
8
8
|
disabled?: boolean;
|
|
9
9
|
allowClear?: boolean;
|
|
10
10
|
showSearch?: boolean;
|
|
11
|
+
/** antd fieldNames:自定义节点字段名,如 { label:'name', value:'id', children:'child' } */
|
|
12
|
+
fieldNames?: {
|
|
13
|
+
label?: string;
|
|
14
|
+
value?: string;
|
|
15
|
+
children?: string;
|
|
16
|
+
};
|
|
11
17
|
style?: React.CSSProperties;
|
|
12
|
-
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}
|
|
20
|
+
export declare function Cascader(props: CascaderProps): React.JSX.Element;
|
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { HbCascader } from '@huibo-ui/react';
|
|
3
|
+
/**
|
|
4
|
+
* 按 antd fieldNames 递归把自定义节点字段映射为 hb-cascader 认的 {label,value,children},
|
|
5
|
+
* 保留原始字段(onChange 第二参数对齐 antd 返回原始 option)。
|
|
6
|
+
*/
|
|
7
|
+
function mapCascader(nodes, fn) {
|
|
8
|
+
if (!nodes || nodes.length === 0)
|
|
9
|
+
return [];
|
|
10
|
+
if (!fn)
|
|
11
|
+
return nodes;
|
|
12
|
+
const ln = fn.label || 'label';
|
|
13
|
+
const vn = fn.value || 'value';
|
|
14
|
+
const cn = fn.children || 'children';
|
|
15
|
+
return nodes.map(n => {
|
|
16
|
+
const mapped = { ...n, label: n[ln], value: n[vn] };
|
|
17
|
+
if (Array.isArray(n[cn]))
|
|
18
|
+
mapped.children = mapCascader(n[cn], fn);
|
|
19
|
+
return mapped;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
3
22
|
export function Cascader(props) {
|
|
4
|
-
|
|
23
|
+
const options = mapCascader(props.options, props.fieldNames);
|
|
24
|
+
return (_jsx(HbCascader, { options: options, modelValue: props.value, multiple: props.multiple, placeholder: props.placeholder, disabled: props.disabled, clearable: props.allowClear, filterable: props.showSearch, onHbChange: (e) => props.onChange?.(e.detail), style: props.style }));
|
|
5
25
|
}
|
package/lib/components/Menu.d.ts
CHANGED
|
@@ -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
|
|
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
|
}
|
package/lib/components/Menu.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
31
|
-
|
|
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
|
};
|
|
@@ -23,6 +23,13 @@ export interface SelectProps {
|
|
|
23
23
|
virtual?: boolean;
|
|
24
24
|
onSearch?: (value: string) => void;
|
|
25
25
|
onDropdownVisibleChange?: (open: boolean) => void;
|
|
26
|
+
/** antd fieldNames:自定义 option 字段名映射,如 { label:'nickName', value:'id' } */
|
|
27
|
+
fieldNames?: {
|
|
28
|
+
label?: string;
|
|
29
|
+
value?: string;
|
|
30
|
+
disabled?: string;
|
|
31
|
+
options?: string;
|
|
32
|
+
};
|
|
26
33
|
}
|
|
27
34
|
export declare function Select(props: SelectProps): React.JSX.Element;
|
|
28
35
|
export declare namespace Select {
|
package/lib/components/Select.js
CHANGED
|
@@ -30,8 +30,32 @@ export function Select(props) {
|
|
|
30
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
|
-
//
|
|
34
|
-
|
|
33
|
+
// antd fieldNames:自定义 option 字段名(如 {label:'nickName', value:'id'})。
|
|
34
|
+
// hb-select 只认 {value,label,disabled},必须按 fieldNames 重映射,否则下拉项无文案、值 undefined。
|
|
35
|
+
const fieldNames = props.fieldNames;
|
|
36
|
+
function mapOptions(opts) {
|
|
37
|
+
if (!opts || opts.length === 0)
|
|
38
|
+
return [];
|
|
39
|
+
if (!fieldNames)
|
|
40
|
+
return opts;
|
|
41
|
+
const ln = fieldNames.label || 'label';
|
|
42
|
+
const vn = fieldNames.value || 'value';
|
|
43
|
+
const dn = fieldNames.disabled || 'disabled';
|
|
44
|
+
const on = fieldNames.options; // OptGroup 子选项字段
|
|
45
|
+
const out = [];
|
|
46
|
+
for (const o of opts) {
|
|
47
|
+
if (on && Array.isArray(o[on])) {
|
|
48
|
+
for (const c of o[on])
|
|
49
|
+
out.push({ ...c, value: c[vn], label: c[ln], disabled: c[dn] });
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
out.push({ ...o, value: o[vn], label: o[ln], disabled: o[dn] });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
// 优先用 options prop;否则从 <Select.Option> children 提取;再按 fieldNames 映射
|
|
58
|
+
const resolvedOptions = mapOptions(options && options.length > 0 ? options : optionsFromChildren(children));
|
|
35
59
|
// 从 options 里查出选中项(单选返回单个 option,多选返回数组),对齐 antd onChange 第二参数
|
|
36
60
|
const resolveOption = (val) => {
|
|
37
61
|
if (!resolvedOptions || resolvedOptions.length === 0)
|