@zerodep-dev/vue-tsx-pro-ui 0.1.0 → 0.2.0
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/code-editor/CodeEditor.d.ts +18 -0
- package/dist/code-editor/CodeEditor.js +57 -0
- package/dist/code-editor/CodeEditor.js.map +1 -0
- package/dist/code-editor/editor.d.ts +52 -0
- package/dist/code-editor/editor.js +225 -0
- package/dist/code-editor/editor.js.map +1 -0
- package/dist/code-editor/index.d.ts +9 -0
- package/dist/code-editor/index.js +3 -0
- package/dist/config/lang/en-US.js +21 -3
- package/dist/config/lang/en-US.js.map +1 -1
- package/dist/config/lang/format.d.ts +2 -1
- package/dist/config/lang/format.js +9 -2
- package/dist/config/lang/format.js.map +1 -1
- package/dist/config/lang/lang.d.ts +19 -6
- package/dist/config/lang/lang.js +11 -2
- package/dist/config/lang/lang.js.map +1 -1
- package/dist/config/lang/zh-CN.js +21 -3
- package/dist/config/lang/zh-CN.js.map +1 -1
- package/dist/diagram/index.d.ts +2 -1
- package/dist/diagram/index.js +2 -1
- package/dist/diagram/layout.d.ts +57 -0
- package/dist/diagram/layout.js +50 -0
- package/dist/diagram/layout.js.map +1 -0
- package/dist/form/Field.d.ts +7 -0
- package/dist/form/Field.js +14 -3
- package/dist/form/Field.js.map +1 -1
- package/dist/form/useForm.d.ts +4 -1
- package/dist/form/useForm.js +35 -13
- package/dist/form/useForm.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +8 -5
- package/dist/input/Combobox.js +1 -1
- package/dist/input/Combobox.js.map +1 -1
- package/dist/input/Mentions.js +1 -1
- package/dist/input/Mentions.js.map +1 -1
- package/dist/input/PeoplePickerPanel.js +11 -11
- package/dist/input/PeoplePickerPanel.js.map +1 -1
- package/dist/input/Select.js +1 -1
- package/dist/input/Select.js.map +1 -1
- package/dist/input/TreeSelect.js +1 -1
- package/dist/input/TreeSelect.js.map +1 -1
- package/dist/input/Upload.js +2 -1
- package/dist/input/Upload.js.map +1 -1
- package/dist/input/condition.d.ts +1 -1
- package/dist/input/condition.js +32 -14
- package/dist/input/condition.js.map +1 -1
- package/dist/input/people.d.ts +15 -14
- package/dist/input/people.js +5 -5
- package/dist/input/people.js.map +1 -1
- package/dist/input/remote-items.d.ts +4 -2
- package/dist/input/remote-items.js +7 -2
- package/dist/input/remote-items.js.map +1 -1
- package/dist/patterns/QueryTable.d.ts +3 -0
- package/dist/patterns/QueryTable.js +39 -14
- package/dist/patterns/QueryTable.js.map +1 -1
- package/dist/patterns/messages.d.ts +2 -0
- package/dist/patterns/messages.js +2 -0
- package/dist/patterns/messages.js.map +1 -1
- package/dist/utils/data/clone.d.ts +8 -0
- package/dist/utils/data/clone.js +21 -0
- package/dist/utils/data/clone.js.map +1 -0
- package/dist/utils/data/date.d.ts +10 -0
- package/dist/utils/data/date.js +13 -1
- package/dist/utils/data/date.js.map +1 -1
- package/dist/utils/data/numeric.d.ts +7 -0
- package/dist/utils/data/numeric.js +46 -1
- package/dist/utils/data/numeric.js.map +1 -1
- package/dist/utils/dom/download.d.ts +8 -1
- package/dist/utils/dom/download.js +19 -2
- package/dist/utils/dom/download.js.map +1 -1
- package/dist/utils/request/error-message.d.ts +17 -0
- package/dist/utils/request/error-message.js +50 -0
- package/dist/utils/request/error-message.js.map +1 -0
- package/dist/utils/request/http.d.ts +68 -4
- package/dist/utils/request/http.js +116 -23
- package/dist/utils/request/http.js.map +1 -1
- package/dist/utils/request/use-autosave.d.ts +35 -0
- package/dist/utils/request/use-autosave.js +69 -0
- package/dist/utils/request/use-autosave.js.map +1 -0
- package/package.json +57 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PeoplePickerPanel.js","names":["_isVNode","_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vtp_untrack","_vue_ref","_vue_shallowRef","_vue_computed","_createVNode","_mergeProps"],"sources":["../../src/input/PeoplePickerPanel.tsx"],"sourcesContent":["/**\n * 人员与组织选择面板:左边按组织架构一层层找(面包屑回到上层),或搜索、看岗位与最近选过的,勾选;右边是已选的,可以移除、清空。\n * PeoplePicker 把它放进对话框;也可以直接放在页面、抽屉里。数据见 people.ts(source)。\n *\n * <PeoplePickerPanel source={source} _value={members} types={['user', 'department']} recentKey=\"project-members\" />\n *\n * - 部门的“下级”进到下一层(不能选部门时点整行也是进去),面包屑点哪一级回到哪一级。\n * - 搜索框有字时列表换成搜索结果(输入停下 300 ms 再搜,只采用最后一次),清空回到原来的页。\n * - types 决定能勾哪几种;max 限制个数,1 时勾一个就换掉原来的。勾过的记进“最近”(recentKey,存在浏览器里,最多 20 个)。\n * - 多选时列表上方有“全选”,只管这一页能勾的。\n *\n * 读屏:左右两个列表各有名称;每行的勾选框以名字为名;“下级”按钮的名称是“下级:某部门”,进去后焦点落到列表上。\n * 部件:Root(div)、Browse(左边)、Selected(右边)。\n */\nimport { Building, BriefcaseBusiness, ChevronRight, Search, X } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { nextTick, ref, watch, type Ref, type VNodeChild } from 'vue';\nimport { Button } from '../base/Button';\nimport { Icon } from '../base/Icon';\nimport { IconButton } from '../base/IconButton';\nimport { Spinner } from '../base/Spinner';\nimport { Small, Strong } from '../base/Text';\nimport { useLang } from '../config/context';\nimport { Avatar } from '../display/Avatar';\nimport { ItemList, ItemListItem } from '../display/ItemList';\nimport { Breadcrumbs } from '../navigation/Breadcrumbs';\nimport { usePersistentState } from '../utils/dom/storage';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { useRequest } from '../utils/request/use-request';\nimport { Checkbox } from './Checkbox';\nimport {\n peopleKey,\n toPeopleValue,\n type PeopleEntry,\n type PeopleSource,\n type PeopleType,\n} from './people';\nimport { TextField, type TextFieldProps } from './TextField';\nimport { ToggleGroup, ToggleItem } from './ToggleGroup';\n\n/** 未声明的属性落到外层 div 上 */\nexport interface PeoplePickerPanelProps extends PartProps<'div'> {\n /** 数据来源 */\n source: PeopleSource;\n /** 选中的:父级写 _value={list} 双向绑定 */\n _value?: readonly PeopleEntry[];\n /** 能选哪几种,默认只有人员 */\n types?: readonly PeopleType[];\n /** 最多选几个;1 时勾一个就换掉原来的 */\n max?: number;\n /** 记“最近选过的”用的键(存在浏览器里);不写时没有“最近”页 */\n recentKey?: string;\n /** 两边列表的高度(CSS 长度),默认 '20rem' */\n listHeight?: string;\n /** 左边的部件属性 */\n propsBrowse?: PartProps<'div'>;\n /** 右边的部件属性 */\n propsSelected?: PartProps<'div'>;\n /** 搜索框的部件属性 */\n propsSearch?: Partial<TextFieldProps>;\n}\n\ntype Page = 'org' | 'positions' | 'recent';\n\n/** “最近”最多记几个 */\nconst RECENT_LIMIT = 20;\n\n/** PeoplePickerPanel 的样式:root、grid、browse、selected、header、list、start */\nexport const peoplePickerPanelStyles = css.create({\n // 按自己的宽度决定左右排还是上下排\n root: { containerType: 'inline-size', minWidth: 0 },\n grid: {\n display: 'grid',\n gap: '_md',\n gridTemplateColumns: {\n default: 'minmax(0, 1fr)',\n '@container (min-width: 36rem)': 'minmax(0, 3fr) minmax(0, 2fr)',\n },\n },\n browse: { display: 'flex', flexDirection: 'column', gap: '_sm', minWidth: 0 },\n selected: {\n display: 'flex',\n flexDirection: 'column',\n gap: '_sm',\n minWidth: 0,\n padding: '_sm',\n backgroundColor: '_surfaceMuted',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_borderMuted',\n borderRadius: '_md',\n },\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '_sm',\n minHeight: '2em',\n },\n list: { overflowY: 'auto', outlineStyle: 'none', minWidth: 0 },\n start: { display: 'flex', alignItems: 'center', gap: '_sm' },\n status: { display: 'flex', justifyContent: 'center', paddingBlock: '_lg', color: '_textMuted' },\n});\n\n/** 随属性变的样式:列表的高度 */\nexport const peoplePickerPanelDynamicStyles = css.create({\n height: (height: string) => ({ height }),\n});\n\n/** 人员与组织选择面板 */\nexport const PeoplePickerPanel = _component(\n ({\n source,\n _value: value = _bindable<readonly PeopleEntry[]>([]),\n types = ['user'],\n max,\n recentKey,\n listHeight = '20rem',\n propsBrowse,\n propsSelected,\n propsSearch,\n ...rest\n }: PeoplePickerPanelProps) => {\n const lang = useLang();\n // 存的键只在创建时取:换键要换一个面板\n const key = _untrack(recentKey);\n let recent = _fromRef(\n key\n ? usePersistentState<PeopleEntry[]>(`vtp.people.recent.${key}`, [])\n : (ref<PeopleEntry[]>([]) as Ref<PeopleEntry[]>),\n );\n let page = _state<Page>('org');\n let query = _state('');\n let path = _state<PeopleEntry[]>([]);\n let list = _state.raw<HTMLElement | null>(null);\n\n const searching = () => !!source.search && query.trim() !== '';\n const level = useRequest(\n (parent: PeopleEntry | null, signal) => source.children(parent, signal),\n { params: () => path.at(-1) ?? null, keepPrevious: false },\n );\n const found = useRequest((text: string, signal) => source.search!(text, signal), {\n params: () => query.trim(),\n debounce: 300,\n immediate: false,\n keepPrevious: false,\n enabled: searching,\n });\n const positions = useRequest((_: undefined, signal) => source.positions!(signal), {\n immediate: false,\n });\n watch(\n () => page,\n (next) => {\n if (next === 'positions' && positions.data == null && !positions.loading) {\n void positions.refresh();\n }\n },\n );\n\n const pages = (): Page[] => [\n 'org',\n ...(source.positions ? (['positions'] as const) : []),\n ...(key ? (['recent'] as const) : []),\n ];\n const pageLabel: Record<Page, () => string> = {\n org: () => lang.peopleOrg,\n positions: () => lang.peoplePositions,\n recent: () => lang.peopleRecent,\n };\n\n /** 现在列出的:搜索结果,或当前页 */\n const shown = (): { items: readonly PeopleEntry[]; loading: boolean } => {\n if (searching()) return { items: found.data ?? [], loading: found.loading };\n if (page === 'positions') return { items: positions.data ?? [], loading: positions.loading };\n if (page === 'recent') return { items: recent, loading: false };\n return { items: level.data ?? [], loading: level.loading };\n };\n\n const selectedKeys = _derived(new Set(value.map(peopleKey)));\n const pickable = (entry: PeopleEntry) => types.includes(entry.type);\n const drillable = (entry: PeopleEntry) =>\n entry.type === 'department' && entry.hasChildren !== false;\n const full = () => max != null && max > 1 && value.length >= max;\n\n function remember(entry: PeopleEntry) {\n if (!key) return;\n recent = [\n toPeopleValue(entry),\n ...recent.filter((item) => peopleKey(item) !== peopleKey(entry)),\n ].slice(0, RECENT_LIMIT);\n }\n\n function toggle(entry: PeopleEntry) {\n const id = peopleKey(entry);\n if (selectedKeys.has(id)) {\n value = value.filter((item) => peopleKey(item) !== id);\n return;\n }\n if (max === 1) value = [toPeopleValue(entry)];\n else if (full()) return;\n else value = [...value, toPeopleValue(entry)];\n remember(entry);\n }\n\n /** 全选这一页能勾的;都勾上了时全部取消。有上限时加到满为止 */\n function toggleAll(items: readonly PeopleEntry[]) {\n const candidates = items.filter(pickable);\n if (candidates.every((entry) => selectedKeys.has(peopleKey(entry)))) {\n const drop = new Set(candidates.map(peopleKey));\n value = value.filter((item) => !drop.has(peopleKey(item)));\n return;\n }\n const adding = candidates.filter((entry) => !selectedKeys.has(peopleKey(entry)));\n const room = max != null ? Math.max(0, max - value.length) : adding.length;\n value = [...value, ...adding.slice(0, room).map(toPeopleValue)];\n }\n\n /** 进到一个部门:从搜索结果进去时面包屑只有它(不知道它的上级) */\n function open(department: PeopleEntry) {\n path = searching() ? [department] : [...path, department];\n query = '';\n page = 'org';\n void nextTick(() => list?.focus());\n }\n\n function onRowClick(entry: PeopleEntry, event: MouseEvent) {\n // 勾选框、按钮自己处理\n if ((event.target as Element).closest('button,input,label,a')) return;\n if (pickable(entry)) toggle(entry);\n else if (drillable(entry)) open(entry);\n }\n\n /** 行首的圆形标记:人员是头像,部门、岗位是图标 */\n const badge = (entry: PeopleEntry): VNodeChild =>\n entry.type === 'user' ? (\n <Avatar name={entry.name} src={entry.avatar} size=\"sm\" />\n ) : (\n <Avatar\n size=\"sm\"\n fallback={<Icon icon={entry.type === 'department' ? Building : BriefcaseBusiness} />}\n />\n );\n\n function renderRow(entry: PeopleEntry): VNodeChild {\n const id = peopleKey(entry);\n const checked = selectedKeys.has(id);\n return (\n <ItemListItem\n key={id}\n start={\n <span {...css.attrs(peoplePickerPanelStyles.start)}>\n {pickable(entry) && (\n <Checkbox\n _checked={checked}\n onUpdate:_checked={() => toggle(entry)}\n disabled={!checked && full()}\n aria-label={entry.name}\n />\n )}\n {badge(entry)}\n </span>\n }\n title={entry.name}\n description={entry.description}\n end={\n drillable(entry) && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n iconEnd={ChevronRight}\n aria-label={lang.peopleOpen(entry.name)}\n onClick={() => open(entry)}\n >\n {lang.peopleChildren}\n </Button>\n )\n }\n onClick={(event: MouseEvent) => onRowClick(entry, event)}\n />\n );\n }\n\n function renderBrowse(): VNodeChild {\n const { items, loading } = shown();\n const candidates = items.filter(pickable);\n const checkedCount = candidates.filter((entry) => selectedKeys.has(peopleKey(entry))).length;\n const height = peoplePickerPanelDynamicStyles.height(listHeight);\n return (\n <div {...mergeAttrs<'div'>({ sx: peoplePickerPanelStyles.browse }, propsBrowse)}>\n {source.search && (\n <TextField\n {...mergePartProps<TextFieldProps>(\n {\n icon: Search,\n placeholder: lang.peopleSearch,\n 'aria-label': lang.peopleSearch,\n clearable: true,\n },\n propsSearch,\n )}\n _value={query}\n />\n )}\n {!searching() && pages().length > 1 && (\n <ToggleGroup\n _value={page}\n onUpdate:_value={(next) => next && (page = next as Page)}\n required\n size=\"sm\"\n aria-label={lang.peoplePages}\n >\n {pages().map((item) => (\n <ToggleItem key={item} value={item}>\n {pageLabel[item]()}\n </ToggleItem>\n ))}\n </ToggleGroup>\n )}\n {!searching() && page === 'org' && (\n <Breadcrumbs\n items={[\n { key: '', label: lang.peopleRoot },\n ...path.map((department) => ({ key: department.id, label: department.name })),\n ]}\n onNavigate={(_, index) => (path = path.slice(0, index))}\n />\n )}\n {max !== 1 && candidates.length > 1 && (\n <Checkbox\n _checked={checkedCount === candidates.length}\n indeterminate={checkedCount > 0 && checkedCount < candidates.length}\n onUpdate:_checked={() => toggleAll(items)}\n size=\"sm\"\n >\n {lang.peopleSelectAll}\n </Checkbox>\n )}\n <div\n {...css.attrs(peoplePickerPanelStyles.list, height)}\n ref={(element: unknown) => (list = element as HTMLElement | null)}\n tabindex={-1}\n >\n {loading ? (\n <div {...css.attrs(peoplePickerPanelStyles.status)}>\n <Spinner label={lang.loading} />\n </div>\n ) : items.length === 0 ? (\n <Small sx={peoplePickerPanelStyles.status}>\n {searching() ? lang.peopleNoResult : lang.peopleEmpty}\n </Small>\n ) : (\n <ItemList\n hoverable\n density=\"compact\"\n aria-label={pageLabel[searching() ? 'org' : page]()}\n >\n {items.map(renderRow)}\n </ItemList>\n )}\n </div>\n </div>\n );\n }\n\n function renderSelected(): VNodeChild {\n return (\n <div {...mergeAttrs<'div'>({ sx: peoplePickerPanelStyles.selected }, propsSelected)}>\n <div {...css.attrs(peoplePickerPanelStyles.header)}>\n <Strong size=\"sm\">{lang.peopleCount(value.length, max)}</Strong>\n {value.length > 0 && (\n <Button variant=\"link\" size=\"sm\" onClick={() => (value = [])}>\n {lang.clear}\n </Button>\n )}\n </div>\n <div\n {...css.attrs(\n peoplePickerPanelStyles.list,\n peoplePickerPanelDynamicStyles.height(listHeight),\n )}\n >\n {value.length === 0 ? (\n <Small sx={peoplePickerPanelStyles.status}>{lang.peopleNothingSelected}</Small>\n ) : (\n <ItemList density=\"compact\" aria-label={lang.peopleSelected}>\n {value.map((entry) => (\n <ItemListItem\n key={peopleKey(entry)}\n start={badge(entry)}\n title={entry.name}\n description={entry.description ?? lang.peopleTypes[entry.type]}\n end={\n <IconButton\n icon={X}\n variant=\"ghost\"\n size=\"sm\"\n aria-label={lang.peopleRemove(entry.name)}\n onClick={() => toggle(entry)}\n />\n }\n />\n ))}\n </ItemList>\n )}\n </div>\n </div>\n );\n }\n\n return () => (\n <div {...mergeAttrs<'div'>({ sx: peoplePickerPanelStyles.root }, rest)}>\n <div {...css.attrs(peoplePickerPanelStyles.grid)}>\n {renderBrowse()}\n {renderSelected()}\n </div>\n </div>\n );\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,SAAA,QAAA,GAAA;CAAA,OAAA,OAAA,MAAA,cAAA,OAAA,UAAA,SAAA,KAAA,CAAA,MAAA,qBAAA,CAAAA,QAAA,CAAA;AAAA;;AAyBA,IAAM,eAAe;;AAGrB,IAAa,0BAA0B,IAAI,OAAO;CAEhD,MAAM;EAAE,eAAe;EAAe,UAAU;CAAE;CAClD,MAAM;EACJ,SAAS;EACT,KAAK;EACL,qBAAqB;GACnB,SAAS;GACT,iCAAiC;EACnC;CACF;CACA,QAAQ;EAAE,SAAS;EAAQ,eAAe;EAAU,KAAK;EAAO,UAAU;CAAE;CAC5E,UAAU;EACR,SAAS;EACT,eAAe;EACf,KAAK;EACL,UAAU;EACV,SAAS;EACT,iBAAiB;EACjB,aAAa;EACb,aAAa;EACb,aAAa;EACb,cAAc;CAChB;CACA,QAAQ;EACN,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,KAAK;EACL,WAAW;CACb;CACA,MAAM;EAAE,WAAW;EAAQ,cAAc;EAAQ,UAAU;CAAE;CAC7D,OAAO;EAAE,SAAS;EAAQ,YAAY;EAAU,KAAK;CAAM;CAC3D,QAAQ;EAAE,SAAS;EAAQ,gBAAgB;EAAU,cAAc;EAAO,OAAO;CAAa;AAChG,CAAC;;AAGD,IAAa,iCAAiC,IAAI,OAAO,EACvD,SAAS,YAAoB,EAAE,OAAO,GACxC,CAAC;;AAGD,IAAa,oBAAoB,0BAC/B,QAAA,SAW8B;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,SAAAC,aAAA,QAAA,QAAA;CAAA,MAAA,QAAAC,SAAA,QAAA,gBATsB,CAAA,GAAE,KAAA,IAAA;CAAA,MAAA,QAAAD,aAAA,QAAA,eAC5C,CAAC,MAAM,CAAC;CAAA,MAAA,MAAAA,aAAA,QAAA,KAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,cAGH,OAAO;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,gBAAAA,aAAA,QAAA,eAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAIjB,OAAIE,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,OAAO,QAAQ;CAErB,MAAM,MAAGC,cAAA,UAAA,KAAA;CACT,IAAI,SACF,MACI,mBAAkC,qBAAqB,OAAO,CAAA,CAAE,IAC/D,IAAmB,CAAA,CAAE;CAE5B,IAAI,OAAIC,IAAgB,KAAK;CAC7B,IAAI,QAAKA,IAAU,EAAE;CACrB,IAAI,OAAIA,IAAyB,CAAA,CAAE;CACnC,IAAI,OAAIC,WAAkC,IAAI;CAE9C,MAAM,kBAAkB,CAAC,CAAC,OAAA,MAAO,UAAU,MAAA,MAAM,KAAK,MAAM;CAC5D,MAAM,QAAQ,YACX,QAA4B,WAAW,OAAA,MAAO,SAAS,QAAQ,MAAM,GACtE;EAAE,cAAc,KAAA,MAAK,GAAG,EAAE,KAAK;EAAM,cAAc;CAAM,CAC3D;CACA,MAAM,QAAQ,YAAY,MAAc,WAAW,OAAA,MAAO,OAAQ,MAAM,MAAM,GAAG;EAC/E,cAAc,MAAA,MAAM,KAAK;EACzB,UAAU;EACV,WAAW;EACX,cAAc;EACd,SAAS;CACX,CAAC;CACD,MAAM,YAAY,YAAY,GAAc,WAAW,OAAA,MAAO,UAAW,MAAM,GAAG,EAChF,WAAW,MACb,CAAC;CACD,YACE,KAAA,QACC,SAAS;EACR,IAAI,SAAS,eAAe,UAAU,QAAQ,QAAQ,CAAC,UAAU,SAC/D,UAAe,QAAQ;CAE3B,CACF;CAEA,MAAM,cAAsB;EAC1B;EACA,GAAI,OAAA,MAAO,YAAa,CAAC,WAAW,IAAc,CAAA;EAClD,GAAI,MAAO,CAAC,QAAQ,IAAc,CAAA;CAAG;CAEvC,MAAM,YAAwC;EAC5C,WAAW,KAAK;EAChB,iBAAiB,KAAK;EACtB,cAAc,KAAK;CACrB;;CAGA,MAAM,cAAmE;EACvE,IAAI,UAAU,GAAG,OAAO;GAAE,OAAO,MAAM,QAAQ,CAAA;GAAI,SAAS,MAAM;EAAQ;EAC1E,IAAI,KAAA,UAAS,aAAa,OAAO;GAAE,OAAO,UAAU,QAAQ,CAAA;GAAI,SAAS,UAAU;EAAQ;EAC3F,IAAI,KAAA,UAAS,UAAU,OAAO;GAAE,OAAK,OAAA;GAAU,SAAS;EAAM;EAC9D,OAAO;GAAE,OAAO,MAAM,QAAQ,CAAA;GAAI,SAAS,MAAM;EAAQ;CAC3D;CAEA,MAAM,eAAYC,eAAY,IAAI,IAAI,MAAA,MAAM,IAAI,SAAS,CAAC,CAAC;CAC3D,MAAM,YAAY,UAAuB,MAAA,MAAM,SAAS,MAAM,IAAI;CAClE,MAAM,aAAa,UACjB,MAAM,SAAS,gBAAgB,MAAM,gBAAgB;CACvD,MAAM,aAAa,IAAA,SAAO,QAAQ,IAAA,QAAM,KAAK,MAAA,MAAM,UAAM,IAAA;CAEzD,SAAS,SAAS,OAAoB;EACpC,IAAI,CAAC,KAAK;EACV,OAAA,QAAS,CACP,cAAc,KAAK,GACnB,GAAG,OAAA,MAAO,QAAQ,SAAS,UAAU,IAAI,MAAM,UAAU,KAAK,CAAC,CAAC,CACjE,CAAC,MAAM,GAAG,YAAY;CACzB;CAEA,SAAS,OAAO,OAAoB;EAClC,MAAM,KAAK,UAAU,KAAK;EAC1B,IAAI,aAAA,MAAa,IAAI,EAAE,GAAG;GACxB,MAAA,QAAQ,MAAA,MAAM,QAAQ,SAAS,UAAU,IAAI,MAAM,EAAE;GACrD;EACF;EACA,IAAI,IAAA,UAAQ,GAAG,MAAA,QAAQ,CAAC,cAAc,KAAK,CAAC;OACvC,IAAI,KAAK,GAAG;OACZ,MAAA,QAAQ,CAAC,GAAA,MAAA,OAAU,cAAc,KAAK,CAAC;EAC5C,SAAS,KAAK;CAChB;;CAGA,SAAS,UAAU,OAA+B;EAChD,MAAM,aAAa,MAAM,OAAO,QAAQ;EACxC,IAAI,WAAW,OAAO,UAAU,aAAA,MAAa,IAAI,UAAU,KAAK,CAAC,CAAC,GAAG;GACnE,MAAM,OAAO,IAAI,IAAI,WAAW,IAAI,SAAS,CAAC;GAC9C,MAAA,QAAQ,MAAA,MAAM,QAAQ,SAAS,CAAC,KAAK,IAAI,UAAU,IAAI,CAAC,CAAC;GACzD;EACF;EACA,MAAM,SAAS,WAAW,QAAQ,UAAU,CAAC,aAAA,MAAa,IAAI,UAAU,KAAK,CAAC,CAAC;EAC/E,MAAM,OAAO,IAAA,SAAO,OAAO,KAAK,IAAI,GAAG,IAAA,QAAM,MAAA,MAAM,MAAM,IAAI,OAAO;EACpE,MAAA,QAAQ,CAAC,GAAA,MAAA,OAAU,GAAG,OAAO,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI,aAAa,CAAC;CAChE;;CAGA,SAAS,KAAK,YAAyB;EACrC,KAAA,QAAO,UAAU,IAAI,CAAC,UAAU,IAAI,CAAC,GAAA,KAAA,OAAS,UAAU;EACxD,MAAA,QAAQ;EACR,KAAA,QAAO;EACP,eAAoB,KAAA,OAAM,MAAM,CAAC;CACnC;CAEA,SAAS,WAAW,OAAoB,OAAmB;EAEzD,IAAK,MAAM,OAAmB,QAAQ,sBAAsB,GAAG;EAC/D,IAAI,SAAS,KAAK,GAAG,OAAO,KAAK;OAC5B,IAAI,UAAU,KAAK,GAAG,KAAK,KAAK;CACvC;;CAGA,MAAM,SAAS,UACb,MAAM,SAAS,SAAMC,YAAA,QAAA;EAAA,QACL,MAAM;EAAI,OAAO,MAAM;EAAM,QAAA;CAAA,GAAA,IAAA,IAAAA,YAAA,QAAA;EAAA,QAAA;EAAA,YAAAA,YAAA,MAAA,EAAA,QAInB,MAAM,SAAS,eAAe,WAAW,kBAAiB,GAAA,IAAA;CAAA,GAAA,IAAA;CAItF,SAAS,UAAU,OAAgC;EACjD,MAAM,KAAK,UAAU,KAAK;EAC1B,MAAM,UAAU,aAAA,MAAa,IAAI,EAAE;EACnC,OAAAA,YAAA,cAAA;GAAA,OAES;GAAE,SAAAA,YAAA,QAEK,IAAI,MAAM,wBAAwB,KAAK,GAAC,CAC/C,SAAS,KAAK,KAACA,YAAA,UAAA;IAAA,YAEF;IAAO,2BACQ,OAAO,KAAK;IAAC,YAC5B,CAAC,WAAW,KAAK;IAAC,cAChB,MAAM;GAAI,GAAA,IAAA,GAGzB,MAAM,KAAK,CAAC,CAAA;GAAA,SAGV,MAAM;GAAI,eACJ,MAAM;GAAW,OAE5B,UAAU,KAAK,KAACA,YAAA,QAAA;IAAA,WAAA;IAAA,QAAA;IAAA,WAIH;IAAY,cACT,KAAK,WAAW,MAAM,IAAI;IAAC,iBACxB,KAAK,KAAK;GAAC,GAAA;IAAA,eAAA,CAEzB,KAAK,cAAc;IAAA,SAAA;GAAA,CAAA;GAEvB,YAEO,UAAsB,WAAW,OAAO,KAAK;EAAC,GAAA,IAAA;CAG9D;CAEA,SAAS,eAA2B;EAAA,IAAA,QAAA;EAClC,MAAM,EAAE,OAAO,YAAY,MAAM;EACjC,MAAM,aAAa,MAAM,OAAO,QAAQ;EACxC,MAAM,eAAe,WAAW,QAAQ,UAAU,aAAA,MAAa,IAAI,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC;EACtF,MAAM,SAAS,+BAA+B,OAAM,WAAA,KAAW;EAC/D,OAAAA,YAAA,OACW,WAAkB,EAAE,IAAI,wBAAwB,OAAO,GAAC,YAAA,KAAa,GAAC;GAC5E,OAAA,MAAO,UAAMA,YAAA,WAAAC,WAEN,eACF;IACE,MAAM;IACN,aAAa,KAAK;IAClB,cAAc,KAAK;IACnB,WAAW;GACb,GAAC,YAAA,KAEH,GAAC;IAAA,UAAA,MAAA;IAAA,mBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,MAAA,QAAA;GAAA,CAAA,GAAA,IAAA;GAIJ,CAAC,UAAU,KAAK,MAAM,CAAC,CAAC,SAAS,KAACD,YAAA,aAAA;IAAA,UAAA,KAAA;IAAA,mBAAA,OAAA,OAAA,OAAA,MAGb,SAAS,SAAS,KAAA,QAAO;IAAa,YAAA;IAAA,QAAA;IAAA,cAG5C,KAAK;GAAW,GAAA,QAAA,SAE3B,MAAM,CAAC,CAAC,KAAK,SAAI;IAAA,IAAA;IAAA,OAAAA,YAAA,YAAA;KAAA,OACC;KAAI,SAAS;IAAI,GAAA,QAAA,QAC/B,UAAU,KAAK,CAAC,CAAC,IAAA,QAAA,EAAA,eAAA,CAAA,KAAA,EAAA,CAAA;GAAA,CAErB,CAAC,IAAA,SAAA,EAAA,eAAA,CAAA,MAAA,EAAA,CAAA;GAGL,CAAC,UAAU,KAAK,KAAA,UAAS,SAAKA,YAAA,aAAA;IAAA,SAEpB,CACL;KAAE,KAAK;KAAI,OAAO,KAAK;IAAW,GAClC,GAAG,KAAA,MAAK,KAAK,gBAAgB;KAAE,KAAK,WAAW;KAAI,OAAO,WAAW;IAAK,EAAE,CAAC;IAC9E,cAAA,OAAA,OAAA,OAAA,MACY,GAAG,UAAW,KAAA,QAAO,KAAA,MAAK,MAAM,GAAG,KAAK;GAAE,GAAA,IAAA;GAG1D,IAAA,UAAQ,KAAK,WAAW,SAAS,KAACA,YAAA,UAAA;IAAA,YAErB,iBAAiB,WAAW;IAAM,iBAC7B,eAAe,KAAK,eAAe,WAAW;IAAM,2BAC1C,UAAU,KAAK;IAAC,QAAA;GAAA,GAAA;IAAA,eAAA,CAGxC,KAAK,eAAe;IAAA,SAAA;GAAA,CAAA;GAExBA,YAAA,OAAAC,WAEK,IAAI,MAAM,wBAAwB,MAAM,MAAM,GAAC;IAAA,OAAA,OAAA,OAAA,OAAA,MAC7C,YAAsB,KAAA,QAAO;IAA8B,YACvD;GAAE,CAAA,GAAA,CAEX,UAAOD,YAAA,OACG,IAAI,MAAM,wBAAwB,MAAM,GAAC,CAAAA,YAAA,SAAA,EAAA,SAChC,KAAK,QAAO,GAAA,IAAA,CAAA,CAAA,IAE5B,MAAM,WAAW,IAACA,YAAA,OAAA,EAAA,MACT,wBAAwB,OAAM,GAAA;IAAA,eAAA,CACtC,UAAU,IAAI,KAAK,iBAAiB,KAAK,WAAW;IAAA,SAAA;GAAA,CAAA,IAAAA,YAAA,UAAA;IAAA,aAAA;IAAA,WAAA;IAAA,cAMzC,UAAU,UAAU,IAAI,QAAK,KAAA,MAAQ,CAAC;GAAC,GAAA,QAAA,SAElD,MAAM,IAAI,SAAS,CAAC,IAAA,SAAA,EAAA,eAAA,CAAA,MAAA,EAAA,CAAA,CAExB,CAAA;EAAA,CAAA;CAIT;CAEA,SAAS,iBAA6B;EACpC,OAAAA,YAAA,OACW,WAAkB,EAAE,IAAI,wBAAwB,SAAS,GAAC,cAAA,KAAe,GAAC,CAAAA,YAAA,OACxE,IAAI,MAAM,wBAAwB,MAAM,GAAC,CAAAA,YAAA,QAAA,EAAA,QAAA,KAAA,GAAA;GAAA,eAAA,CAC7B,KAAK,YAAY,MAAA,MAAM,QAAM,IAAA,KAAK,CAAC;GAAA,SAAA;EAAA,CAAA,GACrD,MAAA,MAAM,SAAS,KAACA,YAAA,QAAA;GAAA,WAAA;GAAA,QAAA;GAAA,WAAA,OAAA,OAAA,OAAA,WACkC,MAAA,QAAQ,CAAA;EAAG,GAAA;GAAA,eAAA,CACzD,KAAK,KAAK;GAAA,SAAA;EAAA,CAAA,CAEd,CAAA,GAAAA,YAAA,OAGG,IAAI,MACN,wBAAwB,MACxB,+BAA+B,OAAM,WAAA,KAAW,CAClD,GAAC,CAEA,MAAA,MAAM,WAAW,IAACA,YAAA,OAAA,EAAA,MACN,wBAAwB,OAAM,GAAA;GAAA,eAAA,CAAG,KAAK,qBAAqB;GAAA,SAAA;EAAA,CAAA,IAAAA,YAAA,UAAA;GAAA,WAAA;GAAA,cAE9B,KAAK;EAAc,GAAA;GAAA,eAAA,CACxD,MAAA,MAAM,KAAK,UAAKA,YAAA,cAAA;IAAA,OAER,UAAU,KAAK;IAAC,SACd,MAAM,KAAK;IAAC,SACZ,MAAM;IAAI,eACJ,MAAM,eAAe,KAAK,YAAY,MAAM;IAAK,OAAAA,YAAA,YAAA;KAAA,QAGpD;KAAC,WAAA;KAAA,QAAA;KAAA,cAGK,KAAK,aAAa,MAAM,IAAI;KAAC,iBAC1B,OAAO,KAAK;IAAC,GAAA,IAAA;GAAA,GAAA,IAAA,CAInC,CAAC;GAAA,SAAA;EAAA,CAAA,CAEL,CAAA,CAAA,CAAA;CAIT;CAEA,aAAOA,YAAA,OACI,WAAkB,EAAE,IAAI,wBAAwB,KAAK,GAAG,IAAI,GAAC,CAAAA,YAAA,OAC3D,IAAI,MAAM,wBAAwB,IAAI,GAAC,CAC7C,aAAa,GACb,eAAe,CAAC,CAAA,CAAA,CAAA;AAIzB,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
|
1
|
+
{"version":3,"file":"PeoplePickerPanel.js","names":["_isVNode","_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vtp_untrack","_vue_ref","_vue_shallowRef","_vue_computed","_createVNode","_mergeProps"],"sources":["../../src/input/PeoplePickerPanel.tsx"],"sourcesContent":["/**\n * 人员与组织选择面板:左边按组织架构一层层找(面包屑回到上层),或搜索、看角色与最近选过的,勾选;右边是已选的,可以移除、清空。\n * PeoplePicker 把它放进对话框;也可以直接放在页面、抽屉里。数据见 people.ts(source)。\n *\n * <PeoplePickerPanel source={source} _value={members} types={['user', 'department']} recentKey=\"project-members\" />\n *\n * - 部门的“下级”进到下一层(不能选部门时点整行也是进去),面包屑点哪一级回到哪一级。\n * - 搜索框有字时列表换成搜索结果(输入停下 300 ms 再搜,只采用最后一次),清空回到原来的页。\n * - types 决定能勾哪几种;max 限制个数,1 时勾一个就换掉原来的。勾过的记进“最近”(recentKey,存在浏览器里,最多 20 个)。\n * - 多选时列表上方有“全选”,只管这一页能勾的。\n *\n * 读屏:左右两个列表各有名称;每行的勾选框以名字为名;“下级”按钮的名称是“下级:某部门”,进去后焦点落到列表上。\n * 部件:Root(div)、Browse(左边)、Selected(右边)。\n */\nimport { Building, BriefcaseBusiness, ChevronRight, Search, X } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { nextTick, ref, watch, type Ref, type VNodeChild } from 'vue';\nimport { Button } from '../base/Button';\nimport { Icon } from '../base/Icon';\nimport { IconButton } from '../base/IconButton';\nimport { Spinner } from '../base/Spinner';\nimport { Small, Strong } from '../base/Text';\nimport { useLang } from '../config/context';\nimport { Avatar } from '../display/Avatar';\nimport { ItemList, ItemListItem } from '../display/ItemList';\nimport { Breadcrumbs } from '../navigation/Breadcrumbs';\nimport { usePersistentState } from '../utils/dom/storage';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { useRequest } from '../utils/request/use-request';\nimport { Checkbox } from './Checkbox';\nimport {\n peopleKey,\n toPeopleValue,\n type PeopleEntry,\n type PeopleSource,\n type PeopleType,\n} from './people';\nimport { TextField, type TextFieldProps } from './TextField';\nimport { ToggleGroup, ToggleItem } from './ToggleGroup';\n\n/** 未声明的属性落到外层 div 上 */\nexport interface PeoplePickerPanelProps extends PartProps<'div'> {\n /** 数据来源 */\n source: PeopleSource;\n /** 选中的:父级写 _value={list} 双向绑定 */\n _value?: readonly PeopleEntry[];\n /** 能选哪几种,默认只有人员 */\n types?: readonly PeopleType[];\n /** 最多选几个;1 时勾一个就换掉原来的 */\n max?: number;\n /** 记“最近选过的”用的键(存在浏览器里);不写时没有“最近”页 */\n recentKey?: string;\n /** 两边列表的高度(CSS 长度),默认 '20rem' */\n listHeight?: string;\n /** 左边的部件属性 */\n propsBrowse?: PartProps<'div'>;\n /** 右边的部件属性 */\n propsSelected?: PartProps<'div'>;\n /** 搜索框的部件属性 */\n propsSearch?: Partial<TextFieldProps>;\n}\n\ntype Page = 'org' | 'roles' | 'recent';\n\n/** “最近”最多记几个 */\nconst RECENT_LIMIT = 20;\n\n/** PeoplePickerPanel 的样式:root、grid、browse、selected、header、list、start */\nexport const peoplePickerPanelStyles = css.create({\n // 按自己的宽度决定左右排还是上下排\n root: { containerType: 'inline-size', minWidth: 0 },\n grid: {\n display: 'grid',\n gap: '_md',\n gridTemplateColumns: {\n default: 'minmax(0, 1fr)',\n '@container (min-width: 36rem)': 'minmax(0, 3fr) minmax(0, 2fr)',\n },\n },\n browse: { display: 'flex', flexDirection: 'column', gap: '_sm', minWidth: 0 },\n selected: {\n display: 'flex',\n flexDirection: 'column',\n gap: '_sm',\n minWidth: 0,\n padding: '_sm',\n backgroundColor: '_surfaceMuted',\n borderWidth: '_xs',\n borderStyle: 'solid',\n borderColor: '_borderMuted',\n borderRadius: '_md',\n },\n header: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '_sm',\n minHeight: '2em',\n },\n list: { overflowY: 'auto', outlineStyle: 'none', minWidth: 0 },\n start: { display: 'flex', alignItems: 'center', gap: '_sm' },\n status: { display: 'flex', justifyContent: 'center', paddingBlock: '_lg', color: '_textMuted' },\n});\n\n/** 随属性变的样式:列表的高度 */\nexport const peoplePickerPanelDynamicStyles = css.create({\n height: (height: string) => ({ height }),\n});\n\n/** 人员与组织选择面板 */\nexport const PeoplePickerPanel = _component(\n ({\n source,\n _value: value = _bindable<readonly PeopleEntry[]>([]),\n types = ['user'],\n max,\n recentKey,\n listHeight = '20rem',\n propsBrowse,\n propsSelected,\n propsSearch,\n ...rest\n }: PeoplePickerPanelProps) => {\n const lang = useLang();\n // 存的键只在创建时取:换键要换一个面板\n const key = _untrack(recentKey);\n let recent = _fromRef(\n key\n ? usePersistentState<PeopleEntry[]>(`vtp.people.recent.${key}`, [])\n : (ref<PeopleEntry[]>([]) as Ref<PeopleEntry[]>),\n );\n let page = _state<Page>('org');\n let query = _state('');\n let path = _state<PeopleEntry[]>([]);\n let list = _state.raw<HTMLElement | null>(null);\n\n const searching = () => !!source.search && query.trim() !== '';\n const level = useRequest(\n (parent: PeopleEntry | null, signal) => source.children(parent, signal),\n { params: () => path.at(-1) ?? null, keepPrevious: false },\n );\n const found = useRequest((text: string, signal) => source.search!(text, signal), {\n params: () => query.trim(),\n debounce: 300,\n immediate: false,\n keepPrevious: false,\n enabled: searching,\n });\n const roles = useRequest((_: undefined, signal) => source.roles!(signal), {\n immediate: false,\n });\n watch(\n () => page,\n (next) => {\n if (next === 'roles' && roles.data == null && !roles.loading) {\n void roles.refresh();\n }\n },\n );\n\n const pages = (): Page[] => [\n 'org',\n ...(source.roles ? (['roles'] as const) : []),\n ...(key ? (['recent'] as const) : []),\n ];\n const pageLabel: Record<Page, () => string> = {\n org: () => lang.peopleOrg,\n roles: () => lang.peopleRoles,\n recent: () => lang.peopleRecent,\n };\n\n /** 现在列出的:搜索结果,或当前页 */\n const shown = (): { items: readonly PeopleEntry[]; loading: boolean } => {\n // 防抖等待时请求还没发出:没有结果也没出错就算加载中,不先显示\"没有找到\"\n if (searching()) {\n return {\n items: found.data ?? [],\n loading: found.loading || (found.data === undefined && !found.error),\n };\n }\n if (page === 'roles') return { items: roles.data ?? [], loading: roles.loading };\n if (page === 'recent') return { items: recent, loading: false };\n return { items: level.data ?? [], loading: level.loading };\n };\n\n const selectedKeys = _derived(new Set(value.map(peopleKey)));\n const pickable = (entry: PeopleEntry) => types.includes(entry.type);\n const drillable = (entry: PeopleEntry) =>\n entry.type === 'department' && entry.hasChildren !== false;\n const full = () => max != null && max > 1 && value.length >= max;\n\n function remember(entry: PeopleEntry) {\n if (!key) return;\n recent = [\n toPeopleValue(entry),\n ...recent.filter((item) => peopleKey(item) !== peopleKey(entry)),\n ].slice(0, RECENT_LIMIT);\n }\n\n function toggle(entry: PeopleEntry) {\n const id = peopleKey(entry);\n if (selectedKeys.has(id)) {\n value = value.filter((item) => peopleKey(item) !== id);\n return;\n }\n if (max === 1) value = [toPeopleValue(entry)];\n else if (full()) return;\n else value = [...value, toPeopleValue(entry)];\n remember(entry);\n }\n\n /** 全选这一页能勾的;都勾上了时全部取消。有上限时加到满为止 */\n function toggleAll(items: readonly PeopleEntry[]) {\n const candidates = items.filter(pickable);\n if (candidates.every((entry) => selectedKeys.has(peopleKey(entry)))) {\n const drop = new Set(candidates.map(peopleKey));\n value = value.filter((item) => !drop.has(peopleKey(item)));\n return;\n }\n const adding = candidates.filter((entry) => !selectedKeys.has(peopleKey(entry)));\n const room = max != null ? Math.max(0, max - value.length) : adding.length;\n value = [...value, ...adding.slice(0, room).map(toPeopleValue)];\n }\n\n /** 进到一个部门:从搜索结果进去时面包屑只有它(不知道它的上级) */\n function open(department: PeopleEntry) {\n path = searching() ? [department] : [...path, department];\n query = '';\n page = 'org';\n void nextTick(() => list?.focus());\n }\n\n function onRowClick(entry: PeopleEntry, event: MouseEvent) {\n // 勾选框、按钮自己处理\n if ((event.target as Element).closest('button,input,label,a')) return;\n if (pickable(entry)) toggle(entry);\n else if (drillable(entry)) open(entry);\n }\n\n /** 行首的圆形标记:人员是头像,部门、角色是图标 */\n const badge = (entry: PeopleEntry): VNodeChild =>\n entry.type === 'user' ? (\n <Avatar name={entry.name} src={entry.avatar} size=\"sm\" />\n ) : (\n <Avatar\n size=\"sm\"\n fallback={<Icon icon={entry.type === 'department' ? Building : BriefcaseBusiness} />}\n />\n );\n\n function renderRow(entry: PeopleEntry): VNodeChild {\n const id = peopleKey(entry);\n const checked = selectedKeys.has(id);\n return (\n <ItemListItem\n key={id}\n start={\n <span {...css.attrs(peoplePickerPanelStyles.start)}>\n {pickable(entry) && (\n <Checkbox\n _checked={checked}\n onUpdate:_checked={() => toggle(entry)}\n disabled={!checked && full()}\n aria-label={entry.name}\n />\n )}\n {badge(entry)}\n </span>\n }\n title={entry.name}\n description={entry.description}\n end={\n drillable(entry) && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n iconEnd={ChevronRight}\n aria-label={lang.peopleOpen(entry.name)}\n onClick={() => open(entry)}\n >\n {lang.peopleChildren}\n </Button>\n )\n }\n onClick={(event: MouseEvent) => onRowClick(entry, event)}\n />\n );\n }\n\n function renderBrowse(): VNodeChild {\n const { items, loading } = shown();\n const candidates = items.filter(pickable);\n const checkedCount = candidates.filter((entry) => selectedKeys.has(peopleKey(entry))).length;\n const height = peoplePickerPanelDynamicStyles.height(listHeight);\n return (\n <div {...mergeAttrs<'div'>({ sx: peoplePickerPanelStyles.browse }, propsBrowse)}>\n {source.search && (\n <TextField\n {...mergePartProps<TextFieldProps>(\n {\n icon: Search,\n placeholder: lang.peopleSearch,\n 'aria-label': lang.peopleSearch,\n clearable: true,\n },\n propsSearch,\n )}\n _value={query}\n />\n )}\n {!searching() && pages().length > 1 && (\n <ToggleGroup\n _value={page}\n onUpdate:_value={(next) => next && (page = next as Page)}\n required\n size=\"sm\"\n aria-label={lang.peoplePages}\n >\n {pages().map((item) => (\n <ToggleItem key={item} value={item}>\n {pageLabel[item]()}\n </ToggleItem>\n ))}\n </ToggleGroup>\n )}\n {!searching() && page === 'org' && (\n <Breadcrumbs\n items={[\n { key: '', label: lang.peopleRoot },\n ...path.map((department) => ({\n key: peopleKey(department),\n label: department.name,\n })),\n ]}\n onNavigate={(_, index) => (path = path.slice(0, index))}\n />\n )}\n {max !== 1 && candidates.length > 1 && (\n <Checkbox\n _checked={checkedCount === candidates.length}\n indeterminate={checkedCount > 0 && checkedCount < candidates.length}\n onUpdate:_checked={() => toggleAll(items)}\n size=\"sm\"\n >\n {lang.peopleSelectAll}\n </Checkbox>\n )}\n <div\n {...css.attrs(peoplePickerPanelStyles.list, height)}\n ref={(element: unknown) => (list = element as HTMLElement | null)}\n tabindex={-1}\n >\n {loading ? (\n <div {...css.attrs(peoplePickerPanelStyles.status)}>\n <Spinner label={lang.loading} />\n </div>\n ) : items.length === 0 ? (\n <Small sx={peoplePickerPanelStyles.status}>\n {searching() ? lang.peopleNoResult : lang.peopleEmpty}\n </Small>\n ) : (\n <ItemList\n hoverable\n density=\"compact\"\n aria-label={pageLabel[searching() ? 'org' : page]()}\n >\n {items.map(renderRow)}\n </ItemList>\n )}\n </div>\n </div>\n );\n }\n\n function renderSelected(): VNodeChild {\n return (\n <div {...mergeAttrs<'div'>({ sx: peoplePickerPanelStyles.selected }, propsSelected)}>\n <div {...css.attrs(peoplePickerPanelStyles.header)}>\n <Strong size=\"sm\">{lang.peopleCount(value.length, max)}</Strong>\n {value.length > 0 && (\n <Button variant=\"link\" size=\"sm\" onClick={() => (value = [])}>\n {lang.clear}\n </Button>\n )}\n </div>\n <div\n {...css.attrs(\n peoplePickerPanelStyles.list,\n peoplePickerPanelDynamicStyles.height(listHeight),\n )}\n >\n {value.length === 0 ? (\n <Small sx={peoplePickerPanelStyles.status}>{lang.peopleNothingSelected}</Small>\n ) : (\n <ItemList density=\"compact\" aria-label={lang.peopleSelected}>\n {value.map((entry) => (\n <ItemListItem\n key={peopleKey(entry)}\n start={badge(entry)}\n title={entry.name}\n description={entry.description ?? lang.peopleTypes[entry.type]}\n end={\n <IconButton\n icon={X}\n variant=\"ghost\"\n size=\"sm\"\n aria-label={lang.peopleRemove(entry.name)}\n onClick={() => toggle(entry)}\n />\n }\n />\n ))}\n </ItemList>\n )}\n </div>\n </div>\n );\n }\n\n return () => (\n <div {...mergeAttrs<'div'>({ sx: peoplePickerPanelStyles.root }, rest)}>\n <div {...css.attrs(peoplePickerPanelStyles.grid)}>\n {renderBrowse()}\n {renderSelected()}\n </div>\n </div>\n );\n },\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,SAAA,QAAA,GAAA;CAAA,OAAA,OAAA,MAAA,cAAA,OAAA,UAAA,SAAA,KAAA,CAAA,MAAA,qBAAA,CAAAA,QAAA,CAAA;AAAA;;AAyBA,IAAM,eAAe;;AAGrB,IAAa,0BAA0B,IAAI,OAAO;CAEhD,MAAM;EAAE,eAAe;EAAe,UAAU;CAAE;CAClD,MAAM;EACJ,SAAS;EACT,KAAK;EACL,qBAAqB;GACnB,SAAS;GACT,iCAAiC;EACnC;CACF;CACA,QAAQ;EAAE,SAAS;EAAQ,eAAe;EAAU,KAAK;EAAO,UAAU;CAAE;CAC5E,UAAU;EACR,SAAS;EACT,eAAe;EACf,KAAK;EACL,UAAU;EACV,SAAS;EACT,iBAAiB;EACjB,aAAa;EACb,aAAa;EACb,aAAa;EACb,cAAc;CAChB;CACA,QAAQ;EACN,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,KAAK;EACL,WAAW;CACb;CACA,MAAM;EAAE,WAAW;EAAQ,cAAc;EAAQ,UAAU;CAAE;CAC7D,OAAO;EAAE,SAAS;EAAQ,YAAY;EAAU,KAAK;CAAM;CAC3D,QAAQ;EAAE,SAAS;EAAQ,gBAAgB;EAAU,cAAc;EAAO,OAAO;CAAa;AAChG,CAAC;;AAGD,IAAa,iCAAiC,IAAI,OAAO,EACvD,SAAS,YAAoB,EAAE,OAAO,GACxC,CAAC;;AAGD,IAAa,oBAAoB,0BAC/B,QAAA,SAW8B;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,SAAAC,aAAA,QAAA,QAAA;CAAA,MAAA,QAAAC,SAAA,QAAA,gBATsB,CAAA,GAAE,KAAA,IAAA;CAAA,MAAA,QAAAD,aAAA,QAAA,eAC5C,CAAC,MAAM,CAAC;CAAA,MAAA,MAAAA,aAAA,QAAA,KAAA;CAAA,MAAA,YAAAA,aAAA,QAAA,WAAA;CAAA,MAAA,aAAAA,aAAA,QAAA,cAGH,OAAO;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAAA,gBAAAA,aAAA,QAAA,eAAA;CAAA,MAAA,cAAAA,aAAA,QAAA,aAAA;CAAA,MAIjB,OAAIE,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,OAAO,QAAQ;CAErB,MAAM,MAAGC,cAAA,UAAA,KAAA;CACT,IAAI,SACF,MACI,mBAAkC,qBAAqB,OAAO,CAAA,CAAE,IAC/D,IAAmB,CAAA,CAAE;CAE5B,IAAI,OAAIC,IAAgB,KAAK;CAC7B,IAAI,QAAKA,IAAU,EAAE;CACrB,IAAI,OAAIA,IAAyB,CAAA,CAAE;CACnC,IAAI,OAAIC,WAAkC,IAAI;CAE9C,MAAM,kBAAkB,CAAC,CAAC,OAAA,MAAO,UAAU,MAAA,MAAM,KAAK,MAAM;CAC5D,MAAM,QAAQ,YACX,QAA4B,WAAW,OAAA,MAAO,SAAS,QAAQ,MAAM,GACtE;EAAE,cAAc,KAAA,MAAK,GAAG,EAAE,KAAK;EAAM,cAAc;CAAM,CAC3D;CACA,MAAM,QAAQ,YAAY,MAAc,WAAW,OAAA,MAAO,OAAQ,MAAM,MAAM,GAAG;EAC/E,cAAc,MAAA,MAAM,KAAK;EACzB,UAAU;EACV,WAAW;EACX,cAAc;EACd,SAAS;CACX,CAAC;CACD,MAAM,QAAQ,YAAY,GAAc,WAAW,OAAA,MAAO,MAAO,MAAM,GAAG,EACxE,WAAW,MACb,CAAC;CACD,YACE,KAAA,QACC,SAAS;EACR,IAAI,SAAS,WAAW,MAAM,QAAQ,QAAQ,CAAC,MAAM,SACnD,MAAW,QAAQ;CAEvB,CACF;CAEA,MAAM,cAAsB;EAC1B;EACA,GAAI,OAAA,MAAO,QAAS,CAAC,OAAO,IAAc,CAAA;EAC1C,GAAI,MAAO,CAAC,QAAQ,IAAc,CAAA;CAAG;CAEvC,MAAM,YAAwC;EAC5C,WAAW,KAAK;EAChB,aAAa,KAAK;EAClB,cAAc,KAAK;CACrB;;CAGA,MAAM,cAAmE;EAEvE,IAAI,UAAU,GACZ,OAAO;GACL,OAAO,MAAM,QAAQ,CAAA;GACrB,SAAS,MAAM,WAAY,MAAM,SAAS,KAAA,KAAa,CAAC,MAAM;EAChE;EAEF,IAAI,KAAA,UAAS,SAAS,OAAO;GAAE,OAAO,MAAM,QAAQ,CAAA;GAAI,SAAS,MAAM;EAAQ;EAC/E,IAAI,KAAA,UAAS,UAAU,OAAO;GAAE,OAAK,OAAA;GAAU,SAAS;EAAM;EAC9D,OAAO;GAAE,OAAO,MAAM,QAAQ,CAAA;GAAI,SAAS,MAAM;EAAQ;CAC3D;CAEA,MAAM,eAAYC,eAAY,IAAI,IAAI,MAAA,MAAM,IAAI,SAAS,CAAC,CAAC;CAC3D,MAAM,YAAY,UAAuB,MAAA,MAAM,SAAS,MAAM,IAAI;CAClE,MAAM,aAAa,UACjB,MAAM,SAAS,gBAAgB,MAAM,gBAAgB;CACvD,MAAM,aAAa,IAAA,SAAO,QAAQ,IAAA,QAAM,KAAK,MAAA,MAAM,UAAM,IAAA;CAEzD,SAAS,SAAS,OAAoB;EACpC,IAAI,CAAC,KAAK;EACV,OAAA,QAAS,CACP,cAAc,KAAK,GACnB,GAAG,OAAA,MAAO,QAAQ,SAAS,UAAU,IAAI,MAAM,UAAU,KAAK,CAAC,CAAC,CACjE,CAAC,MAAM,GAAG,YAAY;CACzB;CAEA,SAAS,OAAO,OAAoB;EAClC,MAAM,KAAK,UAAU,KAAK;EAC1B,IAAI,aAAA,MAAa,IAAI,EAAE,GAAG;GACxB,MAAA,QAAQ,MAAA,MAAM,QAAQ,SAAS,UAAU,IAAI,MAAM,EAAE;GACrD;EACF;EACA,IAAI,IAAA,UAAQ,GAAG,MAAA,QAAQ,CAAC,cAAc,KAAK,CAAC;OACvC,IAAI,KAAK,GAAG;OACZ,MAAA,QAAQ,CAAC,GAAA,MAAA,OAAU,cAAc,KAAK,CAAC;EAC5C,SAAS,KAAK;CAChB;;CAGA,SAAS,UAAU,OAA+B;EAChD,MAAM,aAAa,MAAM,OAAO,QAAQ;EACxC,IAAI,WAAW,OAAO,UAAU,aAAA,MAAa,IAAI,UAAU,KAAK,CAAC,CAAC,GAAG;GACnE,MAAM,OAAO,IAAI,IAAI,WAAW,IAAI,SAAS,CAAC;GAC9C,MAAA,QAAQ,MAAA,MAAM,QAAQ,SAAS,CAAC,KAAK,IAAI,UAAU,IAAI,CAAC,CAAC;GACzD;EACF;EACA,MAAM,SAAS,WAAW,QAAQ,UAAU,CAAC,aAAA,MAAa,IAAI,UAAU,KAAK,CAAC,CAAC;EAC/E,MAAM,OAAO,IAAA,SAAO,OAAO,KAAK,IAAI,GAAG,IAAA,QAAM,MAAA,MAAM,MAAM,IAAI,OAAO;EACpE,MAAA,QAAQ,CAAC,GAAA,MAAA,OAAU,GAAG,OAAO,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI,aAAa,CAAC;CAChE;;CAGA,SAAS,KAAK,YAAyB;EACrC,KAAA,QAAO,UAAU,IAAI,CAAC,UAAU,IAAI,CAAC,GAAA,KAAA,OAAS,UAAU;EACxD,MAAA,QAAQ;EACR,KAAA,QAAO;EACP,eAAoB,KAAA,OAAM,MAAM,CAAC;CACnC;CAEA,SAAS,WAAW,OAAoB,OAAmB;EAEzD,IAAK,MAAM,OAAmB,QAAQ,sBAAsB,GAAG;EAC/D,IAAI,SAAS,KAAK,GAAG,OAAO,KAAK;OAC5B,IAAI,UAAU,KAAK,GAAG,KAAK,KAAK;CACvC;;CAGA,MAAM,SAAS,UACb,MAAM,SAAS,SAAMC,YAAA,QAAA;EAAA,QACL,MAAM;EAAI,OAAO,MAAM;EAAM,QAAA;CAAA,GAAA,IAAA,IAAAA,YAAA,QAAA;EAAA,QAAA;EAAA,YAAAA,YAAA,MAAA,EAAA,QAInB,MAAM,SAAS,eAAe,WAAW,kBAAiB,GAAA,IAAA;CAAA,GAAA,IAAA;CAItF,SAAS,UAAU,OAAgC;EACjD,MAAM,KAAK,UAAU,KAAK;EAC1B,MAAM,UAAU,aAAA,MAAa,IAAI,EAAE;EACnC,OAAAA,YAAA,cAAA;GAAA,OAES;GAAE,SAAAA,YAAA,QAEK,IAAI,MAAM,wBAAwB,KAAK,GAAC,CAC/C,SAAS,KAAK,KAACA,YAAA,UAAA;IAAA,YAEF;IAAO,2BACQ,OAAO,KAAK;IAAC,YAC5B,CAAC,WAAW,KAAK;IAAC,cAChB,MAAM;GAAI,GAAA,IAAA,GAGzB,MAAM,KAAK,CAAC,CAAA;GAAA,SAGV,MAAM;GAAI,eACJ,MAAM;GAAW,OAE5B,UAAU,KAAK,KAACA,YAAA,QAAA;IAAA,WAAA;IAAA,QAAA;IAAA,WAIH;IAAY,cACT,KAAK,WAAW,MAAM,IAAI;IAAC,iBACxB,KAAK,KAAK;GAAC,GAAA;IAAA,eAAA,CAEzB,KAAK,cAAc;IAAA,SAAA;GAAA,CAAA;GAEvB,YAEO,UAAsB,WAAW,OAAO,KAAK;EAAC,GAAA,IAAA;CAG9D;CAEA,SAAS,eAA2B;EAAA,IAAA,QAAA;EAClC,MAAM,EAAE,OAAO,YAAY,MAAM;EACjC,MAAM,aAAa,MAAM,OAAO,QAAQ;EACxC,MAAM,eAAe,WAAW,QAAQ,UAAU,aAAA,MAAa,IAAI,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC;EACtF,MAAM,SAAS,+BAA+B,OAAM,WAAA,KAAW;EAC/D,OAAAA,YAAA,OACW,WAAkB,EAAE,IAAI,wBAAwB,OAAO,GAAC,YAAA,KAAa,GAAC;GAC5E,OAAA,MAAO,UAAMA,YAAA,WAAAC,WAEN,eACF;IACE,MAAM;IACN,aAAa,KAAK;IAClB,cAAc,KAAK;IACnB,WAAW;GACb,GAAC,YAAA,KAEH,GAAC;IAAA,UAAA,MAAA;IAAA,mBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,MAAA,QAAA;GAAA,CAAA,GAAA,IAAA;GAIJ,CAAC,UAAU,KAAK,MAAM,CAAC,CAAC,SAAS,KAACD,YAAA,aAAA;IAAA,UAAA,KAAA;IAAA,mBAAA,OAAA,OAAA,OAAA,MAGb,SAAS,SAAS,KAAA,QAAO;IAAa,YAAA;IAAA,QAAA;IAAA,cAG5C,KAAK;GAAW,GAAA,QAAA,SAE3B,MAAM,CAAC,CAAC,KAAK,SAAI;IAAA,IAAA;IAAA,OAAAA,YAAA,YAAA;KAAA,OACC;KAAI,SAAS;IAAI,GAAA,QAAA,QAC/B,UAAU,KAAK,CAAC,CAAC,IAAA,QAAA,EAAA,eAAA,CAAA,KAAA,EAAA,CAAA;GAAA,CAErB,CAAC,IAAA,SAAA,EAAA,eAAA,CAAA,MAAA,EAAA,CAAA;GAGL,CAAC,UAAU,KAAK,KAAA,UAAS,SAAKA,YAAA,aAAA;IAAA,SAEpB,CACL;KAAE,KAAK;KAAI,OAAO,KAAK;IAAW,GAClC,GAAG,KAAA,MAAK,KAAK,gBAAgB;KAC3B,KAAK,UAAU,UAAU;KACzB,OAAO,WAAW;IACpB,EAAE,CAAC;IACJ,cAAA,OAAA,OAAA,OAAA,MACY,GAAG,UAAW,KAAA,QAAO,KAAA,MAAK,MAAM,GAAG,KAAK;GAAE,GAAA,IAAA;GAG1D,IAAA,UAAQ,KAAK,WAAW,SAAS,KAACA,YAAA,UAAA;IAAA,YAErB,iBAAiB,WAAW;IAAM,iBAC7B,eAAe,KAAK,eAAe,WAAW;IAAM,2BAC1C,UAAU,KAAK;IAAC,QAAA;GAAA,GAAA;IAAA,eAAA,CAGxC,KAAK,eAAe;IAAA,SAAA;GAAA,CAAA;GAExBA,YAAA,OAAAC,WAEK,IAAI,MAAM,wBAAwB,MAAM,MAAM,GAAC;IAAA,OAAA,OAAA,OAAA,OAAA,MAC7C,YAAsB,KAAA,QAAO;IAA8B,YACvD;GAAE,CAAA,GAAA,CAEX,UAAOD,YAAA,OACG,IAAI,MAAM,wBAAwB,MAAM,GAAC,CAAAA,YAAA,SAAA,EAAA,SAChC,KAAK,QAAO,GAAA,IAAA,CAAA,CAAA,IAE5B,MAAM,WAAW,IAACA,YAAA,OAAA,EAAA,MACT,wBAAwB,OAAM,GAAA;IAAA,eAAA,CACtC,UAAU,IAAI,KAAK,iBAAiB,KAAK,WAAW;IAAA,SAAA;GAAA,CAAA,IAAAA,YAAA,UAAA;IAAA,aAAA;IAAA,WAAA;IAAA,cAMzC,UAAU,UAAU,IAAI,QAAK,KAAA,MAAQ,CAAC;GAAC,GAAA,QAAA,SAElD,MAAM,IAAI,SAAS,CAAC,IAAA,SAAA,EAAA,eAAA,CAAA,MAAA,EAAA,CAAA,CAExB,CAAA;EAAA,CAAA;CAIT;CAEA,SAAS,iBAA6B;EACpC,OAAAA,YAAA,OACW,WAAkB,EAAE,IAAI,wBAAwB,SAAS,GAAC,cAAA,KAAe,GAAC,CAAAA,YAAA,OACxE,IAAI,MAAM,wBAAwB,MAAM,GAAC,CAAAA,YAAA,QAAA,EAAA,QAAA,KAAA,GAAA;GAAA,eAAA,CAC7B,KAAK,YAAY,MAAA,MAAM,QAAM,IAAA,KAAK,CAAC;GAAA,SAAA;EAAA,CAAA,GACrD,MAAA,MAAM,SAAS,KAACA,YAAA,QAAA;GAAA,WAAA;GAAA,QAAA;GAAA,WAAA,OAAA,OAAA,OAAA,WACkC,MAAA,QAAQ,CAAA;EAAG,GAAA;GAAA,eAAA,CACzD,KAAK,KAAK;GAAA,SAAA;EAAA,CAAA,CAEd,CAAA,GAAAA,YAAA,OAGG,IAAI,MACN,wBAAwB,MACxB,+BAA+B,OAAM,WAAA,KAAW,CAClD,GAAC,CAEA,MAAA,MAAM,WAAW,IAACA,YAAA,OAAA,EAAA,MACN,wBAAwB,OAAM,GAAA;GAAA,eAAA,CAAG,KAAK,qBAAqB;GAAA,SAAA;EAAA,CAAA,IAAAA,YAAA,UAAA;GAAA,WAAA;GAAA,cAE9B,KAAK;EAAc,GAAA;GAAA,eAAA,CACxD,MAAA,MAAM,KAAK,UAAKA,YAAA,cAAA;IAAA,OAER,UAAU,KAAK;IAAC,SACd,MAAM,KAAK;IAAC,SACZ,MAAM;IAAI,eACJ,MAAM,eAAe,KAAK,YAAY,MAAM;IAAK,OAAAA,YAAA,YAAA;KAAA,QAGpD;KAAC,WAAA;KAAA,QAAA;KAAA,cAGK,KAAK,aAAa,MAAM,IAAI;KAAC,iBAC1B,OAAO,KAAK;IAAC,GAAA,IAAA;GAAA,GAAA,IAAA,CAInC,CAAC;GAAA,SAAA;EAAA,CAAA,CAEL,CAAA,CAAA,CAAA;CAIT;CAEA,aAAOA,YAAA,OACI,WAAkB,EAAE,IAAI,wBAAwB,KAAK,GAAG,IAAI,GAAC,CAAAA,YAAA,OAC3D,IAAI,MAAM,wBAAwB,IAAI,GAAC,CAC7C,aAAa,GACb,eAAe,CAAC,CAAA,CAAA,CAAA;AAIzB,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
package/dist/input/Select.js
CHANGED
|
@@ -394,7 +394,7 @@ var Select = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
394
394
|
"listbox": {
|
|
395
395
|
list,
|
|
396
396
|
id: listId,
|
|
397
|
-
empty: remote.
|
|
397
|
+
empty: remote.failure() ?? (search.value ? lang.selectNoMatch : empty.value),
|
|
398
398
|
renderOption: children.value && ((item, state) => item instanceof Creation ? lang.selectCreate(item.text) : children.value(item, state)),
|
|
399
399
|
multiple: multiple()
|
|
400
400
|
},
|
package/dist/input/Select.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_childrenProp","_vtp_restProps","_vue_shallowRef","_createVNode","_Fragment","_mergeProps"],"sources":["../../src/input/Select.tsx"],"sourcesContent":["/**\n * 下拉选择(WAI-ARIA 的 select-only combobox):点触发框弹出选项列表,单选或多选。\n *\n * <Field label=\"城市\"><Select items={['北京', '上海', '广州']} _value={city} placeholder=\"选择城市\" /></Field>\n * <Select items={tags} selectionMode=\"multiple\" _value={picked} clearable aria-label=\"标签\" />\n * <Select items={users} toOption={(user) => ({ value: user.id, label: user.name })} _value={id} />\n * <Select items={suppliers} searchable _value={supplier} /> 选项多时:弹出层顶部带搜索框\n *\n * <Select items={tags} selectionMode=\"multiple\" maxTagCount=\"responsive\" creatable onCreate={(text) => tags.push(text)} … />\n *\n * 组合已有的部件:外壳是输入框外壳(controlParts),弹出层用 useAnchoredPopover(与外壳同宽、最高到视口剩余空间),\n * 列表是 ListboxView(状态与键盘见 list-state.ts)。焦点一直在触发按钮上,高亮项经 aria-activedescendant 告诉读屏。\n *\n * 多选时已选的项是触发按钮前面的一排 Tag(不能放进 button 里:按钮里不能有按钮),各带移除按钮。\n * 这排标签只给指针用(aria-hidden、移除按钮不进 Tab 顺序):读屏从触发按钮里念出全部已选内容,\n * 键盘在列表里取消勾选,或在触发按钮(及空的搜索框)里按 Backspace 移除最后一项(同 TagsInput)。\n * maxTagCount 是数字时只显示前几个,'responsive' 时排成一行按宽度放,其余收进“+N”(SelectedTags,TreeSelect 共用)。\n *\n * 键盘:关闭时 ↓ / Enter / 空格打开(↑ 打开并高亮最后一项),单选时直接按字母选中以它开头的项(同原生 select);\n * 打开时 ↑↓、Home / End、Page Up / Down 移动,Enter / 空格选中(单选随后关闭),Esc、Tab 关闭。\n * 写了 name 时用隐藏的 input 随表单提交(多选时每个值一个)。\n *\n * searchable 时弹出层顶部有搜索框:打开后焦点在搜索框里,输入就过滤选项(按显示文字,忽略大小写与重音,或用 filter),\n * ↑↓ 移动、Enter 选中、Esc 关闭并把焦点还给触发按钮。已选的项被过滤掉、或 items 换了(远程搜索)时,\n * 触发框里仍显示它的文字(useSelectedOptions)。\n *\n * 远程选项:给 load 时打开就按搜索词请求,输入停下 300 ms 再请求,只采用最后一次(remote-items.ts);\n * 自己管请求时写 _search={keyword} 拿到搜索词、filter={false} 关掉本地过滤,items 与 loading 自己给。\n *\n * <Select searchable load={(q, signal) => http.get<User[]>('/users', { query: { q }, signal })} toOption={…} _value={owner} />\n *\n * creatable 时自带搜索框,输入的文字没有对应的选项时列表末尾多一项“新建“…””:选中后值就是这段文字,onCreate 报告它。\n *\n * 部件:Root(外壳)、Tags(多选时的那排标签)、Tag、Trigger(button,主元素)、Value、Clear、Chevron、Popover、\n * Search(TextField)、Listbox。\n */\nimport { ChevronDown, Plus, Search } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useId, watch, type VNodeChild } from 'vue';\nimport type { PresetIconButtonProps } from '../base/CloseButton';\nimport { buttonBaseStyles } from '../base/button-core';\nimport { Icon, type IconProps } from '../base/Icon';\nimport type { TagProps } from '../base/Tag';\nimport { visuallyHiddenStyles } from '../base/VisuallyHidden';\nimport { useLang, useTextMatcher } from '../config/context';\nimport { useAnchoredPopover, type FloatingPlacement } from '../utils/dom/floating';\nimport { openSetter, type OpenChangeProps } from '../utils/dom/open-change';\nimport { ListPopover } from './ListPopover';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { selectedValues, type SelectionProps } from '../utils/data/selection';\nimport {\n controlClear,\n controlParts,\n clickOnShell,\n focusOnShell,\n controlIcon,\n useControlAppearance,\n type ControlIconOptions,\n type ControlOptions,\n} from './control';\nimport { useFieldControl } from '../form/Field';\nimport { FormValue } from '../form/FormValue';\nimport { disclosureStyles } from '../utils/style/transitions';\nimport { activeOptionId, type ListboxViewProps, type OptionRenderState } from './Listbox';\nimport {\n handleListKeydown,\n useListState,\n useResultAnnouncement,\n useSelectedOptions,\n type ListboxOption,\n type OptionMapping,\n toListboxOption,\n} from './list-state';\nimport { useRemoteItems, type OptionsLoader } from './remote-items';\nimport { SelectedTags, type SelectedTagsProps } from './SelectedTags';\nimport { TextField, type TextFieldProps } from './TextField';\n\n/** 角色、展开状态与可用性由组件管理 */\nexport type SelectManaged =\n | 'type'\n | 'role'\n | 'value'\n | 'disabled'\n | 'aria-haspopup'\n | 'aria-expanded'\n | 'aria-controls'\n | 'aria-activedescendant'\n | 'aria-invalid'\n | 'aria-required'\n | 'aria-describedby';\n\n/** 下拉选择共有的属性;未声明的属性落到触发按钮(主元素)上,外壳用 propsRoot */\nexport interface SelectBaseProps<T>\n extends PartProps<'button', SelectManaged>, ControlOptions, ControlIconOptions, OpenChangeProps {\n /** 数据:ListboxOption、字符串,或任意数据加 toOption;有 load 时是第一次加载之前的选项(编辑页放上已选的那一项) */\n items?: readonly T[];\n /** 没有选择时显示的文字,默认取语言包(请选择) */\n placeholder?: string;\n /** 是否打开:父级可以写 _open={x} */\n _open?: boolean;\n /** 弹出位置,默认 bottom-start */\n placement?: FloatingPlacement;\n /** 有选择时显示清空按钮 */\n clearable?: boolean;\n /** 弹出层顶部带搜索框,输入过滤选项:选项多(供应商、人员、件号)时用 */\n searchable?: boolean;\n /** 搜索时怎样算匹配;默认显示文字包含输入(忽略大小写与重音)。false 时不在本地过滤(服务端按 search 过滤) */\n filter?: false | ((item: T, query: string) => boolean);\n /** 搜索框里的文字:打开时清空;父级写 _search={x} 拿到它(自己发请求时) */\n _search?: string;\n /** 远程选项:打开时与搜索词停下 300 ms 后调用,结果代替 items;只采用最后一次,被顶掉的请求 signal 中止 */\n load?: OptionsLoader<T>;\n /**\n * 可以新建选项(自带搜索框):输入的文字没有对应的选项时,列表末尾多一项“新建“…””,选中后值就是这段文字。\n * 在 onCreate 里把它加进 items(或存到服务端);不加时触发框里照样显示这段文字\n */\n creatable?: boolean;\n /** 新建了一项(creatable 时):参数是输入的文字,也就是选中的值 */\n onCreate?: (text: string) => void;\n /** 多选时触发框里最多显示几个标签,其余收进“+N”;'responsive' 时排成一行、按宽度放,放不下的收起 */\n maxTagCount?: number | 'responsive';\n /** 表单提交时的 name(用隐藏的 input) */\n name?: string;\n /** 不可用 */\n disabled?: boolean;\n /** 只读:能聚焦、能看到已选的内容,不能打开、不能清空 */\n readonly?: boolean;\n /** 必填 */\n required?: boolean;\n /** 校验未通过 */\n invalid?: boolean;\n /** 自定义选项内容:`{(option, { selected, active }) => …}` */\n children?: (item: T, state: OptionRenderState) => VNodeChild;\n /** 自定义触发框里显示的内容(已选的选项);默认单选显示文字、多选显示标签 */\n renderValue?: (selected: readonly T[]) => VNodeChild;\n /** 正在取选项(异步加载):没有选项时列表里是骨架行,已有选项时顶部显示进度条 */\n loading?: boolean;\n /** 没有选项时列表里显示的内容 */\n empty?: VNodeChild;\n /** 外壳的部件属性 */\n propsRoot?: PartProps<'div'>;\n /** 已选内容那一层的部件属性 */\n propsValue?: PartProps<'span'>;\n /** 多选时每个标签的部件属性 */\n propsTag?: Partial<TagProps>;\n /** 多选时那排标签的部件属性 */\n propsTags?: Partial<SelectedTagsProps>;\n /** 清空按钮的部件属性 */\n propsClear?: Partial<PresetIconButtonProps>;\n /** 下拉箭头的部件属性 */\n propsChevron?: Partial<IconProps>;\n /** 弹出层的部件属性 */\n propsPopover?: PartProps<'div', 'id' | 'popover'>;\n /** 列表的部件属性 */\n propsListbox?: Partial<ListboxViewProps<T>>;\n /** 搜索框的部件属性(searchable 时) */\n propsSearch?: Partial<TextFieldProps>;\n}\n\n/** 单选或多选;items 不是 ListboxOption 或字符串时要给 toOption */\nexport type SelectProps<T = ListboxOption> = SelectBaseProps<T> & SelectionProps & OptionMapping<T>;\n\n/** Select 的样式:root、trigger、value、placeholder、chevronOpen 等 */\nexport const selectStyles = css.create({\n // 在 ControlGroup 里也按内容宽(输入框才分剩余宽度)\n root: { cursor: 'pointer', flexGrow: 0 },\n // 去掉按钮默认外观用 buttonBaseStyles;像输入框一样没有自己的焦点环与波纹(焦点环画在外壳上)\n trigger: {\n display: 'flex',\n alignItems: 'center',\n flexGrow: 1,\n minWidth: 0,\n alignSelf: 'stretch',\n textAlign: 'start',\n cursor: 'inherit',\n outlineStyle: 'none',\n },\n value: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '_xs',\n minWidth: 0,\n paddingBlock: '_xs',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n },\n // 是真正的文字(不是原生 placeholder),要满足 4.5:1:用 textMuted 而不是 textPlaceholder\n placeholder: { color: '_textMuted' },\n // 多选时标签排在触发按钮前面(SelectedTags)占剩余宽度,触发按钮里只剩读屏用的文字\n triggerBehindTags: { flexGrow: 0 },\n chevronOpen: { rotate: '180deg' },\n // 弹出层顶部的搜索框占满宽度\n search: { width: '_full' },\n});\n\n/** creatable 时列表末尾那一项“新建“…””:不是 items 里的数据 */\nclass Creation {\n readonly text: string;\n constructor(text: string) {\n this.text = text;\n }\n}\n\n/** 下拉选择:触发框 + 弹出的选项列表 */\nexport const Select = _component(\n <T,>({\n items = [],\n toOption,\n selectionMode = 'single',\n _value: value = _bindable<string | string[] | null>(null),\n _open: open = _bindable(false),\n onOpenChange,\n placeholder,\n placement = 'bottom-start',\n clearable = false,\n searchable = false,\n filter,\n _search: search = _bindable(''),\n load,\n creatable = false,\n onCreate,\n maxTagCount,\n name,\n variant,\n size,\n radius,\n disabled,\n readonly,\n required,\n invalid,\n id,\n icon,\n children,\n renderValue,\n empty,\n loading = false,\n propsRoot,\n propsIcon,\n propsValue,\n propsTag,\n propsTags,\n propsClear,\n propsChevron,\n propsPopover,\n propsListbox,\n propsSearch,\n ...rest\n }: SelectProps<T>) => {\n const lang = useLang();\n const textMatcher = useTextMatcher();\n const listId = useId();\n const appearance = useControlAppearance(() => ({ variant, size, radius }));\n const control = useFieldControl(() => ({ id, invalid, required, disabled, readonly }));\n const multiple = () => selectionMode === 'multiple';\n const optionOf = (item: T) => toListboxOption(item, toOption);\n const remote = useRemoteItems({\n load: () => load,\n query: () => search,\n open: () => open,\n items: () => items,\n });\n // 本地过滤:有 load 或 filter={false} 时交给服务端\n const localFilter = () => !load && filter !== false;\n const matches = (item: T, text: string) =>\n filter ? filter(item, text) : textMatcher()(optionOf(item).label, text);\n // creatable 自带搜索框\n const withSearch = () => searchable || creatable;\n // creatable:输入的文字没有同名(或同值)的选项时,列表末尾多一项“新建”\n const creation = () => {\n const text = search.trim();\n if (!creatable || !text) return undefined;\n const exists = remote.items().some((item) => {\n const option = optionOf(item);\n return option.value === text || option.label.toLowerCase() === text.toLowerCase();\n });\n return exists ? undefined : new Creation(text);\n };\n const list = useListState<T | Creation>({\n items: () => {\n const all = remote.items();\n const shown =\n withSearch() && search && localFilter()\n ? all.filter((item) => matches(item, search))\n : all;\n const created = creation();\n return created ? [...shown, created] : shown;\n },\n toOption: (item) =>\n item instanceof Creation\n ? { value: item.text, label: lang.selectCreate(item.text), icon: Plus }\n : optionOf(item),\n selected: () => selectedValues(value),\n mode: () => selectionMode,\n onChange: (next) => {\n const created = creation();\n if (created && next.includes(created.text)) {\n onCreate?.(created.text);\n search = '';\n }\n value = multiple() ? [...next] : (next[0] ?? null);\n },\n // 单选时再点已选的一项不取消(同原生 select);要清空用清除按钮\n required: () => !multiple(),\n });\n // 已选项的文字取自全部 items(过滤掉的、远程搜索换掉的也在)\n const selectedOptions = useSelectedOptions({ items: remote.items, toOption: optionOf });\n let shell = _state.raw<HTMLElement | null>(null);\n let trigger = _state.raw<HTMLElement | null>(null);\n let panel = _state.raw<HTMLElement | null>(null);\n const setOpen = openSetter(\n () => open,\n (next) => (open = next),\n () => onOpenChange,\n );\n\n /** 多选时移除一项(标签的移除按钮、Backspace) */\n function remove(target: string) {\n value = selectedValues(value).filter((item) => item !== target);\n }\n\n function removeLast() {\n const last = selectedValues(value).at(-1);\n if (last !== undefined) remove(last);\n }\n\n function show(highlight: 'default' | 'last' = 'default', event?: Event) {\n search = '';\n list.activateDefault();\n if (highlight === 'last' && selectedValues(value).length === 0) list.move('last');\n setOpen(true, 'trigger', event);\n }\n\n useResultAnnouncement(() => ({ query: search, count: list.options.length }));\n\n // 输入搜索词:高亮第一个匹配的。打开时清空搜索词不算:那时高亮的是已选项\n watch(\n () => search,\n (text) => text && list.move('first'),\n );\n\n // 选中一项:单选时关闭\n function picked(event?: Event) {\n if (!multiple()) setOpen(false, 'select', event);\n }\n\n /** Ctrl / Cmd / Alt 组合键是浏览器的(刷新、复制、查找),不做按字母跳转 */\n const hasModifier = (event: KeyboardEvent) => event.altKey || event.ctrlKey || event.metaKey;\n\n function onKeydown(event: KeyboardEvent) {\n if (control().disabled || control().readonly) return;\n if (event.key === 'Backspace' && multiple()) {\n event.preventDefault();\n removeLast();\n return;\n }\n if (!open) {\n if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n show('default', event);\n } else if (event.key === 'ArrowUp') {\n event.preventDefault();\n show('last', event);\n } else if (!multiple() && !hasModifier(event) && list.typeahead(event.key)) {\n // 同原生 select:关闭时按字母直接选中\n event.preventDefault();\n if (list.active !== undefined && !list.isSelected(list.active)) list.select(list.active);\n }\n return;\n }\n if (event.key === 'Tab') {\n setOpen(false, 'focus', event);\n return;\n }\n if (handleListKeydown(list, event, { space: true, typeahead: true }, () => picked(event))) {\n event.preventDefault();\n }\n }\n\n // 搜索框里:↑↓ 移动、Enter 选中(Home / End、空格留给输入),Tab 关闭后照常移到下一个;\n // 多选时空的搜索框里按 Backspace 移除最后一项\n function onSearchKeydown(event: KeyboardEvent) {\n if (event.key === 'Tab') {\n setOpen(false, 'focus', event);\n return;\n }\n if (event.key === 'Backspace' && !search && multiple()) {\n event.preventDefault();\n removeLast();\n return;\n }\n if (handleListKeydown(list, event, { homeEnd: false }, () => picked(event)))\n event.preventDefault();\n }\n\n function clear() {\n value = multiple() ? [] : null;\n trigger?.focus();\n }\n\n // 弹层贴着外壳、与它同宽;焦点一直在触发按钮上,关闭时不用归还(searchable 时见 onPanelToggle)\n const { onToggle } = useAnchoredPopover(\n () => ({\n open,\n anchor: shell,\n panel,\n floating: { placement, matchWidth: true, fitHeight: true },\n restoreFocus: false,\n }),\n { close: (reason, event) => setOpen(false, reason, event) },\n );\n\n // searchable:打开后焦点进搜索框;关闭时焦点还在弹层里(Esc、选中、点外面)就还给触发按钮\n function onPanelToggle(event: ToggleEvent) {\n onToggle(event);\n if (!withSearch()) return;\n if (event.newState === 'open') {\n panel?.querySelector<HTMLInputElement>('input[type=\"search\"]')?.focus();\n return;\n }\n const active = document.activeElement;\n if (!active || active === document.body || panel?.contains(active)) trigger?.focus();\n }\n\n return () => {\n const state = control();\n const values = selectedValues(value);\n const selected = selectedOptions(values);\n const off = state.disabled;\n // 只读:能聚焦,不能打开、不能改\n const locked = off || state.readonly;\n // 多选的默认显示:触发按钮前面一排标签,按钮里只留给读屏的文字\n const tagged = multiple() && !renderValue && selected.length > 0;\n const shown = tagged ? (\n <span {...css.attrs(visuallyHiddenStyles.hidden)}>\n {lang.selectSummary(selected.map((option) => option.label))}\n </span>\n ) : (\n (renderValue?.(\n selected.flatMap((option) => (option.item === undefined ? [] : [option.item])),\n ) ??\n (selected.length === 0 ? (\n <span {...css.attrs(selectStyles.placeholder)}>\n {placeholder ?? lang.selectPlaceholder}\n </span>\n ) : (\n selected[0]!.label\n )))\n );\n const tags = tagged && (\n <SelectedTags\n {...mergePartProps<SelectedTagsProps>(\n {\n tags: selected,\n maxTagCount,\n onRemove: locked ? undefined : remove,\n propsTag,\n },\n propsTags,\n )}\n />\n );\n const s = controlParts(appearance(), state);\n return (\n <>\n <div\n {...mergeAttrs<'div'>({ ...s.root, sx: [s.root.sx, selectStyles.root] }, propsRoot, {\n ref: _toRef(shell),\n // 点在外壳空白处(含多选的标签):焦点交给触发按钮,并与点触发按钮一样切换打开(只读时只聚焦)\n onPointerdown: off ? undefined : focusOnShell(() => trigger),\n onClick: locked\n ? undefined\n : clickOnShell(\n () => trigger,\n (event) => (open ? setOpen(false, 'trigger', event) : show('default', event)),\n ),\n })}\n data-state={open ? 'open' : 'closed'}\n data-invalid={state.invalid || undefined}\n data-disabled={off || undefined}\n >\n {controlIcon(s, icon, propsIcon)}\n {tags}\n <button\n {...mergeAttrs<'button'>(\n {\n sx: [\n buttonBaseStyles.root,\n selectStyles.trigger,\n tagged && selectStyles.triggerBehindTags,\n ],\n },\n rest,\n {\n ref: _toRef(trigger),\n id: state.id,\n type: 'button',\n role: 'combobox',\n disabled: off,\n 'aria-haspopup': 'listbox',\n 'aria-expanded': open,\n 'aria-controls': listId,\n 'aria-activedescendant': open ? activeOptionId(list, listId) : undefined,\n 'aria-invalid': state.invalid || undefined,\n 'aria-required': state.required || undefined,\n 'aria-readonly': state.readonly || undefined,\n 'aria-describedby': state.describedBy,\n onClick: (event: MouseEvent) => {\n // Safari 点击按钮不会聚焦它:焦点要在触发按钮上,键盘与 aria-activedescendant 才有效\n trigger?.focus();\n if (locked) return;\n if (open) setOpen(false, 'trigger', event);\n else show('default', event);\n },\n onKeydown,\n },\n )}\n >\n <span {...mergeAttrs<'span'>({ sx: selectStyles.value }, propsValue)}>{shown}</span>\n </button>\n {clearable && values.length > 0 && !locked && controlClear(s, propsClear, clear)}\n <Icon\n {...mergePartProps<IconProps>(\n {\n ...s.icon,\n icon: ChevronDown,\n sx: [s.icon.sx, disclosureStyles.icon, open && selectStyles.chevronOpen],\n },\n propsChevron,\n )}\n />\n <FormValue name={name} value={values} disabled={state.disabled} />\n </div>\n <ListPopover\n panelRef={_toRef(panel)}\n onToggle={onPanelToggle}\n size={appearance().size}\n labelledBy={state.labelledBy}\n label={rest['aria-label']}\n loading={loading || remote.loading()}\n onPick={() => picked()}\n header={\n withSearch() && (\n <TextField\n {...mergePartProps<TextFieldProps>(\n {\n icon: Search,\n variant: 'soft',\n placeholder: lang.selectSearch,\n 'aria-label': lang.selectSearch,\n propsRoot: { sx: selectStyles.search },\n },\n propsSearch,\n )}\n type=\"search\"\n _value={search}\n aria-controls={listId}\n aria-activedescendant={open ? activeOptionId(list, listId) : undefined}\n onKeydown={onSearchKeydown}\n />\n )\n }\n listbox={{\n list,\n id: listId,\n empty: remote.error() ? lang.listLoadFailed : search ? lang.selectNoMatch : empty,\n renderOption:\n children &&\n ((item: T | Creation, state: OptionRenderState) =>\n item instanceof Creation ? lang.selectCreate(item.text) : children(item, state)),\n multiple: multiple(),\n }}\n // 使用方按 T 写列表的部件属性;creatable 时多出的“新建”那一项由上面的 renderOption 处理\n propsListbox={propsListbox as Partial<ListboxViewProps<T | Creation>> | undefined}\n propsPopover={propsPopover}\n />\n </>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Select.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_childrenProp","_vtp_restProps","_vue_shallowRef","_createVNode","_Fragment","_mergeProps"],"sources":["../../src/input/Select.tsx"],"sourcesContent":["/**\n * 下拉选择(WAI-ARIA 的 select-only combobox):点触发框弹出选项列表,单选或多选。\n *\n * <Field label=\"城市\"><Select items={['北京', '上海', '广州']} _value={city} placeholder=\"选择城市\" /></Field>\n * <Select items={tags} selectionMode=\"multiple\" _value={picked} clearable aria-label=\"标签\" />\n * <Select items={users} toOption={(user) => ({ value: user.id, label: user.name })} _value={id} />\n * <Select items={suppliers} searchable _value={supplier} /> 选项多时:弹出层顶部带搜索框\n *\n * <Select items={tags} selectionMode=\"multiple\" maxTagCount=\"responsive\" creatable onCreate={(text) => tags.push(text)} … />\n *\n * 组合已有的部件:外壳是输入框外壳(controlParts),弹出层用 useAnchoredPopover(与外壳同宽、最高到视口剩余空间),\n * 列表是 ListboxView(状态与键盘见 list-state.ts)。焦点一直在触发按钮上,高亮项经 aria-activedescendant 告诉读屏。\n *\n * 多选时已选的项是触发按钮前面的一排 Tag(不能放进 button 里:按钮里不能有按钮),各带移除按钮。\n * 这排标签只给指针用(aria-hidden、移除按钮不进 Tab 顺序):读屏从触发按钮里念出全部已选内容,\n * 键盘在列表里取消勾选,或在触发按钮(及空的搜索框)里按 Backspace 移除最后一项(同 TagsInput)。\n * maxTagCount 是数字时只显示前几个,'responsive' 时排成一行按宽度放,其余收进“+N”(SelectedTags,TreeSelect 共用)。\n *\n * 键盘:关闭时 ↓ / Enter / 空格打开(↑ 打开并高亮最后一项),单选时直接按字母选中以它开头的项(同原生 select);\n * 打开时 ↑↓、Home / End、Page Up / Down 移动,Enter / 空格选中(单选随后关闭),Esc、Tab 关闭。\n * 写了 name 时用隐藏的 input 随表单提交(多选时每个值一个)。\n *\n * searchable 时弹出层顶部有搜索框:打开后焦点在搜索框里,输入就过滤选项(按显示文字,忽略大小写与重音,或用 filter),\n * ↑↓ 移动、Enter 选中、Esc 关闭并把焦点还给触发按钮。已选的项被过滤掉、或 items 换了(远程搜索)时,\n * 触发框里仍显示它的文字(useSelectedOptions)。\n *\n * 远程选项:给 load 时打开就按搜索词请求,输入停下 300 ms 再请求,只采用最后一次(remote-items.ts);\n * 自己管请求时写 _search={keyword} 拿到搜索词、filter={false} 关掉本地过滤,items 与 loading 自己给。\n *\n * <Select searchable load={(q, signal) => http.get<User[]>('/users', { query: { q }, signal })} toOption={…} _value={owner} />\n *\n * creatable 时自带搜索框,输入的文字没有对应的选项时列表末尾多一项“新建“…””:选中后值就是这段文字,onCreate 报告它。\n *\n * 部件:Root(外壳)、Tags(多选时的那排标签)、Tag、Trigger(button,主元素)、Value、Clear、Chevron、Popover、\n * Search(TextField)、Listbox。\n */\nimport { ChevronDown, Plus, Search } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useId, watch, type VNodeChild } from 'vue';\nimport type { PresetIconButtonProps } from '../base/CloseButton';\nimport { buttonBaseStyles } from '../base/button-core';\nimport { Icon, type IconProps } from '../base/Icon';\nimport type { TagProps } from '../base/Tag';\nimport { visuallyHiddenStyles } from '../base/VisuallyHidden';\nimport { useLang, useTextMatcher } from '../config/context';\nimport { useAnchoredPopover, type FloatingPlacement } from '../utils/dom/floating';\nimport { openSetter, type OpenChangeProps } from '../utils/dom/open-change';\nimport { ListPopover } from './ListPopover';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { selectedValues, type SelectionProps } from '../utils/data/selection';\nimport {\n controlClear,\n controlParts,\n clickOnShell,\n focusOnShell,\n controlIcon,\n useControlAppearance,\n type ControlIconOptions,\n type ControlOptions,\n} from './control';\nimport { useFieldControl } from '../form/Field';\nimport { FormValue } from '../form/FormValue';\nimport { disclosureStyles } from '../utils/style/transitions';\nimport { activeOptionId, type ListboxViewProps, type OptionRenderState } from './Listbox';\nimport {\n handleListKeydown,\n useListState,\n useResultAnnouncement,\n useSelectedOptions,\n type ListboxOption,\n type OptionMapping,\n toListboxOption,\n} from './list-state';\nimport { useRemoteItems, type OptionsLoader } from './remote-items';\nimport { SelectedTags, type SelectedTagsProps } from './SelectedTags';\nimport { TextField, type TextFieldProps } from './TextField';\n\n/** 角色、展开状态与可用性由组件管理 */\nexport type SelectManaged =\n | 'type'\n | 'role'\n | 'value'\n | 'disabled'\n | 'aria-haspopup'\n | 'aria-expanded'\n | 'aria-controls'\n | 'aria-activedescendant'\n | 'aria-invalid'\n | 'aria-required'\n | 'aria-describedby';\n\n/** 下拉选择共有的属性;未声明的属性落到触发按钮(主元素)上,外壳用 propsRoot */\nexport interface SelectBaseProps<T>\n extends PartProps<'button', SelectManaged>, ControlOptions, ControlIconOptions, OpenChangeProps {\n /** 数据:ListboxOption、字符串,或任意数据加 toOption;有 load 时是第一次加载之前的选项(编辑页放上已选的那一项) */\n items?: readonly T[];\n /** 没有选择时显示的文字,默认取语言包(请选择) */\n placeholder?: string;\n /** 是否打开:父级可以写 _open={x} */\n _open?: boolean;\n /** 弹出位置,默认 bottom-start */\n placement?: FloatingPlacement;\n /** 有选择时显示清空按钮 */\n clearable?: boolean;\n /** 弹出层顶部带搜索框,输入过滤选项:选项多(供应商、人员、件号)时用 */\n searchable?: boolean;\n /** 搜索时怎样算匹配;默认显示文字包含输入(忽略大小写与重音)。false 时不在本地过滤(服务端按 search 过滤) */\n filter?: false | ((item: T, query: string) => boolean);\n /** 搜索框里的文字:打开时清空;父级写 _search={x} 拿到它(自己发请求时) */\n _search?: string;\n /** 远程选项:打开时与搜索词停下 300 ms 后调用,结果代替 items;只采用最后一次,被顶掉的请求 signal 中止 */\n load?: OptionsLoader<T>;\n /**\n * 可以新建选项(自带搜索框):输入的文字没有对应的选项时,列表末尾多一项“新建“…””,选中后值就是这段文字。\n * 在 onCreate 里把它加进 items(或存到服务端);不加时触发框里照样显示这段文字\n */\n creatable?: boolean;\n /** 新建了一项(creatable 时):参数是输入的文字,也就是选中的值 */\n onCreate?: (text: string) => void;\n /** 多选时触发框里最多显示几个标签,其余收进“+N”;'responsive' 时排成一行、按宽度放,放不下的收起 */\n maxTagCount?: number | 'responsive';\n /** 表单提交时的 name(用隐藏的 input) */\n name?: string;\n /** 不可用 */\n disabled?: boolean;\n /** 只读:能聚焦、能看到已选的内容,不能打开、不能清空 */\n readonly?: boolean;\n /** 必填 */\n required?: boolean;\n /** 校验未通过 */\n invalid?: boolean;\n /** 自定义选项内容:`{(option, { selected, active }) => …}` */\n children?: (item: T, state: OptionRenderState) => VNodeChild;\n /** 自定义触发框里显示的内容(已选的选项);默认单选显示文字、多选显示标签 */\n renderValue?: (selected: readonly T[]) => VNodeChild;\n /** 正在取选项(异步加载):没有选项时列表里是骨架行,已有选项时顶部显示进度条 */\n loading?: boolean;\n /** 没有选项时列表里显示的内容 */\n empty?: VNodeChild;\n /** 外壳的部件属性 */\n propsRoot?: PartProps<'div'>;\n /** 已选内容那一层的部件属性 */\n propsValue?: PartProps<'span'>;\n /** 多选时每个标签的部件属性 */\n propsTag?: Partial<TagProps>;\n /** 多选时那排标签的部件属性 */\n propsTags?: Partial<SelectedTagsProps>;\n /** 清空按钮的部件属性 */\n propsClear?: Partial<PresetIconButtonProps>;\n /** 下拉箭头的部件属性 */\n propsChevron?: Partial<IconProps>;\n /** 弹出层的部件属性 */\n propsPopover?: PartProps<'div', 'id' | 'popover'>;\n /** 列表的部件属性 */\n propsListbox?: Partial<ListboxViewProps<T>>;\n /** 搜索框的部件属性(searchable 时) */\n propsSearch?: Partial<TextFieldProps>;\n}\n\n/** 单选或多选;items 不是 ListboxOption 或字符串时要给 toOption */\nexport type SelectProps<T = ListboxOption> = SelectBaseProps<T> & SelectionProps & OptionMapping<T>;\n\n/** Select 的样式:root、trigger、value、placeholder、chevronOpen 等 */\nexport const selectStyles = css.create({\n // 在 ControlGroup 里也按内容宽(输入框才分剩余宽度)\n root: { cursor: 'pointer', flexGrow: 0 },\n // 去掉按钮默认外观用 buttonBaseStyles;像输入框一样没有自己的焦点环与波纹(焦点环画在外壳上)\n trigger: {\n display: 'flex',\n alignItems: 'center',\n flexGrow: 1,\n minWidth: 0,\n alignSelf: 'stretch',\n textAlign: 'start',\n cursor: 'inherit',\n outlineStyle: 'none',\n },\n value: {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: '_xs',\n minWidth: 0,\n paddingBlock: '_xs',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n },\n // 是真正的文字(不是原生 placeholder),要满足 4.5:1:用 textMuted 而不是 textPlaceholder\n placeholder: { color: '_textMuted' },\n // 多选时标签排在触发按钮前面(SelectedTags)占剩余宽度,触发按钮里只剩读屏用的文字\n triggerBehindTags: { flexGrow: 0 },\n chevronOpen: { rotate: '180deg' },\n // 弹出层顶部的搜索框占满宽度\n search: { width: '_full' },\n});\n\n/** creatable 时列表末尾那一项“新建“…””:不是 items 里的数据 */\nclass Creation {\n readonly text: string;\n constructor(text: string) {\n this.text = text;\n }\n}\n\n/** 下拉选择:触发框 + 弹出的选项列表 */\nexport const Select = _component(\n <T,>({\n items = [],\n toOption,\n selectionMode = 'single',\n _value: value = _bindable<string | string[] | null>(null),\n _open: open = _bindable(false),\n onOpenChange,\n placeholder,\n placement = 'bottom-start',\n clearable = false,\n searchable = false,\n filter,\n _search: search = _bindable(''),\n load,\n creatable = false,\n onCreate,\n maxTagCount,\n name,\n variant,\n size,\n radius,\n disabled,\n readonly,\n required,\n invalid,\n id,\n icon,\n children,\n renderValue,\n empty,\n loading = false,\n propsRoot,\n propsIcon,\n propsValue,\n propsTag,\n propsTags,\n propsClear,\n propsChevron,\n propsPopover,\n propsListbox,\n propsSearch,\n ...rest\n }: SelectProps<T>) => {\n const lang = useLang();\n const textMatcher = useTextMatcher();\n const listId = useId();\n const appearance = useControlAppearance(() => ({ variant, size, radius }));\n const control = useFieldControl(() => ({ id, invalid, required, disabled, readonly }));\n const multiple = () => selectionMode === 'multiple';\n const optionOf = (item: T) => toListboxOption(item, toOption);\n const remote = useRemoteItems({\n load: () => load,\n query: () => search,\n open: () => open,\n items: () => items,\n });\n // 本地过滤:有 load 或 filter={false} 时交给服务端\n const localFilter = () => !load && filter !== false;\n const matches = (item: T, text: string) =>\n filter ? filter(item, text) : textMatcher()(optionOf(item).label, text);\n // creatable 自带搜索框\n const withSearch = () => searchable || creatable;\n // creatable:输入的文字没有同名(或同值)的选项时,列表末尾多一项“新建”\n const creation = () => {\n const text = search.trim();\n if (!creatable || !text) return undefined;\n const exists = remote.items().some((item) => {\n const option = optionOf(item);\n return option.value === text || option.label.toLowerCase() === text.toLowerCase();\n });\n return exists ? undefined : new Creation(text);\n };\n const list = useListState<T | Creation>({\n items: () => {\n const all = remote.items();\n const shown =\n withSearch() && search && localFilter()\n ? all.filter((item) => matches(item, search))\n : all;\n const created = creation();\n return created ? [...shown, created] : shown;\n },\n toOption: (item) =>\n item instanceof Creation\n ? { value: item.text, label: lang.selectCreate(item.text), icon: Plus }\n : optionOf(item),\n selected: () => selectedValues(value),\n mode: () => selectionMode,\n onChange: (next) => {\n const created = creation();\n if (created && next.includes(created.text)) {\n onCreate?.(created.text);\n search = '';\n }\n value = multiple() ? [...next] : (next[0] ?? null);\n },\n // 单选时再点已选的一项不取消(同原生 select);要清空用清除按钮\n required: () => !multiple(),\n });\n // 已选项的文字取自全部 items(过滤掉的、远程搜索换掉的也在)\n const selectedOptions = useSelectedOptions({ items: remote.items, toOption: optionOf });\n let shell = _state.raw<HTMLElement | null>(null);\n let trigger = _state.raw<HTMLElement | null>(null);\n let panel = _state.raw<HTMLElement | null>(null);\n const setOpen = openSetter(\n () => open,\n (next) => (open = next),\n () => onOpenChange,\n );\n\n /** 多选时移除一项(标签的移除按钮、Backspace) */\n function remove(target: string) {\n value = selectedValues(value).filter((item) => item !== target);\n }\n\n function removeLast() {\n const last = selectedValues(value).at(-1);\n if (last !== undefined) remove(last);\n }\n\n function show(highlight: 'default' | 'last' = 'default', event?: Event) {\n search = '';\n list.activateDefault();\n if (highlight === 'last' && selectedValues(value).length === 0) list.move('last');\n setOpen(true, 'trigger', event);\n }\n\n useResultAnnouncement(() => ({ query: search, count: list.options.length }));\n\n // 输入搜索词:高亮第一个匹配的。打开时清空搜索词不算:那时高亮的是已选项\n watch(\n () => search,\n (text) => text && list.move('first'),\n );\n\n // 选中一项:单选时关闭\n function picked(event?: Event) {\n if (!multiple()) setOpen(false, 'select', event);\n }\n\n /** Ctrl / Cmd / Alt 组合键是浏览器的(刷新、复制、查找),不做按字母跳转 */\n const hasModifier = (event: KeyboardEvent) => event.altKey || event.ctrlKey || event.metaKey;\n\n function onKeydown(event: KeyboardEvent) {\n if (control().disabled || control().readonly) return;\n if (event.key === 'Backspace' && multiple()) {\n event.preventDefault();\n removeLast();\n return;\n }\n if (!open) {\n if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n show('default', event);\n } else if (event.key === 'ArrowUp') {\n event.preventDefault();\n show('last', event);\n } else if (!multiple() && !hasModifier(event) && list.typeahead(event.key)) {\n // 同原生 select:关闭时按字母直接选中\n event.preventDefault();\n if (list.active !== undefined && !list.isSelected(list.active)) list.select(list.active);\n }\n return;\n }\n if (event.key === 'Tab') {\n setOpen(false, 'focus', event);\n return;\n }\n if (handleListKeydown(list, event, { space: true, typeahead: true }, () => picked(event))) {\n event.preventDefault();\n }\n }\n\n // 搜索框里:↑↓ 移动、Enter 选中(Home / End、空格留给输入),Tab 关闭后照常移到下一个;\n // 多选时空的搜索框里按 Backspace 移除最后一项\n function onSearchKeydown(event: KeyboardEvent) {\n if (event.key === 'Tab') {\n setOpen(false, 'focus', event);\n return;\n }\n if (event.key === 'Backspace' && !search && multiple()) {\n event.preventDefault();\n removeLast();\n return;\n }\n if (handleListKeydown(list, event, { homeEnd: false }, () => picked(event)))\n event.preventDefault();\n }\n\n function clear() {\n value = multiple() ? [] : null;\n trigger?.focus();\n }\n\n // 弹层贴着外壳、与它同宽;焦点一直在触发按钮上,关闭时不用归还(searchable 时见 onPanelToggle)\n const { onToggle } = useAnchoredPopover(\n () => ({\n open,\n anchor: shell,\n panel,\n floating: { placement, matchWidth: true, fitHeight: true },\n restoreFocus: false,\n }),\n { close: (reason, event) => setOpen(false, reason, event) },\n );\n\n // searchable:打开后焦点进搜索框;关闭时焦点还在弹层里(Esc、选中、点外面)就还给触发按钮\n function onPanelToggle(event: ToggleEvent) {\n onToggle(event);\n if (!withSearch()) return;\n if (event.newState === 'open') {\n panel?.querySelector<HTMLInputElement>('input[type=\"search\"]')?.focus();\n return;\n }\n const active = document.activeElement;\n if (!active || active === document.body || panel?.contains(active)) trigger?.focus();\n }\n\n return () => {\n const state = control();\n const values = selectedValues(value);\n const selected = selectedOptions(values);\n const off = state.disabled;\n // 只读:能聚焦,不能打开、不能改\n const locked = off || state.readonly;\n // 多选的默认显示:触发按钮前面一排标签,按钮里只留给读屏的文字\n const tagged = multiple() && !renderValue && selected.length > 0;\n const shown = tagged ? (\n <span {...css.attrs(visuallyHiddenStyles.hidden)}>\n {lang.selectSummary(selected.map((option) => option.label))}\n </span>\n ) : (\n (renderValue?.(\n selected.flatMap((option) => (option.item === undefined ? [] : [option.item])),\n ) ??\n (selected.length === 0 ? (\n <span {...css.attrs(selectStyles.placeholder)}>\n {placeholder ?? lang.selectPlaceholder}\n </span>\n ) : (\n selected[0]!.label\n )))\n );\n const tags = tagged && (\n <SelectedTags\n {...mergePartProps<SelectedTagsProps>(\n {\n tags: selected,\n maxTagCount,\n onRemove: locked ? undefined : remove,\n propsTag,\n },\n propsTags,\n )}\n />\n );\n const s = controlParts(appearance(), state);\n return (\n <>\n <div\n {...mergeAttrs<'div'>({ ...s.root, sx: [s.root.sx, selectStyles.root] }, propsRoot, {\n ref: _toRef(shell),\n // 点在外壳空白处(含多选的标签):焦点交给触发按钮,并与点触发按钮一样切换打开(只读时只聚焦)\n onPointerdown: off ? undefined : focusOnShell(() => trigger),\n onClick: locked\n ? undefined\n : clickOnShell(\n () => trigger,\n (event) => (open ? setOpen(false, 'trigger', event) : show('default', event)),\n ),\n })}\n data-state={open ? 'open' : 'closed'}\n data-invalid={state.invalid || undefined}\n data-disabled={off || undefined}\n >\n {controlIcon(s, icon, propsIcon)}\n {tags}\n <button\n {...mergeAttrs<'button'>(\n {\n sx: [\n buttonBaseStyles.root,\n selectStyles.trigger,\n tagged && selectStyles.triggerBehindTags,\n ],\n },\n rest,\n {\n ref: _toRef(trigger),\n id: state.id,\n type: 'button',\n role: 'combobox',\n disabled: off,\n 'aria-haspopup': 'listbox',\n 'aria-expanded': open,\n 'aria-controls': listId,\n 'aria-activedescendant': open ? activeOptionId(list, listId) : undefined,\n 'aria-invalid': state.invalid || undefined,\n 'aria-required': state.required || undefined,\n 'aria-readonly': state.readonly || undefined,\n 'aria-describedby': state.describedBy,\n onClick: (event: MouseEvent) => {\n // Safari 点击按钮不会聚焦它:焦点要在触发按钮上,键盘与 aria-activedescendant 才有效\n trigger?.focus();\n if (locked) return;\n if (open) setOpen(false, 'trigger', event);\n else show('default', event);\n },\n onKeydown,\n },\n )}\n >\n <span {...mergeAttrs<'span'>({ sx: selectStyles.value }, propsValue)}>{shown}</span>\n </button>\n {clearable && values.length > 0 && !locked && controlClear(s, propsClear, clear)}\n <Icon\n {...mergePartProps<IconProps>(\n {\n ...s.icon,\n icon: ChevronDown,\n sx: [s.icon.sx, disclosureStyles.icon, open && selectStyles.chevronOpen],\n },\n propsChevron,\n )}\n />\n <FormValue name={name} value={values} disabled={state.disabled} />\n </div>\n <ListPopover\n panelRef={_toRef(panel)}\n onToggle={onPanelToggle}\n size={appearance().size}\n labelledBy={state.labelledBy}\n label={rest['aria-label']}\n loading={loading || remote.loading()}\n onPick={() => picked()}\n header={\n withSearch() && (\n <TextField\n {...mergePartProps<TextFieldProps>(\n {\n icon: Search,\n variant: 'soft',\n placeholder: lang.selectSearch,\n 'aria-label': lang.selectSearch,\n propsRoot: { sx: selectStyles.search },\n },\n propsSearch,\n )}\n type=\"search\"\n _value={search}\n aria-controls={listId}\n aria-activedescendant={open ? activeOptionId(list, listId) : undefined}\n onKeydown={onSearchKeydown}\n />\n )\n }\n listbox={{\n list,\n id: listId,\n empty: remote.failure() ?? (search ? lang.selectNoMatch : empty),\n renderOption:\n children &&\n ((item: T | Creation, state: OptionRenderState) =>\n item instanceof Creation ? lang.selectCreate(item.text) : children(item, state)),\n multiple: multiple(),\n }}\n // 使用方按 T 写列表的部件属性;creatable 时多出的“新建”那一项由上面的 renderOption 处理\n propsListbox={propsListbox as Partial<ListboxViewProps<T | Creation>> | undefined}\n propsPopover={propsPopover}\n />\n </>\n );\n };\n },\n);\n"],"mappings":"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"}
|
package/dist/input/TreeSelect.js
CHANGED
|
@@ -241,7 +241,7 @@ var TreeSelect = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
241
241
|
filter: treeFilter(),
|
|
242
242
|
loadChildren: loadChildren.value,
|
|
243
243
|
loading: remote.loading(),
|
|
244
|
-
empty: remote.
|
|
244
|
+
empty: remote.failure() ?? (search.value.trim() ? lang.selectNoMatch : void 0),
|
|
245
245
|
isDisabled: isDisabled.value,
|
|
246
246
|
propsScrollArea: { sx: treeSelectStyles.tree },
|
|
247
247
|
"aria-labelledby": labelledBy,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TreeSelect.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_ref","_vue_shallowRef","_vue_computed","_createVNode","_Fragment","_mergeProps"],"sources":["../../src/input/TreeSelect.tsx"],"sourcesContent":["/**\n * 树形下拉选择:触发框同 Select,弹出的是一棵树(组织机构、产品结构、物料分类)。单选选一个节点,多选勾选(父子联动)。\n *\n * <TreeSelect\n * items={orgs}\n * getKey={(org) => org.id}\n * getLabel={(org) => org.name}\n * getChildren={(org) => org.children}\n * _value={orgId}\n * searchable\n * />\n * <TreeSelect … selectionMode=\"multiple\" _value={teamIds} /> 勾选:勾上父节点就是整枝\n * <TreeSelect … selectionMode=\"multiple\" checkedStrategy=\"child\" maxTagCount=\"responsive\" />\n *\n * - 单选:点节点或 Enter 选中并关闭;多选:空格或点勾选框,勾上父节点会勾上它的子孙,子节点全勾上时父节点也勾上。\n * value 按 checkedStrategy 放勾上的节点:全部(默认)、整枝时只放父节点、只放叶子(checkedByStrategy)。\n * - 多选的标签同 Select(SelectedTags):排在触发按钮前面,可移除(取消整枝),maxTagCount 收起,Backspace 移除最后一个。\n * 标签只列最上层的整枝(勾上了“研发中心”就不再列它下面的每个组);checkedStrategy=\"child\" 时列叶子。\n * - searchable:弹出层顶部的搜索框,按节点文字过滤,命中节点的祖先一起显示并展开;↓ 从搜索框进到树里。\n * 给 load 时由服务端搜索:打开时与输入停下 300 ms 后按搜索词请求,返回命中的节点连同祖先,结果全部展开\n * (remote-items.ts,同 Select);自己管请求时写 _search={keyword}、filter={false}。\n * - 打开后焦点在树上(或搜索框里),键盘同 Tree;Esc 关闭并把焦点还给触发按钮。懒加载同 Tree 的 loadChildren。\n * - 已选节点的文字从已加载的树里找,远程搜索换掉的按见过的文字显示;从没见过的(懒加载还没取回来)显示成 key。\n *\n * 部件:Root(外壳)、Tags(多选时的那排标签)、Tag、Trigger(button,主元素)、Value、Clear、Chevron、Popover、\n * Search(TextField)、Tree。\n */\nimport { ChevronDown, Search } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useId, watch } from 'vue';\nimport type { PresetIconButtonProps } from '../base/CloseButton';\nimport { buttonBaseStyles } from '../base/button-core';\nimport { Icon, type IconProps } from '../base/Icon';\nimport type { TagProps } from '../base/Tag';\nimport { visuallyHiddenStyles } from '../base/VisuallyHidden';\nimport { useLang } from '../config/context';\nimport { Tree, type TreeProps } from '../display/Tree';\nimport { useFieldControl } from '../form/Field';\nimport { FormValue } from '../form/FormValue';\nimport { selectedValues, type SelectionModes } from '../utils/data/selection';\nimport {\n checkedByStrategy,\n indexTree,\n toggleChecked,\n type CheckStrategies,\n} from '../utils/data/tree';\nimport {\n anchoredPanelSx,\n floatingStyles,\n useAnchoredPopover,\n type FloatingPlacement,\n} from '../utils/dom/floating';\nimport { openSetter, type OpenChangeProps } from '../utils/dom/open-change';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { disclosureStyles } from '../utils/style/transitions';\nimport {\n controlClear,\n controlIcon,\n controlParts,\n clickOnShell,\n focusOnShell,\n useControlAppearance,\n type ControlIconOptions,\n type ControlOptions,\n} from './control';\nimport { listPopoverStyles } from './ListPopover';\nimport { useRemoteItems, type OptionsLoader } from './remote-items';\nimport { selectStyles, type SelectManaged } from './Select';\nimport { SelectedTags, type SelectedTagsProps } from './SelectedTags';\nimport { TextField, type TextFieldProps } from './TextField';\n\n/** 未声明的属性落到触发按钮(主元素)上,外壳用 propsRoot */\nexport interface TreeSelectProps<T>\n extends PartProps<'button', SelectManaged>, ControlOptions, ControlIconOptions, OpenChangeProps {\n /** 顶层节点;有 load 时是第一次加载之前的树(编辑页放上已选节点的那一枝) */\n items?: readonly T[];\n /** 节点的 key,整棵树里唯一:也是选中后写回的值 */\n getKey: (node: T) => string;\n /** 节点的文字 */\n getLabel: (node: T) => string;\n /** 子节点;叶子或还没加载时 undefined */\n getChildren: (node: T) => readonly T[] | undefined;\n /** 有没有子节点(懒加载时子节点还没取回来也算有) */\n hasChildren?: (node: T) => boolean;\n /** 展开还没有子节点的节点时取回来(返回 Promise 时显示加载中) */\n loadChildren?: (node: T) => Promise<unknown> | void;\n /** 不能选的节点(只能展开,如只能选到部门、不能选公司时) */\n isDisabled?: (node: T) => boolean;\n /** 单选(默认)或多选(勾选,父子联动) */\n selectionMode?: keyof SelectionModes;\n /** 单选时是选中节点的 key(没有时 null),多选时是勾上的节点(按 checkedStrategy):父级写 _value={x} */\n _value?: string | string[] | null;\n /**\n * 多选时 value 里放哪些勾上的节点(同 Ant Design 的 showCheckedStrategy):all 全部(默认);\n * parent 父节点全勾上时只放父节点(提交“整个部门”);child 只放叶子(提交“具体的人”)\n */\n checkedStrategy?: keyof CheckStrategies;\n /** 多选时触发框里最多显示几个标签,其余收进“+N”;'responsive' 时排成一行、按宽度放 */\n maxTagCount?: number | 'responsive';\n /** 没有选择时显示的文字,默认取语言包(请选择) */\n placeholder?: string;\n /** 是否打开:父级可以写 _open={x} */\n _open?: boolean;\n /** 弹出位置,默认 bottom-start */\n placement?: FloatingPlacement;\n /** 有选择时显示清空按钮 */\n clearable?: boolean;\n /** 弹出层顶部带搜索框,按节点文字过滤 */\n searchable?: boolean;\n /** 搜索时怎样算命中;默认节点文字包含输入(忽略大小写与重音)。false 时不在本地过滤(服务端按 search 过滤) */\n filter?: false | ((node: T, query: string) => boolean);\n /** 搜索框里的文字:打开时清空;父级写 _search={x} 拿到它(自己发请求时) */\n _search?: string;\n /** 远程搜索:打开时与搜索词停下 300 ms 后调用,返回命中的节点连同祖先(顶层节点),结果代替 items */\n load?: OptionsLoader<T>;\n /** 表单提交时的 name(用隐藏的 input) */\n name?: string;\n /** 不可用 */\n disabled?: boolean;\n /** 只读:能聚焦、能看到已选的内容,不能打开、不能清空 */\n readonly?: boolean;\n /** 必填 */\n required?: boolean;\n /** 校验未通过 */\n invalid?: boolean;\n /** 外壳的部件属性 */\n propsRoot?: PartProps<'div'>;\n /** 已选内容那一层的部件属性 */\n propsValue?: PartProps<'span'>;\n /** 多选时每个标签的部件属性 */\n propsTag?: Partial<TagProps>;\n /** 多选时那排标签的部件属性 */\n propsTags?: Partial<SelectedTagsProps>;\n /** 清空按钮的部件属性 */\n propsClear?: Partial<PresetIconButtonProps>;\n /** 下拉箭头的部件属性 */\n propsChevron?: Partial<IconProps>;\n /** 弹出层的部件属性 */\n propsPopover?: PartProps<'div', 'id' | 'popover'>;\n /** 搜索框的部件属性(searchable 时) */\n propsSearch?: Partial<TextFieldProps>;\n /** 树的部件属性 */\n propsTree?: Partial<TreeProps<T>>;\n}\n\n/** TreeSelect 的样式:tree */\nexport const treeSelectStyles = css.create({\n // 树的滚动区域占满弹出层剩下的高度(最大高度由定位写在弹出层上)\n tree: { flexGrow: 1, minHeight: 0 },\n // 弹出层比触发框窄时也放得下几层缩进\n panel: { minWidth: '16em' },\n});\n\n/** 树形下拉选择:触发框 + 弹出的树,单选或勾选,可搜索 */\nexport const TreeSelect = _component(\n <T,>({\n items = [],\n getKey,\n getLabel,\n getChildren,\n hasChildren,\n loadChildren,\n isDisabled,\n selectionMode = 'single',\n checkedStrategy = 'all',\n maxTagCount,\n _value: value = _bindable<string | string[] | null>(null),\n _open: open = _bindable(false),\n onOpenChange,\n placeholder,\n placement = 'bottom-start',\n clearable = false,\n searchable = false,\n filter,\n _search: search = _bindable(''),\n load,\n name,\n variant,\n size,\n radius,\n disabled,\n readonly,\n required,\n invalid,\n id,\n icon,\n propsRoot,\n propsIcon,\n propsValue,\n propsTag,\n propsTags,\n propsClear,\n propsChevron,\n propsPopover,\n propsSearch,\n propsTree,\n ...rest\n }: TreeSelectProps<T>) => {\n const lang = useLang();\n const treeId = useId();\n const appearance = useControlAppearance(() => ({ variant, size, radius }));\n const control = useFieldControl(() => ({ id, invalid, required, disabled, readonly }));\n const multiple = () => selectionMode === 'multiple';\n const remote = useRemoteItems({\n load: () => load,\n query: () => search,\n open: () => open,\n items: () => items,\n });\n let expanded = _state<string[]>([]);\n let shell = _state.raw<HTMLElement | null>(null);\n let trigger = _state.raw<HTMLElement | null>(null);\n let panel = _state.raw<HTMLElement | null>(null);\n const setOpen = openSetter(\n () => open,\n (next) => (open = next),\n () => onOpenChange,\n );\n\n // 已加载的整棵树:key → 节点与父节点,查已选的文字、搜索时找祖先都用它\n const nodes = _derived.by(() => {\n const map = new Map<string, { node: T; parent: string | undefined }>();\n const visit = (level: readonly T[], parent: string | undefined) => {\n for (const node of level) {\n const key = getKey(node);\n map.set(key, { node, parent });\n visit(getChildren(node) ?? [], key);\n }\n };\n visit(remote.items(), undefined);\n return map;\n });\n // 见过的节点文字:远程搜索换掉了已选的节点,触发框里仍显示它的文字\n const seenLabels = new Map<string, string>();\n watch(\n () => nodes,\n (map) => {\n for (const [key, { node }] of map) seenLabels.set(key, getLabel(node));\n },\n { immediate: true },\n );\n\n // 本地搜索交给 Tree 的 filter(命中的节点与祖先、展开祖先、命中的加粗);\n // 有 load 或 filter={false} 时结果已经是服务端筛好的,不再过滤\n const treeFilter = () => {\n const text = search.trim();\n if (!searchable || !text || load || filter === false) return undefined;\n return filter ? (node: T) => filter(node, text) : text;\n };\n // 服务端搜索的结果全部展开(只有命中的节点与它们的祖先);清空搜索回到自己展开的状态\n const remoteResults = () => !!load && !!search.trim();\n const shownExpanded = () => (remoteResults() ? [...nodes.keys()] : expanded);\n\n const values = () => selectedValues(value);\n // 勾选联动要看到折叠着的节点\n const index = _derived.by(() =>\n indexTree(remote.items(), { getKey: (node) => getKey(node), getChildren }),\n );\n /** 按取法整理勾上的节点:已加载的按树的顺序与父子关系,还没加载(或远程搜索换掉了)的原样跟在后面 */\n const byStrategy = (keys: readonly string[], strategy: keyof CheckStrategies) => [\n ...checkedByStrategy(index, keys, strategy),\n ...keys.filter((key) => !index.parents.has(key)),\n ];\n /** 勾选变了(树、移除标签):按 checkedStrategy 写回,还没加载的已选节点保留 */\n const setChecked = (checked: readonly string[]) => {\n const unknown = values().filter((key) => !index.parents.has(key));\n value = [...checkedByStrategy(index, checked, checkedStrategy), ...unknown];\n };\n /** 触发框里显示的节点:多选时只列最上层的整枝(父节点也勾上了的不重复列),child 时列叶子 */\n const displayKeys = () =>\n multiple()\n ? byStrategy(values(), checkedStrategy === 'child' ? 'child' : 'parent')\n : values();\n\n /** 移除一个标签:取消整枝 */\n function remove(key: string) {\n setChecked(toggleChecked(index, values(), key));\n }\n const labelOf = (key: string) => {\n const found = nodes.get(key);\n return found ? getLabel(found.node) : (seenLabels.get(key) ?? key);\n };\n\n function show(event?: Event) {\n search = '';\n // 打开时展开已选节点的祖先:已选的看得见\n const ancestors = new Set(expanded);\n for (const key of values()) {\n for (let at = nodes.get(key)?.parent; at !== undefined; at = nodes.get(at)?.parent) {\n ancestors.add(at);\n }\n }\n expanded = [...ancestors];\n setOpen(true, 'trigger', event);\n }\n\n function clear() {\n value = multiple() ? [] : null;\n trigger?.focus();\n }\n\n const { onToggle } = useAnchoredPopover(\n () => ({\n open,\n anchor: shell,\n panel,\n floating: { placement, matchWidth: true, fitHeight: true },\n restoreFocus: false,\n }),\n { close: (reason, event) => setOpen(false, reason, event) },\n );\n\n // 打开后焦点进搜索框或树;关闭时焦点还在弹层里(Esc、选中、点外面)就还给触发按钮\n function onPanelToggle(event: ToggleEvent) {\n onToggle(event);\n if (event.newState === 'open') {\n const target = searchable\n ? panel?.querySelector<HTMLElement>('input[type=\"search\"]')\n : panel?.querySelector<HTMLElement>('[role=\"tree\"]');\n target?.focus();\n return;\n }\n const active = document.activeElement;\n if (!active || active === document.body || panel?.contains(active)) trigger?.focus();\n }\n\n function onTriggerKeydown(event: KeyboardEvent) {\n const state = control();\n if (state.disabled || state.readonly) return;\n // 多选:Backspace 移除最后一个标签(同 Select)\n if (event.key === 'Backspace' && multiple()) {\n event.preventDefault();\n const last = displayKeys().at(-1);\n if (last !== undefined) remove(last);\n return;\n }\n if (!open && (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n show(event);\n }\n }\n\n // 搜索框里 ↓ 进到树;Tab 关闭后照常移到下一个\n function onSearchKeydown(event: KeyboardEvent) {\n if (event.key === 'Tab') {\n setOpen(false, 'focus', event);\n return;\n }\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n panel?.querySelector<HTMLElement>('[role=\"tree\"]')?.focus();\n }\n }\n\n /** 树的属性(单选、多选共用):本地搜索时带上 filter */\n const treeProps = (labelledBy: string | undefined) =>\n mergePartProps<TreeProps<T>>(\n {\n items: remote.items(),\n getKey,\n getLabel,\n getChildren,\n hasChildren,\n filter: treeFilter(),\n loadChildren,\n loading: remote.loading(),\n empty: remote.error()\n ? lang.listLoadFailed\n : search.trim()\n ? lang.selectNoMatch\n : undefined,\n isDisabled,\n propsScrollArea: { sx: treeSelectStyles.tree },\n 'aria-labelledby': labelledBy,\n 'aria-label': labelledBy ? undefined : rest['aria-label'],\n },\n propsTree,\n { id: treeId },\n );\n\n // 单选:选中就关闭(树的 selected 里已按 select 关过;父级改 value 时也关,不通知)\n watch(\n () => value,\n () => {\n if (!multiple() && open) open = false;\n },\n );\n\n return () => {\n const state = control();\n const s = controlParts(appearance(), state);\n const off = state.disabled;\n const locked = off || state.readonly;\n const shownKeys = displayKeys();\n // 多选:触发按钮前面一排标签,按钮里只留给读屏的文字(同 Select)\n const tagged = multiple() && shownKeys.length > 0;\n const shown =\n shownKeys.length === 0 ? (\n <span {...css.attrs(selectStyles.placeholder)}>\n {placeholder ?? lang.selectPlaceholder}\n </span>\n ) : tagged ? (\n <span {...css.attrs(visuallyHiddenStyles.hidden)}>\n {lang.selectSummary(shownKeys.map(labelOf))}\n </span>\n ) : (\n labelOf(shownKeys[0]!)\n );\n return (\n <>\n <div\n {...mergeAttrs<'div'>({ ...s.root, sx: [s.root.sx, selectStyles.root] }, propsRoot, {\n ref: _toRef(shell),\n onPointerdown: off ? undefined : focusOnShell(() => trigger),\n onClick:\n off || state.readonly\n ? undefined\n : clickOnShell(\n () => trigger,\n (event) => (open ? setOpen(false, 'trigger', event) : show(event)),\n ),\n })}\n data-state={open ? 'open' : 'closed'}\n data-invalid={state.invalid || undefined}\n data-disabled={off || undefined}\n >\n {controlIcon(s, icon, propsIcon)}\n {tagged && (\n <SelectedTags\n {...mergePartProps<SelectedTagsProps>(\n {\n tags: shownKeys.map((key) => ({ value: key, label: labelOf(key) })),\n maxTagCount,\n onRemove: locked ? undefined : remove,\n propsTag,\n },\n propsTags,\n )}\n />\n )}\n <button\n {...mergeAttrs<'button'>(\n {\n sx: [\n buttonBaseStyles.root,\n selectStyles.trigger,\n tagged && selectStyles.triggerBehindTags,\n ],\n },\n rest,\n {\n ref: _toRef(trigger),\n id: state.id,\n type: 'button',\n role: 'combobox',\n disabled: off,\n 'aria-haspopup': 'tree',\n 'aria-expanded': open,\n 'aria-controls': treeId,\n 'aria-invalid': state.invalid || undefined,\n 'aria-required': state.required || undefined,\n 'aria-readonly': state.readonly || undefined,\n 'aria-describedby': state.describedBy,\n onClick: (event: MouseEvent) => {\n trigger?.focus();\n if (locked) return;\n if (open) setOpen(false, 'trigger', event);\n else show(event);\n },\n onKeydown: onTriggerKeydown,\n },\n )}\n >\n <span {...mergeAttrs<'span'>({ sx: selectStyles.value }, propsValue)}>{shown}</span>\n </button>\n {clearable && shownKeys.length > 0 && !locked && controlClear(s, propsClear, clear)}\n <Icon\n {...mergePartProps<IconProps>(\n {\n ...s.icon,\n icon: ChevronDown,\n sx: [s.icon.sx, disclosureStyles.icon, open && selectStyles.chevronOpen],\n },\n propsChevron,\n )}\n />\n <FormValue name={name} value={values()} disabled={state.disabled} />\n </div>\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n anchoredPanelSx(appearance().size),\n floatingStyles.scrollPanel,\n listPopoverStyles.panel,\n treeSelectStyles.panel,\n ],\n },\n propsPopover,\n { ref: _toRef(panel), popover: 'auto', onToggle: onPanelToggle },\n )}\n >\n {searchable && (\n <div {...css.attrs(listPopoverStyles.header)}>\n <TextField\n {...mergePartProps<TextFieldProps>(\n {\n icon: Search,\n variant: 'soft',\n placeholder: lang.selectSearch,\n 'aria-label': lang.selectSearch,\n propsRoot: { sx: selectStyles.search },\n },\n propsSearch,\n )}\n type=\"search\"\n _value={search}\n aria-controls={treeId}\n onKeydown={onSearchKeydown}\n />\n </div>\n )}\n {open &&\n (multiple() ? (\n <Tree\n {...treeProps(state.labelledBy)}\n checkable\n _checked={[...values()]}\n onUpdate:_checked={(next: string[]) => setChecked(next)}\n _expanded={shownExpanded()}\n onUpdate:_expanded={(next: string[]) =>\n (expanded = remoteResults() ? expanded : next)\n }\n />\n ) : (\n <Tree\n {...treeProps(state.labelledBy)}\n selectionMode=\"single\"\n _selected={[...values()]}\n onUpdate:_selected={(next: string[]) => {\n if (next.length) value = next[0];\n setOpen(false, 'select');\n }}\n _expanded={shownExpanded()}\n onUpdate:_expanded={(next: string[]) =>\n (expanded = remoteResults() ? expanded : next)\n }\n />\n ))}\n </div>\n </>\n );\n };\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TreeSelect.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_ref","_vue_shallowRef","_vue_computed","_createVNode","_Fragment","_mergeProps"],"sources":["../../src/input/TreeSelect.tsx"],"sourcesContent":["/**\n * 树形下拉选择:触发框同 Select,弹出的是一棵树(组织机构、产品结构、物料分类)。单选选一个节点,多选勾选(父子联动)。\n *\n * <TreeSelect\n * items={orgs}\n * getKey={(org) => org.id}\n * getLabel={(org) => org.name}\n * getChildren={(org) => org.children}\n * _value={orgId}\n * searchable\n * />\n * <TreeSelect … selectionMode=\"multiple\" _value={teamIds} /> 勾选:勾上父节点就是整枝\n * <TreeSelect … selectionMode=\"multiple\" checkedStrategy=\"child\" maxTagCount=\"responsive\" />\n *\n * - 单选:点节点或 Enter 选中并关闭;多选:空格或点勾选框,勾上父节点会勾上它的子孙,子节点全勾上时父节点也勾上。\n * value 按 checkedStrategy 放勾上的节点:全部(默认)、整枝时只放父节点、只放叶子(checkedByStrategy)。\n * - 多选的标签同 Select(SelectedTags):排在触发按钮前面,可移除(取消整枝),maxTagCount 收起,Backspace 移除最后一个。\n * 标签只列最上层的整枝(勾上了“研发中心”就不再列它下面的每个组);checkedStrategy=\"child\" 时列叶子。\n * - searchable:弹出层顶部的搜索框,按节点文字过滤,命中节点的祖先一起显示并展开;↓ 从搜索框进到树里。\n * 给 load 时由服务端搜索:打开时与输入停下 300 ms 后按搜索词请求,返回命中的节点连同祖先,结果全部展开\n * (remote-items.ts,同 Select);自己管请求时写 _search={keyword}、filter={false}。\n * - 打开后焦点在树上(或搜索框里),键盘同 Tree;Esc 关闭并把焦点还给触发按钮。懒加载同 Tree 的 loadChildren。\n * - 已选节点的文字从已加载的树里找,远程搜索换掉的按见过的文字显示;从没见过的(懒加载还没取回来)显示成 key。\n *\n * 部件:Root(外壳)、Tags(多选时的那排标签)、Tag、Trigger(button,主元素)、Value、Clear、Chevron、Popover、\n * Search(TextField)、Tree。\n */\nimport { ChevronDown, Search } from '@lucide/icons';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport * as css from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { useId, watch } from 'vue';\nimport type { PresetIconButtonProps } from '../base/CloseButton';\nimport { buttonBaseStyles } from '../base/button-core';\nimport { Icon, type IconProps } from '../base/Icon';\nimport type { TagProps } from '../base/Tag';\nimport { visuallyHiddenStyles } from '../base/VisuallyHidden';\nimport { useLang } from '../config/context';\nimport { Tree, type TreeProps } from '../display/Tree';\nimport { useFieldControl } from '../form/Field';\nimport { FormValue } from '../form/FormValue';\nimport { selectedValues, type SelectionModes } from '../utils/data/selection';\nimport {\n checkedByStrategy,\n indexTree,\n toggleChecked,\n type CheckStrategies,\n} from '../utils/data/tree';\nimport {\n anchoredPanelSx,\n floatingStyles,\n useAnchoredPopover,\n type FloatingPlacement,\n} from '../utils/dom/floating';\nimport { openSetter, type OpenChangeProps } from '../utils/dom/open-change';\nimport { mergeAttrs, mergePartProps, type PartProps } from '../utils/props';\nimport { disclosureStyles } from '../utils/style/transitions';\nimport {\n controlClear,\n controlIcon,\n controlParts,\n clickOnShell,\n focusOnShell,\n useControlAppearance,\n type ControlIconOptions,\n type ControlOptions,\n} from './control';\nimport { listPopoverStyles } from './ListPopover';\nimport { useRemoteItems, type OptionsLoader } from './remote-items';\nimport { selectStyles, type SelectManaged } from './Select';\nimport { SelectedTags, type SelectedTagsProps } from './SelectedTags';\nimport { TextField, type TextFieldProps } from './TextField';\n\n/** 未声明的属性落到触发按钮(主元素)上,外壳用 propsRoot */\nexport interface TreeSelectProps<T>\n extends PartProps<'button', SelectManaged>, ControlOptions, ControlIconOptions, OpenChangeProps {\n /** 顶层节点;有 load 时是第一次加载之前的树(编辑页放上已选节点的那一枝) */\n items?: readonly T[];\n /** 节点的 key,整棵树里唯一:也是选中后写回的值 */\n getKey: (node: T) => string;\n /** 节点的文字 */\n getLabel: (node: T) => string;\n /** 子节点;叶子或还没加载时 undefined */\n getChildren: (node: T) => readonly T[] | undefined;\n /** 有没有子节点(懒加载时子节点还没取回来也算有) */\n hasChildren?: (node: T) => boolean;\n /** 展开还没有子节点的节点时取回来(返回 Promise 时显示加载中) */\n loadChildren?: (node: T) => Promise<unknown> | void;\n /** 不能选的节点(只能展开,如只能选到部门、不能选公司时) */\n isDisabled?: (node: T) => boolean;\n /** 单选(默认)或多选(勾选,父子联动) */\n selectionMode?: keyof SelectionModes;\n /** 单选时是选中节点的 key(没有时 null),多选时是勾上的节点(按 checkedStrategy):父级写 _value={x} */\n _value?: string | string[] | null;\n /**\n * 多选时 value 里放哪些勾上的节点(同 Ant Design 的 showCheckedStrategy):all 全部(默认);\n * parent 父节点全勾上时只放父节点(提交“整个部门”);child 只放叶子(提交“具体的人”)\n */\n checkedStrategy?: keyof CheckStrategies;\n /** 多选时触发框里最多显示几个标签,其余收进“+N”;'responsive' 时排成一行、按宽度放 */\n maxTagCount?: number | 'responsive';\n /** 没有选择时显示的文字,默认取语言包(请选择) */\n placeholder?: string;\n /** 是否打开:父级可以写 _open={x} */\n _open?: boolean;\n /** 弹出位置,默认 bottom-start */\n placement?: FloatingPlacement;\n /** 有选择时显示清空按钮 */\n clearable?: boolean;\n /** 弹出层顶部带搜索框,按节点文字过滤 */\n searchable?: boolean;\n /** 搜索时怎样算命中;默认节点文字包含输入(忽略大小写与重音)。false 时不在本地过滤(服务端按 search 过滤) */\n filter?: false | ((node: T, query: string) => boolean);\n /** 搜索框里的文字:打开时清空;父级写 _search={x} 拿到它(自己发请求时) */\n _search?: string;\n /** 远程搜索:打开时与搜索词停下 300 ms 后调用,返回命中的节点连同祖先(顶层节点),结果代替 items */\n load?: OptionsLoader<T>;\n /** 表单提交时的 name(用隐藏的 input) */\n name?: string;\n /** 不可用 */\n disabled?: boolean;\n /** 只读:能聚焦、能看到已选的内容,不能打开、不能清空 */\n readonly?: boolean;\n /** 必填 */\n required?: boolean;\n /** 校验未通过 */\n invalid?: boolean;\n /** 外壳的部件属性 */\n propsRoot?: PartProps<'div'>;\n /** 已选内容那一层的部件属性 */\n propsValue?: PartProps<'span'>;\n /** 多选时每个标签的部件属性 */\n propsTag?: Partial<TagProps>;\n /** 多选时那排标签的部件属性 */\n propsTags?: Partial<SelectedTagsProps>;\n /** 清空按钮的部件属性 */\n propsClear?: Partial<PresetIconButtonProps>;\n /** 下拉箭头的部件属性 */\n propsChevron?: Partial<IconProps>;\n /** 弹出层的部件属性 */\n propsPopover?: PartProps<'div', 'id' | 'popover'>;\n /** 搜索框的部件属性(searchable 时) */\n propsSearch?: Partial<TextFieldProps>;\n /** 树的部件属性 */\n propsTree?: Partial<TreeProps<T>>;\n}\n\n/** TreeSelect 的样式:tree */\nexport const treeSelectStyles = css.create({\n // 树的滚动区域占满弹出层剩下的高度(最大高度由定位写在弹出层上)\n tree: { flexGrow: 1, minHeight: 0 },\n // 弹出层比触发框窄时也放得下几层缩进\n panel: { minWidth: '16em' },\n});\n\n/** 树形下拉选择:触发框 + 弹出的树,单选或勾选,可搜索 */\nexport const TreeSelect = _component(\n <T,>({\n items = [],\n getKey,\n getLabel,\n getChildren,\n hasChildren,\n loadChildren,\n isDisabled,\n selectionMode = 'single',\n checkedStrategy = 'all',\n maxTagCount,\n _value: value = _bindable<string | string[] | null>(null),\n _open: open = _bindable(false),\n onOpenChange,\n placeholder,\n placement = 'bottom-start',\n clearable = false,\n searchable = false,\n filter,\n _search: search = _bindable(''),\n load,\n name,\n variant,\n size,\n radius,\n disabled,\n readonly,\n required,\n invalid,\n id,\n icon,\n propsRoot,\n propsIcon,\n propsValue,\n propsTag,\n propsTags,\n propsClear,\n propsChevron,\n propsPopover,\n propsSearch,\n propsTree,\n ...rest\n }: TreeSelectProps<T>) => {\n const lang = useLang();\n const treeId = useId();\n const appearance = useControlAppearance(() => ({ variant, size, radius }));\n const control = useFieldControl(() => ({ id, invalid, required, disabled, readonly }));\n const multiple = () => selectionMode === 'multiple';\n const remote = useRemoteItems({\n load: () => load,\n query: () => search,\n open: () => open,\n items: () => items,\n });\n let expanded = _state<string[]>([]);\n let shell = _state.raw<HTMLElement | null>(null);\n let trigger = _state.raw<HTMLElement | null>(null);\n let panel = _state.raw<HTMLElement | null>(null);\n const setOpen = openSetter(\n () => open,\n (next) => (open = next),\n () => onOpenChange,\n );\n\n // 已加载的整棵树:key → 节点与父节点,查已选的文字、搜索时找祖先都用它\n const nodes = _derived.by(() => {\n const map = new Map<string, { node: T; parent: string | undefined }>();\n const visit = (level: readonly T[], parent: string | undefined) => {\n for (const node of level) {\n const key = getKey(node);\n map.set(key, { node, parent });\n visit(getChildren(node) ?? [], key);\n }\n };\n visit(remote.items(), undefined);\n return map;\n });\n // 见过的节点文字:远程搜索换掉了已选的节点,触发框里仍显示它的文字\n const seenLabels = new Map<string, string>();\n watch(\n () => nodes,\n (map) => {\n for (const [key, { node }] of map) seenLabels.set(key, getLabel(node));\n },\n { immediate: true },\n );\n\n // 本地搜索交给 Tree 的 filter(命中的节点与祖先、展开祖先、命中的加粗);\n // 有 load 或 filter={false} 时结果已经是服务端筛好的,不再过滤\n const treeFilter = () => {\n const text = search.trim();\n if (!searchable || !text || load || filter === false) return undefined;\n return filter ? (node: T) => filter(node, text) : text;\n };\n // 服务端搜索的结果全部展开(只有命中的节点与它们的祖先);清空搜索回到自己展开的状态\n const remoteResults = () => !!load && !!search.trim();\n const shownExpanded = () => (remoteResults() ? [...nodes.keys()] : expanded);\n\n const values = () => selectedValues(value);\n // 勾选联动要看到折叠着的节点\n const index = _derived.by(() =>\n indexTree(remote.items(), { getKey: (node) => getKey(node), getChildren }),\n );\n /** 按取法整理勾上的节点:已加载的按树的顺序与父子关系,还没加载(或远程搜索换掉了)的原样跟在后面 */\n const byStrategy = (keys: readonly string[], strategy: keyof CheckStrategies) => [\n ...checkedByStrategy(index, keys, strategy),\n ...keys.filter((key) => !index.parents.has(key)),\n ];\n /** 勾选变了(树、移除标签):按 checkedStrategy 写回,还没加载的已选节点保留 */\n const setChecked = (checked: readonly string[]) => {\n const unknown = values().filter((key) => !index.parents.has(key));\n value = [...checkedByStrategy(index, checked, checkedStrategy), ...unknown];\n };\n /** 触发框里显示的节点:多选时只列最上层的整枝(父节点也勾上了的不重复列),child 时列叶子 */\n const displayKeys = () =>\n multiple()\n ? byStrategy(values(), checkedStrategy === 'child' ? 'child' : 'parent')\n : values();\n\n /** 移除一个标签:取消整枝 */\n function remove(key: string) {\n setChecked(toggleChecked(index, values(), key));\n }\n const labelOf = (key: string) => {\n const found = nodes.get(key);\n return found ? getLabel(found.node) : (seenLabels.get(key) ?? key);\n };\n\n function show(event?: Event) {\n search = '';\n // 打开时展开已选节点的祖先:已选的看得见\n const ancestors = new Set(expanded);\n for (const key of values()) {\n for (let at = nodes.get(key)?.parent; at !== undefined; at = nodes.get(at)?.parent) {\n ancestors.add(at);\n }\n }\n expanded = [...ancestors];\n setOpen(true, 'trigger', event);\n }\n\n function clear() {\n value = multiple() ? [] : null;\n trigger?.focus();\n }\n\n const { onToggle } = useAnchoredPopover(\n () => ({\n open,\n anchor: shell,\n panel,\n floating: { placement, matchWidth: true, fitHeight: true },\n restoreFocus: false,\n }),\n { close: (reason, event) => setOpen(false, reason, event) },\n );\n\n // 打开后焦点进搜索框或树;关闭时焦点还在弹层里(Esc、选中、点外面)就还给触发按钮\n function onPanelToggle(event: ToggleEvent) {\n onToggle(event);\n if (event.newState === 'open') {\n const target = searchable\n ? panel?.querySelector<HTMLElement>('input[type=\"search\"]')\n : panel?.querySelector<HTMLElement>('[role=\"tree\"]');\n target?.focus();\n return;\n }\n const active = document.activeElement;\n if (!active || active === document.body || panel?.contains(active)) trigger?.focus();\n }\n\n function onTriggerKeydown(event: KeyboardEvent) {\n const state = control();\n if (state.disabled || state.readonly) return;\n // 多选:Backspace 移除最后一个标签(同 Select)\n if (event.key === 'Backspace' && multiple()) {\n event.preventDefault();\n const last = displayKeys().at(-1);\n if (last !== undefined) remove(last);\n return;\n }\n if (!open && (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ')) {\n event.preventDefault();\n show(event);\n }\n }\n\n // 搜索框里 ↓ 进到树;Tab 关闭后照常移到下一个\n function onSearchKeydown(event: KeyboardEvent) {\n if (event.key === 'Tab') {\n setOpen(false, 'focus', event);\n return;\n }\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n panel?.querySelector<HTMLElement>('[role=\"tree\"]')?.focus();\n }\n }\n\n /** 树的属性(单选、多选共用):本地搜索时带上 filter */\n const treeProps = (labelledBy: string | undefined) =>\n mergePartProps<TreeProps<T>>(\n {\n items: remote.items(),\n getKey,\n getLabel,\n getChildren,\n hasChildren,\n filter: treeFilter(),\n loadChildren,\n loading: remote.loading(),\n empty: remote.failure() ?? (search.trim() ? lang.selectNoMatch : undefined),\n isDisabled,\n propsScrollArea: { sx: treeSelectStyles.tree },\n 'aria-labelledby': labelledBy,\n 'aria-label': labelledBy ? undefined : rest['aria-label'],\n },\n propsTree,\n { id: treeId },\n );\n\n // 单选:选中就关闭(树的 selected 里已按 select 关过;父级改 value 时也关,不通知)\n watch(\n () => value,\n () => {\n if (!multiple() && open) open = false;\n },\n );\n\n return () => {\n const state = control();\n const s = controlParts(appearance(), state);\n const off = state.disabled;\n const locked = off || state.readonly;\n const shownKeys = displayKeys();\n // 多选:触发按钮前面一排标签,按钮里只留给读屏的文字(同 Select)\n const tagged = multiple() && shownKeys.length > 0;\n const shown =\n shownKeys.length === 0 ? (\n <span {...css.attrs(selectStyles.placeholder)}>\n {placeholder ?? lang.selectPlaceholder}\n </span>\n ) : tagged ? (\n <span {...css.attrs(visuallyHiddenStyles.hidden)}>\n {lang.selectSummary(shownKeys.map(labelOf))}\n </span>\n ) : (\n labelOf(shownKeys[0]!)\n );\n return (\n <>\n <div\n {...mergeAttrs<'div'>({ ...s.root, sx: [s.root.sx, selectStyles.root] }, propsRoot, {\n ref: _toRef(shell),\n onPointerdown: off ? undefined : focusOnShell(() => trigger),\n onClick:\n off || state.readonly\n ? undefined\n : clickOnShell(\n () => trigger,\n (event) => (open ? setOpen(false, 'trigger', event) : show(event)),\n ),\n })}\n data-state={open ? 'open' : 'closed'}\n data-invalid={state.invalid || undefined}\n data-disabled={off || undefined}\n >\n {controlIcon(s, icon, propsIcon)}\n {tagged && (\n <SelectedTags\n {...mergePartProps<SelectedTagsProps>(\n {\n tags: shownKeys.map((key) => ({ value: key, label: labelOf(key) })),\n maxTagCount,\n onRemove: locked ? undefined : remove,\n propsTag,\n },\n propsTags,\n )}\n />\n )}\n <button\n {...mergeAttrs<'button'>(\n {\n sx: [\n buttonBaseStyles.root,\n selectStyles.trigger,\n tagged && selectStyles.triggerBehindTags,\n ],\n },\n rest,\n {\n ref: _toRef(trigger),\n id: state.id,\n type: 'button',\n role: 'combobox',\n disabled: off,\n 'aria-haspopup': 'tree',\n 'aria-expanded': open,\n 'aria-controls': treeId,\n 'aria-invalid': state.invalid || undefined,\n 'aria-required': state.required || undefined,\n 'aria-readonly': state.readonly || undefined,\n 'aria-describedby': state.describedBy,\n onClick: (event: MouseEvent) => {\n trigger?.focus();\n if (locked) return;\n if (open) setOpen(false, 'trigger', event);\n else show(event);\n },\n onKeydown: onTriggerKeydown,\n },\n )}\n >\n <span {...mergeAttrs<'span'>({ sx: selectStyles.value }, propsValue)}>{shown}</span>\n </button>\n {clearable && shownKeys.length > 0 && !locked && controlClear(s, propsClear, clear)}\n <Icon\n {...mergePartProps<IconProps>(\n {\n ...s.icon,\n icon: ChevronDown,\n sx: [s.icon.sx, disclosureStyles.icon, open && selectStyles.chevronOpen],\n },\n propsChevron,\n )}\n />\n <FormValue name={name} value={values()} disabled={state.disabled} />\n </div>\n <div\n {...mergeAttrs<'div'>(\n {\n sx: [\n anchoredPanelSx(appearance().size),\n floatingStyles.scrollPanel,\n listPopoverStyles.panel,\n treeSelectStyles.panel,\n ],\n },\n propsPopover,\n { ref: _toRef(panel), popover: 'auto', onToggle: onPanelToggle },\n )}\n >\n {searchable && (\n <div {...css.attrs(listPopoverStyles.header)}>\n <TextField\n {...mergePartProps<TextFieldProps>(\n {\n icon: Search,\n variant: 'soft',\n placeholder: lang.selectSearch,\n 'aria-label': lang.selectSearch,\n propsRoot: { sx: selectStyles.search },\n },\n propsSearch,\n )}\n type=\"search\"\n _value={search}\n aria-controls={treeId}\n onKeydown={onSearchKeydown}\n />\n </div>\n )}\n {open &&\n (multiple() ? (\n <Tree\n {...treeProps(state.labelledBy)}\n checkable\n _checked={[...values()]}\n onUpdate:_checked={(next: string[]) => setChecked(next)}\n _expanded={shownExpanded()}\n onUpdate:_expanded={(next: string[]) =>\n (expanded = remoteResults() ? expanded : next)\n }\n />\n ) : (\n <Tree\n {...treeProps(state.labelledBy)}\n selectionMode=\"single\"\n _selected={[...values()]}\n onUpdate:_selected={(next: string[]) => {\n if (next.length) value = next[0];\n setOpen(false, 'select');\n }}\n _expanded={shownExpanded()}\n onUpdate:_expanded={(next: string[]) =>\n (expanded = remoteResults() ? expanded : next)\n }\n />\n ))}\n </div>\n </>\n );\n };\n },\n);\n"],"mappings":"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"}
|
package/dist/input/Upload.js
CHANGED
|
@@ -13,6 +13,7 @@ import { useSortable } from "../utils/dom/sortable.js";
|
|
|
13
13
|
import { useFieldControl } from "../form/Field.js";
|
|
14
14
|
import { itemStyles } from "../utils/style/item-styles.js";
|
|
15
15
|
import { useHttp } from "../utils/request/http.js";
|
|
16
|
+
import { errorCodeText } from "../utils/request/error-message.js";
|
|
16
17
|
import { FileDrop, matchesAccept } from "./FileDrop.js";
|
|
17
18
|
import { Fragment, createVNode, isVNode, mergeProps, onBeforeUnmount, shallowRef, watch } from "vue";
|
|
18
19
|
import { _component, bindable, effect, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
@@ -267,7 +268,7 @@ var Upload$1 = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
267
268
|
});
|
|
268
269
|
} catch (error) {
|
|
269
270
|
if (controller.signal.aborted) return;
|
|
270
|
-
const message = error instanceof Error && error.message ? error.message : lang.uploadFailed;
|
|
271
|
+
const message = errorCodeText(error, lang) ?? (error instanceof Error && error.message ? error.message : lang.uploadFailed);
|
|
271
272
|
patch(item.id, {
|
|
272
273
|
status: "error",
|
|
273
274
|
error: message
|