ant-design-vue/components/vc-tree-select/TreeSelect.tsx
tangjinzhou a2e50dc43e
Feat v4 (#6329)
* refactor(icon): remove style dir (#6215)

* refactor: rename locale

* refactor: locale-provider

* refactor: modal

* refactor: menu

* fix: custom class (#6217)

* refactor: tooltip

* refactor: grid (#6220)

* refactor: grid

* fix(grid): align & justify responsive

* chore: update demo and snapshot

* fix: row ts type not work

* doc: update demo

* refactor: ts

* refactor: spin (#6222)

* fix: typo (#6218)

* fix:  typo

* docs<upload>: docs update

* refactor: spin

* refactor: spin

* refactor: spin

* refactor: spinnn

* refactor: spin

---------

Co-authored-by: lyn <76365499@qq.com>

* fix: spin error #6222

* test: test case error (#6225)

* fix: inject value maybe undefined

* fix: tootip emit correct value

* fix: rollback warning suffix avoid test break

* doc(grid): remove unused type="flex"

* refactor: skeleton (#6224)

* refactor: skeleton

* refactor: skeleton style

* chore: modify skeleton demo style

* fix(button): link and text should not have wave (#6226)

* refactor: dropdown

* refactor: popover & popconfirm

* refactor(tag): less to cssinjs (#6227)

* refactor(empty): less to cssinjs (#6230)

* refactor(empty): less to cssinjs

* chore: remove unuse code

* fix: reactivity lose

* fix: empty props #6230

* refactor: progress style (#6234)

* refactor: progress

* refactor: progress style

* fix: progress attrs

* refactor: progress #6234

* refactor: switch (#6236)

* refactor: switch style

* refactor: delete switch style

* refactor:input (#6237)

* refactor:input

* fix inheritAttrs:false

* fix attrs.class

* feat: input add disabled

* refactor:comment (#6238)

* refactor:comment

* fix inheritAttrs: false & attrs.class

* refactor:pageheader (#6239)

* refactor:pageheader

* fix inheritAttrs: false & attrs.class

* refactor:statistic (#6240)

* refactor:statistic

* fix inheritAttrs: false & attrs.class

* refactor:list (#6241)

* refactor:list

* fix inheritAttrs: false & attrs.class

* feat: update type

* refactor(Space): less to cssinjs & add compact mode (#6229)

* refactor(Space): less to cssinjs & add compact mode

* chore(space): update md

* chore(space): add demo

* chore(space): add some demo

* feat(button): add compact mode

* fix: reactivity lose

* docs: fix props version

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* perf: space compact

* refactor:typography (#6244)

* refactor:typography

* fix return

* fix import type

* fix: typography #6244

* refactor:datepicker (#6245)

* refactor: datepicker type

* refactor: rate style (#6254)

* refactor(layout): less to cssinjs (#6249)

* doc: update layout cover

* refactor(result): less to cssinjs (#6246)

* refactor(result): less to cssinjs

* fix: class name is overridden

* docs: update result cover

* refactor:slider (#6250)

* feat: slider  deprecated tooltipVisible

* refactor(crad): less to cssinjs  (#6258)

* update

* switch

* Style adjustment

* refactor(Card): less to cssinjs

* Eliminate invalid code

* optimization and adjustment css

* Adjust the css

* Optimize each item

* adjustment css

* refactor: card #6258

* refactor:carousel (#6262)

* refactor:carousel

* docs:update & refactor: carousel type

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* refactor:transfer (#6247)

* refactor:transfer

* merge v4 branch & fix theme interface conflict

* docs:update & refactor: transfer type

* perf: transfer

* refactor:checkbox (#6248)

* refactor:checkbox

* docs:update & refactor: checkbox type

* feat: checkbox add disabled context

* refactor:pagination (#6251)

* refactor:pagination

* docs:update & refactor: pagination type

* style: update pagination props type

* refactor: mentions (#6255)

* refactor: mentions

* refactor: mentions menu provider

* doc: update mentions demo

* refcator:upload (#6261)

* refcator:upload

* docs:update & refactor: upload type

* Update style.ts

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* perf: upload motion

* refactor:timeline (#6263)

* refactor:timeline

* docs:update & refactor: timeline type

* perf: timeline

* refactor:steps (#6264)

* refactor:steps

* fix ...attrs

* fix StepsToken error

* docs:update & refactor: steps type

* fix: steps icon clss error

* refactor:collapse (#6266)

* refactor:collapse

* fix collapse props version

* docs:update & refactor: collapse type & fix collapsible

* feat: update collapse type

* refactor:inputnumber (#6265)

* refactor:inputnumber

* docs:update & refactor: inputnumber type

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* feat: number add compactSize & disabledContext

* refactor:table (#6267)

* refactor:table

* docs:update & refactor: table type

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* refactor: table

* feat: table add expandColumnTitle slot

* refactor:calendar (#6269)

* refactor:calendar

* docs:update

* refactor:timepicker (#6270)

* refactor:timepicker

* docs:update & refactor: timepicker type

* refactor:tree (#6276)

* Feat v4 fix type errors (#6285)

* fix compile type errors

* fix menuprops type import

* fix lint errors

* fix lint errors

* fix format error

* fix node version

* fix run dist error

* fix run lint

* fix as any

* fix string type

* refactor: rename locale file

* feat: tree add leafIcon

* [tabs] :less to cssinjs  (#6288)

* update

* switch

* Style adjustment

* refactor(Card): less to cssinjs

* tabs: less to cssinjs 开发ing

* add function cssinjs

* Eliminate irrelevant code

* Eliminate irrelevant code 2

* update components

* Eliminate irrelevant input  code

* refactor: tabs #6288

* feat: add segmented (#6286)

* refactor: segmented #6286

* refactor:select (#6295)

* refactor:select

* update doc

* delete useless

* feat: select add context size

* refactor: tree select (#6296)

* feat: tree-select add context size

* perf: table

* docs: update doc toc

* refactor: cascader

* refactor: auto-complete

* refactor: image

* refactor: drawer

* refactor:radio (#6299)

* refactor:radio

* fix attrs

* feat: radio add disabled context

* fix: some type & doc (#6292)

* fix: typo (#6218)

* fix:  typo

* docs<upload>: docs update

* fix: type of minute in props disabledDateTime of DatePicker (#6233)

* docs: typo (#6256)

* feat: tooltip added overlayInnerStyle attribute

* Update abstractTooltipProps.ts

* Update Tooltip.tsx

---------

Co-authored-by: lyn <76365499@qq.com>
Co-authored-by: H1mple <35363759+baohangxing@users.noreply.github.com>
Co-authored-by: tangjinzhou <415800467@qq.com>

* refactor: form

* fix: directive not work

* fix: use open, remove visible

* doc: update cover

* refactor: remove not use code

* chore: update build script

* doc: update doc

* doc: refactor doc

* chore: update token error

* chore: update style

* refactor: rename _style to style

* fix: tag warning

* fix(dropdown): open invalid (#6316)

* feat: add watermark  (#6300)

* feat: add watermark

* feat: add watermark demo

* feat: add mutationObserver

* feat: add watermark demo

* refactor: watermark type

* doc: add theme-editor

* fix: inject value maybe undefined && tag style invalid (#6320)

* fix: inject value maybe undefined

* fix(tag): style invalid

* feat: add qrcode (#6315)

* feat: add qrcode

* fix: qrcode bug

* fix: qrcode value required

* refactor: props  deconstruct

* Feat v4 floatbutton (#6294)

* feat: add float-button components

* fix type &  demo display

* fix components entry

* fix review bug

* fix bug

* fix .value

* refactor: qrcode #6315

* refactor: float-button

* fix: groupsize context error

* fix: floatbutton animation not work

* Feat v4 theme editor (#6348)

* feat: add theme editor container

* feat: add theme editor layout

* add left panel

* add vue-colorful & fix bug

* 修复hue组件抖动问题

* fix bug && add demo

* fix bug

* fix demo preview

* fix theme editor components demo

* fix: token effect error

* Feat v4 theme editor (#6349)

* feat: add theme editor container

* feat: add theme editor layout

* add left panel

* add vue-colorful & fix bug

* 修复hue组件抖动问题

* fix bug && add demo

* fix bug

* fix demo preview

* fix theme editor components demo

* add theme editor token drawer

* add theme editor token drawer

* fix bug

* open commment

* fix error demo

* fix theme editor bug

* fix: cssinjs effect error

* doc: format code

* fix: tag click event not trigger

* release 4.0.0-alpha.1

* fix: qrcode type

* fix: remove not use file

* doc: update doc site

* doc: update site

* doc: fix theme editor bgcolor (#6358)

* fix: motion not work

* release 4.0.0-alpha.2

* fix: qrcode ; error, close #6362

* fix docs dark theme & add docs coverDark (#6367)

* fix docs dark theme & add docs coverDark

* fix theme Editor edit

* fix: dropdown divider disappear, close  #6365 (#6369)

* doc: update baner

* fix: button wave not work

* fix: ant-piker-cell-range-hover-end style error (#6373)

* fix: ant-piker-cell-range-hover-end style error

* feat: be consistent with antd

* feat: be consistent with antd

* fix: ConfigProvider error for style, close #6368

* release 4.0.0-alpha.4

* style: add dark style for `pre` and `code` (#6382)

* docs: version menu (#6390)

* Feat(DatePicker): increase presets prop (#6387)

* feat(date-picker): add PresetDate type

* feat(date-picker): add usePresets hook

* feat(date-picker): add PresetPanel Component

* feat(date-picker): add PresetPanel Component

* feat(demo): update Preset Ranges Examples

* feat(docs): add new prop presets

* feat(docs): add new prop presets with english

* fix(RangePicker): footer is not managed by panels

* chore(Picker): prefixCls default rc-picker

* chore(date-picker): update presetted-ranges demo

* chore(date-picker): update rangePickerProps'presets

* feat(date-picker): presets reactively processing

* chore(date-picker): update type

* refactor(RangePicker): deprecated ranges prop

* chore(date-picker): update type

* chore(PickerPanel): del notuse panelRef

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* fix: datepicker presets error #6387

* docs: update datepicker doc #6387

* feat(Steps): add items prop and variants (#6406)

* refactor(steps): add items prop and variants

* feat(steps): add Label Placement and Inline Steps demo

* feat(steps): Label Placement and Inline Steps snap

* test(steps): Steps demo snap

* feat(Steps): update docs

* fix(Step): progressDot

* chore(useLegacyItems): change from warning to devWarning

* refactor(Steps): Remove useLegacyItems

* refactor(Steps): renderStep

* test(Steps): update test snapshot

* chore(Steps): filterEmpty

* feat(Steps): update docs

* docs: update site

* refactor: steps #6406

* test: update steps

* perf: shallowRef instead ref

* fix(Modal): fix modal locale (#6423)

* feat(StyleProvider): add StyleProvider handle cssinjs features (#6415)

* feat(StyleProvider): StyleProvider

* feat(StyleProvider): refactor to  use context

* chore(StyleProvider): update AStyleProviderProps type

* chore(App): reback

* chore(StyleProvider): export StyleProvider

* feat(StyleProvider): update StyleProvider docs

* feat(StyleProvider): update StyleProvider docs

* feat(StyleProvider): add StyleProvider docs routes

* chore(StyleProvider): with useStyleProvider

* docs: update compatiple #6415

* feat(Progress): enhance size prop and add variants (#6409)

* refactor(progress): Progress size and add variants

* feat(progress): add `getsize`

* refactor(progress): Progress size and add variants

* chore(progress): update props type

* chore(progress): update props type

* feat(progress): update demo

* feat(progress): update docs

* test(progress): update test snap

* fix(Circle): Merging classes

* test(progress): update test snap

* feat(progress): add size demo

* test(progress): add size snapshot

* chore(Progress): reback Circle svg class change

* fix: progress borderRadius reactive #6409

* fix(defaultConfigProvider): add getPopupContainer (#6425), close #6419

* fix: qrcode size error, close #6418

* release 4.0.0-alpha.4

* fix: picker import error

* test: add QRCode unit testing (#6441)

* fix

* fix compile type errors

* fix menuprops type import

* fix lint errors

* fix lint errors

* fix format error

* fix node version

* fix run dist error

* fix run lint

* fix as any

* fix string type

* fix steps error & fix docs version select option & fix theme editor error

* fix(badge): badge props count default value error (#6433)

* docs: update site responsive

* fix: modal api method i18n not work, close #6438

* release 4.0.0-alpha.5

* chore(docs): update docs (#6446)

* docs(space): update demo

* docs(affix): update docs

* fix: cssinjs compatibility (#6454)

* feat: add convertLegacyToken

* docs: v4 vuedocs (#6468)

* fix introduce doc

* fix getting-started doc

* add migration-v4 doc

* fix docs

* Update migration-v4.zh-CN.md

* Update migration-v4.zh-CN.md

* Update migration-v4.en-US.md

* Update migration-v4.zh-CN.md

* Update getting-started.en-US.md

* Update getting-started.zh-CN.md

* Update introduce.en-US.md

* Update introduce.zh-CN.md

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* feat: remove backtop

* feat(anchor): add direction action (#6447)

* refactor(anchor): direction show

* refactor(anchor): update anchor css

* feat(anchor): update demo

* test(anchor): update demo test snap

* feat(anchor): update docs

* Update index.zh-CN.md

* Update index.en-US.md

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* feat: anchor add customTitle slot #6447

* docs: update doc anchor

* feat(menu): icon support function components with items and update demo (#6457)

* fix(menu): icon do not show problem

* fix(menu): icon do not show problem

* feat(menu): update demo

* test(menu): update demo snap

* chore(Menu): update docs

* test(Menu): update demo

* Update MenuItem.tsx

* Update SubMenu.tsx

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* doc: update menu icon

* feat: menu items icon add arg

* fix: antd.min error

* release 4.0.0-alpha.6

* fix: table resizable not work && type error (#6514)

* Refactor(demo): change options to composition api (#6499)

* feat(demo): A-B

* feat(demo): update B-checkbox

* feat(demo): update CheckBox -DatePicker

* feat(demo): update DatePicker - Form

* feat(demo): update Form - List

* feat(demo): update  List-pagination

* feat(demo): update  List - skeleton

* feat(demo): update  skeleton - switch

* feat(demo): update  skeleton - switch

* feat(demo): update   switch - upload

* feat(demo): update  watermark

* fix(demo): del hashId

* fix: submenu type lose theme

* fix: dropdown menu hide error

* fix: dealing with switching topics modal, notification, message does not take effect close #6512 (#6518)

* fix: resolve dark mode not support

* fix: unified expression

* feat(modal): add useModal (#6517)

* feat(modal): add useModal hook

* feat(modal): add HookModal demo

* test(modal): update HookModal demo snap

* feat(modal): update modal docs

* chore(modal): update modal type

* perf: useModal #6517

* release 4.0.0-beta.1

* docs: fix tab demo error

* fix(config-provider): fix ConfigProvider.config is not function close #6528 (#6529)

* Feat(use): add useMessage useNotification (#6527)

* feat(Message): add useMessage hook

* feat(Notification): add useNotification hook

* feat(Message): add Hook demo

* feat(Notification): add Hook demo

* test(Message): update demo snap

* test(Notification): update demo snap

* docs(Message): update docs with FAQ

* docs(Notification): update docs with FAQ

* refactor: useMessage #6527

* refactor: useNotification #6527

* release 4.0.0-beta.2

* docs(button): update demo with space (#6536)

* feat(button): demo space

* test(button): update demo snap

* chore(button): disabled demo Ghost space

* test(button): update disabled demo snap

* docs(introduce): update docs (#6539)

* docs(introduce): update docs

* docs(introduce): add Dollar

* Update introduce.zh-CN.md

* Update introduce.en-US.md

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* docs(customize-theme): update docs (#6540)

* fix introduce doc

* fix getting-started doc

* add migration-v4 doc

* fix docs

* Update migration-v4.zh-CN.md

* Update migration-v4.zh-CN.md

* Update migration-v4.en-US.md

* Update migration-v4.zh-CN.md

* Update getting-started.en-US.md

* Update getting-started.zh-CN.md

* Update introduce.en-US.md

* Update introduce.zh-CN.md

* update customize-theme doc & fix migration-v4 error

* update customize-theme doc

* fix migration-v4 error

* remove SSR & shadowDom

* Update customize-theme.zh-CN.md

* Update customize-theme.en-US.md

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* fix: getPopupContainer not work

* release 4.0.0-beta.3

* release 4.0.0-beta.4

* docs: update grid docs (#6549)

Co-authored-by: zhuzhengjian <zhuzhengjian@hoteamsoft.com>

* test(alert): update demo with space (#6541)

* docs(alert): update demo with space

* docs(alert): update alert test snap

---------

Co-authored-by: zhuzhengjian <zhuzhengjian@hoteamsoft.com>

* fix: components bug & update docs (#6548)

* fix bug

* fix test case and update snapshot,fix space merge class

* docs(grid): update migrate docs && delete xxxl in grid docs (#6562)

* fix: segmentd disabled label is undefined (#6556)

* fix: segmentd disabled label is undefined

* fix: segmentd disabled label is undefined

* fix: segmentd disabled label is undefined

* fix(grid): remove grid xxxl attribute (#6572)

* fix: remove grid xxxl attribute

* docs: remove xxxl in grid docs

* fix: tooltip custom color error

* feat: remove Step __legacy

* feat: add tour (#6332)

* feat v4 add tour

* fix type error

* sync tour from antd5.4.6 & fix type error

* fix error

* refactor: tour #6332

* fix: tour center

* fix: picker support v-show

* test: update snap

* test: update tour test

* fix: tour-mask attrs pointer-events (#6577)

* fix: tour animated

* feat: support vue 3.3 slot type

* release 4.0.0-rc.1

* release 4.0.0-rc.2, close #6588

* 4.0.0-rc.3

* chore: remove vue private api

* fix: paginantion error, close #6590

* release 4.0.0-rc.4

* fix: checxbox style

* fix: pagination mini size style

* release 4.0.0-rc.5

* docs: update v4 tabs doc error(#6606) (#6607)

* docs: add ant-design-vue nuxt module (#6620)

* fix: layout-sider and menu transition style(#6637) (#6640)

* docs: fixed the style error of online demo (#6630)

* feat: checkbox label slot support use option label (#6642)

* docs: 📃change the default  setting of  "treeNodeFilterProp" from  "value" to "label"

* revert: ↩revert this config and create another pr to commit

* feat: checkbox label slot support use option label

* test: 🧪update checkbox *.snap file

---------

Co-authored-by: tangjinzhou <415800467@qq.com>

* fix: add disabledContext override with form components (#6618)

* fix: add disabledContext override with form components

* test: update snap

* fix: LabelWidth demo filename

* fix: fontsize spelling mistake

* fix(tour): target position (#6629)

* style: format lint

* docs(form): add form disabled demo (#6658)

* fix: comment node error

* release 4.0

* fix: portalWrapper add autoLock prop (#6687), close #6649

* fix: image animation & zindex, close #6675

* docs(QRCode): Synchronize QR code demonstration and add SVG (#6660)

* fix: Synchronize QR code demonstration and add SVG

* fix: responsive loss and invalid border style

* docs: synchronize antd5.6.3 QRCode color in dark mode

* feat:  calendar select support info.source param (#6697)

* docs: add ant-design-vue nuxt module

* feat: calendar select support info.source param

* docs: synchronous config-provider demo (#6706)

* revert: #6706

* docs: export space-compact types (#6716)

* release 4.0.0

---------

Co-authored-by: bqy_fe <1743369777@qq.com>
Co-authored-by: zkwolf <chenhao5866@gmail.com>
Co-authored-by: Zev Zhu <45655660+aibayanyu20@users.noreply.github.com>
Co-authored-by: lyn <76365499@qq.com>
Co-authored-by: 果冻橙 <shifeng199307@gmail.com>
Co-authored-by: songsong0707 <74165917+songsong0707@users.noreply.github.com>
Co-authored-by: yang <30883395+webvs2@users.noreply.github.com>
Co-authored-by: selicens <1244620067@qq.com>
Co-authored-by: 一堆菠萝 <53335668+JavanShen@users.noreply.github.com>
Co-authored-by: H1mple <35363759+baohangxing@users.noreply.github.com>
Co-authored-by: Cherry7 <79909910+CCherry07@users.noreply.github.com>
Co-authored-by: Konv Suu <2583695112@qq.com>
Co-authored-by: luoawai <32483950+luoawai@users.noreply.github.com>
Co-authored-by: 鱼见 <657715602@qq.com>
Co-authored-by: zhuzhengjian <zhuzhengjian@hoteamsoft.com>
Co-authored-by: Cupid Valentine <53572196+valcosmos@users.noreply.github.com>
Co-authored-by: 专业逮虾户aa <30494925+waldonUB@users.noreply.github.com>
Co-authored-by: PanStar <PanStar@users.noreply.github.com>
2023-07-14 11:58:27 +08:00

766 lines
24 KiB
Vue

import OptionList from './OptionList';
import { formatStrategyValues, SHOW_CHILD } from './utils/strategyUtil';
import type { CheckedStrategy } from './utils/strategyUtil';
import { useProvideSelectContext } from './TreeSelectContext';
import type { TreeSelectContextProps } from './TreeSelectContext';
import type { LegacyContextProps } from './LegacyContext';
import { useProvideLegacySelectContext } from './LegacyContext';
import useTreeData from './hooks/useTreeData';
import { toArray, fillFieldNames, isNil } from './utils/valueUtil';
import useCache from './hooks/useCache';
import useDataEntities from './hooks/useDataEntities';
import { fillAdditionalInfo, fillLegacyProps } from './utils/legacyUtil';
import useCheckedKeys from './hooks/useCheckedKeys';
import useFilterTreeData from './hooks/useFilterTreeData';
import warningProps from './utils/warningPropsUtil';
import type { Key } from './interface';
import type { DisplayValueType } from '../vc-select/BaseSelect';
import { baseSelectPropsWithoutPrivate } from '../vc-select/BaseSelect';
import { computed, defineComponent, ref, shallowRef, toRaw, toRef, toRefs, watchEffect } from 'vue';
import type { ExtractPropTypes, PropType } from 'vue';
import omit from '../_util/omit';
import PropTypes from '../_util/vue-types';
import type { SelectProps, BaseSelectProps, BaseSelectRef } from '../vc-select';
import { BaseSelect } from '../vc-select';
import { initDefaultProps } from '../_util/props-util';
import useId from '../vc-select/hooks/useId';
import useMergedState from '../_util/hooks/useMergedState';
import type { VueNode } from '../_util/type';
import { conductCheck } from '../vc-tree/utils/conductUtil';
import { warning } from '../vc-util/warning';
import { toReactive } from '../_util/toReactive';
import useMaxLevel from '../vc-tree/useMaxLevel';
import type { ExpandAction } from '../vc-tree/props';
export type OnInternalSelect = (value: RawValueType, info: { selected: boolean }) => void;
export type RawValueType = string | number;
export interface LabeledValueType {
key?: Key;
value?: RawValueType;
label?: any;
/** Only works on `treeCheckStrictly` */
halfChecked?: boolean;
}
export type SelectSource = 'option' | 'selection' | 'input' | 'clear';
export type DraftValueType = RawValueType | LabeledValueType | (RawValueType | LabeledValueType)[];
/** @deprecated This is only used for legacy compatible. Not works on new code. */
export interface LegacyCheckedNode {
pos: string;
node: any;
children?: LegacyCheckedNode[];
}
export interface ChangeEventExtra {
/** @deprecated Please save prev value by control logic instead */
preValue: LabeledValueType[];
triggerValue: RawValueType;
/** @deprecated Use `onSelect` or `onDeselect` instead. */
selected?: boolean;
/** @deprecated Use `onSelect` or `onDeselect` instead. */
checked?: boolean;
// Not sure if exist user still use this. We have to keep but not recommend user to use
/** @deprecated This prop not work as react node anymore. */
triggerNode: any;
/** @deprecated This prop not work as react node anymore. */
allCheckedNodes: LegacyCheckedNode[];
}
export interface FieldNames {
value?: string;
label?: string;
children?: string;
}
export interface InternalFieldName extends Omit<FieldNames, 'label'> {
_title: string[];
}
export interface SimpleModeConfig {
id?: Key;
pId?: Key;
rootPId?: Key;
}
export interface BaseOptionType {
disabled?: boolean;
checkable?: boolean;
disableCheckbox?: boolean;
children?: BaseOptionType[];
[name: string]: any;
}
export interface DefaultOptionType extends BaseOptionType {
value?: RawValueType;
title?: any;
label?: any;
key?: Key;
children?: DefaultOptionType[];
}
export interface LegacyDataNode extends DefaultOptionType {
props: any;
}
export function treeSelectProps<
ValueType = any,
OptionType extends BaseOptionType = DefaultOptionType,
>() {
return {
...omit(baseSelectPropsWithoutPrivate(), ['mode']),
prefixCls: String,
id: String,
value: { type: [String, Number, Object, Array] as PropType<ValueType> },
defaultValue: { type: [String, Number, Object, Array] as PropType<ValueType> },
onChange: {
type: Function as PropType<
(value: ValueType, labelList: any[], extra: ChangeEventExtra) => void
>,
},
searchValue: String,
/** @deprecated Use `searchValue` instead */
inputValue: String,
onSearch: { type: Function as PropType<(value: string) => void> },
autoClearSearchValue: { type: Boolean, default: undefined },
filterTreeNode: {
type: [Boolean, Function] as PropType<
boolean | ((inputValue: string, treeNode: DefaultOptionType) => boolean)
>,
default: undefined,
},
treeNodeFilterProp: String,
// >>> Select
onSelect: Function as PropType<SelectProps['onSelect']>,
onDeselect: Function as PropType<SelectProps['onDeselect']>,
showCheckedStrategy: { type: String as PropType<CheckedStrategy> },
treeNodeLabelProp: String,
fieldNames: { type: Object as PropType<FieldNames> },
// >>> Mode
multiple: { type: Boolean, default: undefined },
treeCheckable: { type: Boolean, default: undefined },
treeCheckStrictly: { type: Boolean, default: undefined },
labelInValue: { type: Boolean, default: undefined },
// >>> Data
treeData: { type: Array as PropType<OptionType[]> },
treeDataSimpleMode: {
type: [Boolean, Object] as PropType<boolean | SimpleModeConfig>,
default: undefined,
},
loadData: { type: Function as PropType<(dataNode: LegacyDataNode) => Promise<unknown>> },
treeLoadedKeys: { type: Array as PropType<Key[]> },
onTreeLoad: { type: Function as PropType<(loadedKeys: Key[]) => void> },
// >>> Expanded
treeDefaultExpandAll: { type: Boolean, default: undefined },
treeExpandedKeys: { type: Array as PropType<Key[]> },
treeDefaultExpandedKeys: { type: Array as PropType<Key[]> },
onTreeExpand: { type: Function as PropType<(expandedKeys: Key[]) => void> },
// >>> Options
virtual: { type: Boolean, default: undefined },
listHeight: Number,
listItemHeight: Number,
onDropdownVisibleChange: { type: Function as PropType<(open: boolean) => void> },
// >>> Tree
treeLine: { type: [Boolean, Object], default: undefined },
treeIcon: PropTypes.any,
showTreeIcon: { type: Boolean, default: undefined },
switcherIcon: PropTypes.any,
treeMotion: PropTypes.any,
children: Array as PropType<VueNode[]>,
treeExpandAction: String as PropType<ExpandAction>,
showArrow: { type: Boolean, default: undefined },
showSearch: { type: Boolean, default: undefined },
open: { type: Boolean, default: undefined },
defaultOpen: { type: Boolean, default: undefined },
disabled: { type: Boolean, default: undefined },
placeholder: PropTypes.any,
maxTagPlaceholder: { type: Function as PropType<(omittedValues: DisplayValueType[]) => any> },
dropdownPopupAlign: PropTypes.any,
customSlots: Object,
};
}
export type TreeSelectProps = Partial<ExtractPropTypes<ReturnType<typeof treeSelectProps>>>;
function isRawValue(value: RawValueType | LabeledValueType): value is RawValueType {
return !value || typeof value !== 'object';
}
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'TreeSelect',
inheritAttrs: false,
props: initDefaultProps(treeSelectProps(), {
treeNodeFilterProp: 'value',
autoClearSearchValue: true,
showCheckedStrategy: SHOW_CHILD,
listHeight: 200,
listItemHeight: 20,
prefixCls: 'vc-tree-select',
}),
setup(props, { attrs, expose, slots }) {
const mergedId = useId(toRef(props, 'id'));
const treeConduction = computed(() => props.treeCheckable && !props.treeCheckStrictly);
const mergedCheckable = computed(() => props.treeCheckable || props.treeCheckStrictly);
const mergedLabelInValue = computed(() => props.treeCheckStrictly || props.labelInValue);
const mergedMultiple = computed(() => mergedCheckable.value || props.multiple);
// ========================== Warning ===========================
if (process.env.NODE_ENV !== 'production') {
watchEffect(() => {
warningProps(props);
});
}
// ========================= FieldNames =========================
const mergedFieldNames = computed<InternalFieldName>(() => fillFieldNames(props.fieldNames));
// =========================== Search ===========================
const [mergedSearchValue, setSearchValue] = useMergedState('', {
value: computed(() =>
props.searchValue !== undefined ? props.searchValue : props.inputValue,
),
postState: search => search || '',
});
const onInternalSearch: BaseSelectProps['onSearch'] = searchText => {
setSearchValue(searchText);
props.onSearch?.(searchText);
};
// ============================ Data ============================
// `useTreeData` only do convert of `children` or `simpleMode`.
// Else will return origin `treeData` for perf consideration.
// Do not do anything to loop the data.
const mergedTreeData = useTreeData(
toRef(props, 'treeData'),
toRef(props, 'children'),
toRef(props, 'treeDataSimpleMode'),
);
const { keyEntities, valueEntities } = useDataEntities(mergedTreeData, mergedFieldNames);
/** Get `missingRawValues` which not exist in the tree yet */
const splitRawValues = (newRawValues: RawValueType[]) => {
const missingRawValues = [];
const existRawValues = [];
// Keep missing value in the cache
newRawValues.forEach(val => {
if (valueEntities.value.has(val)) {
existRawValues.push(val);
} else {
missingRawValues.push(val);
}
});
return { missingRawValues, existRawValues };
};
// Filtered Tree
const filteredTreeData = useFilterTreeData(mergedTreeData, mergedSearchValue, {
fieldNames: mergedFieldNames,
treeNodeFilterProp: toRef(props, 'treeNodeFilterProp'),
filterTreeNode: toRef(props, 'filterTreeNode'),
});
// =========================== Label ============================
const getLabel = (item: DefaultOptionType) => {
if (item) {
if (props.treeNodeLabelProp) {
return item[props.treeNodeLabelProp];
}
// Loop from fieldNames
const { _title: titleList } = mergedFieldNames.value;
for (let i = 0; i < titleList.length; i += 1) {
const title = item[titleList[i]];
if (title !== undefined) {
return title;
}
}
}
};
// ========================= Wrap Value =========================
const toLabeledValues = (draftValues: DraftValueType) => {
const values = toArray(draftValues);
return values.map(val => {
if (isRawValue(val)) {
return { value: val };
}
return val;
});
};
const convert2LabelValues = (draftValues: DraftValueType) => {
const values = toLabeledValues(draftValues);
return values.map(item => {
let { label: rawLabel } = item;
const { value: rawValue, halfChecked: rawHalfChecked } = item;
let rawDisabled: boolean | undefined;
const entity = valueEntities.value.get(rawValue);
// Fill missing label & status
if (entity) {
rawLabel = rawLabel ?? getLabel(entity.node);
rawDisabled = entity.node.disabled;
}
return {
label: rawLabel,
value: rawValue,
halfChecked: rawHalfChecked,
disabled: rawDisabled,
};
});
};
// =========================== Values ===========================
const [internalValue, setInternalValue] = useMergedState(props.defaultValue, {
value: toRef(props, 'value'),
});
const rawMixedLabeledValues = computed(() => toLabeledValues(internalValue.value));
// Split value into full check and half check
const rawLabeledValues = shallowRef([]);
const rawHalfLabeledValues = shallowRef([]);
watchEffect(() => {
const fullCheckValues: LabeledValueType[] = [];
const halfCheckValues: LabeledValueType[] = [];
rawMixedLabeledValues.value.forEach(item => {
if (item.halfChecked) {
halfCheckValues.push(item);
} else {
fullCheckValues.push(item);
}
});
rawLabeledValues.value = fullCheckValues;
rawHalfLabeledValues.value = halfCheckValues;
});
// const [mergedValues] = useCache(rawLabeledValues);
const rawValues = computed(() => rawLabeledValues.value.map(item => item.value));
const { maxLevel, levelEntities } = useMaxLevel(keyEntities);
// Convert value to key. Will fill missed keys for conduct check.
const [rawCheckedValues, rawHalfCheckedValues] = useCheckedKeys(
rawLabeledValues,
rawHalfLabeledValues,
treeConduction,
keyEntities,
maxLevel,
levelEntities,
);
// Convert rawCheckedKeys to check strategy related values
const displayValues = computed(() => {
// Collect keys which need to show
const displayKeys = formatStrategyValues(
rawCheckedValues.value,
props.showCheckedStrategy,
keyEntities.value,
mergedFieldNames.value,
);
// Convert to value and filled with label
const values = displayKeys.map(
key => keyEntities.value[key]?.node?.[mergedFieldNames.value.value] ?? key,
);
// Back fill with origin label
const labeledValues = values.map(val => {
const targetItem = rawLabeledValues.value.find(item => item.value === val);
return {
value: val,
label: targetItem?.label,
};
});
const rawDisplayValues = convert2LabelValues(labeledValues);
const firstVal = rawDisplayValues[0];
if (!mergedMultiple.value && firstVal && isNil(firstVal.value) && isNil(firstVal.label)) {
return [];
}
return rawDisplayValues.map(item => ({
...item,
label: item.label ?? item.value,
}));
});
const [cachedDisplayValues] = useCache(displayValues);
// =========================== Change ===========================
const triggerChange = (
newRawValues: RawValueType[],
extra: { triggerValue?: RawValueType; selected?: boolean },
source: SelectSource,
) => {
const labeledValues = convert2LabelValues(newRawValues);
setInternalValue(labeledValues);
// Clean up if needed
if (props.autoClearSearchValue) {
setSearchValue('');
}
// Generate rest parameters is costly, so only do it when necessary
if (props.onChange) {
let eventValues: RawValueType[] = newRawValues;
if (treeConduction.value) {
const formattedKeyList = formatStrategyValues(
newRawValues,
props.showCheckedStrategy,
keyEntities.value,
mergedFieldNames.value,
);
eventValues = formattedKeyList.map(key => {
const entity = valueEntities.value.get(key);
return entity ? entity.node[mergedFieldNames.value.value] : key;
});
}
const { triggerValue, selected } = extra || {
triggerValue: undefined,
selected: undefined,
};
let returnRawValues: (LabeledValueType | RawValueType)[] = eventValues;
// We need fill half check back
if (props.treeCheckStrictly) {
const halfValues = rawHalfLabeledValues.value.filter(
item => !eventValues.includes(item.value),
);
returnRawValues = [...returnRawValues, ...halfValues];
}
const returnLabeledValues = convert2LabelValues(returnRawValues);
const additionalInfo = {
// [Legacy] Always return as array contains label & value
preValue: rawLabeledValues.value,
triggerValue,
} as ChangeEventExtra;
// [Legacy] Fill legacy data if user query.
// This is expansive that we only fill when user query
// https://github.com/react-component/tree-select/blob/fe33eb7c27830c9ac70cd1fdb1ebbe7bc679c16a/src/Select.jsx
let showPosition = true;
if (props.treeCheckStrictly || (source === 'selection' && !selected)) {
showPosition = false;
}
fillAdditionalInfo(
additionalInfo,
triggerValue,
newRawValues,
mergedTreeData.value,
showPosition,
mergedFieldNames.value,
);
if (mergedCheckable.value) {
additionalInfo.checked = selected;
} else {
additionalInfo.selected = selected;
}
const returnValues = mergedLabelInValue.value
? returnLabeledValues
: returnLabeledValues.map(item => item.value);
props.onChange(
mergedMultiple.value ? returnValues : returnValues[0],
mergedLabelInValue.value ? null : returnLabeledValues.map(item => item.label),
additionalInfo,
);
}
};
// ========================== Options ===========================
/** Trigger by option list */
const onOptionSelect = (
selectedKey: Key,
{ selected, source }: { selected: boolean; source: SelectSource },
) => {
const keyEntitiesValue = toRaw(keyEntities.value);
const valueEntitiesValue = toRaw(valueEntities.value);
const entity = keyEntitiesValue[selectedKey];
const node = entity?.node;
const selectedValue = node?.[mergedFieldNames.value.value] ?? selectedKey;
// Never be falsy but keep it safe
if (!mergedMultiple.value) {
// Single mode always set value
triggerChange([selectedValue], { selected: true, triggerValue: selectedValue }, 'option');
} else {
let newRawValues = selected
? [...rawValues.value, selectedValue]
: rawCheckedValues.value.filter(v => v !== selectedValue);
// Add keys if tree conduction
if (treeConduction.value) {
// Should keep missing values
const { missingRawValues, existRawValues } = splitRawValues(newRawValues);
const keyList = existRawValues.map(val => valueEntitiesValue.get(val).key);
// Conduction by selected or not
let checkedKeys: Key[];
if (selected) {
({ checkedKeys } = conductCheck(
keyList,
true,
keyEntitiesValue,
maxLevel.value,
levelEntities.value,
));
} else {
({ checkedKeys } = conductCheck(
keyList,
{ checked: false, halfCheckedKeys: rawHalfCheckedValues.value },
keyEntitiesValue,
maxLevel.value,
levelEntities.value,
));
}
// Fill back of keys
newRawValues = [
...missingRawValues,
...checkedKeys.map(key => keyEntitiesValue[key].node[mergedFieldNames.value.value]),
];
}
triggerChange(newRawValues, { selected, triggerValue: selectedValue }, source || 'option');
}
// Trigger select event
if (selected || !mergedMultiple.value) {
props.onSelect?.(selectedValue, fillLegacyProps(node));
} else {
props.onDeselect?.(selectedValue, fillLegacyProps(node));
}
};
// ========================== Dropdown ==========================
const onInternalDropdownVisibleChange = (open: boolean) => {
if (props.onDropdownVisibleChange) {
const legacyParam = {};
Object.defineProperty(legacyParam, 'documentClickClose', {
get() {
warning(false, 'Second param of `onDropdownVisibleChange` has been removed.');
return false;
},
});
(props.onDropdownVisibleChange as any)(open, legacyParam);
}
};
// ====================== Display Change ========================
const onDisplayValuesChange: BaseSelectProps['onDisplayValuesChange'] = (newValues, info) => {
const newRawValues = newValues.map(item => item.value);
if (info.type === 'clear') {
triggerChange(newRawValues, {}, 'selection');
return;
}
// TreeSelect only have multiple mode which means display change only has remove
if (info.values.length) {
onOptionSelect(info.values[0].value, { selected: false, source: 'selection' });
}
};
const {
treeNodeFilterProp,
// Data
loadData,
treeLoadedKeys,
onTreeLoad,
// Expanded
treeDefaultExpandAll,
treeExpandedKeys,
treeDefaultExpandedKeys,
onTreeExpand,
// Options
virtual,
listHeight,
listItemHeight,
// Tree
treeLine,
treeIcon,
showTreeIcon,
switcherIcon,
treeMotion,
customSlots,
dropdownMatchSelectWidth,
treeExpandAction,
} = toRefs(props);
useProvideLegacySelectContext(
toReactive({
checkable: mergedCheckable,
loadData,
treeLoadedKeys,
onTreeLoad,
checkedKeys: rawCheckedValues,
halfCheckedKeys: rawHalfCheckedValues,
treeDefaultExpandAll,
treeExpandedKeys,
treeDefaultExpandedKeys,
onTreeExpand,
treeIcon,
treeMotion,
showTreeIcon,
switcherIcon,
treeLine,
treeNodeFilterProp,
keyEntities,
customSlots,
} as unknown as LegacyContextProps),
);
useProvideSelectContext(
toReactive({
virtual,
listHeight,
listItemHeight,
treeData: filteredTreeData,
fieldNames: mergedFieldNames,
onSelect: onOptionSelect,
dropdownMatchSelectWidth,
treeExpandAction,
} as unknown as TreeSelectContextProps),
);
const selectRef = ref<BaseSelectRef>();
expose({
focus() {
selectRef.value?.focus();
},
blur() {
selectRef.value?.blur();
},
scrollTo(arg) {
selectRef.value?.scrollTo(arg);
},
} as BaseSelectRef);
return () => {
const restProps = omit(props, [
'id',
'prefixCls',
'customSlots',
// Value
'value',
'defaultValue',
'onChange',
'onSelect',
'onDeselect',
// Search
'searchValue',
'inputValue',
'onSearch',
'autoClearSearchValue',
'filterTreeNode',
'treeNodeFilterProp',
// Selector
'showCheckedStrategy',
'treeNodeLabelProp',
// Mode
'multiple',
'treeCheckable',
'treeCheckStrictly',
'labelInValue',
// FieldNames
'fieldNames',
// Data
'treeDataSimpleMode',
'treeData',
'children',
'loadData',
'treeLoadedKeys',
'onTreeLoad',
// Expanded
'treeDefaultExpandAll',
'treeExpandedKeys',
'treeDefaultExpandedKeys',
'onTreeExpand',
// Options
'virtual',
'listHeight',
'listItemHeight',
'onDropdownVisibleChange',
// Tree
'treeLine',
'treeIcon',
'showTreeIcon',
'switcherIcon',
'treeMotion',
]);
return (
<BaseSelect
v-slots={slots}
ref={selectRef}
{...attrs}
{...restProps}
// >>> MISC
id={mergedId}
prefixCls={props.prefixCls}
mode={mergedMultiple.value ? 'multiple' : undefined}
// >>> Display Value
displayValues={cachedDisplayValues.value}
onDisplayValuesChange={onDisplayValuesChange}
// >>> Search
searchValue={mergedSearchValue.value}
onSearch={onInternalSearch}
// >>> Options
OptionList={OptionList}
emptyOptions={!mergedTreeData.value.length}
onDropdownVisibleChange={onInternalDropdownVisibleChange}
tagRender={props.tagRender || slots.tagRender}
dropdownMatchSelectWidth={props.dropdownMatchSelectWidth ?? true}
/>
);
};
},
});