diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 603e700d5..1773b8744 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -2,13 +2,15 @@ ## NEXT_VERSION -### Features +### Breaking Changes -- Add slot type for all components. +- (**Vue 3.3+ required**) Add slot type for all components. ### Fixes - Fix `n-data-table` may have multiple expand trigger with tree data. +- Fix `n-date-picker`'s `confirm`, `now`, `clear` slots doesn't work with `'month'`, `'monthrange'`, `'quarter'`, `'quarterrange'`, `'year'` and `'yearrange'` type. +- Fix `n-input`'s `render-count` prop doesn't work when type is not `'textarea'`. ## 2.40.4 diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 5d81e8a5b..49c1600f9 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -2,13 +2,15 @@ ## NEXT_VERSION -### Features +### Breaking Changes -- 为所有的组件增加插槽类型 +- (需要 Vue 3.3+)为所有的组件增加插槽的类型标注 ### Fixes - 修复 `n-data-table` 在使用树形数据的时候出现多个展开 icon +- 修复 `n-date-picker` 的 `confirm`、`now`、`clear` 插槽对 `'month'`、`'monthrange'`、`'quarter'`、`'quarterrange'`、`'year'` 和 `'yearrange'` 类型不生效 +- 修复 `n-input` 的 `render-count` 属性在类型非 `'textarea'` 时不生效 ## 2.40.4 diff --git a/src/_utils/index.ts b/src/_utils/index.ts index ebebd3f47..4e0081955 100644 --- a/src/_utils/index.ts +++ b/src/_utils/index.ts @@ -25,6 +25,7 @@ export { createRefSetter, flatten, getFirstSlotVNode, + getFirstSlotVNodeWithTypedProps, getSlot, getVNodeChildren, isNodeVShowFalse, @@ -36,6 +37,7 @@ export { render, resolveSlot, resolveSlotWithProps, + resolveSlotWithTypedProps, resolveWrappedSlot, resolveWrappedSlotWithProps, Wrapper diff --git a/src/_utils/vue/get-first-slot-vnode.ts b/src/_utils/vue/get-first-slot-vnode.ts index b1facd0b7..5be3cfb66 100644 --- a/src/_utils/vue/get-first-slot-vnode.ts +++ b/src/_utils/vue/get-first-slot-vnode.ts @@ -22,3 +22,19 @@ export function getFirstSlotVNode( return null } } + +export function getFirstSlotVNodeWithTypedProps( + slotName: string, + slot: (props: T) => VNode[], + props: T +): VNode | null { + const slotContent = flatten(slot(props)) + // vue will normalize the slot, so slot must be an array + if (slotContent.length === 1) { + return slotContent[0] + } + else { + warn('getFirstSlotVNode', `slot[${slotName}] should have exactly one child`) + return null + } +} diff --git a/src/_utils/vue/index.ts b/src/_utils/vue/index.ts index bc7c3ffd6..26babad82 100644 --- a/src/_utils/vue/index.ts +++ b/src/_utils/vue/index.ts @@ -4,7 +4,10 @@ export { createDataKey } from './create-data-key' export { createInjectionKey } from './create-injection-key' export { createRefSetter } from './create-ref-setter' export { flatten } from './flatten' -export { getFirstSlotVNode } from './get-first-slot-vnode' +export { + getFirstSlotVNode, + getFirstSlotVNodeWithTypedProps +} from './get-first-slot-vnode' export { getSlot } from './get-slot' export { getVNodeChildren } from './get-v-node-children' export { isNodeVShowFalse } from './is-node-v-show-false' @@ -17,6 +20,7 @@ export { isSlotEmpty, resolveSlot, resolveSlotWithProps, + resolveSlotWithTypedProps, resolveWrappedSlot, resolveWrappedSlotWithProps } from './resolve-slot' diff --git a/src/_utils/vue/resolve-slot.ts b/src/_utils/vue/resolve-slot.ts index 3ca855afe..40a00b937 100644 --- a/src/_utils/vue/resolve-slot.ts +++ b/src/_utils/vue/resolve-slot.ts @@ -47,6 +47,14 @@ export function resolveSlotWithProps( return (slot && ensureValidVNode(slot(props))) || fallback(props) } +export function resolveSlotWithTypedProps( + slot: Slot | undefined, + props: T, + fallback: (props: T) => VNodeArrayChildren +): VNodeArrayChildren { + return (slot && ensureValidVNode(slot(props))) || fallback(props) +} + /** * Resolve slot with wrapper if content exists, no fallback */ diff --git a/src/alert/src/Alert.tsx b/src/alert/src/Alert.tsx index cdf5dd4b1..019c1fbbf 100644 --- a/src/alert/src/Alert.tsx +++ b/src/alert/src/Alert.tsx @@ -11,6 +11,7 @@ import { type PropType, ref, type SlotsType, + type VNode, watchEffect } from 'vue' import { NBaseClose, NBaseIcon, NFadeInExpandTransition } from '../../_internal' @@ -58,9 +59,9 @@ export const alertProps = { export type AlertProps = ExtractPublicPropTypes export interface AlertSlots { - default?: any - icon?: any - header?: any + default?: () => VNode[] + icon?: () => VNode[] + header?: () => VNode[] } export default defineComponent({ diff --git a/src/auto-complete/index.ts b/src/auto-complete/index.ts index 5784eab1e..9b89b382c 100644 --- a/src/auto-complete/index.ts +++ b/src/auto-complete/index.ts @@ -1,7 +1,7 @@ export { autoCompleteProps, default as NAutoComplete } from './src/AutoComplete' export type { AutoCompleteProps, AutoCompleteSlots } from './src/AutoComplete' export type { - AutoCompleteDefaultSlotOptions, + AutoCompleteDefaultSlotProps, AutoCompleteGroupOption, AutoCompleteInst, AutoCompleteOption diff --git a/src/auto-complete/src/AutoComplete.tsx b/src/auto-complete/src/AutoComplete.tsx index 95871e6f5..c0ce1e225 100644 --- a/src/auto-complete/src/AutoComplete.tsx +++ b/src/auto-complete/src/AutoComplete.tsx @@ -12,7 +12,7 @@ import type { } from '../../select/src/interface' import type { AutoCompleteTheme } from '../styles' import type { - AutoCompleteDefaultSlotOptions, + AutoCompleteDefaultSlotProps, AutoCompleteInst, AutoCompleteOption, AutoCompleteOptions, @@ -37,6 +37,7 @@ import { type SlotsType, toRef, Transition, + type VNode, watchEffect, withDirectives } from 'vue' @@ -49,7 +50,7 @@ import { useConfig, useFormItem, useTheme, useThemeClass } from '../../_mixins' import { call, type ExtractPublicPropTypes, - getFirstSlotVNode, + getFirstSlotVNodeWithTypedProps, type MaybeArray, useAdjustedTo, warnOnce @@ -117,10 +118,10 @@ export const autoCompleteProps = { export type AutoCompleteProps = ExtractPublicPropTypes export interface AutoCompleteSlots { - default?: (options: AutoCompleteDefaultSlotOptions) => any - empty?: any - prefix?: any - suffix?: any + default?: (options: AutoCompleteDefaultSlotProps) => VNode[] + empty?: () => VNode[] + prefix?: () => VNode[] + suffix?: () => VNode[] } export default defineComponent({ @@ -373,12 +374,16 @@ export default defineComponent({ default: () => { const defaultSlot = this.$slots.default if (defaultSlot) { - return getFirstSlotVNode(this.$slots, 'default', { - handleInput: this.handleInput, - handleFocus: this.handleFocus, - handleBlur: this.handleBlur, - value: this.mergedValue - } as AutoCompleteDefaultSlotOptions) + return getFirstSlotVNodeWithTypedProps( + 'default', + defaultSlot, + { + handleInput: this.handleInput, + handleFocus: this.handleFocus, + handleBlur: this.handleBlur, + value: this.mergedValue + } + ) } const { mergedTheme } = this return ( diff --git a/src/auto-complete/src/interface.ts b/src/auto-complete/src/interface.ts index f6b280173..bed1b7443 100644 --- a/src/auto-complete/src/interface.ts +++ b/src/auto-complete/src/interface.ts @@ -23,10 +23,9 @@ export interface AutoCompleteInst { blur: () => void } -export interface AutoCompleteDefaultSlotOptions { +export interface AutoCompleteDefaultSlotProps { handleInput: (value: string) => void handleFocus: (e: FocusEvent) => void handleBlur: (e: FocusEvent) => void - value: string - theme: string | null + value: string | null } diff --git a/src/avatar-group/index.ts b/src/avatar-group/index.ts index 25c5da246..6c0be4824 100644 --- a/src/avatar-group/index.ts +++ b/src/avatar-group/index.ts @@ -1,6 +1,10 @@ export { avatarGroupProps, default as NAvatarGroup } from './src/AvatarGroup' -export type { AvatarGroupOption, AvatarGroupProps } from './src/AvatarGroup' export type { - AvatarGroupAvatarSlotOptions, - AvatarGroupRestSlotOptions + AvatarGroupOption, + AvatarGroupProps, + AvatarGroupSlots +} from './src/AvatarGroup' +export type { + AvatarGroupAvatarSlotProps, + AvatarGroupRestSlotProps } from './src/interface' diff --git a/src/avatar-group/src/AvatarGroup.tsx b/src/avatar-group/src/AvatarGroup.tsx index ddd853f84..ea8519898 100644 --- a/src/avatar-group/src/AvatarGroup.tsx +++ b/src/avatar-group/src/AvatarGroup.tsx @@ -3,8 +3,8 @@ import type { ExtractPublicPropTypes } from '../../_utils' import type { Size } from '../../avatar/src/interface' import type { AvatarGroupTheme } from '../styles' import type { - AvatarGroupAvatarSlotOptions, - AvatarGroupRestSlotOptions + AvatarGroupAvatarSlotProps, + AvatarGroupRestSlotProps } from './interface' import { computed, @@ -13,7 +13,8 @@ import { h, type PropType, provide, - type SlotsType + type SlotsType, + type VNode } from 'vue' import { useConfig, useTheme } from '../../_mixins' import { useRtl } from '../../_mixins/use-rtl' @@ -46,9 +47,9 @@ export const avatarGroupProps = { export type AvatarGroupProps = ExtractPublicPropTypes export interface AvatarGroupSlots { - avatar?: (info: AvatarGroupAvatarSlotOptions) => any - rest?: (info: AvatarGroupRestSlotOptions) => any - default?: any + avatar?: (props: AvatarGroupAvatarSlotProps) => VNode[] + rest?: (props: AvatarGroupRestSlotProps) => VNode[] + default?: () => VNode[] } export default defineComponent({ diff --git a/src/avatar-group/src/interface.ts b/src/avatar-group/src/interface.ts index 54a2bd3b1..825f56040 100644 --- a/src/avatar-group/src/interface.ts +++ b/src/avatar-group/src/interface.ts @@ -1,8 +1,10 @@ -export interface AvatarGroupAvatarSlotOptions { - option: Record +import type { AvatarGroupOption } from './AvatarGroup' + +export interface AvatarGroupAvatarSlotProps { + option: AvatarGroupOption } -export interface AvatarGroupRestSlotOptions { - options: Array +export interface AvatarGroupRestSlotProps { + options: Array rest: number } diff --git a/src/avatar/src/Avatar.tsx b/src/avatar/src/Avatar.tsx index ff07efa82..84473833e 100644 --- a/src/avatar/src/Avatar.tsx +++ b/src/avatar/src/Avatar.tsx @@ -13,6 +13,7 @@ import { type PropType, ref, type SlotsType, + type VNode, type VNodeChild, watch, watchEffect @@ -67,9 +68,9 @@ export const avatarProps = { export type AvatarProps = ExtractPublicPropTypes export interface AvatarSlots { - default: any - placeholder: any - fallback: any + default?: () => VNode[] + placeholder?: () => VNode[] + fallback?: () => VNode[] } export default defineComponent({ diff --git a/src/breadcrumb/src/BreadcrumbItem.tsx b/src/breadcrumb/src/BreadcrumbItem.tsx index 25d2c248e..ee404eb08 100644 --- a/src/breadcrumb/src/BreadcrumbItem.tsx +++ b/src/breadcrumb/src/BreadcrumbItem.tsx @@ -5,7 +5,8 @@ import { h, inject, type PropType, - type SlotsType + type SlotsType, + type VNode } from 'vue' import { resolveSlot, warn } from '../../_utils' import { useBrowserLocation } from '../../_utils/composable/use-browser-location' @@ -26,8 +27,8 @@ export type BreadcrumbItemProps = Partial< > export interface BreadcrumbItemSlots { - default?: any - separator?: any + default?: () => VNode[] + separator?: () => VNode[] } export default defineComponent({ diff --git a/src/button/src/Button.tsx b/src/button/src/Button.tsx index c7d82faf8..464782c66 100644 --- a/src/button/src/Button.tsx +++ b/src/button/src/Button.tsx @@ -16,6 +16,7 @@ import { type PropType, ref, type SlotsType, + type VNode, type VNodeChild, watchEffect } from 'vue' @@ -97,8 +98,8 @@ export const buttonProps = { export type ButtonProps = ExtractPublicPropTypes export interface ButtonSlots { - default?: any - icon?: any + default?: () => VNode[] + icon?: () => VNode[] } const Button = defineComponent({ diff --git a/src/calendar/src/Calendar.tsx b/src/calendar/src/Calendar.tsx index 0fae9d49e..bdede35b0 100644 --- a/src/calendar/src/Calendar.tsx +++ b/src/calendar/src/Calendar.tsx @@ -2,8 +2,8 @@ import type { ThemeProps } from '../../_mixins' import type { ExtractPublicPropTypes, MaybeArray } from '../../_utils' import type { CalendarTheme } from '../styles' import type { - CalendarDefaultSlotOptions, - CalendarHeaderSlotOptions, + CalendarDefaultSlotProps, + CalendarHeaderSlotProps, DateItem, OnPanelChange, OnUpdateValue @@ -26,12 +26,13 @@ import { type PropType, ref, type SlotsType, - toRef + toRef, + type VNode } from 'vue' import { NBaseIcon } from '../../_internal' import { ChevronLeftIcon, ChevronRightIcon } from '../../_internal/icons' import { useConfig, useLocale, useTheme, useThemeClass } from '../../_mixins' -import { call, resolveSlotWithProps } from '../../_utils' +import { call, resolveSlotWithTypedProps } from '../../_utils' import { NButton } from '../../button' import { NButtonGroup } from '../../button-group' import { dateArray } from '../../date-picker/src/utils' @@ -54,8 +55,8 @@ export const calendarProps = { export type CalendarProps = ExtractPublicPropTypes export interface CalendarSlots { - default?: (props: CalendarDefaultSlotOptions) => any - header?: (props: CalendarHeaderSlotOptions) => any + default?: (props: CalendarDefaultSlotProps) => VNode[] + header?: (props: CalendarHeaderSlotProps) => VNode[] } export default defineComponent({ @@ -230,7 +231,7 @@ export default defineComponent({ >
- {resolveSlotWithProps( + {resolveSlotWithTypedProps( $slots.header, { year, month: calendarMonth }, () => { diff --git a/src/calendar/src/interface.ts b/src/calendar/src/interface.ts index 07ff430b5..38c88c1c5 100644 --- a/src/calendar/src/interface.ts +++ b/src/calendar/src/interface.ts @@ -8,13 +8,13 @@ export interface DateItem { export type OnPanelChange = (info: { year: number, month: number }) => void -export interface CalendarDefaultSlotOptions { +export interface CalendarDefaultSlotProps { year: number month: number date: number } -export interface CalendarHeaderSlotOptions { +export interface CalendarHeaderSlotProps { year: number month: number } diff --git a/src/card/src/Card.tsx b/src/card/src/Card.tsx index e7d836538..d3018e0c5 100644 --- a/src/card/src/Card.tsx +++ b/src/card/src/Card.tsx @@ -9,6 +9,7 @@ import { h, type PropType, type SlotsType, + type VNode, type VNodeChild } from 'vue' import { NBaseClose } from '../../_internal' @@ -73,12 +74,12 @@ export const cardProps = { export type CardProps = ExtractPublicPropTypes export interface CardSlots { - default?: any - cover?: any - header?: any - 'header-extra'?: any - footer?: any - action?: any + default?: () => VNode[] + cover?: () => VNode[] + header?: () => VNode[] + 'header-extra'?: () => VNode[] + footer?: () => VNode[] + action?: () => VNode[] } export default defineComponent({ diff --git a/src/carousel/index.ts b/src/carousel/index.ts index 686633df4..a4f2f3246 100644 --- a/src/carousel/index.ts +++ b/src/carousel/index.ts @@ -2,7 +2,7 @@ export { carouselProps, default as NCarousel } from './src/Carousel' export type { CarouselProps, CarouselSlots } from './src/Carousel' export { default as NCarouselItem } from './src/CarouselItem' export type { - CarouselArrowSlotOptions, - CarouselDotSlotOptions, + CarouselArrowSlotProps, + CarouselDotSlotProps, CarouselInst } from './src/interface' diff --git a/src/carousel/src/Carousel.tsx b/src/carousel/src/Carousel.tsx index bab37b5e8..7b5d589cd 100644 --- a/src/carousel/src/Carousel.tsx +++ b/src/carousel/src/Carousel.tsx @@ -11,8 +11,8 @@ import type { ExtractPublicPropTypes } from '../../_utils' import type { CarouselTheme } from '../styles' import type { ArrowScopedSlotProps, - CarouselArrowSlotOptions, - CarouselDotSlotOptions, + CarouselArrowSlotProps, + CarouselDotSlotProps, CarouselInst, DotScopedSlotProps, Size @@ -40,7 +40,7 @@ import { } from 'vue' import { VResizeObserver } from 'vueuc' import { useConfig, useTheme, useThemeClass } from '../../_mixins' -import { flatten, keep, resolveSlotWithProps } from '../../_utils' +import { flatten, keep, resolveSlotWithTypedProps } from '../../_utils' import { carouselLight } from '../styles' import NCarouselArrow from './CarouselArrow' import { @@ -149,9 +149,9 @@ export const carouselProps = { export type CarouselProps = ExtractPublicPropTypes export interface CarouselSlots { - default?: () => any - arrow?: (info: CarouselArrowSlotOptions) => any - dots?: (info: CarouselDotSlotOptions) => any + default?: () => VNode[] + arrow?: (props: CarouselArrowSlotProps) => VNode[] + dots?: (props: CarouselDotSlotProps) => VNode[] } // only one carousel is allowed to trigger touch globally @@ -1041,7 +1041,7 @@ export default defineComponent({ {this.showDots && dotSlotProps.total > 1 - && resolveSlotWithProps(dotsSlot, dotSlotProps, () => [ + && resolveSlotWithTypedProps(dotsSlot, dotSlotProps, () => [ ])} {showArrow - && resolveSlotWithProps(arrowSlot, arrowSlotProps, () => [ + && resolveSlotWithTypedProps(arrowSlot, arrowSlotProps, () => [ ])}
diff --git a/src/carousel/src/interface.ts b/src/carousel/src/interface.ts index 5b9d8d42b..a5f75f96e 100644 --- a/src/carousel/src/interface.ts +++ b/src/carousel/src/interface.ts @@ -26,7 +26,7 @@ export interface Size { height: number } -export interface CarouselArrowSlotOptions { +export interface CarouselArrowSlotProps { total: number currentIndex: number to: (index: number) => void @@ -34,7 +34,7 @@ export interface CarouselArrowSlotOptions { next: () => void } -export interface CarouselDotSlotOptions { +export interface CarouselDotSlotProps { total: number currentIndex: number to: (index: number) => void diff --git a/src/cascader/src/Cascader.tsx b/src/cascader/src/Cascader.tsx index b4138a989..d2553cacc 100644 --- a/src/cascader/src/Cascader.tsx +++ b/src/cascader/src/Cascader.tsx @@ -195,10 +195,10 @@ export const cascaderProps = { export type CascaderProps = ExtractPublicPropTypes export interface CascaderSlots { - action?: any - arrow?: any - empty?: any - 'not-found'?: any + action?: () => VNode[] + arrow?: () => VNode[] + empty?: () => VNode[] + 'not-found'?: () => VNode[] } export default defineComponent({ diff --git a/src/collapse/demos/enUS/index.demo-entry.md b/src/collapse/demos/enUS/index.demo-entry.md index 90775f0aa..a0bb7db21 100644 --- a/src/collapse/demos/enUS/index.demo-entry.md +++ b/src/collapse/demos/enUS/index.demo-entry.md @@ -46,14 +46,15 @@ trigger-areas.vue | Name | Parameters | Description | | --- | --- | --- | -| default | `()` | The contents of the collapsible panel. | | arrow | `(props: { collapsed: boolean })` | Custom icons for folding panels. | +| default | `()` | The contents of the collapsible panel. | +| header | `(props: { collapsed: boolean })` | The content of the header of the collapsed panel node. | ### CollapseItem Slots | Name | Parameters | Description | | --- | --- | --- | +| arrow | `(props: { collapsed: boolean })` | The custom icon of the node header of the collapsible panel. | | default | `()` | The contents of the collapsible panel node. | | header | `(props: { collapsed: boolean })` | The content of the header of the collapsed panel node. | | header-extra | `(props: { collapsed: boolean })` | The extra content of the header of the collapsed panel node. | -| arrow | `(props: { collapsed: boolean })` | The custom icon of the node header of the collapsible panel. | diff --git a/src/collapse/demos/zhCN/index.demo-entry.md b/src/collapse/demos/zhCN/index.demo-entry.md index b4a7483e6..5f5f2d02b 100644 --- a/src/collapse/demos/zhCN/index.demo-entry.md +++ b/src/collapse/demos/zhCN/index.demo-entry.md @@ -45,16 +45,17 @@ rtl-debug.vue ### Collapse Slots -| 名称 | 参数 | 说明 | -| ------- | --------------------------------- | -------------------- | -| default | `()` | 折叠面板的内容 | -| arrow | `(props: { collapsed: boolean })` | 折叠面板的自定义图标 | +| 名称 | 参数 | 说明 | +| --- | --- | --- | +| arrow | `(props: { collapsed: boolean })` | 折叠面板的自定义图标 | +| default | `()` | 折叠面板的内容 | +| header-extra | `(props: { collapsed: boolean })` | 折叠面板节点头部的额外内容 | ### CollapseItem Slots | 名称 | 参数 | 说明 | | --- | --- | --- | +| arrow | `(props: { collapsed: boolean })` | 折叠面板节点头部的自定义图标 | | default | `()` | 折叠面板节点的内容 | | header | `(props: { collapsed: boolean })` | 折叠面板节点头部的内容 | | header-extra | `(props: { collapsed: boolean })` | 折叠面板节点头部的额外内容 | -| arrow | `(props: { collapsed: boolean })` | 折叠面板节点头部的自定义图标 | diff --git a/src/collapse/index.ts b/src/collapse/index.ts index 97afb1d04..658ac6add 100644 --- a/src/collapse/index.ts +++ b/src/collapse/index.ts @@ -3,8 +3,8 @@ export type { CollapseProps, CollapseSlots } from './src/Collapse' export { collapseItemProps, default as NCollapseItem } from './src/CollapseItem' export type { CollapseItemProps, CollapseItemSlots } from './src/CollapseItem' export type { - CollapseArrowSlotOptions, - CollapseItemArrowSlotOptions, - CollapseItemHeaderExtraSlotOptions, - CollapseItemHeaderSlotOptions + CollapseArrowSlotProps, + CollapseItemArrowSlotProps, + CollapseItemHeaderExtraSlotProps, + CollapseItemHeaderSlotProps } from './src/interface' diff --git a/src/collapse/src/Collapse.tsx b/src/collapse/src/Collapse.tsx index 951fd26f4..d80eb40b1 100644 --- a/src/collapse/src/Collapse.tsx +++ b/src/collapse/src/Collapse.tsx @@ -1,7 +1,8 @@ import type { ThemeProps } from '../../_mixins' import type { ExtractPublicPropTypes, MaybeArray } from '../../_utils' import type { - CollapseArrowSlotOptions, + CollapseArrowSlotProps, + CollapseItemHeaderExtraSlotProps, HeaderClickInfo, OnItemHeaderClick, OnItemHeaderClickImpl, @@ -19,8 +20,8 @@ import { provide, type Ref, ref, - type Slots, - type SlotsType + type SlotsType, + type VNode } from 'vue' import { useConfig, useTheme, useThemeClass } from '../../_mixins' import { useRtl } from '../../_mixins/use-rtl' @@ -85,15 +86,16 @@ export const collapseProps = { export type CollapseProps = ExtractPublicPropTypes export interface CollapseSlots { - default?: any - arrow?: (props: CollapseArrowSlotOptions) => any + default?: () => VNode[] + arrow?: (props: CollapseArrowSlotProps) => VNode[] + 'header-extra'?: (props: CollapseItemHeaderExtraSlotProps) => VNode[] } export interface NCollapseInjection { props: ExtractPropTypes expandedNamesRef: Ref | null> mergedClsPrefixRef: Ref - slots: Slots + slots: CollapseSlots toggleItem: ( collapse: boolean, name: string | number, diff --git a/src/collapse/src/CollapseItem.tsx b/src/collapse/src/CollapseItem.tsx index 5d629e5d8..5df0886b5 100644 --- a/src/collapse/src/CollapseItem.tsx +++ b/src/collapse/src/CollapseItem.tsx @@ -1,12 +1,20 @@ import type { ExtractPublicPropTypes } from '../../_utils' import type { - CollapseItemArrowSlotOptions, - CollapseItemHeaderExtraSlotOptions, - CollapseItemHeaderSlotOptions + CollapseItemArrowSlotProps, + CollapseItemHeaderExtraSlotProps, + CollapseItemHeaderSlotProps } from './interface' import { createId, happensIn } from 'seemly' import { useMemo } from 'vooks' -import { computed, defineComponent, h, inject, type PropType, toRef } from 'vue' +import { + computed, + defineComponent, + h, + inject, + type PropType, + toRef, + type VNode +} from 'vue' import { NBaseIcon } from '../../_internal' import { ChevronLeftIcon as ArrowLeftIcon, @@ -32,10 +40,10 @@ export const collapseItemProps = { export type CollapseItemProps = ExtractPublicPropTypes export interface CollapseItemSlots { - default?: any - header?: (props: CollapseItemHeaderSlotOptions) => any - 'header-extra'?: (props: CollapseItemHeaderExtraSlotOptions) => any - arrow?: (props: CollapseItemArrowSlotOptions) => any + default?: () => VNode[] + header?: (props: CollapseItemHeaderSlotProps) => VNode[] + 'header-extra'?: (props: CollapseItemHeaderExtraSlotProps) => VNode[] + arrow?: (props: CollapseItemArrowSlotProps) => VNode[] } export default defineComponent({ @@ -160,14 +168,8 @@ export default defineComponent({ {resolveSlotWithProps(arrowSlot, { collapsed }, () => [ {{ - default: - collapseSlots.expandIcon - ?? (() => - this.rtlEnabled ? ( - - ) : ( - - )) + default: () => + this.rtlEnabled ? : }} ])} diff --git a/src/collapse/src/interface.ts b/src/collapse/src/interface.ts index 87cb6c1d5..6b450438f 100644 --- a/src/collapse/src/interface.ts +++ b/src/collapse/src/interface.ts @@ -37,18 +37,18 @@ export interface HeaderClickInfo { event: MouseEvent } -export interface CollapseArrowSlotOptions { +export interface CollapseArrowSlotProps { collapsed: boolean } -export interface CollapseItemHeaderSlotOptions { +export interface CollapseItemHeaderSlotProps { collapsed: boolean } -export interface CollapseItemHeaderExtraSlotOptions { +export interface CollapseItemHeaderExtraSlotProps { collapsed: boolean } -export interface CollapseItemArrowSlotOptions { +export interface CollapseItemArrowSlotProps { collapsed: boolean } diff --git a/src/color-picker/src/ColorPicker.tsx b/src/color-picker/src/ColorPicker.tsx index 9c067af71..c02eb55f7 100644 --- a/src/color-picker/src/ColorPicker.tsx +++ b/src/color-picker/src/ColorPicker.tsx @@ -126,9 +126,9 @@ export const colorPickerProps = { export type ColorPickerProps = ExtractPublicPropTypes export interface ColorPickerSlots { - default?: any - label?: (color: string | null) => any - action?: any + default?: () => VNode[] + label?: (color: string | null) => VNode[] + action?: () => VNode[] } export default defineComponent({ @@ -709,7 +709,7 @@ export default defineComponent({ } }, render() { - const { $slots, mergedClsPrefix, onRender } = this + const { mergedClsPrefix, onRender } = this onRender?.() return (
- {{ - label: $slots.label - }} - + /> ) }} , diff --git a/src/color-picker/src/ColorPickerTrigger.tsx b/src/color-picker/src/ColorPickerTrigger.tsx index 7e15853e2..119f0ca93 100644 --- a/src/color-picker/src/ColorPickerTrigger.tsx +++ b/src/color-picker/src/ColorPickerTrigger.tsx @@ -1,9 +1,10 @@ import { type HSLA, toHslaString } from 'seemly' -import { defineComponent, h, inject, type PropType } from 'vue' +import { defineComponent, h, inject, type PropType, type SlotsType } from 'vue' import { colorPickerInjectionKey } from './context' export default defineComponent({ name: 'ColorPickerTrigger', + slots: Object as SlotsType>, props: { clsPrefix: { type: String, diff --git a/src/color-picker/src/context.ts b/src/color-picker/src/context.ts index d0e0430a9..4f4a9fb69 100644 --- a/src/color-picker/src/context.ts +++ b/src/color-picker/src/context.ts @@ -1,11 +1,12 @@ -import type { ComputedRef, Ref, Slots } from 'vue' +import type { ComputedRef, Ref } from 'vue' import type { MergedTheme } from '../../_mixins' import type { ColorPickerTheme } from '../styles' +import type { ColorPickerSlots } from './ColorPicker' import type { RenderLabel } from './interface' import { createInjectionKey } from '../../_utils' export const colorPickerInjectionKey = createInjectionKey<{ themeRef: ComputedRef> - colorPickerSlots: Slots + colorPickerSlots: ColorPickerSlots renderLabelRef: Ref }>('n-color-picker') diff --git a/src/data-table/src/interface.ts b/src/data-table/src/interface.ts index 064280b12..b965367cd 100644 --- a/src/data-table/src/interface.ts +++ b/src/data-table/src/interface.ts @@ -5,7 +5,7 @@ import type { HTMLAttributes, PropType, Ref, - Slots, + VNode, VNodeChild } from 'vue' import type { VirtualListInst } from 'vueuc' @@ -179,9 +179,9 @@ export const dataTableProps = { } as const export interface DataTableSlots { - default?: any - empty?: any - loading?: any + default?: () => VNode[] + empty?: () => VNode[] + loading?: () => VNode[] } export type FilterOptionValue = string | number @@ -363,7 +363,7 @@ export type DataTableSelectionOptions = Array< > export interface DataTableInjection { props: DataTableSetupProps - slots: Slots + slots: DataTableSlots indentRef: Ref childTriggerColIndexRef: Ref componentId: string diff --git a/src/date-picker/demos/enUS/index.demo-entry.md b/src/date-picker/demos/enUS/index.demo-entry.md index 049b2040b..901337370 100644 --- a/src/date-picker/demos/enUS/index.demo-entry.md +++ b/src/date-picker/demos/enUS/index.demo-entry.md @@ -220,9 +220,9 @@ panel.vue | 名称 | 参数 | 说明 | 版本 | | --- | --- | --- | --- | -| now | `(props: { onNow: () => void, text: string })` | Now button of the panel. | 2.40.0 | | clear | `(props: { onClear: () => void, text: string })` | Clear button of the panel. | 2.40.0 | | confirm | `(props: { onConfirm: () => void, disabled: boolean, text: string })` | Confirm button of the panel. | 2.40.0 | +| now | `(props: { onNow: () => void, text: string })` | Now button of the panel. | 2.40.0 | ### DatePicker Methods diff --git a/src/date-picker/demos/zhCN/index.demo-entry.md b/src/date-picker/demos/zhCN/index.demo-entry.md index 5367b7994..0fa055211 100644 --- a/src/date-picker/demos/zhCN/index.demo-entry.md +++ b/src/date-picker/demos/zhCN/index.demo-entry.md @@ -220,9 +220,9 @@ form-debug.vue | 名称 | 参数 | 说明 | 版本 | | --- | --- | --- | --- | -| now | `(props: { onNow: () => void, text: string })` | 面板的此刻按钮 | 2.40.0 | | clear | `(props: { onClear: () => void, text: string })` | 面板的清除按钮 | 2.40.0 | | confirm | `(props: { onConfirm: () => void, disabled: boolean, text: string })` | 面板的确认按钮 | 2.40.0 | +| now | `(props: { onNow: () => void, text: string })` | 面板的此刻按钮 | 2.40.0 | ### DatePicker Methods diff --git a/src/date-picker/src/DatePicker.tsx b/src/date-picker/src/DatePicker.tsx index 7a98400e4..1bc114f72 100644 --- a/src/date-picker/src/DatePicker.tsx +++ b/src/date-picker/src/DatePicker.tsx @@ -70,13 +70,20 @@ import { export type DatePickerSetupProps = ExtractPropTypes export interface DatePickerSlots { - 'date-icon'?: any - footer?: any - 'next-month'?: any - 'next-year'?: any - 'prev-month'?: any - 'prev-year'?: any - separator?: any + 'date-icon'?: () => VNode[] + footer?: () => VNode[] + 'next-month'?: () => VNode[] + 'next-year'?: () => VNode[] + 'prev-month'?: () => VNode[] + 'prev-year'?: () => VNode[] + separator?: () => VNode[] + confirm?: (props: { + onConfirm: () => void + disabled: boolean + text: string + }) => VNode[] + clear?: (props: { onClear: () => void, text: string }) => VNode[] + now?: (props: { onNow: () => void, text: string }) => VNode[] } export default defineComponent({ diff --git a/src/date-picker/src/interface.ts b/src/date-picker/src/interface.ts index 26c982c6a..b0defadb6 100644 --- a/src/date-picker/src/interface.ts +++ b/src/date-picker/src/interface.ts @@ -1,4 +1,4 @@ -import type { Ref, Slots, UnwrapNestedRefs } from 'vue' +import type { Ref, UnwrapNestedRefs } from 'vue' import type { VirtualListInst } from 'vueuc' import type { ScrollbarInst } from '../../_internal' import type { MergedTheme } from '../../_mixins' @@ -11,6 +11,7 @@ import type { } from '../../time-picker/src/interface' import type { TimePickerProps } from '../../time-picker/src/TimePicker' import type { DatePickerTheme } from '../styles/light' +import type { DatePickerSlots } from './DatePicker' import type { dualCalendarValidation, uniCalendarValidation @@ -135,7 +136,7 @@ export type DatePickerInjection = { monthFormatRef: Ref yearFormatRef: Ref quarterFormatRef: Ref - datePickerSlots: Slots + datePickerSlots: DatePickerSlots yearRangeRef: Ref<[number, number]> } & ReturnType & ReturnType diff --git a/src/date-picker/src/panel/date.tsx b/src/date-picker/src/panel/date.tsx index 94390187b..59f54f99a 100644 --- a/src/date-picker/src/panel/date.tsx +++ b/src/date-picker/src/panel/date.tsx @@ -44,8 +44,14 @@ export default defineComponent({ return useCalendar(props, props.type) }, render() { - const { mergedClsPrefix, mergedTheme, shortcuts, onRender, $slots, type } - = this + const { + mergedClsPrefix, + mergedTheme, + shortcuts, + onRender, + datePickerSlots, + type + } = this onRender?.() return (
- {resolveSlot($slots['prev-year'], () => [])} + {resolveSlot(datePickerSlots['prev-year'], () => [ + + ])}
- {resolveSlot($slots['prev-month'], () => [])} + {resolveSlot(datePickerSlots['prev-month'], () => [ + + ])}
- {resolveSlot($slots['next-month'], () => [])} + {resolveSlot(datePickerSlots['next-month'], () => [ + + ])}
- {resolveSlot($slots['next-year'], () => [])} + {resolveSlot(datePickerSlots['next-year'], () => [ + + ])}
diff --git a/src/date-picker/src/panel/daterange.tsx b/src/date-picker/src/panel/daterange.tsx index 62693541b..74b6bf18f 100644 --- a/src/date-picker/src/panel/daterange.tsx +++ b/src/date-picker/src/panel/daterange.tsx @@ -32,7 +32,13 @@ export default defineComponent({ return useDualCalendar(props, 'daterange') }, render() { - const { mergedClsPrefix, mergedTheme, shortcuts, onRender, $slots } = this + const { + mergedClsPrefix, + mergedTheme, + shortcuts, + onRender, + datePickerSlots + } = this onRender?.() return ( @@ -57,13 +63,17 @@ export default defineComponent({ class={`${mergedClsPrefix}-date-panel-month__fast-prev`} onClick={this.startCalendarPrevYear} > - {resolveSlot($slots['prev-year'], () => [])} + {resolveSlot(datePickerSlots['prev-year'], () => [ + + ])}
- {resolveSlot($slots['prev-month'], () => [])} + {resolveSlot(datePickerSlots['prev-month'], () => [ + + ])}
- {resolveSlot($slots['next-month'], () => [])} + {resolveSlot(datePickerSlots['next-month'], () => [ + + ])}
- {resolveSlot($slots['next-year'], () => [])} + {resolveSlot(datePickerSlots['next-year'], () => [ + + ])}
@@ -148,13 +162,17 @@ export default defineComponent({ class={`${mergedClsPrefix}-date-panel-month__fast-prev`} onClick={this.endCalendarPrevYear} > - {resolveSlot($slots['prev-year'], () => [])} + {resolveSlot(datePickerSlots['prev-year'], () => [ + + ])}
- {resolveSlot($slots['prev-month'], () => [])} + {resolveSlot(datePickerSlots['prev-month'], () => [ + + ])}
- {resolveSlot($slots['next-month'], () => [])} + {resolveSlot(datePickerSlots['next-month'], () => [ + + ])}
- {resolveSlot($slots['next-year'], () => [])} + {resolveSlot(datePickerSlots['next-year'], () => [ + + ])}
@@ -262,7 +284,7 @@ export default defineComponent({
{this.actions?.includes('clear') ? resolveSlotWithProps( - $slots.clear, + datePickerSlots.clear, { onClear: this.handleClearClick, text: this.locale.clear @@ -281,7 +303,7 @@ export default defineComponent({ : null} {this.actions?.includes('confirm') ? resolveSlotWithProps( - $slots.confirm, + datePickerSlots.confirm, { onConfirm: this.handleConfirmClick, disabled: this.isRangeInvalid || this.isSelecting, diff --git a/src/date-picker/src/panel/datetime.tsx b/src/date-picker/src/panel/datetime.tsx index 802631b50..5b4aca68c 100644 --- a/src/date-picker/src/panel/datetime.tsx +++ b/src/date-picker/src/panel/datetime.tsx @@ -36,8 +36,8 @@ export default defineComponent({ mergedTheme, shortcuts, timePickerProps, - onRender, - $slots + datePickerSlots, + onRender } = this onRender?.() @@ -91,13 +91,17 @@ export default defineComponent({ class={`${mergedClsPrefix}-date-panel-month__fast-prev`} onClick={this.prevYear} > - {resolveSlot($slots['prev-year'], () => [])} + {resolveSlot(datePickerSlots['prev-year'], () => [ + + ])}
- {resolveSlot($slots['prev-month'], () => [])} + {resolveSlot(datePickerSlots['prev-month'], () => [ + + ])}
- {resolveSlot($slots['next-month'], () => [])} + {resolveSlot(datePickerSlots['next-month'], () => [ + + ])}
- {resolveSlot($slots['next-year'], () => [])} + {resolveSlot(datePickerSlots['next-year'], () => [ + + ])}
@@ -199,7 +207,7 @@ export default defineComponent({
{this.actions?.includes('clear') ? resolveSlotWithProps( - this.$slots.clear, + this.datePickerSlots.clear, { onClear: this.clearSelectedDateTime, text: this.locale.clear @@ -218,7 +226,7 @@ export default defineComponent({ : null} {this.actions?.includes('now') ? resolveSlotWithProps( - $slots.now, + datePickerSlots.now, { onNow: this.handleNowClick, text: this.locale.now @@ -237,7 +245,7 @@ export default defineComponent({ : null} {this.actions?.includes('confirm') ? resolveSlotWithProps( - $slots.confirm, + datePickerSlots.confirm, { onConfirm: this.handleConfirmClick, disabled: this.isDateInvalid, diff --git a/src/date-picker/src/panel/datetimerange.tsx b/src/date-picker/src/panel/datetimerange.tsx index e5d618f72..e6a719a1b 100644 --- a/src/date-picker/src/panel/datetimerange.tsx +++ b/src/date-picker/src/panel/datetimerange.tsx @@ -40,7 +40,7 @@ export default defineComponent({ shortcuts, timePickerProps, onRender, - $slots + datePickerSlots } = this onRender?.() @@ -132,13 +132,17 @@ export default defineComponent({ class={`${mergedClsPrefix}-date-panel-month__fast-prev`} onClick={this.startCalendarPrevYear} > - {resolveSlot($slots['prev-year'], () => [])} + {resolveSlot(datePickerSlots['prev-year'], () => [ + + ])}
- {resolveSlot($slots['prev-month'], () => [])} + {resolveSlot(datePickerSlots['prev-month'], () => [ + + ])}
- {resolveSlot($slots['next-month'], () => [])} + {resolveSlot(datePickerSlots['next-month'], () => [ + + ])}
- {resolveSlot($slots['next-year'], () => [])} + {resolveSlot(datePickerSlots['next-year'], () => [ + + ])}
@@ -233,13 +241,17 @@ export default defineComponent({ class={`${mergedClsPrefix}-date-panel-month__fast-prev`} onClick={this.endCalendarPrevYear} > - {resolveSlot($slots['prev-year'], () => [])} + {resolveSlot(datePickerSlots['prev-year'], () => [ + + ])}
- {resolveSlot($slots['prev-month'], () => [])} + {resolveSlot(datePickerSlots['prev-month'], () => [ + + ])}
- {resolveSlot($slots['next-month'], () => [])} + {resolveSlot(datePickerSlots['next-month'], () => [ + + ])}
- {resolveSlot($slots['next-year'], () => [])} + {resolveSlot(datePickerSlots['next-year'], () => [ + + ])}
@@ -357,7 +373,7 @@ export default defineComponent({
{this.actions?.includes('clear') ? resolveSlotWithProps( - $slots.clear, + datePickerSlots.clear, { onClear: this.handleClearClick, text: this.locale.clear @@ -376,7 +392,7 @@ export default defineComponent({ : null} {this.actions?.includes('confirm') ? resolveSlotWithProps( - $slots.confirm, + datePickerSlots.confirm, { onConfirm: this.handleConfirmClick, disabled: this.isRangeInvalid || this.isSelecting, diff --git a/src/date-picker/src/panel/month.tsx b/src/date-picker/src/panel/month.tsx index 71c48a937..b63bd6c75 100644 --- a/src/date-picker/src/panel/month.tsx +++ b/src/date-picker/src/panel/month.tsx @@ -8,7 +8,7 @@ import { defineComponent, h, onMounted, type PropType, type VNode } from 'vue' import { VirtualList } from 'vueuc' import { NBaseFocusDetector, NScrollbar } from '../../../_internal' import { useLocale } from '../../../_mixins' -import { resolveSlotWithProps } from '../../../_utils' +import { resolveSlotWithTypedProps, resolveWrappedSlot } from '../../../_utils' import { NButton, NxButton } from '../../../button' import { MONTH_ITEM_HEIGHT } from '../config' import { @@ -212,13 +212,11 @@ export default defineComponent({
) : null}
- {this.datePickerSlots.footer ? ( -
- {{ - default: this.datePickerSlots.footer - }} -
- ) : null} + {resolveWrappedSlot(this.datePickerSlots.footer, (children) => { + return children ? ( +
{children}
+ ) : null + })} {actions?.length || shortcuts ? (
@@ -245,8 +243,8 @@ export default defineComponent({
{actions?.includes('clear') - ? resolveSlotWithProps( - this.$slots.now, + ? resolveSlotWithTypedProps( + this.datePickerSlots.clear, { onClear: this.handleClearClick, text: this.locale.clear @@ -264,8 +262,8 @@ export default defineComponent({ ) : null} {actions?.includes('now') - ? resolveSlotWithProps( - this.$slots.now, + ? resolveSlotWithTypedProps( + this.datePickerSlots.now, { onNow: this.handleNowClick, text: this.locale.now @@ -283,8 +281,8 @@ export default defineComponent({ ) : null} {actions?.includes('confirm') - ? resolveSlotWithProps( - this.$slots.confirm, + ? resolveSlotWithTypedProps( + this.datePickerSlots.confirm, { onConfirm: this.handleConfirmClick, disabled: this.isDateInvalid, diff --git a/src/date-picker/src/panel/monthrange.tsx b/src/date-picker/src/panel/monthrange.tsx index 670114924..ba6e7bc28 100644 --- a/src/date-picker/src/panel/monthrange.tsx +++ b/src/date-picker/src/panel/monthrange.tsx @@ -7,14 +7,17 @@ import { h, onMounted, type PropType, - renderSlot, type VNode, watchEffect } from 'vue' import { VirtualList } from 'vueuc' import { NBaseFocusDetector, NScrollbar } from '../../../_internal' import { useLocale } from '../../../_mixins' -import { resolveSlotWithProps, warnOnce } from '../../../_utils' +import { + resolveSlotWithTypedProps, + resolveWrappedSlot, + warnOnce +} from '../../../_utils' import { NxButton } from '../../../button' import { MONTH_ITEM_HEIGHT } from '../config' import { @@ -270,11 +273,11 @@ export default defineComponent({ ) : null}
- {this.datePickerSlots.footer ? ( -
- {renderSlot(this.datePickerSlots, 'footer')} -
- ) : null} + {resolveWrappedSlot(this.datePickerSlots.footer, (children) => { + return children ? ( +
{children}
+ ) : null + })} {this.actions?.length || shortcuts ? (
@@ -302,8 +305,8 @@ export default defineComponent({
{this.actions?.includes('clear') - ? resolveSlotWithProps( - this.$slots.clear, + ? resolveSlotWithTypedProps( + this.datePickerSlots.clear, { onClear: this.handleClearClick, text: this.locale.clear @@ -321,8 +324,8 @@ export default defineComponent({ ) : null} {this.actions?.includes('confirm') - ? resolveSlotWithProps( - this.$slots.confirm, + ? resolveSlotWithTypedProps( + this.datePickerSlots.confirm, { disabled: this.isRangeInvalid, onConfirm: this.handleConfirmClick, diff --git a/src/descriptions/src/Descriptions.tsx b/src/descriptions/src/Descriptions.tsx index db33ab341..2a787a25e 100644 --- a/src/descriptions/src/Descriptions.tsx +++ b/src/descriptions/src/Descriptions.tsx @@ -60,8 +60,8 @@ export type DescriptionsProps = ExtractPublicPropTypes export type DescriptionProps = DescriptionsProps export interface DescriptionsSlots { - default?: any - header?: any + default?: () => VNode[] + header?: () => VNode[] } export default defineComponent({ diff --git a/src/descriptions/src/DescriptionsItem.ts b/src/descriptions/src/DescriptionsItem.ts index abef07512..f09d7fe93 100644 --- a/src/descriptions/src/DescriptionsItem.ts +++ b/src/descriptions/src/DescriptionsItem.ts @@ -3,7 +3,8 @@ import { type CSSProperties, defineComponent, type PropType, - type SlotsType + type SlotsType, + type VNode } from 'vue' import { DESCRIPTION_ITEM_FLAG } from './utils' @@ -24,8 +25,8 @@ export type DescriptionItemProps = ExtractPublicPropTypes< > export interface DescriptionItemSlots { - default?: any - label?: any + default?: () => VNode[] + label?: () => VNode[] } export default defineComponent({ diff --git a/src/dialog/src/Dialog.tsx b/src/dialog/src/Dialog.tsx index 9b339b19c..50d61cbb1 100644 --- a/src/dialog/src/Dialog.tsx +++ b/src/dialog/src/Dialog.tsx @@ -6,7 +6,8 @@ import { type CSSProperties, defineComponent, h, - type SlotsType + type SlotsType, + type VNode } from 'vue' import { NBaseClose, NBaseIcon } from '../../_internal' import { @@ -36,11 +37,11 @@ const iconRenderMap = { } export interface DialogSlots { - action?: any - default?: any - header?: any - icon?: any - close?: any + action?: () => VNode[] + default?: () => VNode[] + header?: () => VNode[] + icon?: () => VNode[] + close?: () => VNode[] } export const NDialog = defineComponent({ diff --git a/src/drawer/demos/zhCN/dark-4-debug.demo.vue b/src/drawer/demos/zhCN/dark-4-debug.demo.vue index c591f77fe..0423ea5f8 100644 --- a/src/drawer/demos/zhCN/dark-4-debug.demo.vue +++ b/src/drawer/demos/zhCN/dark-4-debug.demo.vue @@ -20,7 +20,7 @@ export default defineComponent({ -