naive-ui
Version:
A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast
180 lines • 5.25 kB
JavaScript
import { createTreeMate } from 'treemate';
import { clickoutside } from 'vdirs';
import { computed, defineComponent, h, inject, ref, Transition, withDirectives } from 'vue';
import { NInternalSelectMenu } from "../../_internal/index.mjs";
import { resolveSlot } from "../../_utils/index.mjs";
import { createTmOptions } from "../../select/src/utils.mjs";
import { cascaderInjectionKey } from "./interface.mjs";
import { createSelectOptions } from "./utils.mjs";
export default defineComponent({
name: 'NCascaderSelectMenu',
props: {
value: {
type: [String, Number, Array],
default: null
},
show: Boolean,
pattern: {
type: String,
default: ''
},
multiple: Boolean,
tmNodes: {
type: Array,
default: () => []
},
filter: Function,
labelField: {
type: String,
required: true
},
separator: {
type: String,
required: true
}
},
setup(props) {
const {
isMountedRef,
mergedValueRef,
mergedClsPrefixRef,
mergedThemeRef,
mergedCheckStrategyRef,
slots: cascaderSlots,
syncSelectMenuPosition,
closeMenu,
handleSelectMenuClickOutside,
doUncheck: cascaderDoUncheck,
doCheck: cascaderDoCheck,
clearPattern
} = inject(cascaderInjectionKey);
const menuInstRef = ref(null);
const selectOptionsRef = computed(() => {
return createSelectOptions(props.tmNodes, mergedCheckStrategyRef.value === 'child', props.labelField, props.separator);
});
const mergedFilterRef = computed(() => {
const {
filter
} = props;
if (filter) return filter;
const {
labelField
} = props;
return (pattern, _, path) => path.some(option => option[labelField] && ~option[labelField].toLowerCase().indexOf(pattern.toLowerCase()));
});
const filteredSelectOptionsRef = computed(() => {
const {
pattern
} = props;
const {
value: mergedFilter
} = mergedFilterRef;
return (pattern ? selectOptionsRef.value.filter(option => {
return mergedFilter(pattern, option.rawNode, option.path);
}) : selectOptionsRef.value).map(option => ({
value: option.value,
label: option.label
}));
});
const selectTreeMateRef = computed(() => {
return createTreeMate(filteredSelectOptionsRef.value, createTmOptions('value', 'children'));
});
function handleResize() {
syncSelectMenuPosition();
}
function handleToggle(tmNode) {
doCheck(tmNode);
}
// We don't care what type the tmNode is, we only care about its key
function doCheck(tmNode) {
if (props.multiple) {
const {
value: mergedValue
} = mergedValueRef;
if (Array.isArray(mergedValue)) {
if (!mergedValue.includes(tmNode.key)) {
cascaderDoCheck(tmNode.key);
} else {
cascaderDoUncheck(tmNode.key);
}
} else if (mergedValue === null) {
cascaderDoCheck(tmNode.key);
}
clearPattern();
} else {
cascaderDoCheck(tmNode.key);
// currently the select menu is set to focusable
// however just leave it here
closeMenu(true);
}
}
function prev() {
var _a;
(_a = menuInstRef.value) === null || _a === void 0 ? void 0 : _a.prev();
}
function next() {
var _a;
(_a = menuInstRef.value) === null || _a === void 0 ? void 0 : _a.next();
}
function enter() {
var _a;
if (menuInstRef) {
const pendingOptionTmNode = (_a = menuInstRef.value) === null || _a === void 0 ? void 0 : _a.getPendingTmNode();
if (pendingOptionTmNode) {
doCheck(pendingOptionTmNode);
}
return true;
}
return false;
}
function handleClickOutside(e) {
handleSelectMenuClickOutside(e);
}
const exposedRef = {
prev,
next,
enter
};
return Object.assign({
isMounted: isMountedRef,
mergedTheme: mergedThemeRef,
mergedClsPrefix: mergedClsPrefixRef,
menuInstRef,
selectTreeMate: selectTreeMateRef,
handleResize,
handleToggle,
handleClickOutside,
cascaderSlots
}, exposedRef);
},
render() {
const {
mergedClsPrefix,
isMounted,
mergedTheme,
cascaderSlots
} = this;
return h(Transition, {
name: "fade-in-scale-up-transition",
appear: isMounted
}, {
default: () => this.show ? withDirectives(h(NInternalSelectMenu, {
ref: "menuInstRef",
onResize: this.handleResize,
clsPrefix: mergedClsPrefix,
class: `${mergedClsPrefix}-cascader-menu`,
autoPending: true,
themeOverrides: mergedTheme.peerOverrides.InternalSelectMenu,
theme: mergedTheme.peers.InternalSelectMenu,
treeMate: this.selectTreeMate,
multiple: this.multiple,
value: this.value,
onToggle: this.handleToggle
}, {
empty: () => resolveSlot(cascaderSlots['not-found'], () => [])
}), [[clickoutside, this.handleClickOutside, undefined, {
capture: true
}]]) : null
});
}
});