diff --git a/src/dropdown-item/index.js b/src/dropdown-item/index.tsx similarity index 78% rename from src/dropdown-item/index.js rename to src/dropdown-item/index.tsx index 0258e083b..33659233d 100644 --- a/src/dropdown-item/index.js +++ b/src/dropdown-item/index.tsx @@ -1,8 +1,14 @@ -import { reactive, Teleport } from 'vue'; +import { + reactive, + Teleport, + PropType, + TeleportProps, + CSSProperties, +} from 'vue'; // Utils import { createNamespace, UnknownProp } from '../utils'; -import { DROPDOWN_KEY } from '../dropdown-menu'; +import { DROPDOWN_KEY, DropdownMenuProvide } from '../dropdown-menu'; // Composition import { useParent } from '@vant/use'; @@ -15,15 +21,21 @@ import Popup from '../popup'; const [createComponent, bem] = createNamespace('dropdown-item'); +export type DropdownItemOption = { + text: string; + icon?: string; + value: number | string; +}; + export default createComponent({ props: { title: String, disabled: Boolean, - teleport: [String, Object], + teleport: [String, Object] as PropType, modelValue: UnknownProp, titleClass: UnknownProp, options: { - type: Array, + type: Array as PropType, default: () => [], }, lazyRender: { @@ -41,9 +53,19 @@ export default createComponent({ showWrapper: false, }); - const { parent } = useParent(DROPDOWN_KEY); + const { parent } = useParent(DROPDOWN_KEY); - const createEmitter = (eventName) => () => emit(eventName); + if (!parent) { + if (process.env.NODE_ENV !== 'production') { + console.error( + '[Vant] DropdownItem must be a child component of DropdownMenu.' + ); + } + return; + } + + const createEmitter = (eventName: 'open' | 'close' | 'opened') => () => + emit(eventName); const onOpen = createEmitter('open'); const onClose = createEmitter('close'); const onOpened = createEmitter('opened'); @@ -53,14 +75,17 @@ export default createComponent({ emit('closed'); }; - const onClickWrapper = (event) => { + const onClickWrapper = (event: MouseEvent) => { // prevent being identified as clicking outside and closed when using teleport if (props.teleport) { event.stopPropagation(); } }; - const toggle = (show = !state.showPopup, options = {}) => { + const toggle = ( + show = !state.showPopup, + options: { immediate?: boolean } = {} + ) => { if (show === state.showPopup) { return; } @@ -89,7 +114,7 @@ export default createComponent({ return match.length ? match[0].text : ''; }; - const renderOption = (option) => { + const renderOption = (option: DropdownItemOption) => { const { activeColor } = parent.props; const active = option.value === props.modelValue; @@ -129,7 +154,10 @@ export default createComponent({ closeOnClickOverlay, } = parent.props; - const style = { zIndex }; + const style: CSSProperties = { + zIndex: zIndex !== undefined ? +zIndex : undefined, + }; + if (direction === 'down') { style.top = `${offset.value}px`; } else { diff --git a/src/dropdown-menu/index.js b/src/dropdown-menu/index.tsx similarity index 77% rename from src/dropdown-menu/index.js rename to src/dropdown-menu/index.tsx index 47439ba5c..74531bb8e 100644 --- a/src/dropdown-menu/index.js +++ b/src/dropdown-menu/index.tsx @@ -1,7 +1,7 @@ -import { ref, computed } from 'vue'; +import { ref, computed, PropType, CSSProperties, Ref } from 'vue'; // Utils -import { createNamespace, isDef } from '../utils'; +import { isDef, ComponentInstance, createNamespace } from '../utils'; // Composition import { @@ -16,6 +16,20 @@ const [createComponent, bem] = createNamespace('dropdown-menu'); export const DROPDOWN_KEY = 'vanDropdownMenu'; +export type DropdownMenuDirection = 'up' | 'down'; + +export type DropdownMenuProvide = { + props: { + zIndex?: number | string; + overlay: boolean; + duration: number | string; + direction: DropdownMenuDirection; + activeColor?: string; + closeOnClickOverlay: boolean; + }; + offset: Ref; +}; + export default createComponent({ props: { zIndex: [Number, String], @@ -29,7 +43,7 @@ export default createComponent({ default: 0.2, }, direction: { - type: String, + type: String as PropType, default: 'down', }, closeOnClickOutside: { @@ -43,11 +57,13 @@ export default createComponent({ }, setup(props, { slots }) { - const root = ref(); + const root = ref(); + const barRef = ref(); const offset = ref(0); - const barRef = ref(); - const { children, linkChildren } = useChildren(DROPDOWN_KEY); + const { children, linkChildren } = useChildren( + DROPDOWN_KEY + ); const scrollParent = useScrollParent(root); const opened = computed(() => @@ -57,8 +73,8 @@ export default createComponent({ const barStyle = computed(() => { if (opened.value && isDef(props.zIndex)) { return { - zIndex: 1 + props.zIndex, - }; + zIndex: +props.zIndex + 1, + } as CSSProperties; } }); @@ -87,7 +103,7 @@ export default createComponent({ } }; - const toggleItem = (active) => { + const toggleItem = (active: number) => { children.forEach((item, index) => { if (index === active) { updateOffset(); @@ -98,7 +114,7 @@ export default createComponent({ }); }; - const renderTitle = (item, index) => { + const renderTitle = (item: ComponentInstance, index: number) => { const { showPopup } = item.state; const { disabled, titleClass } = item; diff --git a/src/vue-tsx-shim.d.ts b/src/vue-tsx-shim.d.ts index e6f4db5fc..38f08abe6 100644 --- a/src/vue-tsx-shim.d.ts +++ b/src/vue-tsx-shim.d.ts @@ -10,12 +10,15 @@ declare module 'vue' { // see: https://github.com/vuejs/vue-next/issues/1553 // https://github.com/vuejs/vue-next/issues/3029 onBlur?: EventHandler; + onOpen?: EventHandler; onEdit?: EventHandler; + onClose?: EventHandler; onFocus?: EventHandler; onInput?: EventHandler; onClick?: EventHandler; onPress?: EventHandler; onCancel?: EventHandler; + onOpened?: EventHandler; onClosed?: EventHandler; onChange?: EventHandler; onSubmit?: EventHandler;