@lark-apaas/client-toolkit 1.0.3 → 1.0.5

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 (83) hide show
  1. package/README.md +1 -1
  2. package/lib/apis/hooks/useTheme.d.ts +1 -0
  3. package/lib/apis/hooks/useTheme.js +1 -0
  4. package/lib/components/AppContainer/api-proxy/core.d.ts +182 -0
  5. package/lib/components/AppContainer/api-proxy/core.js +294 -0
  6. package/lib/components/AppContainer/index.js +2 -0
  7. package/lib/components/AppContainer/utils/api-panel.d.ts +29 -0
  8. package/lib/components/AppContainer/utils/api-panel.js +66 -0
  9. package/lib/components/AppContainer/utils/childApi.js +11 -1
  10. package/lib/components/ErrorRender/index.js +21 -17
  11. package/lib/components/User/UserDisplay.d.ts +1 -1
  12. package/lib/components/User/UserDisplay.js +101 -25
  13. package/lib/components/User/UserProfile/UserProfile.css +1 -1
  14. package/lib/components/User/UserProfile/UserProfileContainer.d.ts +1 -1
  15. package/lib/components/User/UserProfile/UserProfileSkeleton.d.ts +0 -1
  16. package/lib/components/User/UserProfile/UserProfileSkeleton.js +21 -29
  17. package/lib/components/User/UserProfile/UserProfileUI.d.ts +1 -2
  18. package/lib/components/User/UserProfile/UserProfileUI.js +106 -92
  19. package/lib/components/User/UserSelect.d.ts +1 -1
  20. package/lib/components/User/UserSelect.js +17 -143
  21. package/lib/components/User/UserSelectUI/ActionButtons.d.ts +11 -0
  22. package/lib/components/User/UserSelectUI/ActionButtons.js +44 -0
  23. package/lib/components/User/UserSelectUI/Dropdown.d.ts +12 -0
  24. package/lib/components/User/UserSelectUI/Dropdown.js +66 -0
  25. package/lib/components/User/UserSelectUI/MultipleSelectionTags.d.ts +14 -0
  26. package/lib/components/User/UserSelectUI/MultipleSelectionTags.js +48 -0
  27. package/lib/components/User/UserSelectUI/SingleSelectionPreview.d.ts +9 -0
  28. package/lib/components/User/UserSelectUI/SingleSelectionPreview.js +37 -0
  29. package/lib/components/User/UserSelectUI/Spinner.d.ts +2 -0
  30. package/lib/components/User/UserSelectUI/Spinner.js +13 -0
  31. package/lib/components/User/UserSelectUI/UserSelectUI.d.ts +5 -0
  32. package/lib/components/User/UserSelectUI/UserSelectUI.js +230 -0
  33. package/lib/components/User/UserSelectUI/index.d.ts +2 -0
  34. package/lib/components/User/UserSelectUI/index.js +2 -0
  35. package/lib/components/User/UserSelectUI/types.d.ts +14 -0
  36. package/lib/components/User/UserSelectUI/types.js +0 -0
  37. package/lib/components/User/UserWithAvatar.js +38 -21
  38. package/lib/components/index.d.ts +2 -5
  39. package/lib/components/index.js +2 -3
  40. package/lib/components/ui/avatar.d.ts +6 -0
  41. package/lib/components/ui/avatar.js +27 -0
  42. package/lib/components/ui/badge.d.ts +9 -0
  43. package/lib/components/ui/badge.js +29 -0
  44. package/lib/components/ui/button.d.ts +10 -0
  45. package/lib/components/ui/button.js +42 -0
  46. package/lib/components/ui/input.d.ts +3 -0
  47. package/lib/components/ui/input.js +12 -0
  48. package/lib/components/ui/overflow-tooltip-text.d.ts +8 -0
  49. package/lib/components/ui/overflow-tooltip-text.js +66 -0
  50. package/lib/components/ui/popover.d.ts +7 -0
  51. package/lib/components/ui/popover.js +35 -0
  52. package/lib/components/ui/skeleton.d.ts +7 -0
  53. package/lib/components/ui/skeleton.js +10 -0
  54. package/lib/components/ui/tooltip.d.ts +7 -0
  55. package/lib/components/ui/tooltip.js +24 -0
  56. package/lib/override.css +0 -16
  57. package/lib/types/iframe-events.d.ts +9 -0
  58. package/lib/types/index.d.ts +0 -29
  59. package/lib/utils/axiosConfig.js +18 -22
  60. package/package.json +8 -1
  61. package/lib/apis/components/SidebarNav.d.ts +0 -1
  62. package/lib/apis/components/SidebarNav.js +0 -2
  63. package/lib/components/SidebarNav/DrawerNav.d.ts +0 -3
  64. package/lib/components/SidebarNav/DrawerNav.js +0 -64
  65. package/lib/components/SidebarNav/DropdownNav.d.ts +0 -3
  66. package/lib/components/SidebarNav/DropdownNav.js +0 -40
  67. package/lib/components/SidebarNav/Sidebar.d.ts +0 -3
  68. package/lib/components/SidebarNav/Sidebar.js +0 -33
  69. package/lib/components/SidebarNav/index.d.ts +0 -5
  70. package/lib/components/SidebarNav/index.js +0 -61
  71. package/lib/components/User/UserSelect.css +0 -11
  72. package/lib/components/common/LogoInfo.d.ts +0 -5
  73. package/lib/components/common/LogoInfo.js +0 -30
  74. package/lib/components/common/NavItem.d.ts +0 -20
  75. package/lib/components/common/NavItem.js +0 -112
  76. package/lib/components/common/NavMenu.d.ts +0 -9
  77. package/lib/components/common/NavMenu.js +0 -50
  78. package/lib/components/common/UserAvatarLayout.d.ts +0 -4
  79. package/lib/components/common/UserAvatarLayout.js +0 -41
  80. package/lib/components/common/UserAvatarMenu.d.ts +0 -4
  81. package/lib/components/common/UserAvatarMenu.js +0 -58
  82. package/lib/components/common/index.d.ts +0 -9
  83. package/lib/components/common/index.js +0 -10
