UNPKG

@arco-design/web-vue

Version:

Arco Design Vue 2.0: A Vue.js 3 UI Library

675 lines (674 loc) 21 kB
"use strict"; var vue = require("vue"); var utils = require("./utils.js"); var index = require("../trigger/index.js"); var selectView = require("../_components/select-view/select-view.js"); var baseCascaderPanel = require("./base-cascader-panel.js"); var cascaderSearchPanel = require("./cascader-search-panel.js"); var is = require("../_utils/is.js"); var useSelectedPath = require("./hooks/use-selected-path.js"); var keyboard = require("../_utils/keyboard.js"); var context = require("./context.js"); var debounce = require("../_utils/debounce.js"); var useFormItem = require("../_hooks/use-form-item.js"); var pluginVue_exportHelper = require("../_virtual/plugin-vue_export-helper.js"); const _sfc_main = vue.defineComponent({ name: "Cascader", components: { Trigger: index, SelectView: selectView, BaseCascaderPanel: baseCascaderPanel, CascaderSearchPanel: cascaderSearchPanel }, inheritAttrs: false, props: { pathMode: { type: Boolean, default: false }, multiple: { type: Boolean, default: false }, modelValue: { type: [String, Number, Object, Array] }, defaultValue: { type: [String, Number, Object, Array], default: (props) => props.multiple ? [] : props.pathMode ? void 0 : "" }, options: { type: Array, default: () => [] }, disabled: { type: Boolean, default: false }, error: { type: Boolean, default: false }, size: { type: String }, allowSearch: { type: Boolean, default: (props) => Boolean(props.multiple) }, allowClear: { type: Boolean, default: false }, inputValue: { type: String, default: void 0 }, defaultInputValue: { type: String, default: "" }, popupVisible: { type: Boolean, default: void 0 }, expandTrigger: { type: String, default: "click" }, defaultPopupVisible: { type: Boolean, default: false }, placeholder: String, filterOption: { type: Function }, popupContainer: { type: [String, Object] }, maxTagCount: { type: Number, default: 0 }, formatLabel: { type: Function }, triggerProps: { type: Object }, checkStrictly: { type: Boolean, default: false }, loadMore: { type: Function }, loading: { type: Boolean, default: false }, searchOptionOnlyLabel: { type: Boolean, default: false }, searchDelay: { type: Number, default: 500 }, fieldNames: { type: Object }, valueKey: { type: String, default: "value" }, fallback: { type: [Boolean, Function], default: true }, expandChild: { type: Boolean, default: false }, virtualListProps: { type: Object }, tagNowrap: { type: Boolean, default: false } }, emits: { "update:modelValue": (value) => true, "update:popupVisible": (visible) => true, "change": (value) => true, "inputValueChange": (value) => true, "clear": () => true, "search": (value) => true, "popupVisibleChange": (visible) => true, "focus": (ev) => true, "blur": (ev) => true }, setup(props, { emit, slots }) { const { options, checkStrictly, loadMore, formatLabel, modelValue, disabled, valueKey, expandTrigger, expandChild, pathMode, multiple } = vue.toRefs(props); const _value = vue.ref(props.defaultValue); const _inputValue = vue.ref(props.defaultInputValue); const _popupVisible = vue.ref(props.defaultPopupVisible); const { mergedDisabled, eventHandlers } = useFormItem.useFormItem({ disabled }); vue.watch(modelValue, (value) => { if (is.isUndefined(value) || is.isNull(value)) { _value.value = props.multiple ? [] : void 0; } }); const optionInfos = vue.ref([]); const totalLevel = vue.ref(1); const optionMap = vue.reactive(/* @__PURE__ */ new Map()); const leafOptionMap = vue.reactive(/* @__PURE__ */ new Map()); const leafOptionValueMap = vue.reactive(/* @__PURE__ */ new Map()); const leafOptionSet = vue.reactive(/* @__PURE__ */ new Set()); const lazyLoadOptions = vue.reactive({}); const addLazyLoadOptions = (children, key) => { lazyLoadOptions[key] = children; }; const DEFAULT_FIELD_NAMES = { value: "value", label: "label", disabled: "disabled", children: "children", tagProps: "tagProps", render: "render", isLeaf: "isLeaf" }; const mergedFieldNames = vue.computed(() => ({ ...DEFAULT_FIELD_NAMES, ...props.fieldNames })); vue.watch( [options, lazyLoadOptions, mergedFieldNames], ([_options, _lazyLoadOptions, _fieldNames]) => { optionMap.clear(); leafOptionMap.clear(); leafOptionValueMap.clear(); leafOptionSet.clear(); optionInfos.value = utils.getOptionInfos(_options != null ? _options : [], { enabledLazyLoad: Boolean(props.loadMore), lazyLoadOptions, optionMap, leafOptionSet, leafOptionMap, leafOptionValueMap, totalLevel, checkStrictly, valueKey, fieldNames: _fieldNames }); }, { immediate: true, deep: true } ); const computedValueMap = vue.computed(() => { var _a; const values = utils.getValidValues((_a = props.modelValue) != null ? _a : _value.value, { multiple: props.multiple, pathMode: props.pathMode }); return new Map( values.map((value) => [ utils.getValueKey(value, { valueKey: props.valueKey, leafOptionValueMap }), value ]) ); }); const computedInputValue = vue.computed( () => { var _a; return (_a = props.inputValue) != null ? _a : _inputValue.value; } ); const computedPopupVisible = vue.computed( () => { var _a; return (_a = props.popupVisible) != null ? _a : _popupVisible.value; } ); const getFilteredStatus = (label) => { var _a; return label == null ? void 0 : label.toLocaleLowerCase().includes((_a = computedInputValue.value) == null ? void 0 : _a.toLocaleLowerCase()); }; const filteredLeafOptions = vue.computed(() => { const options2 = props.checkStrictly ? Array.from(optionMap.values()) : Array.from(leafOptionSet); return options2.filter((item) => { var _a; if (is.isFunction(props.filterOption)) { return props.filterOption(computedInputValue.value, item.raw); } if (props.checkStrictly) { return getFilteredStatus(item.label); } return (_a = item.path) == null ? void 0 : _a.find((leaf) => getFilteredStatus(leaf.label)); }); }); const updateValue = (values) => { var _a, _b, _c; const value = props.multiple ? values : (_a = values[0]) != null ? _a : ""; if (values.length === 0) { setSelectedPath(); setActiveKey(); } _value.value = value; emit("update:modelValue", value); emit("change", value); (_c = (_b = eventHandlers.value) == null ? void 0 : _b.onChange) == null ? void 0 : _c.call(_b); }; vue.watch([multiple, pathMode], () => { const values = []; computedValueMap.value.forEach((value, key) => { const option = leafOptionMap.get(key); if (option) { values.push(pathMode.value ? option.pathValue : option.value); } }); updateValue(values); }); const handlePopupVisibleChange = (visible) => { if (computedPopupVisible.value !== visible) { _popupVisible.value = visible; emit("popupVisibleChange", visible); } }; const handleRemove = (key) => { if (props.multiple) { const option = leafOptionMap.get(key); if (option) { selectMultiple(option, false); } else { const values = []; computedValueMap.value.forEach((value, _key) => { if (_key !== key) { values.push(value); } }); updateValue(values); } } }; const selectSingle = (option) => { updateValue([props.pathMode ? option.pathValue : option.value]); handlePopupVisibleChange(false); }; const selectMultiple = (option, checked) => { if (checked) { const leafOptionInfos = props.checkStrictly ? [option] : utils.getLeafOptionInfos(option); updateValue([ ...computedValueMap.value.values(), ...leafOptionInfos.filter((item) => !computedValueMap.value.has(item.key)).map((item) => { return props.pathMode ? item.pathValue : item.value; }) ]); } else { const leafOptionKeys = props.checkStrictly ? [option.key] : utils.getLeafOptionKeys(option); const values = []; computedValueMap.value.forEach((value, key) => { if (!leafOptionKeys.includes(key)) { values.push(value); } }); updateValue(values); } handleInputValueChange("", "optionChecked"); }; const handleClickOption = (option, checked) => { if (props.multiple) { selectMultiple(option, checked != null ? checked : true); } else { selectSingle(option); } }; const handleSearch = debounce.debounce((value) => { emit("search", value); }, props.searchDelay); const handleInputValueChange = (value, reason) => { if (value !== computedInputValue.value) { if (reason === "manual" && !computedPopupVisible.value) { _popupVisible.value = true; emit("popupVisibleChange", true); } _inputValue.value = value; emit("inputValueChange", value); if (props.allowSearch) { handleSearch(value); } } }; vue.watch(computedPopupVisible, (value) => { if (value) { if (computedValueMap.value.size > 0) { const keys = Array.from(computedValueMap.value.keys()); const lastKey = keys[keys.length - 1]; const option = leafOptionMap.get(lastKey); if (option && option.key !== activeKey.value) { setSelectedPath(option.key); setActiveKey(option.key); } } } else { if (computedValueMap.value.size === 0) { setSelectedPath(); setActiveKey(); } handleInputValueChange("", "optionListHide"); } }); const handleClear = (e) => { e.stopPropagation(); if (props.multiple) { const newValues = []; computedValueMap.value.forEach((value, key) => { const option = leafOptionMap.get(key); if (option == null ? void 0 : option.disabled) { newValues.push(props.pathMode ? option.pathValue : option.value); } }); updateValue(newValues); } else { updateValue([]); } handleInputValueChange("", "manual"); emit("clear"); }; const showSearchPanel = vue.computed( () => props.allowSearch && computedInputValue.value.length > 0 ); const handleFocus = (e) => { emit("focus", e); }; const handleBlur = (e) => { emit("blur", e); }; const { activeKey, activeOption, selectedPath, displayColumns, setActiveKey, setSelectedPath, getNextActiveNode } = useSelectedPath.useSelectedPath(optionInfos, { optionMap, filteredLeafOptions, showSearchPanel, expandChild }); vue.provide( context.cascaderInjectionKey, vue.reactive({ onClickOption: handleClickOption, setActiveKey, setSelectedPath, loadMore, expandTrigger, addLazyLoadOptions, formatLabel, slots, valueMap: computedValueMap }) ); const handleKeyDown = keyboard.getKeyDownHandler( /* @__PURE__ */ new Map([ [ keyboard.KEYBOARD_KEY.ENTER, (ev) => { if (computedPopupVisible.value) { if (activeOption.value) { let checked; if (props.checkStrictly || activeOption.value.isLeaf) { checked = !computedValueMap.value.has(activeOption.value.key); } else { checked = !utils.getCheckedStatus( activeOption.value, computedValueMap.value ).checked; } setSelectedPath(activeOption.value.key); handleClickOption(activeOption.value, checked); } } else { handlePopupVisibleChange(true); } } ], [ keyboard.KEYBOARD_KEY.ESC, (ev) => { handlePopupVisibleChange(false); } ], [ keyboard.KEYBOARD_KEY.ARROW_DOWN, (ev) => { ev.preventDefault(); const activeNode = getNextActiveNode("next"); setActiveKey(activeNode == null ? void 0 : activeNode.key); } ], [ keyboard.KEYBOARD_KEY.ARROW_UP, (ev) => { ev.preventDefault(); const activeNode = getNextActiveNode("preview"); setActiveKey(activeNode == null ? void 0 : activeNode.key); } ], [ keyboard.KEYBOARD_KEY.ARROW_RIGHT, (ev) => { var _a, _b; if (!showSearchPanel.value) { ev.preventDefault(); if ((_a = activeOption.value) == null ? void 0 : _a.children) { setSelectedPath(activeOption.value.key); setActiveKey((_b = activeOption.value.children[0]) == null ? void 0 : _b.key); } } } ], [ keyboard.KEYBOARD_KEY.ARROW_LEFT, (ev) => { var _a; if (!showSearchPanel.value) { ev.preventDefault(); if ((_a = activeOption.value) == null ? void 0 : _a.parent) { setSelectedPath(activeOption.value.parent.key); setActiveKey(activeOption.value.parent.key); } } } ] ]) ); const selectViewValue = vue.computed(() => { const result = []; computedValueMap.value.forEach((value, key) => { var _a, _b; const option = leafOptionMap.get(key); if (option) { result.push({ value: key, label: (_b = (_a = props.formatLabel) == null ? void 0 : _a.call(props, option.path.map((item) => item.raw))) != null ? _b : utils.getOptionLabel(option), closable: !option.disabled, tagProps: option.tagProps }); } else if (props.fallback) { const label = is.isFunction(props.fallback) ? props.fallback(value) : is.isArray(value) ? value.join(" / ") : String(value); result.push({ value: key, label, closable: true }); } }); return result; }); return { optionInfos, filteredLeafOptions, selectedPath, activeKey, displayColumns, computedInputValue, computedPopupVisible, handleClear, selectViewValue, handleInputValueChange, showSearchPanel, handlePopupVisibleChange, handleFocus, handleBlur, handleRemove, mergedDisabled, handleKeyDown, totalLevel }; } }); function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { const _component_select_view = vue.resolveComponent("select-view"); const _component_cascader_search_panel = vue.resolveComponent("cascader-search-panel"); const _component_base_cascader_panel = vue.resolveComponent("base-cascader-panel"); const _component_trigger = vue.resolveComponent("trigger"); return vue.openBlock(), vue.createBlock(_component_trigger, vue.mergeProps(_ctx.triggerProps, { trigger: "click", "animation-name": "slide-dynamic-origin", "auto-fit-transform-origin": "", "popup-visible": _ctx.computedPopupVisible, position: "bl", disabled: _ctx.mergedDisabled, "popup-offset": 4, "auto-fit-popup-width": _ctx.showSearchPanel, "popup-container": _ctx.popupContainer, "prevent-focus": true, "click-to-close": !_ctx.allowSearch, onPopupVisibleChange: _ctx.handlePopupVisibleChange }), { content: vue.withCtx(() => [ _ctx.showSearchPanel ? (vue.openBlock(), vue.createBlock(_component_cascader_search_panel, { key: 0, options: _ctx.filteredLeafOptions, "active-key": _ctx.activeKey, multiple: _ctx.multiple, "check-strictly": _ctx.checkStrictly, loading: _ctx.loading, "path-label": !_ctx.searchOptionOnlyLabel }, vue.createSlots({ _: 2 }, [ _ctx.$slots.empty ? { name: "empty", fn: vue.withCtx(() => [ vue.renderSlot(_ctx.$slots, "empty") ]), key: "0" } : void 0 ]), 1032, ["options", "active-key", "multiple", "check-strictly", "loading", "path-label"])) : (vue.openBlock(), vue.createBlock(_component_base_cascader_panel, { key: 1, "display-columns": _ctx.displayColumns, "selected-path": _ctx.selectedPath, "active-key": _ctx.activeKey, multiple: _ctx.multiple, "total-level": _ctx.totalLevel, "check-strictly": _ctx.checkStrictly, loading: _ctx.loading, "virtual-list-props": _ctx.virtualListProps, dropdown: "" }, vue.createSlots({ _: 2 }, [ _ctx.$slots.empty ? { name: "empty", fn: vue.withCtx(() => [ vue.renderSlot(_ctx.$slots, "empty") ]), key: "0" } : void 0 ]), 1032, ["display-columns", "selected-path", "active-key", "multiple", "total-level", "check-strictly", "loading", "virtual-list-props"])) ]), default: vue.withCtx(() => [ vue.createVNode(_component_select_view, vue.mergeProps({ "model-value": _ctx.selectViewValue, "input-value": _ctx.computedInputValue, disabled: _ctx.mergedDisabled, error: _ctx.error, multiple: _ctx.multiple, "allow-clear": _ctx.allowClear, "allow-search": _ctx.allowSearch, size: _ctx.size, opened: _ctx.computedPopupVisible, placeholder: _ctx.placeholder, loading: _ctx.loading, "max-tag-count": _ctx.maxTagCount, "tag-nowrap": _ctx.tagNowrap }, _ctx.$attrs, { onInputValueChange: _ctx.handleInputValueChange, onClear: _ctx.handleClear, onFocus: _ctx.handleFocus, onBlur: _ctx.handleBlur, onRemove: _ctx.handleRemove, onKeydown: _ctx.handleKeyDown }), vue.createSlots({ _: 2 }, [ _ctx.$slots.label ? { name: "label", fn: vue.withCtx((data) => [ vue.renderSlot(_ctx.$slots, "label", vue.normalizeProps(vue.guardReactiveProps(data))) ]), key: "0" } : void 0, _ctx.$slots.prefix ? { name: "prefix", fn: vue.withCtx(() => [ vue.renderSlot(_ctx.$slots, "prefix") ]), key: "1" } : void 0, _ctx.$slots["arrow-icon"] ? { name: "arrow-icon", fn: vue.withCtx(() => [ vue.renderSlot(_ctx.$slots, "arrow-icon") ]), key: "2" } : void 0, _ctx.$slots["loading-icon"] ? { name: "loading-icon", fn: vue.withCtx(() => [ vue.renderSlot(_ctx.$slots, "loading-icon") ]), key: "3" } : void 0, _ctx.$slots["search-icon"] ? { name: "search-icon", fn: vue.withCtx(() => [ vue.renderSlot(_ctx.$slots, "search-icon") ]), key: "4" } : void 0 ]), 1040, ["model-value", "input-value", "disabled", "error", "multiple", "allow-clear", "allow-search", "size", "opened", "placeholder", "loading", "max-tag-count", "tag-nowrap", "onInputValueChange", "onClear", "onFocus", "onBlur", "onRemove", "onKeydown"]) ]), _: 3 }, 16, ["popup-visible", "disabled", "auto-fit-popup-width", "popup-container", "click-to-close", "onPopupVisibleChange"]); } var _Cascader = /* @__PURE__ */ pluginVue_exportHelper(_sfc_main, [["render", _sfc_render]]); module.exports = _Cascader;