@aloudata/aloudata-design 3.1.5 → 3.1.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.
- package/dist/ConfigProvider/getUserList.d.ts +47 -0
- package/dist/ConfigProvider/getUserList.js.map +1 -1
- package/dist/ConfigProvider/hooks/useGetUserList.d.ts +7 -0
- package/dist/ConfigProvider/hooks/useGetUserList.js +5 -1
- package/dist/ConfigProvider/hooks/useGetUserList.js.map +1 -1
- package/dist/ConfigProvider/index.d.ts +5 -1
- package/dist/ConfigProvider/index.js +6 -2
- package/dist/ConfigProvider/index.js.map +1 -1
- package/dist/Dropdown/index.d.ts +7 -2
- package/dist/Dropdown/index.js +32 -23
- package/dist/Dropdown/index.js.map +1 -1
- package/dist/MemberPicker/components/MultipleOption.d.ts +1 -0
- package/dist/MemberPicker/components/MultipleOption.js +3 -1
- package/dist/MemberPicker/components/MultipleOption.js.map +1 -1
- package/dist/MemberPicker/components/NickLabel.d.ts +5 -0
- package/dist/MemberPicker/components/NickLabel.js +14 -4
- package/dist/MemberPicker/components/NickLabel.js.map +1 -1
- package/dist/MemberPicker/components/Panel.d.ts +4 -0
- package/dist/MemberPicker/components/Panel.js +13 -4
- package/dist/MemberPicker/components/Panel.js.map +1 -1
- package/dist/MemberPicker/components/PanelWrapper.js +25 -24
- package/dist/MemberPicker/components/PanelWrapper.js.map +1 -1
- package/dist/MemberPicker/components/SearchFeedback.d.ts +7 -0
- package/dist/MemberPicker/components/SearchFeedback.js +27 -0
- package/dist/MemberPicker/components/SearchFeedback.js.map +1 -0
- package/dist/MemberPicker/components/SingleOption.d.ts +1 -0
- package/dist/MemberPicker/components/SingleOption.js +3 -1
- package/dist/MemberPicker/components/SingleOption.js.map +1 -1
- package/dist/MemberPicker/components/UserGroupSelection.d.ts +4 -1
- package/dist/MemberPicker/components/UserGroupSelection.js +29 -22
- package/dist/MemberPicker/components/UserGroupSelection.js.map +1 -1
- package/dist/MemberPicker/dialog/DialogColumns.d.ts +5 -0
- package/dist/MemberPicker/dialog/DialogColumns.js +59 -0
- package/dist/MemberPicker/dialog/DialogColumns.js.map +1 -0
- package/dist/MemberPicker/dialog/DialogContent.d.ts +3 -0
- package/dist/MemberPicker/dialog/DialogContent.js +106 -0
- package/dist/MemberPicker/dialog/DialogContent.js.map +1 -0
- package/dist/MemberPicker/dialog/MemberOption.d.ts +16 -0
- package/dist/MemberPicker/dialog/MemberOption.js +101 -0
- package/dist/MemberPicker/dialog/MemberOption.js.map +1 -0
- package/dist/MemberPicker/dialog/MemberPickerDialog.d.ts +11 -0
- package/dist/MemberPicker/dialog/MemberPickerDialog.js +11 -0
- package/dist/MemberPicker/dialog/MemberPickerDialog.js.map +1 -0
- package/dist/MemberPicker/dialog/SelectedMemberPanel.d.ts +12 -0
- package/dist/MemberPicker/dialog/SelectedMemberPanel.js +37 -0
- package/dist/MemberPicker/dialog/SelectedMemberPanel.js.map +1 -0
- package/dist/MemberPicker/dialog/UserCandidatePanel.d.ts +13 -0
- package/dist/MemberPicker/dialog/UserCandidatePanel.js +168 -0
- package/dist/MemberPicker/dialog/UserCandidatePanel.js.map +1 -0
- package/dist/MemberPicker/dialog/UserGroupCandidatePanel.d.ts +13 -0
- package/dist/MemberPicker/dialog/UserGroupCandidatePanel.js +134 -0
- package/dist/MemberPicker/dialog/UserGroupCandidatePanel.js.map +1 -0
- package/dist/MemberPicker/dialog/helper.d.ts +20 -0
- package/dist/MemberPicker/dialog/helper.js +61 -0
- package/dist/MemberPicker/dialog/helper.js.map +1 -0
- package/dist/MemberPicker/dialog/types.d.ts +41 -0
- package/dist/MemberPicker/dialog/types.js +7 -0
- package/dist/MemberPicker/dialog/types.js.map +1 -0
- package/dist/MemberPicker/dialog/useDialogLocale.d.ts +103 -0
- package/dist/MemberPicker/dialog/useDialogLocale.js +12 -0
- package/dist/MemberPicker/dialog/useDialogLocale.js.map +1 -0
- package/dist/MemberPicker/dialog/useMemberPickerDraft.d.ts +27 -0
- package/dist/MemberPicker/dialog/useMemberPickerDraft.js +95 -0
- package/dist/MemberPicker/dialog/useMemberPickerDraft.js.map +1 -0
- package/dist/MemberPicker/dialog/useUserGroupCandidates.d.ts +29 -0
- package/dist/MemberPicker/dialog/useUserGroupCandidates.js +443 -0
- package/dist/MemberPicker/dialog/useUserGroupCandidates.js.map +1 -0
- package/dist/MemberPicker/index.d.ts +2 -0
- package/dist/MemberPicker/index.js +58 -34
- package/dist/MemberPicker/index.js.map +1 -1
- package/dist/MemberPicker/useMemberSearch.d.ts +8 -0
- package/dist/MemberPicker/useMemberSearch.js +51 -0
- package/dist/MemberPicker/useMemberSearch.js.map +1 -0
- package/dist/Menu/index.d.ts +2 -0
- package/dist/Menu/index.js +3 -2
- package/dist/Menu/index.js.map +1 -1
- package/dist/Modal/index.js +1 -1
- package/dist/Modal/index.js.map +1 -1
- package/dist/aloudata-design.css +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -2
- package/dist/locale/default.d.ts +29 -0
- package/dist/locale/en_US.d.ts +29 -0
- package/dist/locale/en_US.js +29 -0
- package/dist/locale/en_US.js.map +1 -1
- package/dist/locale/zh_CN.d.ts +29 -0
- package/dist/locale/zh_CN.js +29 -0
- package/dist/locale/zh_CN.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import useDialogLocale from "./useDialogLocale.js";
|
|
2
|
+
import { useRef, useState } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/MemberPicker/dialog/DialogColumns.tsx
|
|
5
|
+
/** 两栏分隔器仅持有比例;指针与键盘共用边界,避免全屏切换后像素宽度失效。 */
|
|
6
|
+
function DialogColumns({ children }) {
|
|
7
|
+
const root = useRef(null);
|
|
8
|
+
const [percent, setPercent] = useState(58);
|
|
9
|
+
const t = useDialogLocale();
|
|
10
|
+
const clamp = (value) => Math.min(70, Math.max(35, value));
|
|
11
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
12
|
+
ref: root,
|
|
13
|
+
className: "tw-grid tw-h-full tw-min-h-0 tw-min-w-0",
|
|
14
|
+
style: { gridTemplateColumns: `minmax(0, ${percent}fr) 8px minmax(0, ${100 - percent}fr)` },
|
|
15
|
+
children: [
|
|
16
|
+
children[0],
|
|
17
|
+
/* @__PURE__ */ jsxs("div", {
|
|
18
|
+
role: "separator",
|
|
19
|
+
tabIndex: 0,
|
|
20
|
+
"aria-label": t.MemberPickerDialog.resizePanels,
|
|
21
|
+
"aria-orientation": "vertical",
|
|
22
|
+
"aria-valuemin": 35,
|
|
23
|
+
"aria-valuemax": 70,
|
|
24
|
+
"aria-valuenow": Math.round(percent),
|
|
25
|
+
className: "tw-group tw-relative tw-flex tw-cursor-col-resize tw-touch-none tw-items-center tw-justify-center tw-rounded-r-50 focus-visible:tw-outline focus-visible:tw-outline-2 focus-visible:tw-outline-offset-2 focus-visible:tw-outline-[var(--focus-ring)]",
|
|
26
|
+
onKeyDown: (event) => {
|
|
27
|
+
const offset = event.key === "ArrowLeft" ? -2 : event.key === "ArrowRight" ? 2 : 0;
|
|
28
|
+
if (offset || event.key === "Home" || event.key === "End") {
|
|
29
|
+
event.preventDefault();
|
|
30
|
+
setPercent((current) => event.key === "Home" ? 35 : event.key === "End" ? 70 : clamp(current + offset));
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
onPointerDown: (event) => {
|
|
34
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
35
|
+
},
|
|
36
|
+
onPointerMove: (event) => {
|
|
37
|
+
if (!event.currentTarget.hasPointerCapture(event.pointerId)) return;
|
|
38
|
+
const bounds = root.current?.getBoundingClientRect();
|
|
39
|
+
if (bounds?.width) setPercent(clamp((event.clientX - bounds.left) / bounds.width * 100));
|
|
40
|
+
},
|
|
41
|
+
onPointerUp: (event) => {
|
|
42
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
|
|
43
|
+
},
|
|
44
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
45
|
+
"aria-hidden": "true",
|
|
46
|
+
className: "tw-pointer-events-none tw-h-full tw-w-px tw-bg-border-default group-hover:tw-bg-[var(--border-brand-strong)] group-focus-visible:tw-bg-[var(--border-brand-strong)]"
|
|
47
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
48
|
+
"aria-hidden": "true",
|
|
49
|
+
className: "tw-pointer-events-none tw-absolute tw-h-sp-300 tw-w-full tw-rounded-r-50 tw-border tw-border-solid tw-border-border-default tw-bg-bg-default group-hover:tw-border-[var(--border-brand-strong)] group-focus-visible:tw-border-[var(--border-brand-strong)]"
|
|
50
|
+
})]
|
|
51
|
+
}),
|
|
52
|
+
children[1]
|
|
53
|
+
]
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { DialogColumns as default };
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=DialogColumns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DialogColumns.js","names":[],"sources":["../../../src/MemberPicker/dialog/DialogColumns.tsx"],"sourcesContent":["import { useRef, useState, type ReactNode } from 'react';\nimport useDialogLocale from './useDialogLocale';\n\n/** 两栏分隔器仅持有比例;指针与键盘共用边界,避免全屏切换后像素宽度失效。 */\nexport default function DialogColumns({\n children,\n}: {\n children: [ReactNode, ReactNode];\n}) {\n const root = useRef<HTMLDivElement>(null);\n const [percent, setPercent] = useState(58);\n const t = useDialogLocale();\n const clamp = (value: number) => Math.min(70, Math.max(35, value));\n return (\n <div\n ref={root}\n className=\"tw-grid tw-h-full tw-min-h-0 tw-min-w-0\"\n style={{\n gridTemplateColumns: `minmax(0, ${percent}fr) 8px minmax(0, ${\n 100 - percent\n }fr)`,\n }}\n >\n {children[0]}\n <div\n role=\"separator\"\n tabIndex={0}\n aria-label={t.MemberPickerDialog.resizePanels}\n aria-orientation=\"vertical\"\n aria-valuemin={35}\n aria-valuemax={70}\n aria-valuenow={Math.round(percent)}\n className=\"tw-group tw-relative tw-flex tw-cursor-col-resize tw-touch-none tw-items-center tw-justify-center tw-rounded-r-50 focus-visible:tw-outline focus-visible:tw-outline-2 focus-visible:tw-outline-offset-2 focus-visible:tw-outline-[var(--focus-ring)]\"\n onKeyDown={(event) => {\n const offset =\n event.key === 'ArrowLeft' ? -2 : event.key === 'ArrowRight' ? 2 : 0;\n if (offset || event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n setPercent((current) =>\n event.key === 'Home'\n ? 35\n : event.key === 'End'\n ? 70\n : clamp(current + offset),\n );\n }\n }}\n onPointerDown={(event) => {\n event.currentTarget.setPointerCapture(event.pointerId);\n }}\n onPointerMove={(event) => {\n if (!event.currentTarget.hasPointerCapture(event.pointerId)) return;\n const bounds = root.current?.getBoundingClientRect();\n if (bounds?.width)\n setPercent(\n clamp(((event.clientX - bounds.left) / bounds.width) * 100),\n );\n }}\n onPointerUp={(event) => {\n if (event.currentTarget.hasPointerCapture(event.pointerId))\n event.currentTarget.releasePointerCapture(event.pointerId);\n }}\n >\n <span\n aria-hidden=\"true\"\n className=\"tw-pointer-events-none tw-h-full tw-w-px tw-bg-border-default group-hover:tw-bg-[var(--border-brand-strong)] group-focus-visible:tw-bg-[var(--border-brand-strong)]\"\n />\n <span\n aria-hidden=\"true\"\n className=\"tw-pointer-events-none tw-absolute tw-h-sp-300 tw-w-full tw-rounded-r-50 tw-border tw-border-solid tw-border-border-default tw-bg-bg-default group-hover:tw-border-[var(--border-brand-strong)] group-focus-visible:tw-border-[var(--border-brand-strong)]\"\n />\n </div>\n {children[1]}\n </div>\n );\n}\n"],"mappings":";;;;;AAIA,SAAwB,cAAc,EACpC,YAGC;CACD,MAAM,OAAO,OAAuB,KAAK;CACzC,MAAM,CAAC,SAAS,cAAc,SAAS,GAAG;CAC1C,MAAM,IAAI,iBAAiB;CAC3B,MAAM,SAAS,UAAkB,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC;AAClE,QACE,qBAAC,OAAD;EACE,KAAK;EACL,WAAU;EACV,OAAO,EACL,qBAAqB,aAAa,QAAQ,oBACxC,MAAM,QACP,MACF;YAPH;GASG,SAAS;GACV,qBAAC,OAAD;IACE,MAAK;IACL,UAAU;IACV,cAAY,EAAE,mBAAmB;IACjC,oBAAiB;IACjB,iBAAe;IACf,iBAAe;IACf,iBAAe,KAAK,MAAM,QAAQ;IAClC,WAAU;IACV,YAAY,UAAU;KACpB,MAAM,SACJ,MAAM,QAAQ,cAAc,KAAK,MAAM,QAAQ,eAAe,IAAI;AACpE,SAAI,UAAU,MAAM,QAAQ,UAAU,MAAM,QAAQ,OAAO;AACzD,YAAM,gBAAgB;AACtB,kBAAY,YACV,MAAM,QAAQ,SACV,KACA,MAAM,QAAQ,QACd,KACA,MAAM,UAAU,OAAO,CAC5B;;;IAGL,gBAAgB,UAAU;AACxB,WAAM,cAAc,kBAAkB,MAAM,UAAU;;IAExD,gBAAgB,UAAU;AACxB,SAAI,CAAC,MAAM,cAAc,kBAAkB,MAAM,UAAU,CAAE;KAC7D,MAAM,SAAS,KAAK,SAAS,uBAAuB;AACpD,SAAI,QAAQ,MACV,YACE,OAAQ,MAAM,UAAU,OAAO,QAAQ,OAAO,QAAS,IAAI,CAC5D;;IAEL,cAAc,UAAU;AACtB,SAAI,MAAM,cAAc,kBAAkB,MAAM,UAAU,CACxD,OAAM,cAAc,sBAAsB,MAAM,UAAU;;cApChE,CAuCE,oBAAC,QAAD;KACE,eAAY;KACZ,WAAU;KACV,CAAA,EACF,oBAAC,QAAD;KACE,eAAY;KACZ,WAAU;KACV,CAAA,CACE;;GACL,SAAS;GACN"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { MemberPickerDialogProps } from './MemberPickerDialog.js';
|
|
2
|
+
/** 打开期间拥有选择草稿;确认才通知业务,取消与卸载均释放请求和临时选择。 */
|
|
3
|
+
export default function DialogContent({ value, onChange, onClose, type, lockedIds, filterItem, renderItem, renderSelectedTag, zIndex, footer, onCancel, }: MemberPickerDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import IconButton from "../../IconButton/index.js";
|
|
2
|
+
import Modal from "../../Modal/index.js";
|
|
3
|
+
import Tabs from "../../Tabs/index.js";
|
|
4
|
+
import SelectorFooter from "../components/SelectorFooter.js";
|
|
5
|
+
import { EMemberPicker } from "../interface.js";
|
|
6
|
+
import useDialogLocale from "./useDialogLocale.js";
|
|
7
|
+
import DialogColumns from "./DialogColumns.js";
|
|
8
|
+
import SelectedMemberPanel from "./SelectedMemberPanel.js";
|
|
9
|
+
import UserCandidatePanel from "./UserCandidatePanel.js";
|
|
10
|
+
import UserGroupCandidatePanel from "./UserGroupCandidatePanel.js";
|
|
11
|
+
import { useMemberPickerDraft } from "./useMemberPickerDraft.js";
|
|
12
|
+
import { useState } from "react";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
import { MaximizeLine, MinimizeLine } from "@aloudata/icons-react";
|
|
15
|
+
//#region src/MemberPicker/dialog/DialogContent.tsx
|
|
16
|
+
/** 打开期间拥有选择草稿;确认才通知业务,取消与卸载均释放请求和临时选择。 */
|
|
17
|
+
function DialogContent({ value, onChange, onClose, type = EMemberPicker.BOTH, lockedIds, filterItem, renderItem, renderSelectedTag, zIndex, footer, onCancel }) {
|
|
18
|
+
const t = useDialogLocale();
|
|
19
|
+
const [fullscreen, setFullscreen] = useState(false);
|
|
20
|
+
const draft = useMemberPickerDraft({
|
|
21
|
+
value,
|
|
22
|
+
onChange,
|
|
23
|
+
type,
|
|
24
|
+
lockedIds
|
|
25
|
+
});
|
|
26
|
+
const panelProps = {
|
|
27
|
+
selectedByKey: draft.selectedByKey,
|
|
28
|
+
onToggle: draft.toggleMember,
|
|
29
|
+
onToggleAll: draft.toggleMembers,
|
|
30
|
+
lockedIds,
|
|
31
|
+
filterItem,
|
|
32
|
+
renderItem
|
|
33
|
+
};
|
|
34
|
+
const confirm = () => {
|
|
35
|
+
if (draft.hydrating) return;
|
|
36
|
+
draft.confirm();
|
|
37
|
+
onClose();
|
|
38
|
+
};
|
|
39
|
+
const cancel = () => {
|
|
40
|
+
onCancel?.();
|
|
41
|
+
onClose();
|
|
42
|
+
};
|
|
43
|
+
const users = /* @__PURE__ */ jsx(UserCandidatePanel, { ...panelProps });
|
|
44
|
+
const groups = /* @__PURE__ */ jsx(UserGroupCandidatePanel, { ...panelProps });
|
|
45
|
+
return /* @__PURE__ */ jsx(Modal, {
|
|
46
|
+
open: true,
|
|
47
|
+
centered: true,
|
|
48
|
+
paddingLess: true,
|
|
49
|
+
zIndex,
|
|
50
|
+
width: fullscreen ? "calc(100vw - 24px)" : "min(800px, calc(100vw - 32px))",
|
|
51
|
+
title: /* @__PURE__ */ jsxs("div", {
|
|
52
|
+
className: "tw-flex tw-items-center tw-gap-sp-150",
|
|
53
|
+
children: [/* @__PURE__ */ jsx("span", { children: t.MemberPickerDialog.title }), /* @__PURE__ */ jsx(IconButton, {
|
|
54
|
+
size: "small",
|
|
55
|
+
"aria-label": fullscreen ? t.MemberPickerDialog.exitMaximize : t.MemberPickerDialog.maximize,
|
|
56
|
+
tooltip: { title: fullscreen ? t.MemberPickerDialog.exitMaximize : t.MemberPickerDialog.maximize },
|
|
57
|
+
icon: fullscreen ? /* @__PURE__ */ jsx(MinimizeLine, {}) : /* @__PURE__ */ jsx(MaximizeLine, {}),
|
|
58
|
+
onClick: () => setFullscreen((current) => !current)
|
|
59
|
+
})]
|
|
60
|
+
}),
|
|
61
|
+
okText: t.MemberPickerDialog.ok,
|
|
62
|
+
cancelText: t.MemberPickerDialog.cancel,
|
|
63
|
+
okButtonProps: { disabled: draft.hydrating },
|
|
64
|
+
onOk: confirm,
|
|
65
|
+
footer: footer ? /* @__PURE__ */ jsx(SelectorFooter, {
|
|
66
|
+
footer,
|
|
67
|
+
onOk: confirm,
|
|
68
|
+
onCancel: cancel
|
|
69
|
+
}) : void 0,
|
|
70
|
+
onCancel: cancel,
|
|
71
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
72
|
+
className: "ald-member-picker-dialog tw-min-h-0 tw-p-sp-200",
|
|
73
|
+
style: { height: fullscreen ? "calc(100dvh - 150px)" : "min(560px, calc(100dvh - 180px))" },
|
|
74
|
+
children: /* @__PURE__ */ jsxs(DialogColumns, { children: [/* @__PURE__ */ jsx("div", {
|
|
75
|
+
className: "tw-h-full tw-min-w-0 tw-overflow-hidden",
|
|
76
|
+
children: type === EMemberPicker.BOTH ? /* @__PURE__ */ jsx(Tabs, {
|
|
77
|
+
compact: true,
|
|
78
|
+
adaptHeight: true,
|
|
79
|
+
className: "tw-h-full",
|
|
80
|
+
activeKey: draft.activeTab,
|
|
81
|
+
onChange: (key) => {
|
|
82
|
+
if (key === EMemberPicker.USER || key === EMemberPicker.GROUP) draft.setActiveTab(key);
|
|
83
|
+
},
|
|
84
|
+
items: [{
|
|
85
|
+
key: EMemberPicker.USER,
|
|
86
|
+
label: t.MemberPickerDialog.user,
|
|
87
|
+
children: users
|
|
88
|
+
}, {
|
|
89
|
+
key: EMemberPicker.GROUP,
|
|
90
|
+
label: t.MemberPickerDialog.userGroup,
|
|
91
|
+
children: groups
|
|
92
|
+
}]
|
|
93
|
+
}) : type === EMemberPicker.GROUP ? groups : users
|
|
94
|
+
}), /* @__PURE__ */ jsx(SelectedMemberPanel, {
|
|
95
|
+
members: draft.selectedMembers,
|
|
96
|
+
lockedIds,
|
|
97
|
+
onRemove: draft.removeMember,
|
|
98
|
+
renderSelectedTag
|
|
99
|
+
})] })
|
|
100
|
+
})
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
export { DialogContent as default };
|
|
105
|
+
|
|
106
|
+
//# sourceMappingURL=DialogContent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DialogContent.js","names":[],"sources":["../../../src/MemberPicker/dialog/DialogContent.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { MaximizeLine, MinimizeLine } from '@aloudata/icons-react';\nimport IconButton from '../../IconButton';\nimport Modal from '../../Modal';\nimport Tabs from '../../Tabs';\nimport SelectorFooter from '../components/SelectorFooter';\nimport { EMemberPicker } from '../interface';\nimport type { MemberPickerDialogProps } from './MemberPickerDialog';\nimport DialogColumns from './DialogColumns';\nimport SelectedMemberPanel from './SelectedMemberPanel';\nimport UserCandidatePanel from './UserCandidatePanel';\nimport UserGroupCandidatePanel from './UserGroupCandidatePanel';\nimport { useMemberPickerDraft } from './useMemberPickerDraft';\nimport useDialogLocale from './useDialogLocale';\n\n/** 打开期间拥有选择草稿;确认才通知业务,取消与卸载均释放请求和临时选择。 */\nexport default function DialogContent({\n value,\n onChange,\n onClose,\n type = EMemberPicker.BOTH,\n lockedIds,\n filterItem,\n renderItem,\n renderSelectedTag,\n zIndex,\n footer,\n onCancel,\n}: MemberPickerDialogProps) {\n const t = useDialogLocale();\n const [fullscreen, setFullscreen] = useState(false);\n const draft = useMemberPickerDraft({ value, onChange, type, lockedIds });\n const panelProps = {\n selectedByKey: draft.selectedByKey,\n onToggle: draft.toggleMember,\n onToggleAll: draft.toggleMembers,\n lockedIds,\n filterItem,\n renderItem,\n };\n const confirm = () => {\n if (draft.hydrating) return;\n draft.confirm();\n onClose();\n };\n const cancel = () => {\n onCancel?.();\n onClose();\n };\n const users = <UserCandidatePanel {...panelProps} />;\n const groups = <UserGroupCandidatePanel {...panelProps} />;\n return (\n <Modal\n open\n centered\n paddingLess\n zIndex={zIndex}\n width={\n fullscreen ? 'calc(100vw - 24px)' : 'min(800px, calc(100vw - 32px))'\n }\n title={\n <div className=\"tw-flex tw-items-center tw-gap-sp-150\">\n <span>{t.MemberPickerDialog.title}</span>\n <IconButton\n size=\"small\"\n aria-label={\n fullscreen\n ? t.MemberPickerDialog.exitMaximize\n : t.MemberPickerDialog.maximize\n }\n tooltip={{\n title: fullscreen\n ? t.MemberPickerDialog.exitMaximize\n : t.MemberPickerDialog.maximize,\n }}\n icon={fullscreen ? <MinimizeLine /> : <MaximizeLine />}\n onClick={() => setFullscreen((current) => !current)}\n />\n </div>\n }\n okText={t.MemberPickerDialog.ok}\n cancelText={t.MemberPickerDialog.cancel}\n okButtonProps={{ disabled: draft.hydrating }}\n onOk={confirm}\n footer={\n footer ? (\n <SelectorFooter footer={footer} onOk={confirm} onCancel={cancel} />\n ) : undefined\n }\n onCancel={cancel}\n >\n <div\n className=\"ald-member-picker-dialog tw-min-h-0 tw-p-sp-200\"\n style={{\n height: fullscreen\n ? 'calc(100dvh - 150px)'\n : 'min(560px, calc(100dvh - 180px))',\n }}\n >\n <DialogColumns>\n <div className=\"tw-h-full tw-min-w-0 tw-overflow-hidden\">\n {type === EMemberPicker.BOTH ? (\n <Tabs\n compact\n adaptHeight\n className=\"tw-h-full\"\n activeKey={draft.activeTab}\n onChange={(key) => {\n if (key === EMemberPicker.USER || key === EMemberPicker.GROUP)\n draft.setActiveTab(key);\n }}\n items={[\n {\n key: EMemberPicker.USER,\n label: t.MemberPickerDialog.user,\n children: users,\n },\n {\n key: EMemberPicker.GROUP,\n label: t.MemberPickerDialog.userGroup,\n children: groups,\n },\n ]}\n />\n ) : type === EMemberPicker.GROUP ? (\n groups\n ) : (\n users\n )}\n </div>\n <SelectedMemberPanel\n members={draft.selectedMembers}\n lockedIds={lockedIds}\n onRemove={draft.removeMember}\n renderSelectedTag={renderSelectedTag}\n />\n </DialogColumns>\n </div>\n </Modal>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,SAAwB,cAAc,EACpC,OACA,UACA,SACA,OAAO,cAAc,MACrB,WACA,YACA,YACA,mBACA,QACA,QACA,YAC0B;CAC1B,MAAM,IAAI,iBAAiB;CAC3B,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,QAAQ,qBAAqB;EAAE;EAAO;EAAU;EAAM;EAAW,CAAC;CACxE,MAAM,aAAa;EACjB,eAAe,MAAM;EACrB,UAAU,MAAM;EAChB,aAAa,MAAM;EACnB;EACA;EACA;EACD;CACD,MAAM,gBAAgB;AACpB,MAAI,MAAM,UAAW;AACrB,QAAM,SAAS;AACf,WAAS;;CAEX,MAAM,eAAe;AACnB,cAAY;AACZ,WAAS;;CAEX,MAAM,QAAQ,oBAAC,oBAAD,EAAoB,GAAI,YAAc,CAAA;CACpD,MAAM,SAAS,oBAAC,yBAAD,EAAyB,GAAI,YAAc,CAAA;AAC1D,QACE,oBAAC,OAAD;EACE,MAAA;EACA,UAAA;EACA,aAAA;EACQ;EACR,OACE,aAAa,uBAAuB;EAEtC,OACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,QAAD,EAAA,UAAO,EAAE,mBAAmB,OAAa,CAAA,EACzC,oBAAC,YAAD;IACE,MAAK;IACL,cACE,aACI,EAAE,mBAAmB,eACrB,EAAE,mBAAmB;IAE3B,SAAS,EACP,OAAO,aACH,EAAE,mBAAmB,eACrB,EAAE,mBAAmB,UAC1B;IACD,MAAM,aAAa,oBAAC,cAAD,EAAgB,CAAA,GAAG,oBAAC,cAAD,EAAgB,CAAA;IACtD,eAAe,eAAe,YAAY,CAAC,QAAQ;IACnD,CAAA,CACE;;EAER,QAAQ,EAAE,mBAAmB;EAC7B,YAAY,EAAE,mBAAmB;EACjC,eAAe,EAAE,UAAU,MAAM,WAAW;EAC5C,MAAM;EACN,QACE,SACE,oBAAC,gBAAD;GAAwB;GAAQ,MAAM;GAAS,UAAU;GAAU,CAAA,GACjE;EAEN,UAAU;YAEV,oBAAC,OAAD;GACE,WAAU;GACV,OAAO,EACL,QAAQ,aACJ,yBACA,oCACL;aAED,qBAAC,eAAD,EAAA,UAAA,CACE,oBAAC,OAAD;IAAK,WAAU;cACZ,SAAS,cAAc,OACtB,oBAAC,MAAD;KACE,SAAA;KACA,aAAA;KACA,WAAU;KACV,WAAW,MAAM;KACjB,WAAW,QAAQ;AACjB,UAAI,QAAQ,cAAc,QAAQ,QAAQ,cAAc,MACtD,OAAM,aAAa,IAAI;;KAE3B,OAAO,CACL;MACE,KAAK,cAAc;MACnB,OAAO,EAAE,mBAAmB;MAC5B,UAAU;MACX,EACD;MACE,KAAK,cAAc;MACnB,OAAO,EAAE,mBAAmB;MAC5B,UAAU;MACX,CACF;KACD,CAAA,GACA,SAAS,cAAc,QACzB,SAEA;IAEE,CAAA,EACN,oBAAC,qBAAD;IACE,SAAS,MAAM;IACJ;IACX,UAAU,MAAM;IACG;IACnB,CAAA,CACY,EAAA,CAAA;GACZ,CAAA;EACA,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { IMemberPickerProps as MemberPickerProps } from '../interface';
|
|
2
|
+
import { TMember } from './types.js';
|
|
3
|
+
interface IProps {
|
|
4
|
+
member: TMember;
|
|
5
|
+
selected: boolean;
|
|
6
|
+
locked: boolean;
|
|
7
|
+
onToggle?: (member: TMember) => void;
|
|
8
|
+
onRemove?: (member: TMember) => void;
|
|
9
|
+
onOpenGroup?: () => void;
|
|
10
|
+
renderItem?: MemberPickerProps['renderItem'];
|
|
11
|
+
searchValue?: string;
|
|
12
|
+
renderSelectedTag?: MemberPickerProps['renderSelectedTag'];
|
|
13
|
+
}
|
|
14
|
+
/** 候选项统一用名称切换选择;用户组仅通过右侧独立入口进入下级。 */
|
|
15
|
+
export default function MemberOption({ member, selected, locked, onToggle, onRemove, onOpenGroup, renderItem, renderSelectedTag, searchValue, }: IProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils.js";
|
|
2
|
+
import Avatar from "../../Avatar/index.js";
|
|
3
|
+
import Button_default from "../../Button/index.js";
|
|
4
|
+
import Checkbox from "../../Checkbox/index.js";
|
|
5
|
+
import Tooltip from "../../Tooltip/index.js";
|
|
6
|
+
import Tag from "../../Tag/index.js";
|
|
7
|
+
import { EUserType } from "../../ConfigProvider/getUserList.js";
|
|
8
|
+
import HighlightText from "../../HighlightText/index.js";
|
|
9
|
+
import useDialogLocale from "./useDialogLocale.js";
|
|
10
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
//#region src/MemberPicker/dialog/MemberOption.tsx
|
|
12
|
+
/** 候选项统一用名称切换选择;用户组仅通过右侧独立入口进入下级。 */
|
|
13
|
+
function MemberOption({ member, selected, locked, onToggle, onRemove, onOpenGroup, renderItem, renderSelectedTag, searchValue }) {
|
|
14
|
+
const t = useDialogLocale();
|
|
15
|
+
const toggle = () => onToggle?.(member);
|
|
16
|
+
const displayName = member.type === EUserType.USER_GROUP && searchValue?.trim() && member.path?.length ? member.path.map((node) => node.name).join(" / ") : member.name;
|
|
17
|
+
const ancestors = member.type === EUserType.USER_GROUP && searchValue?.trim() ? member.path?.slice(0, -1).map((node) => node.name).join(" / ") : void 0;
|
|
18
|
+
const nameNode = /* @__PURE__ */ jsx(Tooltip, {
|
|
19
|
+
title: displayName,
|
|
20
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
21
|
+
title: displayName,
|
|
22
|
+
className: cn("tw-min-w-0 tw-flex-1 tw-truncate tw-text-typography-body-dense", locked ? "tw-text-content-disabled" : "tw-text-content-primary"),
|
|
23
|
+
children: [ancestors && /* @__PURE__ */ jsxs("span", {
|
|
24
|
+
className: "tw-text-content-secondary",
|
|
25
|
+
children: [ancestors, " / "]
|
|
26
|
+
}), /* @__PURE__ */ jsx(HighlightText, {
|
|
27
|
+
keyword: searchValue?.trim() ?? "",
|
|
28
|
+
children: member.name ?? ""
|
|
29
|
+
})]
|
|
30
|
+
})
|
|
31
|
+
});
|
|
32
|
+
const renderedCandidate = renderItem?.(member, {
|
|
33
|
+
isSelected: selected,
|
|
34
|
+
disabled: locked,
|
|
35
|
+
onClick: toggle,
|
|
36
|
+
onNextLevel: onOpenGroup,
|
|
37
|
+
originNode: nameNode
|
|
38
|
+
});
|
|
39
|
+
const content = (onRemove ? renderSelectedTag?.(member, nameNode) : void 0) ?? renderedCandidate ?? nameNode;
|
|
40
|
+
if (onRemove) return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Tag, {
|
|
41
|
+
closable: true,
|
|
42
|
+
disabled: locked,
|
|
43
|
+
status: "default",
|
|
44
|
+
mode: "border",
|
|
45
|
+
title: member.name,
|
|
46
|
+
className: "tw-min-w-0 tw-max-w-full",
|
|
47
|
+
onClose: (event) => {
|
|
48
|
+
event.stopPropagation();
|
|
49
|
+
onRemove(member);
|
|
50
|
+
},
|
|
51
|
+
children: content
|
|
52
|
+
}) });
|
|
53
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
54
|
+
className: "tw-flex tw-min-w-0 tw-items-center tw-gap-sp-100 tw-rounded-r-50 tw-px-sp-150 tw-py-sp-100 hover:tw-bg-bg-subtle",
|
|
55
|
+
onClick: locked ? void 0 : toggle,
|
|
56
|
+
children: [
|
|
57
|
+
/* @__PURE__ */ jsx("span", {
|
|
58
|
+
onClick: (event) => event.stopPropagation(),
|
|
59
|
+
children: /* @__PURE__ */ jsx(Checkbox, {
|
|
60
|
+
checked: selected,
|
|
61
|
+
disabled: locked,
|
|
62
|
+
"aria-label": member.name,
|
|
63
|
+
onChange: toggle
|
|
64
|
+
})
|
|
65
|
+
}),
|
|
66
|
+
/* @__PURE__ */ jsx("span", {
|
|
67
|
+
"aria-hidden": "true",
|
|
68
|
+
className: "tw-flex tw-shrink-0",
|
|
69
|
+
children: /* @__PURE__ */ jsx(Avatar, {
|
|
70
|
+
size: 20,
|
|
71
|
+
type: member.type === EUserType.USER ? "user" : "team"
|
|
72
|
+
})
|
|
73
|
+
}),
|
|
74
|
+
/* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("button", {
|
|
75
|
+
type: "button",
|
|
76
|
+
disabled: locked,
|
|
77
|
+
className: "tw-flex tw-min-w-0 tw-flex-1 tw-items-center tw-border-0 tw-bg-transparent tw-p-0 tw-text-left focus-visible:tw-outline focus-visible:tw-outline-2 focus-visible:tw-outline-offset-2 focus-visible:tw-outline-[var(--focus-ring)]",
|
|
78
|
+
onClick: (event) => {
|
|
79
|
+
event.stopPropagation();
|
|
80
|
+
toggle();
|
|
81
|
+
},
|
|
82
|
+
children: content
|
|
83
|
+
}) }),
|
|
84
|
+
onOpenGroup ? /* @__PURE__ */ jsx(Button_default, {
|
|
85
|
+
type: "link",
|
|
86
|
+
size: "small",
|
|
87
|
+
className: "tw-ml-auto tw-shrink-0",
|
|
88
|
+
disabled: locked,
|
|
89
|
+
onClick: (event) => {
|
|
90
|
+
event.stopPropagation();
|
|
91
|
+
onOpenGroup();
|
|
92
|
+
},
|
|
93
|
+
children: t.MemberPickerDialog.subLevel
|
|
94
|
+
}) : null
|
|
95
|
+
]
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
export { MemberOption as default };
|
|
100
|
+
|
|
101
|
+
//# sourceMappingURL=MemberOption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemberOption.js","names":[],"sources":["../../../src/MemberPicker/dialog/MemberOption.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport Button from '../../Button';\nimport Avatar from '../../Avatar';\nimport { cn } from '../../lib/utils';\nimport Checkbox from '../../Checkbox';\nimport { EUserType } from '../../ConfigProvider/getUserList';\nimport Tag from '../../Tag';\nimport Tooltip from '../../Tooltip';\nimport HighlightText from '../../HighlightText';\nimport type { IMemberPickerProps as MemberPickerProps } from '../interface';\nimport useDialogLocale from './useDialogLocale';\nimport type { TMember } from './types';\n\ninterface IProps {\n member: TMember;\n selected: boolean;\n locked: boolean;\n onToggle?: (member: TMember) => void;\n onRemove?: (member: TMember) => void;\n onOpenGroup?: () => void;\n renderItem?: MemberPickerProps['renderItem'];\n searchValue?: string;\n renderSelectedTag?: MemberPickerProps['renderSelectedTag'];\n}\n\n/** 候选项统一用名称切换选择;用户组仅通过右侧独立入口进入下级。 */\nexport default function MemberOption({\n member,\n selected,\n locked,\n onToggle,\n onRemove,\n onOpenGroup,\n renderItem,\n renderSelectedTag,\n searchValue,\n}: IProps) {\n const t = useDialogLocale();\n const toggle = () => onToggle?.(member);\n const displayName =\n member.type === EUserType.USER_GROUP &&\n searchValue?.trim() &&\n member.path?.length\n ? member.path.map((node) => node.name).join(' / ')\n : member.name;\n // 祖先路径提供定位上下文,命中名称仍使用正文层级;标签保持原名称。\n const ancestors =\n member.type === EUserType.USER_GROUP && searchValue?.trim()\n ? member.path\n ?.slice(0, -1)\n .map((node) => node.name)\n .join(' / ')\n : undefined;\n const nameTextNode = (\n <span\n title={displayName}\n className={cn(\n 'tw-min-w-0 tw-flex-1 tw-truncate tw-text-typography-body-dense',\n locked ? 'tw-text-content-disabled' : 'tw-text-content-primary',\n )}\n >\n {ancestors && (\n <span className=\"tw-text-content-secondary\">{ancestors} / </span>\n )}\n <HighlightText keyword={searchValue?.trim() ?? ''}>\n {member.name ?? ''}\n </HighlightText>\n </span>\n );\n const nameNode = <Tooltip title={displayName}>{nameTextNode}</Tooltip>;\n const renderedCandidate = renderItem?.(member, {\n isSelected: selected,\n disabled: locked,\n onClick: toggle,\n onNextLevel: onOpenGroup,\n originNode: nameNode,\n });\n const renderedSelected = onRemove\n ? renderSelectedTag?.(member, nameNode)\n : undefined;\n const content: ReactNode = renderedSelected ?? renderedCandidate ?? nameNode;\n\n if (onRemove) {\n return (\n <>\n <Tag\n closable\n disabled={locked}\n status=\"default\"\n mode=\"border\"\n title={member.name}\n className=\"tw-min-w-0 tw-max-w-full\"\n onClose={(event) => {\n event.stopPropagation();\n onRemove(member);\n }}\n >\n {content}\n </Tag>\n </>\n );\n }\n\n return (\n <div\n className=\"tw-flex tw-min-w-0 tw-items-center tw-gap-sp-100 tw-rounded-r-50 tw-px-sp-150 tw-py-sp-100 hover:tw-bg-bg-subtle\"\n onClick={locked ? undefined : toggle}\n >\n <span onClick={(event) => event.stopPropagation()}>\n <Checkbox\n checked={selected}\n disabled={locked}\n aria-label={member.name}\n onChange={toggle}\n />\n </span>\n <span aria-hidden=\"true\" className=\"tw-flex tw-shrink-0\">\n <Avatar\n size={20}\n type={member.type === EUserType.USER ? 'user' : 'team'}\n />\n </span>\n <>\n <button\n type=\"button\"\n disabled={locked}\n className=\"tw-flex tw-min-w-0 tw-flex-1 tw-items-center tw-border-0 tw-bg-transparent tw-p-0 tw-text-left focus-visible:tw-outline focus-visible:tw-outline-2 focus-visible:tw-outline-offset-2 focus-visible:tw-outline-[var(--focus-ring)]\"\n onClick={(event) => {\n event.stopPropagation();\n toggle();\n }}\n >\n {content}\n </button>\n </>\n {onOpenGroup ? (\n <Button\n type=\"link\"\n size=\"small\"\n className=\"tw-ml-auto tw-shrink-0\"\n disabled={locked}\n onClick={(event) => {\n event.stopPropagation();\n onOpenGroup();\n }}\n >\n {t.MemberPickerDialog.subLevel}\n </Button>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AA0BA,SAAwB,aAAa,EACnC,QACA,UACA,QACA,UACA,UACA,aACA,YACA,mBACA,eACS;CACT,MAAM,IAAI,iBAAiB;CAC3B,MAAM,eAAe,WAAW,OAAO;CACvC,MAAM,cACJ,OAAO,SAAS,UAAU,cAC1B,aAAa,MAAM,IACnB,OAAO,MAAM,SACT,OAAO,KAAK,KAAK,SAAS,KAAK,KAAK,CAAC,KAAK,MAAM,GAChD,OAAO;CAEb,MAAM,YACJ,OAAO,SAAS,UAAU,cAAc,aAAa,MAAM,GACvD,OAAO,MACH,MAAM,GAAG,GAAG,CACb,KAAK,SAAS,KAAK,KAAK,CACxB,KAAK,MAAM,GACd;CAiBN,MAAM,WAAW,oBAAC,SAAD;EAAS,OAAO;YAf/B,qBAAC,QAAD;GACE,OAAO;GACP,WAAW,GACT,kEACA,SAAS,6BAA6B,0BACvC;aALH,CAOG,aACC,qBAAC,QAAD;IAAM,WAAU;cAAhB,CAA6C,WAAU,MAAU;OAEnE,oBAAC,eAAD;IAAe,SAAS,aAAa,MAAM,IAAI;cAC5C,OAAO,QAAQ;IACF,CAAA,CACX;;EAE6D,CAAA;CACtE,MAAM,oBAAoB,aAAa,QAAQ;EAC7C,YAAY;EACZ,UAAU;EACV,SAAS;EACT,aAAa;EACb,YAAY;EACb,CAAC;CAIF,MAAM,WAHmB,WACrB,oBAAoB,QAAQ,SAAS,GACrC,WAC2C,qBAAqB;AAEpE,KAAI,SACF,QACE,oBAAA,UAAA,EAAA,UACE,oBAAC,KAAD;EACE,UAAA;EACA,UAAU;EACV,QAAO;EACP,MAAK;EACL,OAAO,OAAO;EACd,WAAU;EACV,UAAU,UAAU;AAClB,SAAM,iBAAiB;AACvB,YAAS,OAAO;;YAGjB;EACG,CAAA,EACL,CAAA;AAIP,QACE,qBAAC,OAAD;EACE,WAAU;EACV,SAAS,SAAS,SAAY;YAFhC;GAIE,oBAAC,QAAD;IAAM,UAAU,UAAU,MAAM,iBAAiB;cAC/C,oBAAC,UAAD;KACE,SAAS;KACT,UAAU;KACV,cAAY,OAAO;KACnB,UAAU;KACV,CAAA;IACG,CAAA;GACP,oBAAC,QAAD;IAAM,eAAY;IAAO,WAAU;cACjC,oBAAC,QAAD;KACE,MAAM;KACN,MAAM,OAAO,SAAS,UAAU,OAAO,SAAS;KAChD,CAAA;IACG,CAAA;GACP,oBAAA,UAAA,EAAA,UACE,oBAAC,UAAD;IACE,MAAK;IACL,UAAU;IACV,WAAU;IACV,UAAU,UAAU;AAClB,WAAM,iBAAiB;AACvB,aAAQ;;cAGT;IACM,CAAA,EACR,CAAA;GACF,cACC,oBAAC,gBAAD;IACE,MAAK;IACL,MAAK;IACL,WAAU;IACV,UAAU;IACV,UAAU,UAAU;AAClB,WAAM,iBAAiB;AACvB,kBAAa;;cAGd,EAAE,mBAAmB;IACf,CAAA,GACP;GACA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TUser } from '../../ConfigProvider/getUserList';
|
|
2
|
+
import { IBaseMemberSelectorProps } from '../interface';
|
|
3
|
+
/** 独立弹窗公开契约,数据从最近的 ConfigProvider 读取。 */
|
|
4
|
+
export interface MemberPickerDialogProps extends Pick<IBaseMemberSelectorProps, 'value' | 'type' | 'lockedIds' | 'filterItem' | 'renderItem' | 'renderSelectedTag' | 'footer' | 'onCancel'> {
|
|
5
|
+
open: boolean;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
onChange?: (members: TUser) => void;
|
|
8
|
+
zIndex?: number;
|
|
9
|
+
}
|
|
10
|
+
/** 关闭时不挂载内容,避免关闭态请求和选择草稿串入下次打开。 */
|
|
11
|
+
export default function MemberPickerDialog(props: MemberPickerDialogProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import DialogContent from "./DialogContent.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/MemberPicker/dialog/MemberPickerDialog.tsx
|
|
4
|
+
/** 关闭时不挂载内容,避免关闭态请求和选择草稿串入下次打开。 */
|
|
5
|
+
function MemberPickerDialog(props) {
|
|
6
|
+
return props.open ? /* @__PURE__ */ jsx(DialogContent, { ...props }) : null;
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { MemberPickerDialog as default };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=MemberPickerDialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemberPickerDialog.js","names":[],"sources":["../../../src/MemberPicker/dialog/MemberPickerDialog.tsx"],"sourcesContent":["import type { TUser } from '../../ConfigProvider/getUserList';\nimport type { IBaseMemberSelectorProps } from '../interface';\nimport DialogContent from './DialogContent';\n\n/** 独立弹窗公开契约,数据从最近的 ConfigProvider 读取。 */\nexport interface MemberPickerDialogProps\n extends Pick<\n IBaseMemberSelectorProps,\n | 'value'\n | 'type'\n | 'lockedIds'\n | 'filterItem'\n | 'renderItem'\n | 'renderSelectedTag'\n | 'footer'\n | 'onCancel'\n > {\n open: boolean;\n onClose: () => void;\n onChange?: (members: TUser) => void;\n zIndex?: number;\n}\n\n/** 关闭时不挂载内容,避免关闭态请求和选择草稿串入下次打开。 */\nexport default function MemberPickerDialog(props: MemberPickerDialogProps) {\n return props.open ? <DialogContent {...props} /> : null;\n}\n"],"mappings":";;;;AAwBA,SAAwB,mBAAmB,OAAgC;AACzE,QAAO,MAAM,OAAO,oBAAC,eAAD,EAAe,GAAI,OAAS,CAAA,GAAG"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IMemberPickerProps as MemberPickerProps } from '../interface';
|
|
2
|
+
import { TUserGroupValue, TUserValue, TUser } from '../../ConfigProvider/getUserList';
|
|
3
|
+
import { TMember } from './types.js';
|
|
4
|
+
interface IProps {
|
|
5
|
+
members: TUser;
|
|
6
|
+
lockedIds?: Array<TUserValue | TUserGroupValue>;
|
|
7
|
+
onRemove: (member: TMember) => void;
|
|
8
|
+
renderSelectedTag?: MemberPickerProps['renderSelectedTag'];
|
|
9
|
+
}
|
|
10
|
+
/** 用户和用户组共享一个已选区,顺序直接来自不可变选择 Map。 */
|
|
11
|
+
export default function SelectedMemberPanel({ members, lockedIds, onRemove, renderSelectedTag, }: IProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Empty from "../../Empty/index.js";
|
|
2
|
+
import useDialogLocale from "./useDialogLocale.js";
|
|
3
|
+
import { getMemberKey, isMemberLocked } from "./helper.js";
|
|
4
|
+
import MemberOption from "./MemberOption.js";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/MemberPicker/dialog/SelectedMemberPanel.tsx
|
|
7
|
+
/** 用户和用户组共享一个已选区,顺序直接来自不可变选择 Map。 */
|
|
8
|
+
function SelectedMemberPanel({ members, lockedIds = [], onRemove, renderSelectedTag }) {
|
|
9
|
+
const t = useDialogLocale();
|
|
10
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
11
|
+
className: "tw-flex tw-h-full tw-min-h-0 tw-flex-col tw-overflow-hidden",
|
|
12
|
+
"data-testid": "selected-member-panel",
|
|
13
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
14
|
+
className: "tw-flex tw-h-10 tw-shrink-0 tw-items-center tw-gap-sp-100 tw-border-b tw-border-solid tw-border-border-default tw-px-sp-150 tw-text-content-primary tw-text-typography-label",
|
|
15
|
+
children: [t.MemberPickerDialog.selected, /* @__PURE__ */ jsx("span", {
|
|
16
|
+
className: "tw-text-content-secondary tw-text-typography-caption",
|
|
17
|
+
children: members.length
|
|
18
|
+
})]
|
|
19
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
20
|
+
className: "tw-min-h-0 tw-flex-1 tw-overflow-auto tw-p-sp-150",
|
|
21
|
+
children: !members.length ? /* @__PURE__ */ jsx(Empty, { description: t.MemberPickerDialog.emptySelected }) : /* @__PURE__ */ jsx("div", {
|
|
22
|
+
className: "tw-flex tw-flex-wrap tw-content-start tw-gap-sp-100",
|
|
23
|
+
children: members.map((member) => /* @__PURE__ */ jsx(MemberOption, {
|
|
24
|
+
member,
|
|
25
|
+
selected: true,
|
|
26
|
+
locked: isMemberLocked(member, lockedIds),
|
|
27
|
+
onRemove,
|
|
28
|
+
renderSelectedTag
|
|
29
|
+
}, getMemberKey(member)))
|
|
30
|
+
})
|
|
31
|
+
})]
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { SelectedMemberPanel as default };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=SelectedMemberPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelectedMemberPanel.js","names":[],"sources":["../../../src/MemberPicker/dialog/SelectedMemberPanel.tsx"],"sourcesContent":["import Empty from '../../Empty';\nimport type { IMemberPickerProps as MemberPickerProps } from '../interface';\nimport type {\n TUserGroupValue,\n TUserValue,\n} from '../../ConfigProvider/getUserList';\nimport type { TUser } from '../../ConfigProvider/getUserList';\nimport useDialogLocale from './useDialogLocale';\nimport { getMemberKey, isMemberLocked } from './helper';\nimport MemberOption from './MemberOption';\nimport type { TMember } from './types';\n\ninterface IProps {\n members: TUser;\n lockedIds?: Array<TUserValue | TUserGroupValue>;\n onRemove: (member: TMember) => void;\n renderSelectedTag?: MemberPickerProps['renderSelectedTag'];\n}\n\n/** 用户和用户组共享一个已选区,顺序直接来自不可变选择 Map。 */\nexport default function SelectedMemberPanel({\n members,\n lockedIds = [],\n onRemove,\n renderSelectedTag,\n}: IProps) {\n const t = useDialogLocale();\n return (\n <section\n className=\"tw-flex tw-h-full tw-min-h-0 tw-flex-col tw-overflow-hidden\"\n data-testid=\"selected-member-panel\"\n >\n <div className=\"tw-flex tw-h-10 tw-shrink-0 tw-items-center tw-gap-sp-100 tw-border-b tw-border-solid tw-border-border-default tw-px-sp-150 tw-text-content-primary tw-text-typography-label\">\n {t.MemberPickerDialog.selected}\n <span className=\"tw-text-content-secondary tw-text-typography-caption\">\n {members.length}\n </span>\n </div>\n <div className=\"tw-min-h-0 tw-flex-1 tw-overflow-auto tw-p-sp-150\">\n {!members.length ? (\n <Empty description={t.MemberPickerDialog.emptySelected} />\n ) : (\n <div className=\"tw-flex tw-flex-wrap tw-content-start tw-gap-sp-100\">\n {members.map((member) => (\n <MemberOption\n key={getMemberKey(member)}\n member={member}\n selected\n locked={isMemberLocked(member, lockedIds)}\n onRemove={onRemove}\n renderSelectedTag={renderSelectedTag}\n />\n ))}\n </div>\n )}\n </div>\n </section>\n );\n}\n"],"mappings":";;;;;;;AAoBA,SAAwB,oBAAoB,EAC1C,SACA,YAAY,EAAE,EACd,UACA,qBACS;CACT,MAAM,IAAI,iBAAiB;AAC3B,QACE,qBAAC,WAAD;EACE,WAAU;EACV,eAAY;YAFd,CAIE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,EAAE,mBAAmB,UACtB,oBAAC,QAAD;IAAM,WAAU;cACb,QAAQ;IACJ,CAAA,CACH;MACN,oBAAC,OAAD;GAAK,WAAU;aACZ,CAAC,QAAQ,SACR,oBAAC,OAAD,EAAO,aAAa,EAAE,mBAAmB,eAAiB,CAAA,GAE1D,oBAAC,OAAD;IAAK,WAAU;cACZ,QAAQ,KAAK,WACZ,oBAAC,cAAD;KAEU;KACR,UAAA;KACA,QAAQ,eAAe,QAAQ,UAAU;KAC/B;KACS;KACnB,EANK,aAAa,OAAO,CAMzB,CACF;IACE,CAAA;GAEJ,CAAA,CACE"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IMemberPickerProps as MemberPickerProps } from '../interface';
|
|
2
|
+
import { TMember, TMemberKey } from './types.js';
|
|
3
|
+
interface IProps {
|
|
4
|
+
selectedByKey: ReadonlyMap<TMemberKey, TMember>;
|
|
5
|
+
onToggle: (member: TMember) => void;
|
|
6
|
+
onToggleAll: (members: readonly TMember[], selected: boolean) => void;
|
|
7
|
+
lockedIds?: MemberPickerProps['lockedIds'];
|
|
8
|
+
filterItem?: MemberPickerProps['filterItem'];
|
|
9
|
+
renderItem?: MemberPickerProps['renderItem'];
|
|
10
|
+
}
|
|
11
|
+
/** 用户候选继续使用设计系统既有查询语义,并在前端只接受 USER。 */
|
|
12
|
+
export default function UserCandidatePanel({ selectedByKey, onToggle, onToggleAll, lockedIds, filterItem, renderItem, }: IProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import Button_default from "../../Button/index.js";
|
|
2
|
+
import Checkbox from "../../Checkbox/index.js";
|
|
3
|
+
import InputSearch from "../../InputSearch/index.js";
|
|
4
|
+
import Empty from "../../Empty/index.js";
|
|
5
|
+
import { EUserType } from "../../ConfigProvider/getUserList.js";
|
|
6
|
+
import AldCustomConfigProvider from "../../ConfigProvider/index.js";
|
|
7
|
+
import CustomSpin from "../../Spin/index.js";
|
|
8
|
+
import useDialogLocale from "./useDialogLocale.js";
|
|
9
|
+
import { getMemberKey, isMemberLocked } from "./helper.js";
|
|
10
|
+
import MemberOption from "./MemberOption.js";
|
|
11
|
+
import SearchFeedback from "../components/SearchFeedback.js";
|
|
12
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
//#region src/MemberPicker/dialog/UserCandidatePanel.tsx
|
|
15
|
+
var SEARCH_DEBOUNCE_MS = 300;
|
|
16
|
+
/** 用户候选继续使用设计系统既有查询语义,并在前端只接受 USER。 */
|
|
17
|
+
function UserCandidatePanel({ selectedByKey, onToggle, onToggleAll, lockedIds = [], filterItem, renderItem }) {
|
|
18
|
+
const t = useDialogLocale();
|
|
19
|
+
const { getUsersByKeywords, getUsersByKeywordsWithMeta, getMemberPickerUserPage } = AldCustomConfigProvider.useGetUserList();
|
|
20
|
+
const [keyword, setKeyword] = useState("");
|
|
21
|
+
const [items, setItems] = useState([]);
|
|
22
|
+
const [loading, setLoading] = useState(false);
|
|
23
|
+
const [error, setError] = useState(false);
|
|
24
|
+
const [hasMore, setHasMore] = useState(false);
|
|
25
|
+
const [truncated, setTruncated] = useState(false);
|
|
26
|
+
const [loadingMore, setLoadingMore] = useState(false);
|
|
27
|
+
const pageRef = useRef(1);
|
|
28
|
+
const loadedKeywordRef = useRef();
|
|
29
|
+
const loadingMoreRef = useRef(false);
|
|
30
|
+
const requestVersionRef = useRef(0);
|
|
31
|
+
const selectableItems = items.filter((member) => !isMemberLocked(member, lockedIds));
|
|
32
|
+
const selectedCount = selectableItems.filter((member) => selectedByKey.has(getMemberKey(member))).length;
|
|
33
|
+
const allSelected = selectableItems.length > 0 && selectedCount === selectableItems.length;
|
|
34
|
+
const partiallySelected = selectedCount > 0 && !allSelected;
|
|
35
|
+
const queryUsers = useCallback(async (searchKeyword, pageNumber = 1) => {
|
|
36
|
+
if (pageNumber > 1 && (loadingMoreRef.current || loadedKeywordRef.current !== searchKeyword)) return;
|
|
37
|
+
const version = pageNumber === 1 ? ++requestVersionRef.current : requestVersionRef.current;
|
|
38
|
+
if (pageNumber === 1) {
|
|
39
|
+
setLoading(true);
|
|
40
|
+
setHasMore(false);
|
|
41
|
+
setTruncated(false);
|
|
42
|
+
} else {
|
|
43
|
+
loadingMoreRef.current = true;
|
|
44
|
+
setLoadingMore(true);
|
|
45
|
+
}
|
|
46
|
+
setError(false);
|
|
47
|
+
try {
|
|
48
|
+
if (!getMemberPickerUserPage && !getUsersByKeywordsWithMeta && !getUsersByKeywords) throw new Error(t.MemberPickerDialog.missingUserSource);
|
|
49
|
+
const result = getMemberPickerUserPage ? await getMemberPickerUserPage({
|
|
50
|
+
keyword: searchKeyword,
|
|
51
|
+
pager: {
|
|
52
|
+
pageNumber,
|
|
53
|
+
pageSize: 100
|
|
54
|
+
}
|
|
55
|
+
}) : getUsersByKeywordsWithMeta ? await getUsersByKeywordsWithMeta(searchKeyword) : getUsersByKeywords ? {
|
|
56
|
+
items: await getUsersByKeywords(searchKeyword),
|
|
57
|
+
hasMore: false
|
|
58
|
+
} : {
|
|
59
|
+
items: [],
|
|
60
|
+
hasMore: false
|
|
61
|
+
};
|
|
62
|
+
if (requestVersionRef.current !== version) return;
|
|
63
|
+
const members = ("data" in result ? result.data : result.items).filter((member) => member.type === EUserType.USER && (filterItem ? filterItem(member) : true));
|
|
64
|
+
setItems((current) => pageNumber === 1 ? members : Array.from(new Map([...current, ...members].map((member) => [getMemberKey(member), member])).values()));
|
|
65
|
+
loadedKeywordRef.current = searchKeyword;
|
|
66
|
+
pageRef.current = pageNumber;
|
|
67
|
+
setHasMore(Boolean(getMemberPickerUserPage && result.hasMore));
|
|
68
|
+
setTruncated(Boolean(!getMemberPickerUserPage && result.hasMore));
|
|
69
|
+
} catch {
|
|
70
|
+
if (requestVersionRef.current === version) setError(true);
|
|
71
|
+
} finally {
|
|
72
|
+
if (requestVersionRef.current === version) {
|
|
73
|
+
setLoading(false);
|
|
74
|
+
setLoadingMore(false);
|
|
75
|
+
loadingMoreRef.current = false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}, [
|
|
79
|
+
filterItem,
|
|
80
|
+
getMemberPickerUserPage,
|
|
81
|
+
getUsersByKeywords,
|
|
82
|
+
getUsersByKeywordsWithMeta,
|
|
83
|
+
t.MemberPickerDialog.missingUserSource
|
|
84
|
+
]);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
setLoading(true);
|
|
87
|
+
setHasMore(false);
|
|
88
|
+
loadedKeywordRef.current = void 0;
|
|
89
|
+
loadingMoreRef.current = false;
|
|
90
|
+
setLoadingMore(false);
|
|
91
|
+
const version = requestVersionRef.current + 1;
|
|
92
|
+
requestVersionRef.current = version;
|
|
93
|
+
const timer = window.setTimeout(() => {
|
|
94
|
+
if (requestVersionRef.current === version) queryUsers(keyword.trim());
|
|
95
|
+
}, SEARCH_DEBOUNCE_MS);
|
|
96
|
+
return () => {
|
|
97
|
+
window.clearTimeout(timer);
|
|
98
|
+
requestVersionRef.current += 1;
|
|
99
|
+
};
|
|
100
|
+
}, [keyword, queryUsers]);
|
|
101
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
102
|
+
className: "tw-flex tw-h-full tw-min-h-0 tw-flex-col",
|
|
103
|
+
"data-testid": "user-candidate-panel",
|
|
104
|
+
children: [
|
|
105
|
+
/* @__PURE__ */ jsx("div", {
|
|
106
|
+
className: "tw-shrink-0 tw-border-b tw-border-solid tw-border-border-default tw-p-sp-150",
|
|
107
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
108
|
+
className: "tw-flex tw-min-w-0 tw-flex-col tw-gap-sp-150",
|
|
109
|
+
children: [/* @__PURE__ */ jsx(InputSearch, {
|
|
110
|
+
inputMode: true,
|
|
111
|
+
size: "middle",
|
|
112
|
+
className: "tw-w-full",
|
|
113
|
+
debounce: false,
|
|
114
|
+
value: keyword,
|
|
115
|
+
placeholder: t.MemberPickerDialog.searchUsersPlaceholder,
|
|
116
|
+
onChange: setKeyword,
|
|
117
|
+
onSearch: setKeyword
|
|
118
|
+
}), /* @__PURE__ */ jsx(Checkbox, {
|
|
119
|
+
size: "small",
|
|
120
|
+
className: "tw-self-start",
|
|
121
|
+
checked: allSelected,
|
|
122
|
+
indeterminate: partiallySelected,
|
|
123
|
+
disabled: loading || error || selectableItems.length === 0,
|
|
124
|
+
onChange: (event) => onToggleAll(selectableItems, event.target.checked),
|
|
125
|
+
children: allSelected ? t.MemberPickerDialog.deselectCurrent(items.length) : t.MemberPickerDialog.selectCurrent(items.length)
|
|
126
|
+
})]
|
|
127
|
+
})
|
|
128
|
+
}),
|
|
129
|
+
/* @__PURE__ */ jsx(SearchFeedback, {
|
|
130
|
+
hasMore: truncated,
|
|
131
|
+
retry: () => queryUsers(keyword.trim())
|
|
132
|
+
}),
|
|
133
|
+
/* @__PURE__ */ jsxs("div", {
|
|
134
|
+
className: "tw-min-h-0 tw-flex-1 tw-overflow-auto tw-py-sp-100",
|
|
135
|
+
children: [
|
|
136
|
+
loading ? /* @__PURE__ */ jsx(CustomSpin, {}) : null,
|
|
137
|
+
error ? /* @__PURE__ */ jsxs("div", {
|
|
138
|
+
className: "tw-flex tw-h-full tw-flex-col tw-items-center tw-justify-center tw-gap-2",
|
|
139
|
+
children: [/* @__PURE__ */ jsx("span", { children: t.MemberPickerDialog.loadFailed }), /* @__PURE__ */ jsx(Button_default, {
|
|
140
|
+
type: "link",
|
|
141
|
+
onClick: () => queryUsers(keyword.trim()),
|
|
142
|
+
children: t.MemberPickerDialog.retry
|
|
143
|
+
})]
|
|
144
|
+
}) : null,
|
|
145
|
+
!loading && !error && !items.length ? /* @__PURE__ */ jsx(Empty, { description: t.MemberPickerDialog.emptyCandidates }) : null,
|
|
146
|
+
!loading && !error ? items.map((member) => /* @__PURE__ */ jsx(MemberOption, {
|
|
147
|
+
member,
|
|
148
|
+
selected: selectedByKey.has(getMemberKey(member)),
|
|
149
|
+
locked: isMemberLocked(member, lockedIds),
|
|
150
|
+
onToggle,
|
|
151
|
+
renderItem,
|
|
152
|
+
searchValue: keyword
|
|
153
|
+
}, getMemberKey(member))) : null,
|
|
154
|
+
hasMore && /* @__PURE__ */ jsx(Button_default, {
|
|
155
|
+
type: "link",
|
|
156
|
+
loading: loadingMore,
|
|
157
|
+
onClick: () => queryUsers(keyword.trim(), pageRef.current + 1),
|
|
158
|
+
children: t.MemberPickerDialog.loadMore
|
|
159
|
+
})
|
|
160
|
+
]
|
|
161
|
+
})
|
|
162
|
+
]
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
export { UserCandidatePanel as default };
|
|
167
|
+
|
|
168
|
+
//# sourceMappingURL=UserCandidatePanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserCandidatePanel.js","names":[],"sources":["../../../src/MemberPicker/dialog/UserCandidatePanel.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport Button from '../../Button';\nimport Checkbox from '../../Checkbox';\nimport ConfigProvider from '../../ConfigProvider';\nimport { EUserType } from '../../ConfigProvider/getUserList';\nimport Empty from '../../Empty';\nimport InputSearch from '../../InputSearch';\nimport Spin from '../../Spin';\nimport type { IMemberPickerProps as MemberPickerProps } from '../interface';\nimport SearchFeedback from '../components/SearchFeedback';\nimport useDialogLocale from './useDialogLocale';\nimport { getMemberKey, isMemberLocked } from './helper';\nimport MemberOption from './MemberOption';\nimport type { TMember, TMemberKey } from './types';\n\nconst SEARCH_DEBOUNCE_MS = 300;\n\ninterface IProps {\n selectedByKey: ReadonlyMap<TMemberKey, TMember>;\n onToggle: (member: TMember) => void;\n onToggleAll: (members: readonly TMember[], selected: boolean) => void;\n lockedIds?: MemberPickerProps['lockedIds'];\n filterItem?: MemberPickerProps['filterItem'];\n renderItem?: MemberPickerProps['renderItem'];\n}\n\n/** 用户候选继续使用设计系统既有查询语义,并在前端只接受 USER。 */\nexport default function UserCandidatePanel({\n selectedByKey,\n onToggle,\n onToggleAll,\n lockedIds = [],\n filterItem,\n renderItem,\n}: IProps) {\n const t = useDialogLocale();\n const {\n getUsersByKeywords,\n getUsersByKeywordsWithMeta,\n getMemberPickerUserPage,\n } = ConfigProvider.useGetUserList();\n const [keyword, setKeyword] = useState('');\n const [items, setItems] = useState<TMember[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(false);\n const [hasMore, setHasMore] = useState(false);\n const [truncated, setTruncated] = useState(false);\n const [loadingMore, setLoadingMore] = useState(false);\n const pageRef = useRef(1);\n const loadedKeywordRef = useRef<string>();\n const loadingMoreRef = useRef(false);\n const requestVersionRef = useRef(0);\n const selectableItems = items.filter(\n (member) => !isMemberLocked(member, lockedIds),\n );\n const selectedCount = selectableItems.filter((member) =>\n selectedByKey.has(getMemberKey(member)),\n ).length;\n const allSelected =\n selectableItems.length > 0 && selectedCount === selectableItems.length;\n const partiallySelected = selectedCount > 0 && !allSelected;\n\n const queryUsers = useCallback(\n async (searchKeyword: string, pageNumber = 1) => {\n // 只能追加已成功加载首屏的关键词,避免换词防抖期间使用旧页码。\n if (\n pageNumber > 1 &&\n (loadingMoreRef.current || loadedKeywordRef.current !== searchKeyword)\n )\n return;\n const version =\n pageNumber === 1\n ? ++requestVersionRef.current\n : requestVersionRef.current;\n if (pageNumber === 1) {\n setLoading(true);\n setHasMore(false);\n setTruncated(false);\n } else {\n loadingMoreRef.current = true;\n setLoadingMore(true);\n }\n setError(false);\n try {\n if (\n !getMemberPickerUserPage &&\n !getUsersByKeywordsWithMeta &&\n !getUsersByKeywords\n )\n throw new Error(t.MemberPickerDialog.missingUserSource);\n const result = getMemberPickerUserPage\n ? await getMemberPickerUserPage({\n keyword: searchKeyword,\n pager: { pageNumber, pageSize: 100 },\n })\n : getUsersByKeywordsWithMeta\n ? await getUsersByKeywordsWithMeta(searchKeyword)\n : getUsersByKeywords\n ? { items: await getUsersByKeywords(searchKeyword), hasMore: false }\n : { items: [], hasMore: false };\n if (requestVersionRef.current !== version) return;\n const members = ('data' in result ? result.data : result.items).filter(\n (member) =>\n member.type === EUserType.USER &&\n (filterItem ? filterItem(member) : true),\n );\n setItems((current) =>\n pageNumber === 1\n ? members\n : Array.from(\n new Map(\n [...current, ...members].map((member) => [\n getMemberKey(member),\n member,\n ]),\n ).values(),\n ),\n );\n loadedKeywordRef.current = searchKeyword;\n pageRef.current = pageNumber;\n setHasMore(Boolean(getMemberPickerUserPage && result.hasMore));\n setTruncated(Boolean(!getMemberPickerUserPage && result.hasMore));\n } catch {\n if (requestVersionRef.current === version) setError(true);\n } finally {\n if (requestVersionRef.current === version) {\n setLoading(false);\n setLoadingMore(false);\n loadingMoreRef.current = false;\n }\n }\n },\n [\n filterItem,\n getMemberPickerUserPage,\n getUsersByKeywords,\n getUsersByKeywordsWithMeta,\n t.MemberPickerDialog.missingUserSource,\n ],\n );\n\n useEffect(() => {\n // 输入改变立即隐藏旧候选和分页入口,防抖只延迟请求,不延迟失效旧结果。\n setLoading(true);\n setHasMore(false);\n loadedKeywordRef.current = undefined;\n loadingMoreRef.current = false;\n setLoadingMore(false);\n const version = requestVersionRef.current + 1;\n requestVersionRef.current = version;\n const timer = window.setTimeout(() => {\n if (requestVersionRef.current === version) {\n queryUsers(keyword.trim());\n }\n }, SEARCH_DEBOUNCE_MS);\n return () => {\n window.clearTimeout(timer);\n requestVersionRef.current += 1;\n };\n }, [keyword, queryUsers]);\n\n return (\n <section\n className=\"tw-flex tw-h-full tw-min-h-0 tw-flex-col\"\n data-testid=\"user-candidate-panel\"\n >\n <div className=\"tw-shrink-0 tw-border-b tw-border-solid tw-border-border-default tw-p-sp-150\">\n <div className=\"tw-flex tw-min-w-0 tw-flex-col tw-gap-sp-150\">\n <InputSearch\n inputMode\n size=\"middle\"\n className=\"tw-w-full\"\n debounce={false}\n value={keyword}\n placeholder={t.MemberPickerDialog.searchUsersPlaceholder}\n onChange={setKeyword}\n onSearch={setKeyword}\n />\n <Checkbox\n size=\"small\"\n className=\"tw-self-start\"\n checked={allSelected}\n indeterminate={partiallySelected}\n disabled={loading || error || selectableItems.length === 0}\n onChange={(event) =>\n onToggleAll(selectableItems, event.target.checked)\n }\n >\n {allSelected\n ? t.MemberPickerDialog.deselectCurrent(items.length)\n : t.MemberPickerDialog.selectCurrent(items.length)}\n </Checkbox>\n </div>\n </div>\n <SearchFeedback\n hasMore={truncated}\n retry={() => queryUsers(keyword.trim())}\n />\n <div className=\"tw-min-h-0 tw-flex-1 tw-overflow-auto tw-py-sp-100\">\n {loading ? <Spin /> : null}\n {error ? (\n <div className=\"tw-flex tw-h-full tw-flex-col tw-items-center tw-justify-center tw-gap-2\">\n <span>{t.MemberPickerDialog.loadFailed}</span>\n <Button type=\"link\" onClick={() => queryUsers(keyword.trim())}>\n {t.MemberPickerDialog.retry}\n </Button>\n </div>\n ) : null}\n {!loading && !error && !items.length ? (\n <Empty description={t.MemberPickerDialog.emptyCandidates} />\n ) : null}\n {!loading && !error\n ? items.map((member) => (\n <MemberOption\n key={getMemberKey(member)}\n member={member}\n selected={selectedByKey.has(getMemberKey(member))}\n locked={isMemberLocked(member, lockedIds)}\n onToggle={onToggle}\n renderItem={renderItem}\n searchValue={keyword}\n />\n ))\n : null}\n {hasMore && (\n <Button\n type=\"link\"\n loading={loadingMore}\n onClick={() => queryUsers(keyword.trim(), pageRef.current + 1)}\n >\n {t.MemberPickerDialog.loadMore}\n </Button>\n )}\n </div>\n </section>\n );\n}\n"],"mappings":"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"}
|