@@ -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,2 @@
1
+ import * as React from 'react';
2
+ export declare function Spinner({ className }: React.ComponentProps<'svg'>): React.JSX.Element;
@@ -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,2 @@
1
+ export { UserSelectUI } from './UserSelectUI';
2
+ export type { UserSelectProps, UserSelectValue } from './types';
@@ -0,0 +1,2 @@
1
+ import { UserSelectUI } from "./UserSelectUI.js";
2
+ 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,11 +1,22 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import "react";
3
- import { Avatar, Typography } from "antd";
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 avatarSizeMap = {
6
- small: 16,
7
- medium: 20,
8
- large: 24
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]',
@@ -14,26 +25,32 @@ const textVariantMap = {
14
25
  };
15
26
  function UserWithAvatar({ data, size = 'medium', mode = 'tag', className }) {
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-[rgba(31,35,41,0.1)]': 'tag' === mode,
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__*/ jsx(Avatar, {
27
- src: avatar,
28
- size: avatarSizeMap[size],
29
- className: "shrink-0 border-0"
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(Typography.Text, {
32
- ellipsis: {
33
- tooltip: displayName
34
- },
35
- className: textVariantMap[size],
36
- children: displayName
51
+ /*#__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';
@@ -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 };
@@ -0,0 +1,27 @@
1
+ "use client"
2
+ import { jsx } from "react/jsx-runtime";
3
+ import "react";
4
+ import { Fallback, Image, Root } from "@radix-ui/react-avatar";
5
+ import { clsxWithTw } from "../../utils/utils.js";
6
+ function Avatar({ className, ...props }) {
7
+ return /*#__PURE__*/ jsx(Root, {
8
+ "data-slot": "avatar",
9
+ className: clsxWithTw("relative flex size-8 shrink-0 overflow-hidden rounded-full", className),
10
+ ...props
11
+ });
12
+ }
13
+ function AvatarImage({ className, ...props }) {
14
+ return /*#__PURE__*/ jsx(Image, {
15
+ "data-slot": "avatar-image",
16
+ className: clsxWithTw("aspect-square size-full", className),
17
+ ...props
18
+ });
19
+ }
20
+ function AvatarFallback({ className, ...props }) {
21
+ return /*#__PURE__*/ jsx(Fallback, {
22
+ "data-slot": "avatar-fallback",
23
+ className: clsxWithTw("bg-muted flex size-full items-center justify-center rounded-full", className),
24
+ ...props
25
+ });
26
+ }
27
+ export { Avatar, AvatarFallback, AvatarImage };