@lark-apaas/client-toolkit 0.1.0-alpha.log.2 → 0.1.0-alpha.message.1
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.
- package/lib/apis/hooks/useTheme.d.ts +1 -0
- package/lib/apis/hooks/useTheme.js +1 -0
- package/lib/components/AppContainer/LogInterceptor.js +27 -5
- package/lib/components/AppContainer/api-proxy/core.d.ts +155 -0
- package/lib/components/AppContainer/api-proxy/core.js +270 -0
- package/lib/components/AppContainer/index.js +2 -0
- package/lib/components/AppContainer/utils/api-panel.d.ts +29 -0
- package/lib/components/AppContainer/utils/api-panel.js +66 -0
- package/lib/components/AppContainer/utils/childApi.js +11 -1
- package/lib/components/ErrorRender/index.js +3 -3
- package/lib/components/User/UserDisplay.d.ts +2 -1
- package/lib/components/User/UserDisplay.js +103 -26
- package/lib/components/User/UserProfile/UserProfile.css +1 -1
- package/lib/components/User/UserProfile/UserProfileContainer.d.ts +1 -1
- package/lib/components/User/UserProfile/UserProfileSkeleton.d.ts +0 -1
- package/lib/components/User/UserProfile/UserProfileSkeleton.js +21 -29
- package/lib/components/User/UserProfile/UserProfileUI.d.ts +1 -2
- package/lib/components/User/UserProfile/UserProfileUI.js +106 -92
- package/lib/components/User/UserSelect.d.ts +1 -1
- package/lib/components/User/UserSelect.js +17 -143
- package/lib/components/User/UserSelectUI/ActionButtons.d.ts +11 -0
- package/lib/components/User/UserSelectUI/ActionButtons.js +44 -0
- package/lib/components/User/UserSelectUI/Dropdown.d.ts +12 -0
- package/lib/components/User/UserSelectUI/Dropdown.js +66 -0
- package/lib/components/User/UserSelectUI/MultipleSelectionTags.d.ts +14 -0
- package/lib/components/User/UserSelectUI/MultipleSelectionTags.js +48 -0
- package/lib/components/User/UserSelectUI/SingleSelectionPreview.d.ts +9 -0
- package/lib/components/User/UserSelectUI/SingleSelectionPreview.js +37 -0
- package/lib/components/User/UserSelectUI/Spinner.d.ts +2 -0
- package/lib/components/User/UserSelectUI/Spinner.js +13 -0
- package/lib/components/User/UserSelectUI/UserSelectUI.d.ts +5 -0
- package/lib/components/User/UserSelectUI/UserSelectUI.js +230 -0
- package/lib/components/User/UserSelectUI/index.d.ts +2 -0
- package/lib/components/User/UserSelectUI/index.js +2 -0
- package/lib/components/User/UserSelectUI/types.d.ts +14 -0
- package/lib/components/User/UserSelectUI/types.js +0 -0
- package/lib/components/User/UserWithAvatar.d.ts +1 -1
- package/lib/components/User/UserWithAvatar.js +39 -22
- package/lib/components/User/type.d.ts +4 -0
- package/lib/components/index.d.ts +2 -5
- package/lib/components/index.js +2 -3
- package/lib/components/ui/avatar.d.ts +6 -0
- package/lib/components/ui/avatar.js +27 -0
- package/lib/components/ui/badge.d.ts +9 -0
- package/lib/components/ui/badge.js +29 -0
- package/lib/components/ui/button.d.ts +10 -0
- package/lib/components/ui/button.js +42 -0
- package/lib/components/ui/input.d.ts +3 -0
- package/lib/components/ui/input.js +12 -0
- package/lib/components/ui/overflow-tooltip-text.d.ts +8 -0
- package/lib/components/ui/overflow-tooltip-text.js +66 -0
- package/lib/components/ui/popover.d.ts +7 -0
- package/lib/components/ui/popover.js +35 -0
- package/lib/components/ui/skeleton.d.ts +7 -0
- package/lib/components/ui/skeleton.js +10 -0
- package/lib/components/ui/tooltip.d.ts +7 -0
- package/lib/components/ui/tooltip.js +24 -0
- package/lib/hooks/useAppInfo.js +12 -1
- package/lib/integrations/getAppInfo.js +4 -2
- package/lib/logger/__tests__/batch-logger.test.d.ts +1 -0
- package/lib/logger/__tests__/batch-logger.test.js +367 -0
- package/lib/logger/batch-logger.d.ts +78 -0
- package/lib/logger/batch-logger.js +134 -0
- package/lib/override.css +0 -16
- package/lib/types/iframe-events.d.ts +9 -0
- package/lib/types/index.d.ts +0 -29
- package/lib/utils/axiosConfig.js +7 -7
- package/lib/utils/getAppId.js +4 -3
- package/lib/utils/getParentOrigin.d.ts +1 -1
- package/lib/utils/getParentOrigin.js +1 -0
- package/package.json +8 -1
- package/lib/apis/components/SidebarNav.d.ts +0 -1
- package/lib/apis/components/SidebarNav.js +0 -2
- package/lib/components/SidebarNav/DrawerNav.d.ts +0 -3
- package/lib/components/SidebarNav/DrawerNav.js +0 -64
- package/lib/components/SidebarNav/DropdownNav.d.ts +0 -3
- package/lib/components/SidebarNav/DropdownNav.js +0 -40
- package/lib/components/SidebarNav/Sidebar.d.ts +0 -3
- package/lib/components/SidebarNav/Sidebar.js +0 -33
- package/lib/components/SidebarNav/index.d.ts +0 -5
- package/lib/components/SidebarNav/index.js +0 -61
- package/lib/components/User/UserSelect.css +0 -11
- package/lib/components/common/LogoInfo.d.ts +0 -5
- package/lib/components/common/LogoInfo.js +0 -30
- package/lib/components/common/NavItem.d.ts +0 -20
- package/lib/components/common/NavItem.js +0 -112
- package/lib/components/common/NavMenu.d.ts +0 -9
- package/lib/components/common/NavMenu.js +0 -50
- package/lib/components/common/UserAvatarLayout.d.ts +0 -4
- package/lib/components/common/UserAvatarLayout.js +0 -41
- package/lib/components/common/UserAvatarMenu.d.ts +0 -4
- package/lib/components/common/UserAvatarMenu.js +0 -58
- package/lib/components/common/index.d.ts +0 -9
- package/lib/components/common/index.js +0 -10
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
interface ActionButtonsProps {
|
|
3
|
+
loading: boolean;
|
|
4
|
+
canClear: boolean;
|
|
5
|
+
isOpen: boolean;
|
|
6
|
+
disabled: boolean;
|
|
7
|
+
onClear: (event: React.MouseEvent) => void;
|
|
8
|
+
onToggle: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare const ActionButtons: React.ForwardRefExoticComponent<ActionButtonsProps & React.RefAttributes<HTMLDivElement>>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { ChevronDown, X } from "lucide-react";
|
|
4
|
+
import { clsxWithTw } from "../../../utils/utils.js";
|
|
5
|
+
import { Spinner } from "./Spinner.js";
|
|
6
|
+
const ActionButtons_ActionButtons = /*#__PURE__*/ forwardRef(function({ loading, canClear, isOpen, disabled, onClear, onToggle }, ref) {
|
|
7
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
8
|
+
ref: ref,
|
|
9
|
+
className: "flex flex-shrink-0 items-center gap-1 pr-2.5 ml-auto",
|
|
10
|
+
children: [
|
|
11
|
+
loading && /*#__PURE__*/ jsx(Spinner, {
|
|
12
|
+
className: "text-muted-foreground"
|
|
13
|
+
}),
|
|
14
|
+
canClear && /*#__PURE__*/ jsx("button", {
|
|
15
|
+
type: "button",
|
|
16
|
+
onClick: onClear,
|
|
17
|
+
"data-select-interactive": "true",
|
|
18
|
+
className: clsxWithTw('flex size-3 items-center justify-center rounded-full bg-foreground/60 transition-colors duration-200', 'cursor-pointer hover:bg-primary', 'opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto'),
|
|
19
|
+
children: /*#__PURE__*/ jsx(X, {
|
|
20
|
+
size: 11,
|
|
21
|
+
strokeWidth: 3,
|
|
22
|
+
className: "text-white"
|
|
23
|
+
})
|
|
24
|
+
}),
|
|
25
|
+
/*#__PURE__*/ jsx("button", {
|
|
26
|
+
type: "button",
|
|
27
|
+
"data-select-interactive": "true",
|
|
28
|
+
className: clsxWithTw('flex size-3 items-center justify-center rounded-full text-muted-foreground transition-all duration-200 cursor-pointer', {
|
|
29
|
+
'rotate-180': isOpen,
|
|
30
|
+
'opacity-50 cursor-not-allowed': disabled
|
|
31
|
+
}),
|
|
32
|
+
onClick: ()=>{
|
|
33
|
+
if (!disabled) onToggle();
|
|
34
|
+
},
|
|
35
|
+
children: /*#__PURE__*/ jsx(ChevronDown, {
|
|
36
|
+
size: 12,
|
|
37
|
+
strokeWidth: 3
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
]
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
ActionButtons_ActionButtons.displayName = 'ActionButtons';
|
|
44
|
+
export { ActionButtons_ActionButtons as ActionButtons };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { IUserProfile } from '../../../apis/udt-types';
|
|
3
|
+
interface DropdownProps {
|
|
4
|
+
isOpen: boolean;
|
|
5
|
+
fetching: boolean;
|
|
6
|
+
searchResults: IUserProfile[];
|
|
7
|
+
onSelect: (user: IUserProfile) => void;
|
|
8
|
+
isSelected: (userId: string) => boolean;
|
|
9
|
+
onOptionMouseDown?: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function Dropdown({ isOpen, fetching, searchResults, onSelect, isSelected, onOptionMouseDown, }: DropdownProps): React.JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../../ui/avatar.js";
|
|
4
|
+
import { clsxWithTw } from "../../../utils/utils.js";
|
|
5
|
+
import { Check } from "lucide-react";
|
|
6
|
+
import { Spinner } from "./Spinner.js";
|
|
7
|
+
function Dropdown({ isOpen, fetching, searchResults, onSelect, isSelected, onOptionMouseDown }) {
|
|
8
|
+
if (!isOpen) return null;
|
|
9
|
+
return /*#__PURE__*/ jsx("div", {
|
|
10
|
+
className: "absolute top-full left-0 right-0 z-50 mt-1 rounded-md border border-solid border-border bg-card shadow-lg",
|
|
11
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
12
|
+
className: "max-h-[165px] overflow-y-auto p-[3px]",
|
|
13
|
+
children: fetching ? /*#__PURE__*/ jsx("div", {
|
|
14
|
+
className: "flex items-center justify-center px-3 py-2",
|
|
15
|
+
children: /*#__PURE__*/ jsx(Spinner, {
|
|
16
|
+
className: "text-primary"
|
|
17
|
+
})
|
|
18
|
+
}) : 0 === searchResults.length ? /*#__PURE__*/ jsx("div", {
|
|
19
|
+
className: "px-2 py-[5px] text-[14px] leading-[22px] text-muted-foreground",
|
|
20
|
+
children: "无结果,建议更换搜索词"
|
|
21
|
+
}) : searchResults.map((user)=>{
|
|
22
|
+
const selected = isSelected(user.user_id);
|
|
23
|
+
const displayName = user.name?.trim() || '无效人员';
|
|
24
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
25
|
+
className: "relative flex cursor-pointer items-center gap-2 rounded-sm py-1 pl-3 pr-[7px] text-foreground transition-colors hover:bg-foreground/8",
|
|
26
|
+
onMouseDown: (event)=>{
|
|
27
|
+
onOptionMouseDown?.();
|
|
28
|
+
event.preventDefault();
|
|
29
|
+
},
|
|
30
|
+
onClick: ()=>onSelect(user),
|
|
31
|
+
children: [
|
|
32
|
+
/*#__PURE__*/ jsxs("div", {
|
|
33
|
+
className: "flex min-w-0 flex-1 items-center gap-2",
|
|
34
|
+
children: [
|
|
35
|
+
/*#__PURE__*/ jsxs(Avatar, {
|
|
36
|
+
className: "h-6 w-6 flex-shrink-0",
|
|
37
|
+
children: [
|
|
38
|
+
/*#__PURE__*/ jsx(AvatarImage, {
|
|
39
|
+
src: user.avatar,
|
|
40
|
+
alt: displayName
|
|
41
|
+
}),
|
|
42
|
+
/*#__PURE__*/ jsx(AvatarFallback, {
|
|
43
|
+
className: "bg-muted text-[12px] text-foreground",
|
|
44
|
+
children: displayName.slice(0, 1)
|
|
45
|
+
})
|
|
46
|
+
]
|
|
47
|
+
}),
|
|
48
|
+
/*#__PURE__*/ jsx("span", {
|
|
49
|
+
className: clsxWithTw('truncate text-[14px] leading-[20px]', selected ? 'text-primary' : void 0),
|
|
50
|
+
title: displayName,
|
|
51
|
+
children: displayName
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
}),
|
|
55
|
+
selected ? /*#__PURE__*/ jsx(Check, {
|
|
56
|
+
size: 12,
|
|
57
|
+
className: "text-primary",
|
|
58
|
+
strokeWidth: 3
|
|
59
|
+
}) : null
|
|
60
|
+
]
|
|
61
|
+
}, user.user_id);
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export { Dropdown };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { IUserProfile } from '../../../apis/udt-types';
|
|
3
|
+
interface MultipleSelectionTagsProps {
|
|
4
|
+
displayUsers: IUserProfile[];
|
|
5
|
+
allUsers: IUserProfile[];
|
|
6
|
+
hiddenCount: number;
|
|
7
|
+
onRemove: (userId: string, event: React.MouseEvent) => void;
|
|
8
|
+
containerRef?: React.RefObject<HTMLDivElement>;
|
|
9
|
+
measureRef?: React.RefObject<HTMLDivElement>;
|
|
10
|
+
moreIndicatorRef?: React.RefObject<HTMLSpanElement>;
|
|
11
|
+
maxWidth?: number | null;
|
|
12
|
+
}
|
|
13
|
+
export declare function MultipleSelectionTags({ displayUsers, onRemove, }: MultipleSelectionTagsProps): React.JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import { clsxWithTw } from "../../../utils/utils.js";
|
|
4
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../../ui/avatar.js";
|
|
5
|
+
import { Badge } from "../../ui/badge.js";
|
|
6
|
+
import { Button } from "../../ui/button.js";
|
|
7
|
+
import { X } from "lucide-react";
|
|
8
|
+
function MultipleSelectionTags({ displayUsers, onRemove }) {
|
|
9
|
+
return /*#__PURE__*/ jsx(Fragment, {
|
|
10
|
+
children: displayUsers?.map((user, index)=>/*#__PURE__*/ jsxs(Badge, {
|
|
11
|
+
variant: "secondary",
|
|
12
|
+
"data-user-tag": true,
|
|
13
|
+
className: clsxWithTw('cursor-pointer flex items-center pl-0.5 pr-2 max-w-[200px] group bg-foreground/10 hover:bg-foreground/10 transition-colors rounded-full whitespace-nowrap my-[3px]', index > 0 ? 'ml-0' : ''),
|
|
14
|
+
children: [
|
|
15
|
+
/*#__PURE__*/ jsxs(Avatar, {
|
|
16
|
+
className: "size-[20px] flex-shrink-0",
|
|
17
|
+
children: [
|
|
18
|
+
/*#__PURE__*/ jsx(AvatarImage, {
|
|
19
|
+
src: user.avatar
|
|
20
|
+
}),
|
|
21
|
+
/*#__PURE__*/ jsx(AvatarFallback, {
|
|
22
|
+
className: "bg-primary/10 text-[10px]",
|
|
23
|
+
children: user.name.slice(0, 1)
|
|
24
|
+
})
|
|
25
|
+
]
|
|
26
|
+
}),
|
|
27
|
+
/*#__PURE__*/ jsx("span", {
|
|
28
|
+
className: "inline-block truncate text-foreground text-[14px] leading-[20px]",
|
|
29
|
+
title: user.name,
|
|
30
|
+
children: user.name
|
|
31
|
+
}),
|
|
32
|
+
/*#__PURE__*/ jsx(Button, {
|
|
33
|
+
variant: "ghost",
|
|
34
|
+
size: "sm",
|
|
35
|
+
"data-select-interactive": "true",
|
|
36
|
+
className: "h-3 w-3 p-0 rounded-full opacity-60 hover:opacity-100 transition-all duration-200 cursor-pointer flex-shrink-0",
|
|
37
|
+
onClick: (e)=>onRemove(user.user_id, e),
|
|
38
|
+
title: `移除 ${user.name}`,
|
|
39
|
+
children: /*#__PURE__*/ jsx(X, {
|
|
40
|
+
size: 10,
|
|
41
|
+
strokeWidth: 3
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
]
|
|
45
|
+
}, user.user_id))
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export { MultipleSelectionTags };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { IUserProfile } from '../../../apis/udt-types';
|
|
3
|
+
interface SingleSelectionPreviewProps {
|
|
4
|
+
user: IUserProfile;
|
|
5
|
+
canClear: boolean;
|
|
6
|
+
isOpen: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function SingleSelectionPreview({ user, canClear, isOpen, }: SingleSelectionPreviewProps): React.JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../../ui/avatar.js";
|
|
4
|
+
import { clsxWithTw } from "../../../utils/utils.js";
|
|
5
|
+
function SingleSelectionPreview({ user, canClear, isOpen }) {
|
|
6
|
+
const displayName = user.name?.trim() ?? '';
|
|
7
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
8
|
+
className: clsxWithTw('pointer-events-none absolute top-1/2 z-0 flex -translate-y-1/2 items-center gap-1 rounded-md text-sm text-foreground/70', {
|
|
9
|
+
'opacity-50': isOpen
|
|
10
|
+
}),
|
|
11
|
+
style: {
|
|
12
|
+
left: 'calc(0.75rem + 1px)',
|
|
13
|
+
right: canClear ? '3.5rem' : '2.5rem',
|
|
14
|
+
maxWidth: canClear ? 'calc(100% - 3.5rem)' : 'calc(100% - 2.5rem)'
|
|
15
|
+
},
|
|
16
|
+
children: [
|
|
17
|
+
/*#__PURE__*/ jsxs(Avatar, {
|
|
18
|
+
className: "h-5 w-5 flex-shrink-0",
|
|
19
|
+
children: [
|
|
20
|
+
/*#__PURE__*/ jsx(AvatarImage, {
|
|
21
|
+
src: user.avatar
|
|
22
|
+
}),
|
|
23
|
+
/*#__PURE__*/ jsx(AvatarFallback, {
|
|
24
|
+
className: "text-[11px] text-foreground",
|
|
25
|
+
children: displayName.slice(0, 1)
|
|
26
|
+
})
|
|
27
|
+
]
|
|
28
|
+
}),
|
|
29
|
+
/*#__PURE__*/ jsx("span", {
|
|
30
|
+
className: "truncate text-[14px] leading-[20px] text-foreground",
|
|
31
|
+
title: displayName,
|
|
32
|
+
children: displayName
|
|
33
|
+
})
|
|
34
|
+
]
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export { SingleSelectionPreview };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import { Loader2Icon } from "lucide-react";
|
|
4
|
+
import { clsxWithTw } from "../../../utils/utils.js";
|
|
5
|
+
function Spinner({ className }) {
|
|
6
|
+
return /*#__PURE__*/ jsx(Loader2Icon, {
|
|
7
|
+
role: "status",
|
|
8
|
+
"aria-label": "Loading",
|
|
9
|
+
strokeWidth: 2,
|
|
10
|
+
className: clsxWithTw('size-4 animate-spin', className)
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export { Spinner };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** biome-ignore-all lint/a11y/noStaticElementInteractions: <explanation> */
|
|
2
|
+
/** biome-ignore-all lint/a11y/useKeyWithClickEvents: <explanation> */
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import type { UserSelectProps } from './types';
|
|
5
|
+
export declare const UserSelectUI: React.FC<UserSelectProps>;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { debounce } from "lodash";
|
|
4
|
+
import { clsxWithTw } from "../../../utils/utils.js";
|
|
5
|
+
import { Input } from "../../ui/input.js";
|
|
6
|
+
import { MultipleSelectionTags } from "./MultipleSelectionTags.js";
|
|
7
|
+
import { SingleSelectionPreview } from "./SingleSelectionPreview.js";
|
|
8
|
+
import { ActionButtons } from "./ActionButtons.js";
|
|
9
|
+
import { Dropdown } from "./Dropdown.js";
|
|
10
|
+
const UserSelectUI = ({ mode = 'single', value, onChange, placeholder = '请选择用户', disabled = false, allowClear = true, onSearch, loading = false, options = [] })=>{
|
|
11
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
12
|
+
const [searchText, setSearchText] = useState('');
|
|
13
|
+
const [searchResults, setSearchResults] = useState([]);
|
|
14
|
+
const [fetching, setFetching] = useState(false);
|
|
15
|
+
const fetchRef = useRef(0);
|
|
16
|
+
const inputRef = useRef(null);
|
|
17
|
+
const isUserSearchingRef = useRef(false);
|
|
18
|
+
const onSearchRef = useRef(onSearch);
|
|
19
|
+
const optionsRef = useRef(options);
|
|
20
|
+
const dropdownInteractionRef = useRef(false);
|
|
21
|
+
const debouncedSearchRef = useRef();
|
|
22
|
+
const triggerRef = useRef(null);
|
|
23
|
+
const inputContainerRef = useRef(null);
|
|
24
|
+
const actionButtonsRef = useRef(null);
|
|
25
|
+
useEffect(()=>{
|
|
26
|
+
onSearchRef.current = onSearch;
|
|
27
|
+
}, [
|
|
28
|
+
onSearch
|
|
29
|
+
]);
|
|
30
|
+
useEffect(()=>{
|
|
31
|
+
optionsRef.current = options;
|
|
32
|
+
}, [
|
|
33
|
+
options
|
|
34
|
+
]);
|
|
35
|
+
const selectedUsers = useMemo(()=>{
|
|
36
|
+
if (!value) return [];
|
|
37
|
+
return Array.isArray(value) ? value : [
|
|
38
|
+
value
|
|
39
|
+
];
|
|
40
|
+
}, [
|
|
41
|
+
value
|
|
42
|
+
]);
|
|
43
|
+
useEffect(()=>{
|
|
44
|
+
const debounced = debounce(async (searchValue)=>{
|
|
45
|
+
const latestOnSearch = onSearchRef.current;
|
|
46
|
+
const latestOptions = optionsRef.current;
|
|
47
|
+
if (!latestOnSearch) {
|
|
48
|
+
const filtered = latestOptions.filter((user)=>user.name.toLowerCase().includes(searchValue.toLowerCase()));
|
|
49
|
+
setSearchResults(filtered);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
fetchRef.current += 1;
|
|
53
|
+
const fetchId = fetchRef.current;
|
|
54
|
+
setFetching(true);
|
|
55
|
+
try {
|
|
56
|
+
const results = await latestOnSearch(searchValue);
|
|
57
|
+
if (fetchId === fetchRef.current) setSearchResults(results);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('Search failed:', error);
|
|
60
|
+
if (fetchId === fetchRef.current) setSearchResults([]);
|
|
61
|
+
} finally{
|
|
62
|
+
if (fetchId === fetchRef.current) setFetching(false);
|
|
63
|
+
}
|
|
64
|
+
}, 500);
|
|
65
|
+
debouncedSearchRef.current = debounced;
|
|
66
|
+
return ()=>{
|
|
67
|
+
debounced.cancel();
|
|
68
|
+
};
|
|
69
|
+
}, []);
|
|
70
|
+
const handleSearch = (value)=>{
|
|
71
|
+
setSearchText(value);
|
|
72
|
+
isUserSearchingRef.current = true;
|
|
73
|
+
if (!isOpen) setIsOpen(true);
|
|
74
|
+
debouncedSearchRef.current?.(value);
|
|
75
|
+
};
|
|
76
|
+
const handleInputFocus = ()=>{
|
|
77
|
+
setIsOpen(true);
|
|
78
|
+
};
|
|
79
|
+
const handleInputBlur = ()=>{
|
|
80
|
+
setTimeout(()=>{
|
|
81
|
+
if ('multiple' === mode && dropdownInteractionRef.current) {
|
|
82
|
+
dropdownInteractionRef.current = false;
|
|
83
|
+
inputRef.current?.focus();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
dropdownInteractionRef.current = false;
|
|
87
|
+
const inputElement = inputRef.current;
|
|
88
|
+
if (!inputElement || 'undefined' == typeof document) return void setIsOpen(false);
|
|
89
|
+
const activeElement = document.activeElement;
|
|
90
|
+
if (activeElement !== inputElement) setIsOpen(false);
|
|
91
|
+
}, 150);
|
|
92
|
+
};
|
|
93
|
+
const handleSelect = (user)=>{
|
|
94
|
+
if (!onChange) return;
|
|
95
|
+
if ('single' === mode) {
|
|
96
|
+
onChange(user);
|
|
97
|
+
setIsOpen(false);
|
|
98
|
+
setSearchText('');
|
|
99
|
+
isUserSearchingRef.current = false;
|
|
100
|
+
inputRef.current?.blur();
|
|
101
|
+
} else {
|
|
102
|
+
const isAlreadySelected = selectedUsers.some((u)=>u.user_id === user.user_id);
|
|
103
|
+
if (isAlreadySelected) {
|
|
104
|
+
const newValue = selectedUsers.filter((u)=>u.user_id !== user.user_id);
|
|
105
|
+
onChange(newValue);
|
|
106
|
+
} else onChange([
|
|
107
|
+
...selectedUsers,
|
|
108
|
+
user
|
|
109
|
+
]);
|
|
110
|
+
isUserSearchingRef.current = false;
|
|
111
|
+
setSearchText('');
|
|
112
|
+
setTimeout(()=>{
|
|
113
|
+
inputRef.current?.focus();
|
|
114
|
+
}, 0);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
const handleRemove = (userId, e)=>{
|
|
118
|
+
e.stopPropagation();
|
|
119
|
+
if (!onChange) return;
|
|
120
|
+
if ('single' === mode) onChange(void 0);
|
|
121
|
+
else {
|
|
122
|
+
const newValue = selectedUsers.filter((u)=>u.user_id !== userId);
|
|
123
|
+
onChange(newValue);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const handleClear = (e)=>{
|
|
127
|
+
e.stopPropagation();
|
|
128
|
+
if (!onChange) return;
|
|
129
|
+
onChange('single' === mode ? void 0 : []);
|
|
130
|
+
setSearchText('');
|
|
131
|
+
setIsOpen(false);
|
|
132
|
+
};
|
|
133
|
+
const isSelected = (userId)=>selectedUsers.some((u)=>u.user_id === userId);
|
|
134
|
+
const hasSelection = selectedUsers.length > 0;
|
|
135
|
+
const singleSelectedUser = 'single' === mode && hasSelection ? selectedUsers[0] : void 0;
|
|
136
|
+
const showSingleSelection = !!singleSelectedUser && 0 === searchText.length;
|
|
137
|
+
const canClear = allowClear && hasSelection && !disabled;
|
|
138
|
+
const inputPlaceholder = hasSelection ? '' : placeholder;
|
|
139
|
+
const focusInputAtStart = ()=>{
|
|
140
|
+
const input = inputRef.current;
|
|
141
|
+
if (!input) return;
|
|
142
|
+
try {
|
|
143
|
+
input.setSelectionRange(0, 0);
|
|
144
|
+
} catch {}
|
|
145
|
+
};
|
|
146
|
+
const toggleDropdown = ()=>{
|
|
147
|
+
if (disabled) return;
|
|
148
|
+
setIsOpen((prev)=>{
|
|
149
|
+
const next = !prev;
|
|
150
|
+
if (!prev && !disabled) inputRef.current?.focus();
|
|
151
|
+
return next;
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
const handleTriggerMouseDown = (event)=>{
|
|
155
|
+
if (disabled) return;
|
|
156
|
+
const target = event.target;
|
|
157
|
+
if (target.closest('[data-select-interactive="true"]')) return;
|
|
158
|
+
if (inputRef.current && (target === inputRef.current || inputRef.current.contains(target))) return;
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
inputRef.current?.focus();
|
|
161
|
+
if ('single' === mode) if ('undefined' != typeof window) window.requestAnimationFrame(()=>focusInputAtStart());
|
|
162
|
+
else focusInputAtStart();
|
|
163
|
+
};
|
|
164
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
165
|
+
className: "relative w-full",
|
|
166
|
+
children: [
|
|
167
|
+
/*#__PURE__*/ jsxs("div", {
|
|
168
|
+
ref: triggerRef,
|
|
169
|
+
className: clsxWithTw('group relative flex w-full flex-wrap items-center gap-2 rounded-md border bg-background text-sm transition-colors', disabled ? 'cursor-not-allowed opacity-50' : '', isOpen ? 'border-primary' : 'border-border'),
|
|
170
|
+
onMouseDown: handleTriggerMouseDown,
|
|
171
|
+
children: [
|
|
172
|
+
/*#__PURE__*/ jsxs("div", {
|
|
173
|
+
className: clsxWithTw('flex-1 flex flex-wrap items-center gap-1 gap-y-0', 'multiple' === mode && hasSelection ? 'pl-1' : 'py-0'),
|
|
174
|
+
children: [
|
|
175
|
+
hasSelection && 'multiple' === mode && /*#__PURE__*/ jsx(MultipleSelectionTags, {
|
|
176
|
+
displayUsers: selectedUsers,
|
|
177
|
+
allUsers: selectedUsers,
|
|
178
|
+
hiddenCount: 0,
|
|
179
|
+
onRemove: handleRemove
|
|
180
|
+
}),
|
|
181
|
+
showSingleSelection && singleSelectedUser && /*#__PURE__*/ jsx(SingleSelectionPreview, {
|
|
182
|
+
user: singleSelectedUser,
|
|
183
|
+
canClear: canClear,
|
|
184
|
+
isOpen: isOpen
|
|
185
|
+
}),
|
|
186
|
+
/*#__PURE__*/ jsx("div", {
|
|
187
|
+
ref: inputContainerRef,
|
|
188
|
+
className: clsxWithTw('relative min-w-[120px] flex-grow', 'multiple' === mode && hasSelection ? 'mr-1' : ''),
|
|
189
|
+
children: /*#__PURE__*/ jsx(Input, {
|
|
190
|
+
ref: inputRef,
|
|
191
|
+
value: searchText,
|
|
192
|
+
onChange: (e)=>handleSearch(e.target.value),
|
|
193
|
+
onFocus: handleInputFocus,
|
|
194
|
+
onBlur: handleInputBlur,
|
|
195
|
+
placeholder: inputPlaceholder,
|
|
196
|
+
disabled: disabled,
|
|
197
|
+
className: clsxWithTw('relative z-[1] h-8 border-0 !bg-transparent text-[14px] shadow-none focus-visible:ring-0 focus-visible:ring-offset-0', 'multiple' === mode && hasSelection ? 'px-0' : 'pl-3 pr-2')
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
]
|
|
201
|
+
}),
|
|
202
|
+
/*#__PURE__*/ jsx(ActionButtons, {
|
|
203
|
+
ref: actionButtonsRef,
|
|
204
|
+
loading: loading,
|
|
205
|
+
canClear: canClear,
|
|
206
|
+
isOpen: isOpen,
|
|
207
|
+
disabled: disabled,
|
|
208
|
+
onClear: handleClear,
|
|
209
|
+
onToggle: toggleDropdown
|
|
210
|
+
})
|
|
211
|
+
]
|
|
212
|
+
}),
|
|
213
|
+
/*#__PURE__*/ jsx(Dropdown, {
|
|
214
|
+
isOpen: isOpen,
|
|
215
|
+
fetching: fetching,
|
|
216
|
+
searchResults: searchResults,
|
|
217
|
+
onSelect: handleSelect,
|
|
218
|
+
isSelected: isSelected,
|
|
219
|
+
onOptionMouseDown: ()=>{
|
|
220
|
+
if ('multiple' === mode) dropdownInteractionRef.current = true;
|
|
221
|
+
}
|
|
222
|
+
}),
|
|
223
|
+
isOpen && /*#__PURE__*/ jsx("div", {
|
|
224
|
+
className: "fixed inset-0 z-40",
|
|
225
|
+
onClick: ()=>setIsOpen(false)
|
|
226
|
+
})
|
|
227
|
+
]
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
export { UserSelectUI };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IUserProfile } from '../../../apis/udt-types';
|
|
2
|
+
export type UserSelectValue = IUserProfile | IUserProfile[];
|
|
3
|
+
export interface UserSelectProps {
|
|
4
|
+
value?: UserSelectValue;
|
|
5
|
+
onChange?: (value: UserSelectValue) => void;
|
|
6
|
+
defaultValue?: UserSelectValue;
|
|
7
|
+
mode?: 'single' | 'multiple';
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
allowClear?: boolean;
|
|
11
|
+
onSearch?: (searchText: string) => Promise<IUserProfile[]>;
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
options?: IUserProfile[];
|
|
14
|
+
}
|
|
File without changes
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { UserWithAvatarProps } from './type';
|
|
3
|
-
export declare function UserWithAvatar({ data, size, mode, className, }: UserWithAvatarProps): React.JSX.Element;
|
|
3
|
+
export declare function UserWithAvatar({ data, size, mode, className, showLabel, }: UserWithAvatarProps): React.JSX.Element;
|
|
@@ -1,39 +1,56 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import "react";
|
|
3
|
-
import { Avatar,
|
|
3
|
+
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar.js";
|
|
4
|
+
import { OverflowTooltipText } from "../ui/overflow-tooltip-text.js";
|
|
4
5
|
import { clsxWithTw } from "../../utils/utils.js";
|
|
5
|
-
const
|
|
6
|
-
small:
|
|
7
|
-
medium:
|
|
8
|
-
large:
|
|
6
|
+
const avatarDimensionClassMap = {
|
|
7
|
+
small: 'h-4 w-4',
|
|
8
|
+
medium: 'h-5 w-5',
|
|
9
|
+
large: 'h-6 w-6'
|
|
10
|
+
};
|
|
11
|
+
const avatarFallbackTextClassMap = {
|
|
12
|
+
small: 'text-[10px]',
|
|
13
|
+
medium: 'text-[12px]',
|
|
14
|
+
large: 'text-[14px]'
|
|
15
|
+
};
|
|
16
|
+
const sizeClassMap = {
|
|
17
|
+
small: 'py-0.5 pl-0.5 pr-1.5 max-w-[148px]',
|
|
18
|
+
medium: 'py-0.5 pl-0.5 pr-2 max-w-[172px]',
|
|
19
|
+
large: 'py-1 pl-1 pr-2.5 max-w-[196px]'
|
|
9
20
|
};
|
|
10
21
|
const textVariantMap = {
|
|
11
22
|
small: 'text-[12px] leading-[16px]',
|
|
12
23
|
medium: 'text-[14px] leading-[20px]',
|
|
13
24
|
large: 'text-[16px] leading-[24px]'
|
|
14
25
|
};
|
|
15
|
-
function UserWithAvatar({ data, size = 'medium', mode = 'tag', className }) {
|
|
26
|
+
function UserWithAvatar({ data, size = 'medium', mode = 'tag', className, showLabel = true }) {
|
|
16
27
|
const { avatar, name } = data;
|
|
17
|
-
const displayName = name || '无效人员';
|
|
28
|
+
const displayName = name?.trim() || '无效人员';
|
|
29
|
+
const formatSize = [
|
|
30
|
+
'small',
|
|
31
|
+
'medium',
|
|
32
|
+
'large'
|
|
33
|
+
].includes(size) ? size : 'medium';
|
|
18
34
|
return /*#__PURE__*/ jsxs("div", {
|
|
19
|
-
className: clsxWithTw('flex items-center gap-1 rounded-full', {
|
|
20
|
-
'bg-
|
|
21
|
-
'py-0.5 pl-0.5 pr-1.5 max-w-[148px]': 'small' === size,
|
|
22
|
-
'py-0.5 pl-0.5 pr-2 max-w-[172px]': 'medium' === size,
|
|
23
|
-
'py-1 pl-1 pr-2.5 max-w-[196px]': 'large' === size
|
|
35
|
+
className: clsxWithTw('flex min-w-0 items-center gap-1 rounded-full', sizeClassMap[formatSize], {
|
|
36
|
+
'bg-muted': 'tag' === mode
|
|
24
37
|
}, className),
|
|
25
38
|
children: [
|
|
26
|
-
/*#__PURE__*/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
/*#__PURE__*/ jsxs(Avatar, {
|
|
40
|
+
className: clsxWithTw(avatarDimensionClassMap[formatSize]),
|
|
41
|
+
children: [
|
|
42
|
+
/*#__PURE__*/ jsx(AvatarImage, {
|
|
43
|
+
src: avatar,
|
|
44
|
+
alt: displayName
|
|
45
|
+
}),
|
|
46
|
+
/*#__PURE__*/ jsx(AvatarFallback, {
|
|
47
|
+
className: clsxWithTw('text-foreground leading-none', avatarFallbackTextClassMap[formatSize])
|
|
48
|
+
})
|
|
49
|
+
]
|
|
30
50
|
}),
|
|
31
|
-
/*#__PURE__*/ jsx(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
className: textVariantMap[size],
|
|
36
|
-
children: displayName
|
|
51
|
+
showLabel && /*#__PURE__*/ jsx(OverflowTooltipText, {
|
|
52
|
+
text: displayName,
|
|
53
|
+
className: clsxWithTw('text-card-foreground', textVariantMap[formatSize])
|
|
37
54
|
})
|
|
38
55
|
]
|
|
39
56
|
});
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
export * from './SidebarNav';
|
|
2
|
-
export * from './User';
|
|
3
|
-
export * from './SidebarNav';
|
|
4
|
-
export * from './User';
|
|
5
|
-
export * from './theme';
|
|
6
1
|
export * from './AppContainer';
|
|
7
2
|
export * from './ErrorRender';
|
|
8
3
|
export * from './NotFoundRender';
|
|
4
|
+
export * from './theme';
|
|
5
|
+
export * from './User';
|
package/lib/components/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export * from "./SidebarNav/index.js";
|
|
2
|
-
export * from "./User/index.js";
|
|
3
|
-
export * from "./theme/index.js";
|
|
4
1
|
export * from "./AppContainer/index.js";
|
|
5
2
|
export * from "./ErrorRender/index.js";
|
|
6
3
|
export * from "./NotFoundRender/index.js";
|
|
4
|
+
export * from "./theme/index.js";
|
|
5
|
+
export * from "./User/index.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
3
|
+
declare function Avatar({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Root>): React.JSX.Element;
|
|
4
|
+
declare function AvatarImage({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Image>): React.JSX.Element;
|
|
5
|
+
declare function AvatarFallback({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Fallback>): React.JSX.Element;
|
|
6
|
+
export { Avatar, AvatarImage, AvatarFallback };
|