@aloudata/aloudata-design 3.1.7 → 3.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Dropdown/index.js +22 -1
- package/dist/Dropdown/index.js.map +1 -1
- package/dist/MemberPicker/components/Panel.d.ts +2 -1
- package/dist/MemberPicker/components/Panel.js +4 -1
- package/dist/MemberPicker/components/Panel.js.map +1 -1
- package/dist/MemberPicker/components/PanelWrapper.d.ts +3 -2
- package/dist/MemberPicker/components/PanelWrapper.js +2 -1
- package/dist/MemberPicker/components/PanelWrapper.js.map +1 -1
- package/dist/MemberPicker/components/UserGroupSelection.d.ts +3 -1
- package/dist/MemberPicker/components/UserGroupSelection.js +2 -2
- package/dist/MemberPicker/components/UserGroupSelection.js.map +1 -1
- package/dist/MemberPicker/dialog/DialogContent.d.ts +1 -1
- package/dist/MemberPicker/dialog/DialogContent.js +5 -2
- package/dist/MemberPicker/dialog/DialogContent.js.map +1 -1
- package/dist/MemberPicker/dialog/MemberPickerDialog.d.ts +1 -1
- package/dist/MemberPicker/dialog/MemberPickerDialog.js.map +1 -1
- package/dist/MemberPicker/dialog/UserGroupCandidatePanel.d.ts +2 -1
- package/dist/MemberPicker/dialog/UserGroupCandidatePanel.js +2 -2
- package/dist/MemberPicker/dialog/UserGroupCandidatePanel.js.map +1 -1
- package/dist/MemberPicker/index.js +3 -1
- package/dist/MemberPicker/index.js.map +1 -1
- package/dist/MemberPicker/interface.d.ts +6 -0
- package/dist/MemberPicker/interface.js.map +1 -1
- package/dist/Menu/index.d.ts +4 -0
- package/dist/Menu/index.js +19 -8
- package/dist/Menu/index.js.map +1 -1
- package/dist/aloudata-design.css +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TUser } from '../../ConfigProvider/getUserList';
|
|
2
2
|
import { IBaseMemberSelectorProps } from '../interface';
|
|
3
3
|
/** 独立弹窗公开契约,数据从最近的 ConfigProvider 读取。 */
|
|
4
|
-
export interface MemberPickerDialogProps extends Pick<IBaseMemberSelectorProps, 'value' | 'type' | 'lockedIds' | 'filterItem' | 'renderItem' | 'renderSelectedTag' | 'footer' | 'onCancel'> {
|
|
4
|
+
export interface MemberPickerDialogProps extends Pick<IBaseMemberSelectorProps, 'value' | 'type' | 'lockedIds' | 'filterItem' | 'canEnterGroup' | 'renderItem' | 'renderSelectedTag' | 'footer' | 'onCancel'> {
|
|
5
5
|
open: boolean;
|
|
6
6
|
onClose: () => void;
|
|
7
7
|
onChange?: (members: TUser) => void;
|
|
@@ -1 +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":";;;;
|
|
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 | 'canEnterGroup'\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":";;;;AAyBA,SAAwB,mBAAmB,OAAgC;AACzE,QAAO,MAAM,OAAO,oBAAC,eAAD,EAAe,GAAI,OAAS,CAAA,GAAG"}
|
|
@@ -6,8 +6,9 @@ interface IProps {
|
|
|
6
6
|
onToggleAll: (members: readonly TMember[], selected: boolean) => void;
|
|
7
7
|
lockedIds?: MemberPickerProps['lockedIds'];
|
|
8
8
|
filterItem?: MemberPickerProps['filterItem'];
|
|
9
|
+
canEnterGroup?: MemberPickerProps['canEnterGroup'];
|
|
9
10
|
renderItem?: MemberPickerProps['renderItem'];
|
|
10
11
|
}
|
|
11
12
|
/** 用户组树与跨层搜索共用选择事实源,但互不重置彼此的浏览状态。 */
|
|
12
|
-
export default function UserGroupCandidatePanel({ selectedByKey, onToggle, onToggleAll, lockedIds, filterItem, renderItem, }: IProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export default function UserGroupCandidatePanel({ selectedByKey, onToggle, onToggleAll, lockedIds, filterItem, canEnterGroup, renderItem, }: IProps): import("react/jsx-runtime").JSX.Element;
|
|
13
14
|
export {};
|
|
@@ -15,7 +15,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
15
15
|
var LOAD_MORE_THRESHOLD = 32;
|
|
16
16
|
var ROOT_BREADCRUMB_INDEX = -1;
|
|
17
17
|
/** 用户组树与跨层搜索共用选择事实源,但互不重置彼此的浏览状态。 */
|
|
18
|
-
function UserGroupCandidatePanel({ selectedByKey, onToggle, onToggleAll, lockedIds = [], filterItem, renderItem }) {
|
|
18
|
+
function UserGroupCandidatePanel({ selectedByKey, onToggle, onToggleAll, lockedIds = [], filterItem, canEnterGroup, renderItem }) {
|
|
19
19
|
const t = useDialogLocale();
|
|
20
20
|
const candidates = useUserGroupCandidates();
|
|
21
21
|
const listRef = useRef(null);
|
|
@@ -103,7 +103,7 @@ function UserGroupCandidatePanel({ selectedByKey, onToggle, onToggleAll, lockedI
|
|
|
103
103
|
selected: selectedByKey.has(getMemberKey(group)),
|
|
104
104
|
locked: isMemberLocked(group, lockedIds),
|
|
105
105
|
onToggle,
|
|
106
|
-
onOpenGroup: isSearching ? void 0 : () => {
|
|
106
|
+
onOpenGroup: isSearching || canEnterGroup?.(group) === false ? void 0 : () => {
|
|
107
107
|
const sourceGroup = candidates.items.find((item) => item.groupId === group.groupId);
|
|
108
108
|
if (sourceGroup) candidates.enterGroup(sourceGroup, listRef.current?.scrollTop ?? 0);
|
|
109
109
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserGroupCandidatePanel.js","names":[],"sources":["../../../src/MemberPicker/dialog/UserGroupCandidatePanel.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef } from 'react';\nimport Breadcrumb from '../../Breadcrumb';\nimport Button from '../../Button';\nimport Checkbox from '../../Checkbox';\nimport Empty from '../../Empty';\nimport InputSearch from '../../InputSearch';\nimport Spin from '../../Spin';\nimport type { IMemberPickerProps as MemberPickerProps } from '../interface';\nimport useDialogLocale from './useDialogLocale';\nimport { getMemberKey, isMemberLocked } from './helper';\nimport SearchFeedback from '../components/SearchFeedback';\nimport MemberOption from './MemberOption';\nimport type { TMember, TMemberKey } from './types';\nimport { useUserGroupCandidates } from './useUserGroupCandidates';\n\nconst LOAD_MORE_THRESHOLD = 32;\nconst ROOT_BREADCRUMB_INDEX = -1;\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/** 用户组树与跨层搜索共用选择事实源,但互不重置彼此的浏览状态。 */\nexport default function UserGroupCandidatePanel({\n selectedByKey,\n onToggle,\n onToggleAll,\n lockedIds = [],\n filterItem,\n renderItem,\n}: IProps) {\n const t = useDialogLocale();\n const candidates = useUserGroupCandidates();\n const listRef = useRef<HTMLDivElement>(null);\n const isSearching = candidates.keyword.trim().length > 0;\n const groups = useMemo(\n () =>\n candidates.items.filter((group) =>\n filterItem ? filterItem(group) : true,\n ),\n [candidates.items, filterItem],\n );\n const selectableGroups = groups.filter(\n (group) => !isMemberLocked(group, lockedIds),\n );\n const selectedCount = selectableGroups.filter((group) =>\n selectedByKey.has(getMemberKey(group)),\n ).length;\n const allSelected =\n selectableGroups.length > 0 && selectedCount === selectableGroups.length;\n const partiallySelected = selectedCount > 0 && !allSelected;\n\n useEffect(() => {\n if (!isSearching && listRef.current) {\n listRef.current.scrollTop = candidates.scrollTop;\n }\n }, [candidates.breadcrumbPath, candidates.scrollTop, isSearching]);\n\n return (\n <section\n className=\"tw-flex tw-h-full tw-min-h-0 tw-flex-col\"\n data-testid=\"user-group-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 {!isSearching && candidates.breadcrumbPath.length > 0 ? (\n <Breadcrumb\n className=\"tw-min-w-0 tw-flex-wrap\"\n items={[\n {\n title: t.MemberPickerDialog.root,\n onClick: () =>\n candidates.returnToBreadcrumb(\n ROOT_BREADCRUMB_INDEX,\n listRef.current?.scrollTop ?? 0,\n ),\n },\n ...candidates.breadcrumbPath.map((group, index) => ({\n title: group.name,\n onClick: () =>\n candidates.returnToBreadcrumb(\n index,\n listRef.current?.scrollTop ?? 0,\n ),\n })),\n ]}\n />\n ) : null}\n <InputSearch\n inputMode\n size=\"middle\"\n className=\"tw-w-full\"\n debounce={false}\n value={candidates.keyword}\n placeholder={t.MemberPickerDialog.searchGroupsPlaceholder}\n onChange={candidates.setKeyword}\n onSearch={candidates.setKeyword}\n />\n <Checkbox\n size=\"small\"\n className=\"tw-self-start\"\n checked={allSelected}\n indeterminate={partiallySelected}\n disabled={\n candidates.loading ||\n candidates.error ||\n selectableGroups.length === 0\n }\n onChange={(event) =>\n onToggleAll(selectableGroups, event.target.checked)\n }\n >\n {allSelected\n ? t.MemberPickerDialog.deselectCurrent(groups.length)\n : t.MemberPickerDialog.selectCurrent(groups.length)}\n </Checkbox>\n </div>\n </div>\n <SearchFeedback\n pathWarningCode={candidates.pathWarningCode}\n hasMore={candidates.truncated}\n retry={candidates.retry}\n />\n <div\n ref={listRef}\n className=\"tw-min-h-0 tw-flex-1 tw-overflow-auto tw-py-sp-100\"\n onScroll={(event) => {\n const target = event.currentTarget;\n if (!isSearching) {\n candidates.updateScrollTop(target.scrollTop);\n }\n if (\n target.scrollHeight - target.scrollTop - target.clientHeight <=\n LOAD_MORE_THRESHOLD\n ) {\n candidates.loadMore();\n }\n }}\n >\n {candidates.loading ? <Spin /> : null}\n {candidates.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={candidates.retry}>\n {t.MemberPickerDialog.retry}\n </Button>\n </div>\n ) : null}\n {!candidates.loading && !candidates.error && !groups.length ? (\n <Empty description={t.MemberPickerDialog.emptyCandidates} />\n ) : null}\n {!candidates.loading && !candidates.error\n ? groups.map((group) => (\n <MemberOption\n key={group.groupId}\n member={group}\n searchValue={candidates.keyword}\n selected={selectedByKey.has(getMemberKey(group))}\n locked={isMemberLocked(group, lockedIds)}\n onToggle={onToggle}\n onOpenGroup={\n isSearching\n ? undefined\n : () => {\n const sourceGroup = candidates.items.find(\n (item) => item.groupId === group.groupId,\n );\n if (sourceGroup) {\n candidates.enterGroup(\n sourceGroup,\n listRef.current?.scrollTop ?? 0,\n );\n }\n }\n }\n renderItem={renderItem}\n />\n ))\n : null}\n {!candidates.loading &&\n !candidates.error &&\n candidates.hasMore &&\n !candidates.loadMoreError ? (\n <Button\n type=\"link\"\n loading={candidates.loadingMore}\n onClick={candidates.loadMore}\n >\n {t.MemberPickerDialog.loadMore}\n </Button>\n ) : null}\n {candidates.loadMoreError ? (\n <div className=\"tw-flex tw-items-center tw-justify-center tw-gap-2 tw-py-2\">\n <span>{t.MemberPickerDialog.loadMoreFailed}</span>\n <Button type=\"link\" onClick={candidates.retryLoadMore}>\n {t.MemberPickerDialog.retry}\n </Button>\n </div>\n ) : null}\n </div>\n </section>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UserGroupCandidatePanel.js","names":[],"sources":["../../../src/MemberPicker/dialog/UserGroupCandidatePanel.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef } from 'react';\nimport Breadcrumb from '../../Breadcrumb';\nimport Button from '../../Button';\nimport Checkbox from '../../Checkbox';\nimport Empty from '../../Empty';\nimport InputSearch from '../../InputSearch';\nimport Spin from '../../Spin';\nimport type { IMemberPickerProps as MemberPickerProps } from '../interface';\nimport useDialogLocale from './useDialogLocale';\nimport { getMemberKey, isMemberLocked } from './helper';\nimport SearchFeedback from '../components/SearchFeedback';\nimport MemberOption from './MemberOption';\nimport type { TMember, TMemberKey } from './types';\nimport { useUserGroupCandidates } from './useUserGroupCandidates';\n\nconst LOAD_MORE_THRESHOLD = 32;\nconst ROOT_BREADCRUMB_INDEX = -1;\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 canEnterGroup?: MemberPickerProps['canEnterGroup'];\n renderItem?: MemberPickerProps['renderItem'];\n}\n\n/** 用户组树与跨层搜索共用选择事实源,但互不重置彼此的浏览状态。 */\nexport default function UserGroupCandidatePanel({\n selectedByKey,\n onToggle,\n onToggleAll,\n lockedIds = [],\n filterItem,\n canEnterGroup,\n renderItem,\n}: IProps) {\n const t = useDialogLocale();\n const candidates = useUserGroupCandidates();\n const listRef = useRef<HTMLDivElement>(null);\n const isSearching = candidates.keyword.trim().length > 0;\n const groups = useMemo(\n () =>\n candidates.items.filter((group) =>\n filterItem ? filterItem(group) : true,\n ),\n [candidates.items, filterItem],\n );\n const selectableGroups = groups.filter(\n (group) => !isMemberLocked(group, lockedIds),\n );\n const selectedCount = selectableGroups.filter((group) =>\n selectedByKey.has(getMemberKey(group)),\n ).length;\n const allSelected =\n selectableGroups.length > 0 && selectedCount === selectableGroups.length;\n const partiallySelected = selectedCount > 0 && !allSelected;\n\n useEffect(() => {\n if (!isSearching && listRef.current) {\n listRef.current.scrollTop = candidates.scrollTop;\n }\n }, [candidates.breadcrumbPath, candidates.scrollTop, isSearching]);\n\n return (\n <section\n className=\"tw-flex tw-h-full tw-min-h-0 tw-flex-col\"\n data-testid=\"user-group-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 {!isSearching && candidates.breadcrumbPath.length > 0 ? (\n <Breadcrumb\n className=\"tw-min-w-0 tw-flex-wrap\"\n items={[\n {\n title: t.MemberPickerDialog.root,\n onClick: () =>\n candidates.returnToBreadcrumb(\n ROOT_BREADCRUMB_INDEX,\n listRef.current?.scrollTop ?? 0,\n ),\n },\n ...candidates.breadcrumbPath.map((group, index) => ({\n title: group.name,\n onClick: () =>\n candidates.returnToBreadcrumb(\n index,\n listRef.current?.scrollTop ?? 0,\n ),\n })),\n ]}\n />\n ) : null}\n <InputSearch\n inputMode\n size=\"middle\"\n className=\"tw-w-full\"\n debounce={false}\n value={candidates.keyword}\n placeholder={t.MemberPickerDialog.searchGroupsPlaceholder}\n onChange={candidates.setKeyword}\n onSearch={candidates.setKeyword}\n />\n <Checkbox\n size=\"small\"\n className=\"tw-self-start\"\n checked={allSelected}\n indeterminate={partiallySelected}\n disabled={\n candidates.loading ||\n candidates.error ||\n selectableGroups.length === 0\n }\n onChange={(event) =>\n onToggleAll(selectableGroups, event.target.checked)\n }\n >\n {allSelected\n ? t.MemberPickerDialog.deselectCurrent(groups.length)\n : t.MemberPickerDialog.selectCurrent(groups.length)}\n </Checkbox>\n </div>\n </div>\n <SearchFeedback\n pathWarningCode={candidates.pathWarningCode}\n hasMore={candidates.truncated}\n retry={candidates.retry}\n />\n <div\n ref={listRef}\n className=\"tw-min-h-0 tw-flex-1 tw-overflow-auto tw-py-sp-100\"\n onScroll={(event) => {\n const target = event.currentTarget;\n if (!isSearching) {\n candidates.updateScrollTop(target.scrollTop);\n }\n if (\n target.scrollHeight - target.scrollTop - target.clientHeight <=\n LOAD_MORE_THRESHOLD\n ) {\n candidates.loadMore();\n }\n }}\n >\n {candidates.loading ? <Spin /> : null}\n {candidates.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={candidates.retry}>\n {t.MemberPickerDialog.retry}\n </Button>\n </div>\n ) : null}\n {!candidates.loading && !candidates.error && !groups.length ? (\n <Empty description={t.MemberPickerDialog.emptyCandidates} />\n ) : null}\n {!candidates.loading && !candidates.error\n ? groups.map((group) => (\n <MemberOption\n key={group.groupId}\n member={group}\n searchValue={candidates.keyword}\n selected={selectedByKey.has(getMemberKey(group))}\n locked={isMemberLocked(group, lockedIds)}\n onToggle={onToggle}\n onOpenGroup={\n isSearching || canEnterGroup?.(group) === false\n ? undefined\n : () => {\n const sourceGroup = candidates.items.find(\n (item) => item.groupId === group.groupId,\n );\n if (sourceGroup) {\n candidates.enterGroup(\n sourceGroup,\n listRef.current?.scrollTop ?? 0,\n );\n }\n }\n }\n renderItem={renderItem}\n />\n ))\n : null}\n {!candidates.loading &&\n !candidates.error &&\n candidates.hasMore &&\n !candidates.loadMoreError ? (\n <Button\n type=\"link\"\n loading={candidates.loadingMore}\n onClick={candidates.loadMore}\n >\n {t.MemberPickerDialog.loadMore}\n </Button>\n ) : null}\n {candidates.loadMoreError ? (\n <div className=\"tw-flex tw-items-center tw-justify-center tw-gap-2 tw-py-2\">\n <span>{t.MemberPickerDialog.loadMoreFailed}</span>\n <Button type=\"link\" onClick={candidates.retryLoadMore}>\n {t.MemberPickerDialog.retry}\n </Button>\n </div>\n ) : null}\n </div>\n </section>\n );\n}\n"],"mappings":"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"}
|
|
@@ -23,7 +23,7 @@ var DEFAULT_MULTIPLE_DROPDOWN_WIDTH = 480;
|
|
|
23
23
|
var Component = (props) => {
|
|
24
24
|
const { locale } = useContext(LocaleContext);
|
|
25
25
|
const t = getTranslator(locale);
|
|
26
|
-
const { value, type = EMemberPicker.USER, onChange, multiple, dropdownWidth, open, onOpenChange, placeholder = t.MemberPicker.select, className, allowClear, disabled, onClear, size, prefix: propPrefix, lockedIds = [], footer, onCancel, filterItem, mode: propsMode, renderItem, renderSelectedTag, "data-testid": dataTestId, "aria-label": ariaLabel, ...restProps } = props;
|
|
26
|
+
const { value, type = EMemberPicker.USER, onChange, multiple, dropdownWidth, open, onOpenChange, placeholder = t.MemberPicker.select, className, allowClear, disabled, onClear, size, prefix: propPrefix, lockedIds = [], footer, onCancel, filterItem, mode: propsMode, canEnterGroup, renderItem, renderSelectedTag, "data-testid": dataTestId, "aria-label": ariaLabel, ...restProps } = props;
|
|
27
27
|
const allowOverlap = props.allowOverlap ?? false;
|
|
28
28
|
const { getUsersByIds, getUsersByKeywords, getUsersByKeywordsWithMeta, getNextLevelGroup, getNextLevelGroupWithMeta } = AldCustomConfigProvider.useGetUserList();
|
|
29
29
|
const inheritedDisabled = useContext(DisabledContext);
|
|
@@ -304,6 +304,7 @@ var Component = (props) => {
|
|
|
304
304
|
onCancel?.();
|
|
305
305
|
setDropdownOpen(false);
|
|
306
306
|
},
|
|
307
|
+
canEnterGroup,
|
|
307
308
|
renderItem,
|
|
308
309
|
renderSelectedTag,
|
|
309
310
|
searchValue: searchString
|
|
@@ -316,6 +317,7 @@ var Component = (props) => {
|
|
|
316
317
|
type,
|
|
317
318
|
lockedIds,
|
|
318
319
|
filterItem,
|
|
320
|
+
canEnterGroup,
|
|
319
321
|
renderItem,
|
|
320
322
|
renderSelectedTag,
|
|
321
323
|
onChange: (members) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/MemberPicker/index.tsx"],"sourcesContent":["import MemberPickerDialog from './dialog/MemberPickerDialog';\nimport DisabledContext from '../ConfigProvider/DisabledContext';\nimport useMemberSearch from './useMemberSearch';\nimport SearchFeedback from './components/SearchFeedback';\nimport { usePrevious } from 'ahooks';\nimport { cn } from '../lib/utils';\nimport _ from 'lodash';\nimport React, {\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport Avatar from '../Avatar';\nimport ConfigProvider from '../ConfigProvider';\nimport './member-picker.css';\nimport {\n EUserType,\n IUser,\n IUserGroup,\n TUser,\n} from '../ConfigProvider/getUserList';\nimport { LocaleContext, getTranslator } from '../locale/default';\nimport Panel from './components/Panel';\nimport MemberPanel from './components/PanelWrapper';\nimport {\n EMemberPicker,\n IMemberPickerProps,\n TUserGroupValue,\n TUserValue,\n} from './interface';\nimport { getId } from './utils';\nimport { isUser, isUserGroup } from './utils/getUsersWithUserId';\nimport Select from '../Select';\nimport { Mode } from '../Select/BaseSelect';\n\nconst DEFAULT_MULTIPLE_DROPDOWN_WIDTH = 480;\n\nconst Component: React.FC<IMemberPickerProps> = (props) => {\n const { locale } = useContext(LocaleContext);\n const t = getTranslator(locale);\n\n const {\n value,\n type = EMemberPicker.USER,\n onChange,\n multiple,\n dropdownWidth,\n open,\n onOpenChange,\n placeholder = t.MemberPicker.select,\n className,\n allowClear,\n disabled,\n onClear,\n size,\n prefix: propPrefix,\n lockedIds = [],\n footer,\n onCancel,\n filterItem,\n mode: propsMode,\n renderItem,\n renderSelectedTag,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n ...restProps\n } = props;\n const allowOverlap = props.allowOverlap ?? false;\n const {\n getUsersByIds,\n getUsersByKeywords,\n getUsersByKeywordsWithMeta,\n getNextLevelGroup,\n getNextLevelGroupWithMeta,\n } = ConfigProvider.useGetUserList();\n const inheritedDisabled = useContext(DisabledContext);\n const isDisabled = disabled ?? inheritedDisabled;\n // 呈现方式由选择模式唯一决定,多选使用弹窗,单选保持浮窗。\n const usesDialog = !!multiple;\n const usesGroupQuery =\n (type === EMemberPicker.GROUP ||\n (!getUsersByKeywords && !getUsersByKeywordsWithMeta)) &&\n !!(getNextLevelGroup || getNextLevelGroupWithMeta);\n const [selectedUserList, setSelectedUserList] = useState<TUser>([]);\n const prevValue = usePrevious(value);\n const [searchString, setSearchString] = useState<string>('');\n const [dropdownContentWidth, setDropdownContentWidth] =\n useState(dropdownWidth);\n const selectOwnerRef = useRef<HTMLDivElement>(null);\n // 单选默认浮窗跟触发器等宽(true),调用方传 dropdownWidth 时用具体数字;\n // 多选默认 480,单条记录只有 头像+nickname 时触发器太窄装不下选中标签,故固定值。\n const panelWidth: number | boolean =\n dropdownWidth ?? (multiple ? DEFAULT_MULTIPLE_DROPDOWN_WIDTH : true);\n\n const triggerRef = useRef<HTMLDivElement>(null);\n const onSearchValueChange = useCallback((v: string) => {\n setSearchString(v);\n }, []);\n const [dropdownOpen, setDropdownOpen] = useState<boolean>(!!open);\n const queryByKeywords = useMemo(() => {\n if (getUsersByKeywordsWithMeta) return getUsersByKeywordsWithMeta;\n if (getUsersByKeywords) {\n return getUsersByKeywords;\n }\n\n return (_params = {}) =>\n Promise.reject({\n message: t.MemberPicker.pleaseConfigGetUserByKeywordsMethod,\n });\n }, [\n getUsersByKeywords,\n getUsersByKeywordsWithMeta,\n t.MemberPicker.pleaseConfigGetUserByKeywordsMethod,\n ]);\n const queryByIds = useMemo(() => {\n if (getUsersByIds) {\n return getUsersByIds;\n }\n\n return (_params = {}) =>\n Promise.reject({\n message: t.MemberPicker.pleaseConfigGetUserByIdsMethod,\n });\n }, [getUsersByIds, t.MemberPicker.pleaseConfigGetUserByIdsMethod]);\n const {\n result: searchResult,\n loading,\n failed,\n retry,\n } = useMemberSearch<IUser | IUserGroup>(\n queryByKeywords,\n searchString,\n dropdownOpen && !usesGroupQuery && !usesDialog,\n );\n const searchUserList = searchResult.items;\n\n const totalUserList: TUser = useMemo(() => {\n const totalUsers = searchUserList.concat(\n selectedUserList.filter((item) => {\n const name =\n (item.type === EUserType.USER ? item.nickname : item.name) || '';\n\n return name.toUpperCase().includes(searchString.trim().toUpperCase());\n }),\n );\n\n return _.uniqBy(totalUsers, (data: IUser | IUserGroup) => {\n if (data.type === EUserType.USER) return data.userId;\n\n return data.groupId;\n }).filter((item) => {\n if (filterItem) {\n return filterItem(item);\n }\n\n return true;\n });\n }, [filterItem, searchString, searchUserList, selectedUserList]);\n\n useEffect(() => {\n if (_.isEqual(value, prevValue)) {\n return;\n }\n if (Array.isArray(value) && (isUser(value[0]) || isUserGroup(value[0]))) {\n setSelectedUserList(value as TUser);\n } else if (isUser(value) || isUserGroup(value)) {\n setSelectedUserList([value]);\n } else {\n let param: Array<TUserValue | TUserGroupValue> = [];\n if (!value) return;\n if (Array.isArray(value) && value.length === 0) {\n setSelectedUserList([]);\n\n return;\n }\n if (!Array.isArray(value)) {\n param = [value];\n } else {\n param = value;\n }\n queryByIds(param).then((result) => {\n // 如果用户已被删除,则需要展示出id\n const valueNotInResult: TUser = param\n .filter((item) => {\n if (item.type === EUserType.USER) {\n return result\n .filter(isUser)\n .every((user) => user.userId !== item.userId);\n }\n\n return result\n .filter(isUserGroup)\n .every((user) => user.groupId !== item.groupId);\n })\n .map((item) => {\n if (item.type === EUserType.USER) {\n return {\n type: EUserType.USER,\n userId: item.userId,\n } as IUser;\n }\n\n return {\n type: EUserType.USER_GROUP,\n groupId: item.groupId,\n } as IUserGroup;\n });\n\n setSelectedUserList([...result, ...valueNotInResult]);\n });\n }\n }, [value, queryByIds, prevValue]);\n\n const onSelectedChange = useCallback(\n (users: TUser) => {\n if (multiple) {\n setSelectedUserList(users);\n onChange?.(users);\n } else {\n setSelectedUserList(users);\n onChange?.(users[0]);\n }\n setDropdownOpen(false);\n },\n [multiple, onChange, setDropdownOpen],\n );\n\n useEffect(() => {\n if (typeof open === 'boolean') {\n setDropdownOpen(open);\n }\n }, [open]);\n\n useEffect(() => {\n if (!dropdownContentWidth) {\n const resizeEle = triggerRef.current;\n if (!resizeEle) return;\n const resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width } = entry.contentRect;\n setDropdownContentWidth(width);\n }\n });\n resizeObserver.observe(resizeEle);\n\n return () => {\n resizeObserver.unobserve(resizeEle);\n };\n }\n }, [triggerRef, dropdownContentWidth]);\n const onDropdownOpenChange = useCallback(\n (open: boolean) => {\n if (isDisabled) return;\n if (!usesDialog || typeof props.open !== 'boolean') setDropdownOpen(open);\n onOpenChange?.(open);\n },\n [isDisabled, usesDialog, props.open, onOpenChange],\n );\n const mode = useMemo(() => {\n if (multiple === true) {\n if (typeof propsMode === 'object')\n return {\n ...(propsMode || {}),\n type: 'multiple',\n responsive: _.isNil(propsMode.responsive)\n ? true\n : propsMode.responsive,\n } as Mode;\n return {\n type: 'multiple',\n responsive: true,\n } as Mode;\n }\n\n return multiple ? (multiple as Mode) : propsMode;\n }, [multiple, propsMode]);\n\n const selectOpen = typeof open === 'boolean' ? open : dropdownOpen;\n const listboxId = `ald-member-picker-listbox-${React.useId().replace(\n /:/g,\n '',\n )}`;\n const activeMember = totalUserList[0];\n const activeMemberId = activeMember\n ? `ald-member-picker-option-${getId(activeMember)}`\n : undefined;\n\n useEffect(() => {\n const owner = selectOwnerRef.current?.querySelector<HTMLElement>(\n '[role=\"combobox\"], input',\n );\n if (!owner) return;\n owner.setAttribute('aria-controls', listboxId);\n if (selectOpen && activeMemberId) {\n owner.setAttribute('aria-activedescendant', activeMemberId);\n } else {\n owner.removeAttribute('aria-activedescendant');\n }\n }, [activeMemberId, listboxId, selectOpen]);\n\n return (\n <div ref={selectOwnerRef} style={{ display: 'contents' }}>\n <Select\n prefix={propPrefix}\n {...restProps}\n data-testid={dataTestId}\n aria-label={ariaLabel}\n showSearch={!usesDialog}\n searchValue={searchString}\n onSearch={onSearchValueChange}\n aria-controls={listboxId}\n aria-activedescendant={selectOpen ? activeMemberId : undefined}\n size={size}\n className={cn('ald-member-picker-select', className)}\n value={selectedUserList.map((item) => {\n if (item.type === EUserType.USER_GROUP) {\n return item.groupId;\n } else {\n return item.userId;\n }\n })}\n onChange={(value) => {\n if (_.isEmpty(value)) {\n setSelectedUserList([]);\n if (multiple) {\n onChange?.([]);\n } else {\n onChange?.();\n }\n } else {\n const valueList = Array.isArray(value) ? value : [value];\n if (multiple) {\n const users = valueList\n .map((item) => {\n const user = totalUserList.find((user) => {\n if (user.type === EUserType.USER) {\n return user.userId === item;\n }\n\n return user.groupId === item;\n });\n\n return user;\n })\n .filter(Boolean) as TUser;\n setSelectedUserList(users);\n onChange?.(users);\n } else {\n const user = totalUserList.find((user) => {\n if (user.type === EUserType.USER) {\n return user.userId === value;\n }\n\n return user.groupId === value;\n });\n if (user) {\n setSelectedUserList([user]);\n onChange?.(user);\n } else {\n setSelectedUserList([]);\n onChange?.();\n }\n }\n }\n }}\n disabled={isDisabled}\n allowClear={allowClear}\n onClear={() => {\n setSelectedUserList([]);\n if (multiple) {\n onChange?.([]);\n } else {\n onChange?.();\n }\n onClear?.();\n }}\n popupClassName={cn(\n 'ald-member-picker-popup',\n allowOverlap && 'ald-member-picker-popup-adaptive',\n props.popupClassName,\n )}\n mode={mode}\n options={selectedUserList.map((item) => {\n if (item.type === EUserType.USER_GROUP)\n return {\n label: item.name || item.groupId,\n value: item.groupId,\n tag: (\n <div className={'ald-member-picker-tag'}>\n <Avatar type=\"team\" size={20}>\n {item.name || item.groupId}\n </Avatar>\n {item.name || item.groupId}\n </div>\n ),\n };\n\n return {\n label: item.nickname || item.name || item.userId,\n value: item.userId,\n tag: (\n <div className={'ald-member-picker-tag'}>\n <Avatar\n size={20}\n src={item.photo}\n type={item.type === EUserType.USER ? 'user' : 'team'}\n >\n {item.nickname || item.name || item.userId}\n </Avatar>\n {item.nickname || item.name || item.userId}\n </div>\n ),\n };\n })}\n optionLabelProp={multiple ? 'label' : 'tag'}\n open={usesDialog ? false : selectOpen}\n onOpenChange={onDropdownOpenChange}\n popupMatchSelectWidth={panelWidth}\n allowOverlap={allowOverlap}\n placeholder={placeholder}\n dropdownRender={() => {\n return (\n <div\n ref={triggerRef}\n id={listboxId}\n role=\"group\"\n aria-label=\"Member selection results\"\n className={cn({\n 'ald-member-picker-popup-body': allowOverlap,\n })}\n >\n <Panel\n active={dropdownOpen}\n searchFailed={failed}\n useProvidedGroupSearch={\n type === EMemberPicker.BOTH && !usesGroupQuery\n }\n searchFeedback={\n !usesGroupQuery && (\n <SearchFeedback\n failed={failed}\n hasMore={searchResult.hasMore}\n pathWarningCode={searchResult.pathWarningCode}\n retry={retry}\n />\n )\n }\n type={type}\n footer={footer}\n value={selectedUserList}\n dataList={totalUserList}\n lockedIds={lockedIds}\n multiple={!!multiple}\n loading={loading}\n dropdownWidth={dropdownContentWidth}\n onSearchValueChange={onSearchValueChange}\n onChange={onSelectedChange}\n onCancel={() => {\n onCancel?.();\n setDropdownOpen(false);\n }}\n renderItem={renderItem}\n renderSelectedTag={renderSelectedTag}\n searchValue={searchString}\n ></Panel>\n </div>\n );\n }}\n ></Select>\n {usesDialog && !isDisabled && (\n <MemberPickerDialog\n open={selectOpen}\n value={value ?? selectedUserList}\n type={type}\n lockedIds={lockedIds}\n filterItem={filterItem}\n renderItem={renderItem}\n renderSelectedTag={renderSelectedTag}\n onChange={(members) => {\n setSelectedUserList(members);\n if (props.multiple) props.onChange?.(members);\n }}\n footer={footer}\n onCancel={onCancel}\n onClose={() => onDropdownOpenChange(false)}\n />\n )}\n </div>\n );\n};\n\nconst MemberSelector = Component as React.FC<IMemberPickerProps> & {\n MemberPanel: typeof MemberPanel;\n Dialog: typeof MemberPickerDialog;\n};\n\nMemberSelector.MemberPanel = MemberPanel;\nMemberSelector.Dialog = MemberPickerDialog;\nexport default MemberSelector;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/MemberPicker/index.tsx"],"sourcesContent":["import MemberPickerDialog from './dialog/MemberPickerDialog';\nimport DisabledContext from '../ConfigProvider/DisabledContext';\nimport useMemberSearch from './useMemberSearch';\nimport SearchFeedback from './components/SearchFeedback';\nimport { usePrevious } from 'ahooks';\nimport { cn } from '../lib/utils';\nimport _ from 'lodash';\nimport React, {\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport Avatar from '../Avatar';\nimport ConfigProvider from '../ConfigProvider';\nimport './member-picker.css';\nimport {\n EUserType,\n IUser,\n IUserGroup,\n TUser,\n} from '../ConfigProvider/getUserList';\nimport { LocaleContext, getTranslator } from '../locale/default';\nimport Panel from './components/Panel';\nimport MemberPanel from './components/PanelWrapper';\nimport {\n EMemberPicker,\n IMemberPickerProps,\n TUserGroupValue,\n TUserValue,\n} from './interface';\nimport { getId } from './utils';\nimport { isUser, isUserGroup } from './utils/getUsersWithUserId';\nimport Select from '../Select';\nimport { Mode } from '../Select/BaseSelect';\n\nconst DEFAULT_MULTIPLE_DROPDOWN_WIDTH = 480;\n\nconst Component: React.FC<IMemberPickerProps> = (props) => {\n const { locale } = useContext(LocaleContext);\n const t = getTranslator(locale);\n\n const {\n value,\n type = EMemberPicker.USER,\n onChange,\n multiple,\n dropdownWidth,\n open,\n onOpenChange,\n placeholder = t.MemberPicker.select,\n className,\n allowClear,\n disabled,\n onClear,\n size,\n prefix: propPrefix,\n lockedIds = [],\n footer,\n onCancel,\n filterItem,\n mode: propsMode,\n canEnterGroup,\n renderItem,\n renderSelectedTag,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n ...restProps\n } = props;\n const allowOverlap = props.allowOverlap ?? false;\n const {\n getUsersByIds,\n getUsersByKeywords,\n getUsersByKeywordsWithMeta,\n getNextLevelGroup,\n getNextLevelGroupWithMeta,\n } = ConfigProvider.useGetUserList();\n const inheritedDisabled = useContext(DisabledContext);\n const isDisabled = disabled ?? inheritedDisabled;\n // 呈现方式由选择模式唯一决定,多选使用弹窗,单选保持浮窗。\n const usesDialog = !!multiple;\n const usesGroupQuery =\n (type === EMemberPicker.GROUP ||\n (!getUsersByKeywords && !getUsersByKeywordsWithMeta)) &&\n !!(getNextLevelGroup || getNextLevelGroupWithMeta);\n const [selectedUserList, setSelectedUserList] = useState<TUser>([]);\n const prevValue = usePrevious(value);\n const [searchString, setSearchString] = useState<string>('');\n const [dropdownContentWidth, setDropdownContentWidth] =\n useState(dropdownWidth);\n const selectOwnerRef = useRef<HTMLDivElement>(null);\n // 单选默认浮窗跟触发器等宽(true),调用方传 dropdownWidth 时用具体数字;\n // 多选默认 480,单条记录只有 头像+nickname 时触发器太窄装不下选中标签,故固定值。\n const panelWidth: number | boolean =\n dropdownWidth ?? (multiple ? DEFAULT_MULTIPLE_DROPDOWN_WIDTH : true);\n\n const triggerRef = useRef<HTMLDivElement>(null);\n const onSearchValueChange = useCallback((v: string) => {\n setSearchString(v);\n }, []);\n const [dropdownOpen, setDropdownOpen] = useState<boolean>(!!open);\n const queryByKeywords = useMemo(() => {\n if (getUsersByKeywordsWithMeta) return getUsersByKeywordsWithMeta;\n if (getUsersByKeywords) {\n return getUsersByKeywords;\n }\n\n return (_params = {}) =>\n Promise.reject({\n message: t.MemberPicker.pleaseConfigGetUserByKeywordsMethod,\n });\n }, [\n getUsersByKeywords,\n getUsersByKeywordsWithMeta,\n t.MemberPicker.pleaseConfigGetUserByKeywordsMethod,\n ]);\n const queryByIds = useMemo(() => {\n if (getUsersByIds) {\n return getUsersByIds;\n }\n\n return (_params = {}) =>\n Promise.reject({\n message: t.MemberPicker.pleaseConfigGetUserByIdsMethod,\n });\n }, [getUsersByIds, t.MemberPicker.pleaseConfigGetUserByIdsMethod]);\n const {\n result: searchResult,\n loading,\n failed,\n retry,\n } = useMemberSearch<IUser | IUserGroup>(\n queryByKeywords,\n searchString,\n dropdownOpen && !usesGroupQuery && !usesDialog,\n );\n const searchUserList = searchResult.items;\n\n const totalUserList: TUser = useMemo(() => {\n const totalUsers = searchUserList.concat(\n selectedUserList.filter((item) => {\n const name =\n (item.type === EUserType.USER ? item.nickname : item.name) || '';\n\n return name.toUpperCase().includes(searchString.trim().toUpperCase());\n }),\n );\n\n return _.uniqBy(totalUsers, (data: IUser | IUserGroup) => {\n if (data.type === EUserType.USER) return data.userId;\n\n return data.groupId;\n }).filter((item) => {\n if (filterItem) {\n return filterItem(item);\n }\n\n return true;\n });\n }, [filterItem, searchString, searchUserList, selectedUserList]);\n\n useEffect(() => {\n if (_.isEqual(value, prevValue)) {\n return;\n }\n if (Array.isArray(value) && (isUser(value[0]) || isUserGroup(value[0]))) {\n setSelectedUserList(value as TUser);\n } else if (isUser(value) || isUserGroup(value)) {\n setSelectedUserList([value]);\n } else {\n let param: Array<TUserValue | TUserGroupValue> = [];\n if (!value) return;\n if (Array.isArray(value) && value.length === 0) {\n setSelectedUserList([]);\n\n return;\n }\n if (!Array.isArray(value)) {\n param = [value];\n } else {\n param = value;\n }\n queryByIds(param).then((result) => {\n // 如果用户已被删除,则需要展示出id\n const valueNotInResult: TUser = param\n .filter((item) => {\n if (item.type === EUserType.USER) {\n return result\n .filter(isUser)\n .every((user) => user.userId !== item.userId);\n }\n\n return result\n .filter(isUserGroup)\n .every((user) => user.groupId !== item.groupId);\n })\n .map((item) => {\n if (item.type === EUserType.USER) {\n return {\n type: EUserType.USER,\n userId: item.userId,\n } as IUser;\n }\n\n return {\n type: EUserType.USER_GROUP,\n groupId: item.groupId,\n } as IUserGroup;\n });\n\n setSelectedUserList([...result, ...valueNotInResult]);\n });\n }\n }, [value, queryByIds, prevValue]);\n\n const onSelectedChange = useCallback(\n (users: TUser) => {\n if (multiple) {\n setSelectedUserList(users);\n onChange?.(users);\n } else {\n setSelectedUserList(users);\n onChange?.(users[0]);\n }\n setDropdownOpen(false);\n },\n [multiple, onChange, setDropdownOpen],\n );\n\n useEffect(() => {\n if (typeof open === 'boolean') {\n setDropdownOpen(open);\n }\n }, [open]);\n\n useEffect(() => {\n if (!dropdownContentWidth) {\n const resizeEle = triggerRef.current;\n if (!resizeEle) return;\n const resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width } = entry.contentRect;\n setDropdownContentWidth(width);\n }\n });\n resizeObserver.observe(resizeEle);\n\n return () => {\n resizeObserver.unobserve(resizeEle);\n };\n }\n }, [triggerRef, dropdownContentWidth]);\n const onDropdownOpenChange = useCallback(\n (open: boolean) => {\n if (isDisabled) return;\n if (!usesDialog || typeof props.open !== 'boolean') setDropdownOpen(open);\n onOpenChange?.(open);\n },\n [isDisabled, usesDialog, props.open, onOpenChange],\n );\n const mode = useMemo(() => {\n if (multiple === true) {\n if (typeof propsMode === 'object')\n return {\n ...(propsMode || {}),\n type: 'multiple',\n responsive: _.isNil(propsMode.responsive)\n ? true\n : propsMode.responsive,\n } as Mode;\n return {\n type: 'multiple',\n responsive: true,\n } as Mode;\n }\n\n return multiple ? (multiple as Mode) : propsMode;\n }, [multiple, propsMode]);\n\n const selectOpen = typeof open === 'boolean' ? open : dropdownOpen;\n const listboxId = `ald-member-picker-listbox-${React.useId().replace(\n /:/g,\n '',\n )}`;\n const activeMember = totalUserList[0];\n const activeMemberId = activeMember\n ? `ald-member-picker-option-${getId(activeMember)}`\n : undefined;\n\n useEffect(() => {\n const owner = selectOwnerRef.current?.querySelector<HTMLElement>(\n '[role=\"combobox\"], input',\n );\n if (!owner) return;\n owner.setAttribute('aria-controls', listboxId);\n if (selectOpen && activeMemberId) {\n owner.setAttribute('aria-activedescendant', activeMemberId);\n } else {\n owner.removeAttribute('aria-activedescendant');\n }\n }, [activeMemberId, listboxId, selectOpen]);\n\n return (\n <div ref={selectOwnerRef} style={{ display: 'contents' }}>\n <Select\n prefix={propPrefix}\n {...restProps}\n data-testid={dataTestId}\n aria-label={ariaLabel}\n showSearch={!usesDialog}\n searchValue={searchString}\n onSearch={onSearchValueChange}\n aria-controls={listboxId}\n aria-activedescendant={selectOpen ? activeMemberId : undefined}\n size={size}\n className={cn('ald-member-picker-select', className)}\n value={selectedUserList.map((item) => {\n if (item.type === EUserType.USER_GROUP) {\n return item.groupId;\n } else {\n return item.userId;\n }\n })}\n onChange={(value) => {\n if (_.isEmpty(value)) {\n setSelectedUserList([]);\n if (multiple) {\n onChange?.([]);\n } else {\n onChange?.();\n }\n } else {\n const valueList = Array.isArray(value) ? value : [value];\n if (multiple) {\n const users = valueList\n .map((item) => {\n const user = totalUserList.find((user) => {\n if (user.type === EUserType.USER) {\n return user.userId === item;\n }\n\n return user.groupId === item;\n });\n\n return user;\n })\n .filter(Boolean) as TUser;\n setSelectedUserList(users);\n onChange?.(users);\n } else {\n const user = totalUserList.find((user) => {\n if (user.type === EUserType.USER) {\n return user.userId === value;\n }\n\n return user.groupId === value;\n });\n if (user) {\n setSelectedUserList([user]);\n onChange?.(user);\n } else {\n setSelectedUserList([]);\n onChange?.();\n }\n }\n }\n }}\n disabled={isDisabled}\n allowClear={allowClear}\n onClear={() => {\n setSelectedUserList([]);\n if (multiple) {\n onChange?.([]);\n } else {\n onChange?.();\n }\n onClear?.();\n }}\n popupClassName={cn(\n 'ald-member-picker-popup',\n allowOverlap && 'ald-member-picker-popup-adaptive',\n props.popupClassName,\n )}\n mode={mode}\n options={selectedUserList.map((item) => {\n if (item.type === EUserType.USER_GROUP)\n return {\n label: item.name || item.groupId,\n value: item.groupId,\n tag: (\n <div className={'ald-member-picker-tag'}>\n <Avatar type=\"team\" size={20}>\n {item.name || item.groupId}\n </Avatar>\n {item.name || item.groupId}\n </div>\n ),\n };\n\n return {\n label: item.nickname || item.name || item.userId,\n value: item.userId,\n tag: (\n <div className={'ald-member-picker-tag'}>\n <Avatar\n size={20}\n src={item.photo}\n type={item.type === EUserType.USER ? 'user' : 'team'}\n >\n {item.nickname || item.name || item.userId}\n </Avatar>\n {item.nickname || item.name || item.userId}\n </div>\n ),\n };\n })}\n optionLabelProp={multiple ? 'label' : 'tag'}\n open={usesDialog ? false : selectOpen}\n onOpenChange={onDropdownOpenChange}\n popupMatchSelectWidth={panelWidth}\n allowOverlap={allowOverlap}\n placeholder={placeholder}\n dropdownRender={() => {\n return (\n <div\n ref={triggerRef}\n id={listboxId}\n role=\"group\"\n aria-label=\"Member selection results\"\n className={cn({\n 'ald-member-picker-popup-body': allowOverlap,\n })}\n >\n <Panel\n active={dropdownOpen}\n searchFailed={failed}\n useProvidedGroupSearch={\n type === EMemberPicker.BOTH && !usesGroupQuery\n }\n searchFeedback={\n !usesGroupQuery && (\n <SearchFeedback\n failed={failed}\n hasMore={searchResult.hasMore}\n pathWarningCode={searchResult.pathWarningCode}\n retry={retry}\n />\n )\n }\n type={type}\n footer={footer}\n value={selectedUserList}\n dataList={totalUserList}\n lockedIds={lockedIds}\n multiple={!!multiple}\n loading={loading}\n dropdownWidth={dropdownContentWidth}\n onSearchValueChange={onSearchValueChange}\n onChange={onSelectedChange}\n onCancel={() => {\n onCancel?.();\n setDropdownOpen(false);\n }}\n canEnterGroup={canEnterGroup}\n renderItem={renderItem}\n renderSelectedTag={renderSelectedTag}\n searchValue={searchString}\n ></Panel>\n </div>\n );\n }}\n ></Select>\n {usesDialog && !isDisabled && (\n <MemberPickerDialog\n open={selectOpen}\n value={value ?? selectedUserList}\n type={type}\n lockedIds={lockedIds}\n filterItem={filterItem}\n canEnterGroup={canEnterGroup}\n renderItem={renderItem}\n renderSelectedTag={renderSelectedTag}\n onChange={(members) => {\n setSelectedUserList(members);\n if (props.multiple) props.onChange?.(members);\n }}\n footer={footer}\n onCancel={onCancel}\n onClose={() => onDropdownOpenChange(false)}\n />\n )}\n </div>\n );\n};\n\nconst MemberSelector = Component as React.FC<IMemberPickerProps> & {\n MemberPanel: typeof MemberPanel;\n Dialog: typeof MemberPickerDialog;\n};\n\nMemberSelector.MemberPanel = MemberPanel;\nMemberSelector.Dialog = MemberPickerDialog;\nexport default MemberSelector;\n"],"mappings":"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"}
|
|
@@ -20,6 +20,12 @@ export interface IBaseMemberSelectorProps extends Omit<ISelectProps, 'type'> {
|
|
|
20
20
|
lockedIds?: Array<TUserValue | TUserGroupValue>;
|
|
21
21
|
footer?: ReactNode;
|
|
22
22
|
onCancel?: () => void;
|
|
23
|
+
/**
|
|
24
|
+
* 控制用户组是否允许进入下级。返回 false 时隐藏“下级”入口,且不提供
|
|
25
|
+
* renderItem 的 onNextLevel;不影响该组的选择。省略时保留原有层级行为。
|
|
26
|
+
* 平铺列表可传 () => false,也可按组判断;仅限制原有入口,不推断层级数据。
|
|
27
|
+
*/
|
|
28
|
+
canEnterGroup?: (group: IUserGroup) => boolean;
|
|
23
29
|
filterItem?: (item: IUser | IUserGroup) => boolean;
|
|
24
30
|
renderItem?: (item: IUser | IUserGroup, context: {
|
|
25
31
|
isSelected: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interface.js","names":[],"sources":["../../src/MemberPicker/interface.ts"],"sourcesContent":["import { ReactNode } from 'react';\nimport { IUser, IUserGroup, TUser } from '../ConfigProvider/getUserList';\nimport { ISelectProps } from '../Select/interface';\nimport { RequiredProperties } from '../_utils/type';\nimport { Mode } from '../Select/BaseSelect';\n\nexport type TUserValue = RequiredProperties<IUser, 'type' | 'userId'>;\nexport type TUserGroupValue = RequiredProperties<\n IUserGroup,\n 'type' | 'groupId'\n>;\n\nexport type TMemberPickerValue =\n | TUserValue\n | TUserGroupValue\n | Array<TUserValue | TUserGroupValue>;\n\nexport enum EMemberPicker {\n GROUP = 'GROUP',\n USER = 'USER',\n BOTH = 'BOTH',\n}\n\nexport interface IBaseMemberSelectorProps extends Omit<ISelectProps, 'type'> {\n value?: TMemberPickerValue;\n dropdownWidth?: number;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n type?: EMemberPicker;\n lockedIds?: Array<TUserValue | TUserGroupValue>;\n footer?: ReactNode;\n onCancel?: () => void;\n filterItem?: (item: IUser | IUserGroup) => boolean;\n renderItem?: (\n item: IUser | IUserGroup,\n context: {\n isSelected: boolean;\n disabled: boolean;\n onClick: () => void;\n onNextLevel?: () => void;\n originNode: ReactNode;\n },\n ) => ReactNode;\n renderSelectedTag?: (\n user: IUser | IUserGroup,\n originNode: ReactNode,\n ) => ReactNode;\n}\n\nexport interface IMultipleMemberSelectorProps extends IBaseMemberSelectorProps {\n multiple: true | Mode;\n onChange?(v: TUser): void;\n}\n\nexport interface ISingleMemberSelectorProps extends IBaseMemberSelectorProps {\n multiple?: false;\n onChange?(v?: IUser | IUserGroup): void;\n}\n\nexport type IMemberPickerProps =\n | IMultipleMemberSelectorProps\n | ISingleMemberSelectorProps;\n\nexport type { IUser };\n"],"mappings":";AAiBA,IAAY,gBAAL,yBAAA,eAAA;AACL,eAAA,WAAA;AACA,eAAA,UAAA;AACA,eAAA,UAAA;;KACD"}
|
|
1
|
+
{"version":3,"file":"interface.js","names":[],"sources":["../../src/MemberPicker/interface.ts"],"sourcesContent":["import { ReactNode } from 'react';\nimport { IUser, IUserGroup, TUser } from '../ConfigProvider/getUserList';\nimport { ISelectProps } from '../Select/interface';\nimport { RequiredProperties } from '../_utils/type';\nimport { Mode } from '../Select/BaseSelect';\n\nexport type TUserValue = RequiredProperties<IUser, 'type' | 'userId'>;\nexport type TUserGroupValue = RequiredProperties<\n IUserGroup,\n 'type' | 'groupId'\n>;\n\nexport type TMemberPickerValue =\n | TUserValue\n | TUserGroupValue\n | Array<TUserValue | TUserGroupValue>;\n\nexport enum EMemberPicker {\n GROUP = 'GROUP',\n USER = 'USER',\n BOTH = 'BOTH',\n}\n\nexport interface IBaseMemberSelectorProps extends Omit<ISelectProps, 'type'> {\n value?: TMemberPickerValue;\n dropdownWidth?: number;\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n type?: EMemberPicker;\n lockedIds?: Array<TUserValue | TUserGroupValue>;\n footer?: ReactNode;\n onCancel?: () => void;\n /**\n * 控制用户组是否允许进入下级。返回 false 时隐藏“下级”入口,且不提供\n * renderItem 的 onNextLevel;不影响该组的选择。省略时保留原有层级行为。\n * 平铺列表可传 () => false,也可按组判断;仅限制原有入口,不推断层级数据。\n */\n canEnterGroup?: (group: IUserGroup) => boolean;\n filterItem?: (item: IUser | IUserGroup) => boolean;\n renderItem?: (\n item: IUser | IUserGroup,\n context: {\n isSelected: boolean;\n disabled: boolean;\n onClick: () => void;\n onNextLevel?: () => void;\n originNode: ReactNode;\n },\n ) => ReactNode;\n renderSelectedTag?: (\n user: IUser | IUserGroup,\n originNode: ReactNode,\n ) => ReactNode;\n}\n\nexport interface IMultipleMemberSelectorProps extends IBaseMemberSelectorProps {\n multiple: true | Mode;\n onChange?(v: TUser): void;\n}\n\nexport interface ISingleMemberSelectorProps extends IBaseMemberSelectorProps {\n multiple?: false;\n onChange?(v?: IUser | IUserGroup): void;\n}\n\nexport type IMemberPickerProps =\n | IMultipleMemberSelectorProps\n | ISingleMemberSelectorProps;\n\nexport type { IUser };\n"],"mappings":";AAiBA,IAAY,gBAAL,yBAAA,eAAA;AACL,eAAA,WAAA;AACA,eAAA,UAAA;AACA,eAAA,UAAA;;KACD"}
|
package/dist/Menu/index.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export interface MenuProps {
|
|
|
10
10
|
ariaLabel?: string;
|
|
11
11
|
/** @internal Requested initial keyboard position when the popup opens. */
|
|
12
12
|
initialFocus?: 'first' | 'last';
|
|
13
|
+
/** @internal Whether the current popup entry originated from keyboard input. */
|
|
14
|
+
keyboardFocus?: boolean;
|
|
15
|
+
/** @internal Whether opening the popup should transfer focus to an item. */
|
|
16
|
+
captureFocus?: boolean;
|
|
13
17
|
/** @internal Requests that the owning popup close, including on Escape. */
|
|
14
18
|
onRequestClose?: (reason: 'click' | 'cancel' | 'blur' | 'scroll') => void;
|
|
15
19
|
rootClosing?: boolean;
|
package/dist/Menu/index.js
CHANGED
|
@@ -2,19 +2,23 @@ import { cn } from "../lib/utils.js";
|
|
|
2
2
|
import Memo from "../Icon/components/CheckLightLine.js";
|
|
3
3
|
import Memo$1 from "../Icon/components/ChevronRightLine.js";
|
|
4
4
|
/* empty css */
|
|
5
|
-
import { useCallback, useMemo, useRef } from "react";
|
|
5
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import _ from "lodash";
|
|
8
8
|
import { ControlledMenu, MenuDivider, MenuHeader, MenuItem, SubMenu } from "@szhsin/react-menu";
|
|
9
9
|
//#region src/Menu/index.tsx
|
|
10
10
|
function Menu(props) {
|
|
11
|
-
const { className, items, onClick: onMenuClick, onBeforeLeafItemClick, selectedKeys, menuStyle, ariaLabel, initialFocus = "first", onRequestClose, rootClosing = false, externalOverflow, preserveRootHeight = false } = props;
|
|
11
|
+
const { className, items, onClick: onMenuClick, onBeforeLeafItemClick, selectedKeys, menuStyle, ariaLabel, initialFocus = "first", keyboardFocus = false, captureFocus = true, onRequestClose, rootClosing = false, externalOverflow, preserveRootHeight = false } = props;
|
|
12
12
|
const selectedKeySet = new Set(selectedKeys || []);
|
|
13
13
|
const typeaheadRef = useRef({
|
|
14
14
|
query: "",
|
|
15
15
|
timestamp: 0
|
|
16
16
|
});
|
|
17
|
-
const
|
|
17
|
+
const [isKeyboardFocus, setIsKeyboardFocus] = useState(keyboardFocus);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
setIsKeyboardFocus(keyboardFocus);
|
|
20
|
+
}, [keyboardFocus]);
|
|
21
|
+
const menuItemFocus = useMemo(() => captureFocus ? { position: initialFocus } : void 0, [captureFocus, initialFocus]);
|
|
18
22
|
const onMenuKeyDown = useCallback((event) => {
|
|
19
23
|
if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.key.length !== 1) return;
|
|
20
24
|
const now = Date.now();
|
|
@@ -71,14 +75,17 @@ function Menu(props) {
|
|
|
71
75
|
}),
|
|
72
76
|
disabled: item.disabled,
|
|
73
77
|
"aria-label": item.ariaLabel || titleStr || void 0,
|
|
74
|
-
itemProps: {
|
|
78
|
+
itemProps: {
|
|
79
|
+
className: isLeafSubMenu ? "ald-menu-submenu-leaf-trigger" : void 0,
|
|
80
|
+
onPointerMove: () => setIsKeyboardFocus(false)
|
|
81
|
+
},
|
|
75
82
|
align: "start",
|
|
76
83
|
direction: "right",
|
|
77
84
|
gap: -8,
|
|
78
85
|
position: "anchor",
|
|
79
86
|
shift: 0,
|
|
80
87
|
menuStyle: { ...menuStyle },
|
|
81
|
-
menuClassName: cn("ald-menu-submenu-popup", isLeafSubMenu && "ald-menu-submenu-popup-leaf", !isLeafSubMenu && menuStyle?.maxHeight !== void 0 && "ald-menu-submenu-popup-scrollable", item.popupClassName, rootClosing && "ald-menu-submenu-popup-root-closing"),
|
|
88
|
+
menuClassName: cn("ald-menu-submenu-popup", isLeafSubMenu && "ald-menu-submenu-popup-leaf", !isLeafSubMenu && menuStyle?.maxHeight !== void 0 && "ald-menu-submenu-popup-scrollable", item.popupClassName, isKeyboardFocus && "ald-menu-focus-visible", rootClosing && "ald-menu-submenu-popup-root-closing"),
|
|
82
89
|
overflow: subMenuOverflow,
|
|
83
90
|
children: getMenuItems(item.children, currPath)
|
|
84
91
|
}, item.key);
|
|
@@ -90,6 +97,7 @@ function Menu(props) {
|
|
|
90
97
|
}),
|
|
91
98
|
disabled: item.disabled,
|
|
92
99
|
"aria-label": item.ariaLabel || titleStr || void 0,
|
|
100
|
+
onPointerMove: () => setIsKeyboardFocus(false),
|
|
93
101
|
onClick: ({ syntheticEvent }) => {
|
|
94
102
|
onBeforeLeafItemClick?.();
|
|
95
103
|
const menuInfo = {
|
|
@@ -142,12 +150,15 @@ function Menu(props) {
|
|
|
142
150
|
setDownOverflow: preserveRootHeight,
|
|
143
151
|
menuStyle: { ...menuStyle },
|
|
144
152
|
anchorRef: ref,
|
|
145
|
-
className: cn(className, "ald-menu", isLeafRootMenu && !externalOverflow && "ald-menu-leaf", !isLeafRootMenu && menuStyle?.maxHeight !== void 0 && !externalOverflow && "ald-menu-scrollable"),
|
|
153
|
+
className: cn(className, "ald-menu", isKeyboardFocus && "ald-menu-focus-visible", isLeafRootMenu && !externalOverflow && "ald-menu-leaf", !isLeafRootMenu && menuStyle?.maxHeight !== void 0 && !externalOverflow && "ald-menu-scrollable"),
|
|
146
154
|
position: externalOverflow ? "initial" : "auto",
|
|
147
155
|
"aria-label": ariaLabel,
|
|
148
|
-
captureFocus
|
|
156
|
+
captureFocus,
|
|
149
157
|
menuItemFocus,
|
|
150
|
-
onKeyDown:
|
|
158
|
+
onKeyDown: (event) => {
|
|
159
|
+
setIsKeyboardFocus(true);
|
|
160
|
+
onMenuKeyDown(event);
|
|
161
|
+
},
|
|
151
162
|
onClose: ({ reason }) => onRequestClose?.(reason),
|
|
152
163
|
viewScroll: externalOverflow ? "initial" : "auto",
|
|
153
164
|
overflow: externalOverflow ? "visible" : menuOverflow,
|
package/dist/Menu/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/Menu/index.tsx"],"sourcesContent":["import {\n ControlledMenu,\n MenuDivider,\n MenuHeader,\n MenuItem,\n SubMenu,\n} from '@szhsin/react-menu';\nimport { cn } from '../lib/utils';\nimport _ from 'lodash';\nimport React, { useCallback, useMemo, useRef } from 'react';\nimport { CheckLightLine, ChevronRightLine } from '../Icon';\nimport './menu.css';\n\nexport interface MenuProps {\n onClick?: (info: MenuInfo) => void;\n onBeforeLeafItemClick?: () => void;\n className?: string;\n items: MenuItemType[];\n selectedKeys?: string[];\n menuStyle?: React.CSSProperties;\n /** Accessible name for the command menu when its context is not otherwise clear. */\n ariaLabel?: string;\n /** @internal Requested initial keyboard position when the popup opens. */\n initialFocus?: 'first' | 'last';\n /** @internal Requests that the owning popup close, including on Escape. */\n onRequestClose?: (reason: 'click' | 'cancel' | 'blur' | 'scroll') => void;\n rootClosing?: boolean;\n /** @internal 由 Dropdown 注入,禁用内部定位/overflow 计算。不支持含 SubMenu 的菜单 */\n externalOverflow?: boolean;\n /** @internal Dropdown 已按实际裁剪区域计算根菜单高度,保留其限制。 */\n preserveRootHeight?: boolean;\n}\n\nexport default function Menu(props: MenuProps) {\n const {\n className,\n items,\n onClick: onMenuClick,\n onBeforeLeafItemClick,\n selectedKeys,\n menuStyle,\n ariaLabel,\n initialFocus = 'first',\n onRequestClose,\n rootClosing = false,\n externalOverflow,\n preserveRootHeight = false,\n } = props;\n const selectedKeySet = new Set(selectedKeys || []);\n const typeaheadRef = useRef({ query: '', timestamp: 0 });\n const menuItemFocus = useMemo(\n () => ({ position: initialFocus }),\n [initialFocus],\n );\n const onMenuKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLUListElement>) => {\n if (\n event.defaultPrevented ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey ||\n event.key.length !== 1\n ) {\n return;\n }\n\n const now = Date.now();\n const previous = typeaheadRef.current;\n const query = `${now - previous.timestamp > 500 ? '' : previous.query}${\n event.key\n }`.toLocaleLowerCase();\n typeaheadRef.current = { query, timestamp: now };\n\n const menuItems = Array.from(\n event.currentTarget.querySelectorAll<HTMLElement>(\n '[role=\"menuitem\"]:not([aria-hidden=\"true\"]):not([aria-disabled=\"true\"])',\n ),\n );\n const activeIndex = menuItems.indexOf(\n document.activeElement as HTMLElement,\n );\n const orderedItems = [\n ...menuItems.slice(activeIndex + 1),\n ...menuItems.slice(0, activeIndex + 1),\n ];\n const match = orderedItems.find((item) => {\n const accessibleName =\n item.getAttribute('aria-label') || item.textContent || '';\n return accessibleName.trim().toLocaleLowerCase().startsWith(query);\n });\n\n if (match) {\n event.preventDefault();\n match.focus();\n }\n },\n [],\n );\n\n const hasDirectSelectedItem = (items: MenuItemType[]): boolean =>\n items.some((item) => {\n if (isSubMenuItem(item)) {\n return selectedKeySet.has(item.key);\n }\n if (isHeaderMenuItem(item) || isDivideMenuItem(item)) {\n return false;\n }\n return selectedKeySet.has(item.key);\n });\n\n const getMenuItems = (items: MenuItemType[], parentPath: string[] = []) => {\n const isSelectableGroup = hasDirectSelectedItem(items);\n\n return items.map((item, index) => {\n if (isDivideMenuItem(item)) {\n return <MenuDivider key={`divider_${index}`} />;\n }\n if (isHeaderMenuItem(item)) {\n return <MenuHeader key={`header_${index}`}>{item.label}</MenuHeader>;\n }\n const currPath = [...parentPath, item.key];\n const titleStr = _.isString(item.label) ? item.label : '';\n const isSelected = selectedKeys?.includes(item.key);\n\n if (isSubMenuItem(item)) {\n const isLeafSubMenu = isLeafMenu(item.children);\n const subMenuOverflow =\n isLeafSubMenu || menuStyle?.maxHeight !== undefined\n ? 'auto'\n : undefined;\n const subMenuItemLabel = (\n <div className=\"ald-menu-item-label\" title={titleStr}>\n {item.icon ? (\n <div className=\"ald-menu-item-label-icon\">{item.icon}</div>\n ) : null}\n <div className=\"ald-menu-item-label-text tw-text-typography-body-dense\">\n {item.label}\n </div>\n <div className=\"ald-menu-item-right\">\n {isSelectableGroup ? (\n <div className=\"ald-menu-item-selection-slot\" />\n ) : null}\n <ChevronRightLine size={16} />\n </div>\n </div>\n );\n\n return (\n <SubMenu\n key={item.key}\n label={subMenuItemLabel}\n disabled={item.disabled}\n aria-label={item.ariaLabel || titleStr || undefined}\n itemProps={{\n className: isLeafSubMenu\n ? 'ald-menu-submenu-leaf-trigger'\n : undefined,\n }}\n align=\"start\"\n direction=\"right\"\n gap={-8}\n position=\"anchor\"\n shift={0}\n menuStyle={{\n ...menuStyle,\n }}\n menuClassName={cn(\n 'ald-menu-submenu-popup',\n isLeafSubMenu && 'ald-menu-submenu-popup-leaf',\n !isLeafSubMenu &&\n menuStyle?.maxHeight !== undefined &&\n 'ald-menu-submenu-popup-scrollable',\n item.popupClassName,\n rootClosing && 'ald-menu-submenu-popup-root-closing',\n )}\n overflow={subMenuOverflow}\n >\n {getMenuItems(item.children, currPath)}\n </SubMenu>\n );\n }\n\n return (\n <MenuItem\n key={item.key}\n className={cn(item.className, {\n 'ald-menu-item-danger': item.danger,\n 'ald-menu-item-selected': isSelected,\n })}\n disabled={item.disabled}\n aria-label={item.ariaLabel || titleStr || undefined}\n onClick={({ syntheticEvent }) => {\n onBeforeLeafItemClick?.();\n const menuInfo = {\n key: item.key,\n keyPath: currPath,\n domEvent: syntheticEvent,\n };\n if (item.onClick) {\n item.onClick(menuInfo);\n }\n if (onMenuClick) {\n onMenuClick(menuInfo);\n }\n }}\n >\n <div className=\"ald-menu-item-label\" title={titleStr}>\n {item.icon ? (\n <div className=\"ald-menu-item-label-icon\">{item.icon}</div>\n ) : null}\n <div className=\"ald-menu-item-label-text tw-text-typography-body-dense\">\n {item.label}\n </div>\n {isSelectableGroup ? (\n <div className=\"ald-menu-item-right\">\n <div\n className={cn(\n 'ald-menu-item-selection-slot',\n isSelected &&\n 'ald-menu-item-selection-slot-selected ald-menu-item-selected-icon',\n )}\n >\n {isSelected ? <CheckLightLine /> : null}\n </div>\n </div>\n ) : null}\n </div>\n </MenuItem>\n );\n });\n };\n\n const ref = useRef(null);\n\n const isLeafRootMenu = isLeafMenu(items);\n const menuOverflow =\n isLeafRootMenu || menuStyle?.maxHeight !== undefined ? 'auto' : undefined;\n\n return (\n <>\n <div style={{ display: 'none' }} ref={ref} />\n <ControlledMenu\n state=\"open\"\n transition={{ open: true, close: true }}\n transitionTimeout={200}\n submenuOpenDelay={0}\n submenuCloseDelay={0}\n // Root positioning is owned by the surrounding surface (see menu.css).\n // Keep an explicit caller height: React-Menu otherwise replaces it with\n // its viewport limit. Descendant SubMenus still calculate their own overflow.\n setDownOverflow={preserveRootHeight}\n menuStyle={{\n ...menuStyle,\n }}\n anchorRef={ref}\n className={cn(\n className,\n 'ald-menu',\n isLeafRootMenu && !externalOverflow && 'ald-menu-leaf',\n !isLeafRootMenu &&\n menuStyle?.maxHeight !== undefined &&\n !externalOverflow &&\n 'ald-menu-scrollable',\n )}\n position={externalOverflow ? 'initial' : 'auto'}\n aria-label={ariaLabel}\n captureFocus\n menuItemFocus={menuItemFocus}\n onKeyDown={onMenuKeyDown}\n onClose={({ reason }) => onRequestClose?.(reason)}\n viewScroll={externalOverflow ? 'initial' : 'auto'}\n overflow={externalOverflow ? 'visible' : menuOverflow}\n >\n {getMenuItems(items || [])}\n </ControlledMenu>\n </>\n );\n}\n\nexport function isHeaderMenuItem(\n menuItem: MenuItemType,\n): menuItem is MenuHeaderType {\n return 'type' in menuItem && menuItem.type === 'header';\n}\n\nexport function isDivideMenuItem(\n menuItem: MenuItemType,\n): menuItem is MenuDividerType {\n return 'type' in menuItem && menuItem.type === 'divider';\n}\n\nexport function isSubMenuItem(menuItem: MenuItemType): menuItem is SubMenuType {\n return 'children' in menuItem && !!menuItem.children;\n}\n\nexport function isLeafMenu(menuItems: MenuItemType[]) {\n return !_.some(menuItems, (item) => isSubMenuItem(item));\n}\n\nexport type MenuItemType =\n | ItemType\n | MenuDividerType\n | SubMenuType\n | MenuHeaderType;\n\nexport interface ItemType {\n key: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n onClick?: (info: MenuInfo) => void;\n danger?: boolean;\n disabled?: boolean;\n className?: string;\n ariaLabel?: string;\n}\n\nexport interface MenuDividerType {\n key?: string;\n type: 'divider';\n}\n\nexport interface MenuHeaderType {\n key?: string;\n type: 'header';\n label: string;\n}\n\nexport interface SubMenuType {\n children: MenuItemType[];\n key: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n popupClassName?: string;\n disabled?: boolean;\n ariaLabel?: string;\n}\n\nexport interface MenuInfo {\n key: string;\n keyPath: string[];\n domEvent: MouseEvent | KeyboardEvent;\n // 阻止菜单关闭\n keepOpen?: boolean;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/Menu/index.tsx"],"sourcesContent":["import {\n ControlledMenu,\n MenuDivider,\n MenuHeader,\n MenuItem,\n SubMenu,\n} from '@szhsin/react-menu';\nimport { cn } from '../lib/utils';\nimport _ from 'lodash';\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { CheckLightLine, ChevronRightLine } from '../Icon';\nimport './menu.css';\n\nexport interface MenuProps {\n onClick?: (info: MenuInfo) => void;\n onBeforeLeafItemClick?: () => void;\n className?: string;\n items: MenuItemType[];\n selectedKeys?: string[];\n menuStyle?: React.CSSProperties;\n /** Accessible name for the command menu when its context is not otherwise clear. */\n ariaLabel?: string;\n /** @internal Requested initial keyboard position when the popup opens. */\n initialFocus?: 'first' | 'last';\n /** @internal Whether the current popup entry originated from keyboard input. */\n keyboardFocus?: boolean;\n /** @internal Whether opening the popup should transfer focus to an item. */\n captureFocus?: boolean;\n /** @internal Requests that the owning popup close, including on Escape. */\n onRequestClose?: (reason: 'click' | 'cancel' | 'blur' | 'scroll') => void;\n rootClosing?: boolean;\n /** @internal 由 Dropdown 注入,禁用内部定位/overflow 计算。不支持含 SubMenu 的菜单 */\n externalOverflow?: boolean;\n /** @internal Dropdown 已按实际裁剪区域计算根菜单高度,保留其限制。 */\n preserveRootHeight?: boolean;\n}\n\nexport default function Menu(props: MenuProps) {\n const {\n className,\n items,\n onClick: onMenuClick,\n onBeforeLeafItemClick,\n selectedKeys,\n menuStyle,\n ariaLabel,\n initialFocus = 'first',\n keyboardFocus = false,\n captureFocus = true,\n onRequestClose,\n rootClosing = false,\n externalOverflow,\n preserveRootHeight = false,\n } = props;\n const selectedKeySet = new Set(selectedKeys || []);\n const typeaheadRef = useRef({ query: '', timestamp: 0 });\n const [isKeyboardFocus, setIsKeyboardFocus] = useState(keyboardFocus);\n\n useEffect(() => {\n setIsKeyboardFocus(keyboardFocus);\n }, [keyboardFocus]);\n\n const menuItemFocus = useMemo(\n () => (captureFocus ? { position: initialFocus } : undefined),\n [captureFocus, initialFocus],\n );\n const onMenuKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLElement>) => {\n if (\n event.defaultPrevented ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey ||\n event.key.length !== 1\n ) {\n return;\n }\n\n const now = Date.now();\n const previous = typeaheadRef.current;\n const query = `${now - previous.timestamp > 500 ? '' : previous.query}${\n event.key\n }`.toLocaleLowerCase();\n typeaheadRef.current = { query, timestamp: now };\n\n const menuItems = Array.from(\n event.currentTarget.querySelectorAll<HTMLElement>(\n '[role=\"menuitem\"]:not([aria-hidden=\"true\"]):not([aria-disabled=\"true\"])',\n ),\n );\n const activeIndex = menuItems.indexOf(\n document.activeElement as HTMLElement,\n );\n const orderedItems = [\n ...menuItems.slice(activeIndex + 1),\n ...menuItems.slice(0, activeIndex + 1),\n ];\n const match = orderedItems.find((item) => {\n const accessibleName =\n item.getAttribute('aria-label') || item.textContent || '';\n return accessibleName.trim().toLocaleLowerCase().startsWith(query);\n });\n\n if (match) {\n event.preventDefault();\n match.focus();\n }\n },\n [],\n );\n\n const hasDirectSelectedItem = (items: MenuItemType[]): boolean =>\n items.some((item) => {\n if (isSubMenuItem(item)) {\n return selectedKeySet.has(item.key);\n }\n if (isHeaderMenuItem(item) || isDivideMenuItem(item)) {\n return false;\n }\n return selectedKeySet.has(item.key);\n });\n\n const getMenuItems = (items: MenuItemType[], parentPath: string[] = []) => {\n const isSelectableGroup = hasDirectSelectedItem(items);\n\n return items.map((item, index) => {\n if (isDivideMenuItem(item)) {\n return <MenuDivider key={`divider_${index}`} />;\n }\n if (isHeaderMenuItem(item)) {\n return <MenuHeader key={`header_${index}`}>{item.label}</MenuHeader>;\n }\n const currPath = [...parentPath, item.key];\n const titleStr = _.isString(item.label) ? item.label : '';\n const isSelected = selectedKeys?.includes(item.key);\n\n if (isSubMenuItem(item)) {\n const isLeafSubMenu = isLeafMenu(item.children);\n const subMenuOverflow =\n isLeafSubMenu || menuStyle?.maxHeight !== undefined\n ? 'auto'\n : undefined;\n const subMenuItemLabel = (\n <div className=\"ald-menu-item-label\" title={titleStr}>\n {item.icon ? (\n <div className=\"ald-menu-item-label-icon\">{item.icon}</div>\n ) : null}\n <div className=\"ald-menu-item-label-text tw-text-typography-body-dense\">\n {item.label}\n </div>\n <div className=\"ald-menu-item-right\">\n {isSelectableGroup ? (\n <div className=\"ald-menu-item-selection-slot\" />\n ) : null}\n <ChevronRightLine size={16} />\n </div>\n </div>\n );\n\n return (\n <SubMenu\n key={item.key}\n label={subMenuItemLabel}\n disabled={item.disabled}\n aria-label={item.ariaLabel || titleStr || undefined}\n itemProps={{\n className: isLeafSubMenu\n ? 'ald-menu-submenu-leaf-trigger'\n : undefined,\n onPointerMove: () => setIsKeyboardFocus(false),\n }}\n align=\"start\"\n direction=\"right\"\n gap={-8}\n position=\"anchor\"\n shift={0}\n menuStyle={{\n ...menuStyle,\n }}\n menuClassName={cn(\n 'ald-menu-submenu-popup',\n isLeafSubMenu && 'ald-menu-submenu-popup-leaf',\n !isLeafSubMenu &&\n menuStyle?.maxHeight !== undefined &&\n 'ald-menu-submenu-popup-scrollable',\n item.popupClassName,\n isKeyboardFocus && 'ald-menu-focus-visible',\n rootClosing && 'ald-menu-submenu-popup-root-closing',\n )}\n overflow={subMenuOverflow}\n >\n {getMenuItems(item.children, currPath)}\n </SubMenu>\n );\n }\n\n return (\n <MenuItem\n key={item.key}\n className={cn(item.className, {\n 'ald-menu-item-danger': item.danger,\n 'ald-menu-item-selected': isSelected,\n })}\n disabled={item.disabled}\n aria-label={item.ariaLabel || titleStr || undefined}\n onPointerMove={() => setIsKeyboardFocus(false)}\n onClick={({ syntheticEvent }) => {\n onBeforeLeafItemClick?.();\n const menuInfo = {\n key: item.key,\n keyPath: currPath,\n domEvent: syntheticEvent,\n };\n if (item.onClick) {\n item.onClick(menuInfo);\n }\n if (onMenuClick) {\n onMenuClick(menuInfo);\n }\n }}\n >\n <div className=\"ald-menu-item-label\" title={titleStr}>\n {item.icon ? (\n <div className=\"ald-menu-item-label-icon\">{item.icon}</div>\n ) : null}\n <div className=\"ald-menu-item-label-text tw-text-typography-body-dense\">\n {item.label}\n </div>\n {isSelectableGroup ? (\n <div className=\"ald-menu-item-right\">\n <div\n className={cn(\n 'ald-menu-item-selection-slot',\n isSelected &&\n 'ald-menu-item-selection-slot-selected ald-menu-item-selected-icon',\n )}\n >\n {isSelected ? <CheckLightLine /> : null}\n </div>\n </div>\n ) : null}\n </div>\n </MenuItem>\n );\n });\n };\n\n const ref = useRef(null);\n\n const isLeafRootMenu = isLeafMenu(items);\n const menuOverflow =\n isLeafRootMenu || menuStyle?.maxHeight !== undefined ? 'auto' : undefined;\n\n return (\n <>\n <div style={{ display: 'none' }} ref={ref} />\n <ControlledMenu\n state=\"open\"\n transition={{ open: true, close: true }}\n transitionTimeout={200}\n submenuOpenDelay={0}\n submenuCloseDelay={0}\n // Root positioning is owned by the surrounding surface (see menu.css).\n // Keep an explicit caller height: React-Menu otherwise replaces it with\n // its viewport limit. Descendant SubMenus still calculate their own overflow.\n setDownOverflow={preserveRootHeight}\n menuStyle={{\n ...menuStyle,\n }}\n anchorRef={ref}\n className={cn(\n className,\n 'ald-menu',\n isKeyboardFocus && 'ald-menu-focus-visible',\n isLeafRootMenu && !externalOverflow && 'ald-menu-leaf',\n !isLeafRootMenu &&\n menuStyle?.maxHeight !== undefined &&\n !externalOverflow &&\n 'ald-menu-scrollable',\n )}\n position={externalOverflow ? 'initial' : 'auto'}\n aria-label={ariaLabel}\n captureFocus={captureFocus}\n menuItemFocus={menuItemFocus}\n onKeyDown={(event) => {\n setIsKeyboardFocus(true);\n onMenuKeyDown(event);\n }}\n onClose={({ reason }) => onRequestClose?.(reason)}\n viewScroll={externalOverflow ? 'initial' : 'auto'}\n overflow={externalOverflow ? 'visible' : menuOverflow}\n >\n {getMenuItems(items || [])}\n </ControlledMenu>\n </>\n );\n}\n\nexport function isHeaderMenuItem(\n menuItem: MenuItemType,\n): menuItem is MenuHeaderType {\n return 'type' in menuItem && menuItem.type === 'header';\n}\n\nexport function isDivideMenuItem(\n menuItem: MenuItemType,\n): menuItem is MenuDividerType {\n return 'type' in menuItem && menuItem.type === 'divider';\n}\n\nexport function isSubMenuItem(menuItem: MenuItemType): menuItem is SubMenuType {\n return 'children' in menuItem && !!menuItem.children;\n}\n\nexport function isLeafMenu(menuItems: MenuItemType[]) {\n return !_.some(menuItems, (item) => isSubMenuItem(item));\n}\n\nexport type MenuItemType =\n | ItemType\n | MenuDividerType\n | SubMenuType\n | MenuHeaderType;\n\nexport interface ItemType {\n key: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n onClick?: (info: MenuInfo) => void;\n danger?: boolean;\n disabled?: boolean;\n className?: string;\n ariaLabel?: string;\n}\n\nexport interface MenuDividerType {\n key?: string;\n type: 'divider';\n}\n\nexport interface MenuHeaderType {\n key?: string;\n type: 'header';\n label: string;\n}\n\nexport interface SubMenuType {\n children: MenuItemType[];\n key: string;\n label: React.ReactNode;\n icon?: React.ReactNode;\n popupClassName?: string;\n disabled?: boolean;\n ariaLabel?: string;\n}\n\nexport interface MenuInfo {\n key: string;\n keyPath: string[];\n domEvent: MouseEvent | KeyboardEvent;\n // 阻止菜单关闭\n keepOpen?: boolean;\n}\n"],"mappings":"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"}
|