types(Cascader): add CascaderProps type (#9684)

This commit is contained in:
neverland 2021-10-15 17:53:04 +08:00 committed by GitHub
parent 843fd4f2a3
commit ab1543cf3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 70 deletions

View File

@ -1,4 +1,11 @@
import { nextTick, PropType, reactive, watch, defineComponent } from 'vue'; import {
ref,
watch,
nextTick,
PropType,
defineComponent,
ExtractPropTypes,
} from 'vue';
import { import {
extend, extend,
truthProp, truthProp,
@ -15,53 +22,34 @@ import { Icon } from '../icon';
// Types // Types
import type { TabsClickTabEventParams } from '../tabs/types'; import type { TabsClickTabEventParams } from '../tabs/types';
import type { CascaderTab, CascaderOption, CascaderFieldNames } from './types';
const [name, bem, t] = createNamespace('cascader'); const [name, bem, t] = createNamespace('cascader');
export type CascaderOption = { const props = {
text?: string; title: String,
value?: string | number; options: makeArrayProp<CascaderOption>(),
color?: string; closeable: truthProp,
disabled?: boolean; swipeable: truthProp,
children?: CascaderOption[]; closeIcon: makeStringProp('cross'),
className?: unknown; modelValue: numericProp,
// for custom filed names fieldNames: Object as PropType<CascaderFieldNames>,
[key: PropertyKey]: any; placeholder: String,
activeColor: String,
}; };
type CascaderTab = { export type CascaderProps = ExtractPropTypes<typeof props>;
options: CascaderOption[];
selected: CascaderOption | null;
};
export type CascaderFieldNames = {
text?: string;
value?: string;
children?: string;
};
export default defineComponent({ export default defineComponent({
name, name,
props: { props,
title: String,
options: makeArrayProp<CascaderOption>(),
closeable: truthProp,
swipeable: truthProp,
closeIcon: makeStringProp('cross'),
modelValue: numericProp,
fieldNames: Object as PropType<CascaderFieldNames>,
placeholder: String,
activeColor: String,
},
emits: ['close', 'change', 'finish', 'update:modelValue', 'click-tab'], emits: ['close', 'change', 'finish', 'click-tab', 'update:modelValue'],
setup(props, { slots, emit }) { setup(props, { slots, emit }) {
const state = reactive({ const tabs = ref<CascaderTab[]>([]);
tabs: [] as CascaderTab[], const activeTab = ref(0);
activeTab: 0,
});
const { const {
text: textKey, text: textKey,
@ -98,16 +86,15 @@ export default defineComponent({
}; };
const updateTabs = () => { const updateTabs = () => {
if (props.modelValue || props.modelValue === 0) { const { options, modelValue } = props;
const selectedOptions = getSelectedOptionsByValue(
props.options, if (modelValue !== undefined) {
props.modelValue const selectedOptions = getSelectedOptionsByValue(options, modelValue);
);
if (selectedOptions) { if (selectedOptions) {
let optionsCursor = props.options; let optionsCursor = options;
state.tabs = selectedOptions.map((option) => { tabs.value = selectedOptions.map((option) => {
const tab = { const tab = {
options: optionsCursor, options: optionsCursor,
selected: option, selected: option,
@ -124,23 +111,23 @@ export default defineComponent({
}); });
if (optionsCursor) { if (optionsCursor) {
state.tabs.push({ tabs.value.push({
options: optionsCursor, options: optionsCursor,
selected: null, selected: null,
}); });
} }
nextTick(() => { nextTick(() => {
state.activeTab = state.tabs.length - 1; activeTab.value = tabs.value.length - 1;
}); });
return; return;
} }
} }
state.tabs = [ tabs.value = [
{ {
options: props.options, options,
selected: null, selected: null,
}, },
]; ];
@ -151,10 +138,10 @@ export default defineComponent({
return; return;
} }
state.tabs[tabIndex].selected = option; tabs.value[tabIndex].selected = option;
if (state.tabs.length > tabIndex + 1) { if (tabs.value.length > tabIndex + 1) {
state.tabs = state.tabs.slice(0, tabIndex + 1); tabs.value = tabs.value.slice(0, tabIndex + 1);
} }
if (option[childrenKey]) { if (option[childrenKey]) {
@ -163,31 +150,32 @@ export default defineComponent({
selected: null, selected: null,
}; };
if (state.tabs[tabIndex + 1]) { if (tabs.value[tabIndex + 1]) {
state.tabs[tabIndex + 1] = nextTab; tabs.value[tabIndex + 1] = nextTab;
} else { } else {
state.tabs.push(nextTab); tabs.value.push(nextTab);
} }
nextTick(() => { nextTick(() => {
state.activeTab++; activeTab.value++;
}); });
} }
const selectedOptions = state.tabs const selectedOptions = tabs.value
.map((tab) => tab.selected) .map((tab) => tab.selected)
.filter(Boolean); .filter(Boolean);
const eventParams = { emit('update:modelValue', option[valueKey]);
const params = {
value: option[valueKey], value: option[valueKey],
tabIndex, tabIndex,
selectedOptions, selectedOptions,
}; };
emit('update:modelValue', option[valueKey]); emit('change', params);
emit('change', eventParams);
if (!option[childrenKey]) { if (!option[childrenKey]) {
emit('finish', eventParams); emit('finish', params);
} }
}; };
@ -260,9 +248,8 @@ export default defineComponent({
const renderTab = (tab: CascaderTab, tabIndex: number) => { const renderTab = (tab: CascaderTab, tabIndex: number) => {
const { options, selected } = tab; const { options, selected } = tab;
const title = selected const placeholder = props.placeholder || t('select');
? selected[textKey] const title = selected ? selected[textKey] : placeholder;
: props.placeholder || t('select');
return ( return (
<Tab <Tab
@ -278,7 +265,7 @@ export default defineComponent({
const renderTabs = () => ( const renderTabs = () => (
<Tabs <Tabs
v-model:active={state.activeTab} v-model:active={activeTab.value}
animated animated
class={bem('tabs')} class={bem('tabs')}
color={props.activeColor} color={props.activeColor}
@ -286,7 +273,7 @@ export default defineComponent({
swipeable={props.swipeable} swipeable={props.swipeable}
onClick-tab={onClickTab} onClick-tab={onClickTab}
> >
{state.tabs.map(renderTab)} {tabs.value.map(renderTab)}
</Tabs> </Tabs>
); );
@ -296,8 +283,8 @@ export default defineComponent({
watch( watch(
() => props.modelValue, () => props.modelValue,
(value) => { (value) => {
if (value || value === 0) { if (value !== undefined) {
const values = state.tabs.map((tab) => tab.selected?.[valueKey]); const values = tabs.value.map((tab) => tab.selected?.[valueKey]);
if (values.includes(value)) { if (values.includes(value)) {
return; return;
} }

View File

@ -245,7 +245,7 @@ export default {
The component exports the following type definitions: The component exports the following type definitions:
```ts ```ts
import type { CascaderOption, CascaderFieldNames } from 'vant'; import type { CascaderProps, CascaderOption, CascaderFieldNames } from 'vant';
``` ```
## Theming ## Theming

View File

@ -257,7 +257,7 @@ export default {
组件导出以下类型定义: 组件导出以下类型定义:
```ts ```ts
import type { CascaderOption, CascaderFieldNames } from 'vant'; import type { CascaderProps, CascaderOption, CascaderFieldNames } from 'vant';
``` ```
## 主题定制 ## 主题定制

View File

@ -1,6 +1,7 @@
import { withInstall } from '../utils'; import { withInstall } from '../utils';
import _Cascader from './Cascader'; import _Cascader, { CascaderProps } from './Cascader';
export const Cascader = withInstall(_Cascader); export const Cascader = withInstall(_Cascader);
export default Cascader; export default Cascader;
export type { CascaderOption, CascaderFieldNames } from './Cascader'; export type { CascaderProps };
export type { CascaderOption, CascaderFieldNames } from './types';

View File

@ -0,0 +1,21 @@
export type CascaderOption = {
text?: string;
value?: string | number;
color?: string;
disabled?: boolean;
children?: CascaderOption[];
className?: unknown;
// for custom filed names
[key: PropertyKey]: any;
};
export type CascaderTab = {
options: CascaderOption[];
selected: CascaderOption | null;
};
export type CascaderFieldNames = {
text?: string;
value?: string;
children?: string;
};