diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index b9134bee9df..786cfabdbd5 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -1,13 +1,108 @@ # CHANGELOG +## Pending + ### Feats +- `n-message-provider` add `closable` props, closes [#795](https://github.com/TuSimple/naive-ui/issues/795). - `n-layout` add `sider-placement` prop, closes [#566](https://github.com/TuSimple/naive-ui/issues/566). +### Fixes + +- Fix `n-avatar`'s scale value is incorrect while use v-show, closes [#779](https://github.com/TuSimple/naive-ui/issues/779). +- Fix `n-menu` show a blue background when click the menu on mobile phone, closes [#799](https://github.com/TuSimple/naive-ui/issues/799). +- Fix `n-select` filterable select breaks, closes [#510](https://github.com/TuSimple/naive-ui/issues/510). + +## 2.16.1 (2020-08-06) + +### Feats + +- `n-loading-bar` add `loading-bar-style` props, closes [#457](https://github.com/TuSimple/naive-ui/issues/457). +- `n-button` add `text-color` prop. +- `n-form` export `FormValidationError` type. +- `n-popconfirm` support not show action components, closes [#770](https://github.com/TuSimple/naive-ui/issues/770). + +### Fixes + +- Fix `n-slider` loss floating point decimal precision, closes [#751](https://github.com/TuSimple/naive-ui/issues/751). +- Fix `n-data-table` `onUpdatePage` and `onUpdatePageSize` not triggered while using jsx. +- Fix `n-progress`'s `percentage` prop default value doesn't work with different types. +- Fix `n-select` hide close icon when option is disabled. +- Fix `n-modal` can't be closed when using custom content, closes [#788](https://github.com/TuSimple/naive-ui/issues/788). + +## 2.16.0 (2021-08-02) + +### Breaking Changes + +- `useLoadingBar`'s `finish` method won't work if no `start` is called. +- `n-input`'s `type='input'` is renamed to `type='text'`. + +### Feats + +- `n-scrollbar` add `scrollbarWidth`, `scrollbarHeight` and `scrollbarBorderRadius` common theme variables, closes [#649](https://github.com/TuSimple/naive-ui/issues/649). +- `n-menu` doesn't should icon placeholder when `render-icon` returns falsy value, closes [#722](https://github.com/TuSimple/naive-ui/issues/722). +- `n-menu` add `render-extra` prop. +- `n-select` add `on-clear` prop. +- `n-form` add `disabled` prop, closes [#538](https://github.com/TuSimple/naive-ui/issues/538). +- `n-dynamic-tags` add `max` prop. + +### Fixes + +- Fix `n-dropdown` click exception when using v-for. +- Fix `n-modal` cannot customize classes when use preset, closes [#744](https://github.com/TuSimple/naive-ui/issues/744). +- Fix `n-cascader` menu width shifts in virtual scroll mode, closes [#728](https://github.com/TuSimple/naive-ui/issues/728). + +## 2.15.11 (2021-07-29) + +### Fixes + +- Fix `n-data-table` pagination's error. + +## 2.15.10 (2021-07-29) + +### Feats + +- `n-pagination` adds `prev` and `next` slots, ref [#648](https://github.com/TuSimple/naive-ui/issues/648). +- `n-tag` add `color` prop, closes [#693](https://github.com/TuSimple/naive-ui/issues/693). +- `n-dynamic-tags` add `color`, closes [#693](https://github.com/TuSimple/naive-ui/issues/693). +- `n-time-picker` optimization the now button logic, closes [#401](https://github.com/TuSimple/naive-ui/issues/401). +- `n-pagination` `PaginationInfo` add `itemCount` prop, closes [#585](https://github.com/TuSimple/naive-ui/issues/585). +- `n-select` add `on-clear` prop. + +### Fixes + +- Fix `n-message`'s `destroyAll` method doesn't work. +- Fix `n-timeline`'s header slot is invalid when using alone. +- Fix `n-select` incorrect style when props has `disabled` and `filterable`, closes [#698](https://github.com/TuSimple/naive-ui/issues/698). +- Fix `n-upload` operation buttons displayed when has `file-list` & `disabled` props, closes [#668](https://github.com/TuSimple/naive-ui/issues/668). + +## 2.15.9 (2021-07-28) + +### Feats + +- `n-message` add `destroyAll` method. +- `n-input-number` add `prefix`, `suffix` slots, closes [#609](https://github.com/TuSimple/naive-ui/issues/609). + +### Fixes + +- Fix `n-message` options' `duration` prop doesn't work. + +## 2.15.8 (2021-07-27) + +### Feats + +- `n-menu` add `expand-icon` prop, closes [#414](https://github.com/TuSimple/naive-ui/issues/414). +- `n-descriptions`, `n-descriptions-item` add `label-style` and `content-style` props, closes [#536](https://github.com/TuSimple/naive-ui/issues/536). + +### Fixes + +- Fix `n-data-table` the style penetration of the `n-spin`, closes [#663](https://github.com/TuSimple/naive-ui/issues/663). + ## 2.15.7 (2021-07-25) ### Feats +- `n-dropdown` add `show-arrow` prop, closes [#647](https://github.com/TuSimple/naive-ui/issues/647). - `n-time-picker` add `actions` prop, closes [#401](https://github.com/TuSimple/naive-ui/issues/401). - `n-mention` add `render-label` prop. - `n-switch` add `checked`, `unchecked` slots. @@ -81,7 +176,7 @@ - Fix `n-select` placeholder transition. - Fix `n-loading-bar` `useLoadingBar`'s return type can be undefined. - Fix `n-tag`'s `type` prop add `primary` type. -- Fix `n-dynamic-tag`'s `type` prop add `primary` type. +- Fix `n-dynamic-tags`'s `type` prop add `primary` type. ## 2.15.4 (2021-07-09) diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 87fd3e80e6e..18a111ea7da 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -1,13 +1,107 @@ # CHANGELOG +## Pending + ### Feats +- `n-message-provider` 新增 `closable` 属性,关闭 [#795](https://github.com/TuSimple/naive-ui/issues/795) - `n-layout` 增加 `sider-placement` 属性,关闭 [#566](https://github.com/TuSimple/naive-ui/issues/566) +### Fixes + +- 修复 `n-avatar` 的缩放在使用 `v-show` 时不正确,关闭 [#779](https://github.com/TuSimple/naive-ui/issues/779) +- 修复 `n-menu` 在手机端点击菜单的时候出现蓝色背景问题,关闭 [#799](https://github.com/TuSimple/naive-ui/issues/799) +- 修复 `n-select` 可过滤的选择器失效,关闭 [#510](https://github.com/TuSimple/naive-ui/issues/510) + +## 2.16.1 (2020-08-06) + +### Feats + +- `n-loading-bar` 新增 `loading-bar-style` 属性,关闭 [#457](https://github.com/TuSimple/naive-ui/issues/457) +- `n-button` 新增 `text-color` 属性 +- `n-form` 导出 `FormValidationError` 类型 +- `n-popconfirm` 支持不显示操作组件,关闭 [#770](https://github.com/TuSimple/naive-ui/issues/770) + +### Fixes + +- 修复 `n-slider` 丢失浮点数小数精度,关闭 [#751](https://github.com/TuSimple/naive-ui/issues/751) +- 修复 `n-data-table` `onUpdatePage` 和 `onUpdatePageSize` 在使用 jsx 时不触发的问题 +- 修复 `n-progress` 的 `percentage` 属性默认值不能适应多种类型 +- 修复 `n-select` 当选项禁用时未隐藏关闭图标 +- 修复 `n-modal` 使用自定义内容无法正常关闭,关闭 [#788](https://github.com/TuSimple/naive-ui/issues/788) + +## 2.16.0 (2021-08-02) + +### Breaking Changes + +- `useLoadingBar` 中 `finish` 方法只有在调用过 `start` 后才生效 +- `n-input` 的 `type='input'` 被重命名为 `type='text'` + +### Feats + +- `n-scrollbar` 增加 `scrollbarWidth`、`scrollbarHeight` 和 `scrollbarBorderRadius` 公共主题变量,关闭 [#649](https://github.com/TuSimple/naive-ui/issues/649) +- `n-menu` 在 `render-icon` 返回 falsy 值的时候不渲染 icon 的占位符,关闭 [#722](https://github.com/TuSimple/naive-ui/issues/722) +- `n-menu` 新增 `render-extra` 属性 +- `n-select` 新增 `on-clear` 属性 +- `n-form` 增加 `disabled` 属性,关闭 [#538](https://github.com/TuSimple/naive-ui/issues/538) +- `n-dynamic-tags` 新增 `max` 属性 + +### Fixes + +- 修复 `n-dropdown` 循环渲染时点击异常 +- 修复 `n-modal` 使用预设时无法自定义类,关闭 [#744](https://github.com/TuSimple/naive-ui/issues/744) +- 修复 `n-cascader` 的菜单虚拟滚动时宽度展示不一致问题,关闭 [#728](https://github.com/TuSimple/naive-ui/issues/728) + +## 2.15.11 (2021-07-29) + +### Fixes + +- 修复 `n-data-table` pagination 的报错 + +## 2.15.10 (2021-07-29) + +### Feats + +- `n-pagination` 新增 `prev`、`next` 插槽,有关 [#648](https://github.com/TuSimple/naive-ui/issues/648) +- `n-tag` 新增 `color`,关闭 [#693](https://github.com/TuSimple/naive-ui/issues/693) +- `n-dynamic-tags` 新增 `color`,关闭 [#693](https://github.com/TuSimple/naive-ui/issues/693) +- `n-time-picker` 优化 now 按钮的逻辑,关闭 [#401](https://github.com/TuSimple/naive-ui/issues/401) +- `n-pagination` `PaginationInfo` 增加 `itemCount` 属性,关闭 [#585](https://github.com/TuSimple/naive-ui/issues/585) + +### Fixes + +- 修复 `n-message` 的 `destroyAll` 方法不生效 +- 修复 `n-timeline` 的 header slot 单独使用无效的问题 +- 修复 `n-select` 当属性是 `disabled` 和 `filterable` 时样式错误, 关闭 [#698](https://github.com/TuSimple/naive-ui/issues/698) +- 修复 `n-upload` 拥有 `file-list` & `disabled` 属性时操作按钮仍然显示,关闭 [#668](https://github.com/TuSimple/naive-ui/issues/668) + +## 2.15.9 (2021-07-28) + +### Feats + +- `n-message` 增加 `destroyAll` 方法 +- `n-input-number` 增加 `prefix`、`suffix` slots, 关闭 [#609](https://github.com/TuSimple/naive-ui/issues/609) + +### Fixes + +- 修复 `n-message` 的 options 中 `duration` 配置无效 + +## 2.15.8 (2021-07-27) + +### Feats + +- `n-menu` 新增 `expand-icon` 属性, 关闭 [#414](https://github.com/TuSimple/naive-ui/issues/414) +- `n-descriptions`,`n-descriptions-item` 增加 `label-style` 和 `content-style` 属性,关闭 [#536](https://github.com/TuSimple/naive-ui/issues/536) + +### Fixes + +- 修复 `n-data-table` `n-spin`的样式穿透问题,关闭 [#663](https://github.com/TuSimple/naive-ui/issues/663) + ## 2.15.7 (2021-07-25) ### Feats +- `n-dropdown` 选项新增 `show-arrow`属性,关闭 [#647](https://github.com/TuSimple/naive-ui/issues/647) - `n-time-picker` 增加 `actions` 属性, 关闭 [#401](https://github.com/TuSimple/naive-ui/issues/401) - `n-mention` 新增 `render-label` 属性 - `n-switch` 增加 `checked`、`unchecked` 插槽 @@ -81,7 +175,7 @@ - 修复 `n-select` placeholder transition - 修复 `n-loading-bar` `useLoadingBar` 返回类型可能为 undefined - 修复 `n-tag` 的 `type` 增加 `primary` 类型 -- 修复 `n-dynamic-tag` 的 `type` 增加 `primary` 类型 +- 修复 `n-dynamic-tags` 的 `type` 增加 `primary` 类型 ## 2.15.4 (2021-07-09) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 11dd11f1042..0480704b15c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,6 +39,8 @@ npm run build:site - Don't add period `。` in any description in Chinese API tables (and any log of changelogs). - Add space bewteen Chinese and Latin charactors. - Don't use Chinese punctuation in English docs. +- Don't write changelogs in a released version. +- When rebase the branch, pay attention to whether it is placed in the released version. For Example: @@ -61,6 +63,18 @@ Chinese Changelog: Space bewteen Chinese and Latin charactors: 星之 star 卡比 kirby + +Changelog position: + +# CHANGELOG + +## Pending + +### Feats + +your changelog + +### Fixes ``` # 贡献代码 @@ -104,6 +118,8 @@ npm run build:site - 不要在中文 API 表和中文的变更日志中加句号 - 在每一个中文和拉丁字母(数字)之间要加空格 - 不要在英文文档中使用中文标点 +- 不要写在已经发布的版本中 +- rebase 分支时注意是否放到已发布的版本中 例如: @@ -126,4 +142,16 @@ Chinese Changelog: 中英文之间要加空格: 星之 star 卡比 kirby + +Changelog 位置: + +# CHANGELOG + +## Pending + +### Feats + +你的 changelog + +### Fixes ``` diff --git a/package.json b/package.json index c19ad2af20e..6276ba6e105 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "naive-ui", - "version": "2.15.7", + "version": "2.16.1", "description": "A Vue 3 Component Library. Fairly Complete, Customizable Themes, Uses TypeScript, Not Too Slow", "main": "lib/index.js", "module": "es/index.js", @@ -72,9 +72,9 @@ "@types/jest": "^26.0.20", "@typescript-eslint/eslint-plugin": "^4.15.1", "@typescript-eslint/parser": "^4.15.1", - "@vicons/fluent": "^0.10.0", - "@vicons/ionicons4": "^0.10.0", - "@vicons/ionicons5": "^0.10.0", + "@vicons/fluent": "^0.11.0", + "@vicons/ionicons4": "^0.11.0", + "@vicons/ionicons5": "^0.11.0", "@vitejs/plugin-vue": "^1.2.1", "@vue/compiler-sfc": "^3.0.10", "@vue/eslint-config-standard": "^6.0.0", @@ -93,7 +93,7 @@ "eslint-plugin-import": "^2.22.1", "eslint-plugin-markdown": "^2.0.0", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.3.1", + "eslint-plugin-promise": "^5.1.0", "eslint-plugin-standard": "^5.0.0", "eslint-plugin-vue": "^7.6.0", "express": "^4.17.1", diff --git a/src/_internal/selection/src/Selection.tsx b/src/_internal/selection/src/Selection.tsx index 547e404de7d..21e57995baf 100644 --- a/src/_internal/selection/src/Selection.tsx +++ b/src/_internal/selection/src/Selection.tsx @@ -505,7 +505,7 @@ export default defineComponent({ ) : ( this.handleDeleteOption(option)} diff --git a/src/_internal/selection/src/styles/index.cssr.ts b/src/_internal/selection/src/styles/index.cssr.ts index 5eaad685671..f0da53832f5 100644 --- a/src/_internal/selection/src/styles/index.cssr.ts +++ b/src/_internal/selection/src/styles/index.cssr.ts @@ -196,6 +196,9 @@ export default c([ cE('input', ` cursor: not-allowed; color: var(--text-color-disabled); + `), + cE('render-label', ` + color: var(--text-color-disabled); `) ]), cB('base-selection-tags', ` diff --git a/src/_mixins/use-form-item.ts b/src/_mixins/use-form-item.ts index 5a86d278653..431c8854c36 100644 --- a/src/_mixins/use-form-item.ts +++ b/src/_mixins/use-form-item.ts @@ -9,10 +9,11 @@ import { } from 'vue' type FormItemSize = 'small' | 'medium' | 'large' -type AllowedSize = 'tiny' | 'small' | 'medium' | 'large' | 'huge' +type AllowedSize = 'tiny' | 'small' | 'medium' | 'large' | 'huge' | number export interface FormItemInjection { path: Ref + disabled: Ref mergedSize: ComputedRef restoreValidation: () => void handleContentBlur: () => void @@ -27,16 +28,17 @@ export const formItemInjectionKey: InjectionKey = interface UseFormItemOptions { defaultSize?: FormItemSize mergedSize?: (formItem: FormItemInjection | null) => T + mergedDisabled?: (formItem: FormItemInjection | null) => boolean } -type UseFormItemProps = - | { - size?: T - } - | {} +interface UseFormItemProps { + size?: T + disabled?: boolean +} export interface UseFormItem { mergedSizeRef: ComputedRef + mergedDisabledRef: ComputedRef nTriggerFormBlur: () => void nTriggerFormChange: () => void nTriggerFormFocus: () => void @@ -45,7 +47,11 @@ export interface UseFormItem { export default function useFormItem ( props: UseFormItemProps, - { defaultSize = 'medium', mergedSize }: UseFormItemOptions = {} + { + defaultSize = 'medium', + mergedSize, + mergedDisabled + }: UseFormItemOptions = {} ): UseFormItem { const NFormItem = inject(formItemInjectionKey, null) provide(formItemInjectionKey, null) @@ -64,6 +70,20 @@ export default function useFormItem ( return defaultSize as T } ) + const mergedDisabledRef = computed( + mergedDisabled + ? () => mergedDisabled(NFormItem) + : () => { + const { disabled } = props + if (disabled !== undefined) { + return disabled + } + if (NFormItem) { + return NFormItem.disabled.value + } + return false + } + ) onBeforeUnmount(() => { if (NFormItem) { NFormItem.restoreValidation() @@ -71,6 +91,7 @@ export default function useFormItem ( }) return { mergedSizeRef, + mergedDisabledRef, nTriggerFormBlur () { if (NFormItem) { NFormItem.handleContentBlur() diff --git a/src/_styles/common/dark.ts b/src/_styles/common/dark.ts index 7da299b0596..1262037ccf0 100644 --- a/src/_styles/common/dark.ts +++ b/src/_styles/common/dark.ts @@ -154,6 +154,9 @@ const derived: ThemeCommonVars = { scrollbarColor: overlay(base.alphaScrollbar), scrollbarColorHover: overlay(base.alphaScrollbarHover), + scrollbarWidth: '5px', + scrollbarHeight: '5px', + scrollbarBorderRadius: '5px', progressRailColor: overlay(base.alphaProgressRail), railColor: overlay(base.alphaRail), diff --git a/src/_styles/common/light.ts b/src/_styles/common/light.ts index 052096e533e..051e4d9233a 100644 --- a/src/_styles/common/light.ts +++ b/src/_styles/common/light.ts @@ -153,6 +153,9 @@ const derived = { scrollbarColor: overlay(base.alphaScrollbar), scrollbarColorHover: overlay(base.alphaScrollbarHover), + scrollbarWidth: '5px', + scrollbarHeight: '5px', + scrollbarBorderRadius: '5px', progressRailColor: neutral(base.alphaProgressRail), railColor: 'rgb(219, 219, 223)', diff --git a/src/_styles/global/index.cssr.ts b/src/_styles/global/index.cssr.ts index f8f7419afdf..6e58c01fc8a 100644 --- a/src/_styles/global/index.cssr.ts +++ b/src/_styles/global/index.cssr.ts @@ -7,12 +7,17 @@ import commonVariables from '../common/_common' // // Technically we can remove font-size & font-family & line-height to make // it pure. However the coding cost doesn't worth it. +// +// -webkit-tap-hilight-color: +// https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-tap-highlight-color +// In some android devices, there will be the style. export default c('body', ` margin: 0; font-size: ${commonVariables.fontSize}; font-family: ${commonVariables.fontFamily}; line-height: ${commonVariables.lineHeight}; -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: transparent; `, [ c('input', ` font-family: inherit; diff --git a/src/anchor/demos/enUS/basic.demo.md b/src/anchor/demos/enUS/basic.demo.md index a32d4c1f129..fc8e4a53393 100644 --- a/src/anchor/demos/enUS/basic.demo.md +++ b/src/anchor/demos/enUS/basic.demo.md @@ -5,7 +5,7 @@ Show Rail Show Background - + diff --git a/src/auto-complete/demos/enUS/after-select.demo.md b/src/auto-complete/demos/enUS/after-select.demo.md index 01e5397e1d6..f93b5ec62c9 100644 --- a/src/auto-complete/demos/enUS/after-select.demo.md +++ b/src/auto-complete/demos/enUS/after-select.demo.md @@ -20,23 +20,24 @@ Blur after selection or clear after selection. ``` ```js -export default { - computed: { - options () { - return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { - const value = this.value === null ? '' : this.value - const prefix = value.split('@')[0] - return { - label: prefix + suffix, - value: prefix + suffix - } - }) - } - }, - data () { +import { defineComponent, ref, computed } from 'vue' + +export default defineComponent({ + setup () { + const valueRef = ref('') return { - value: null + value: valueRef, + options: computed(() => { + return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { + const value = valueRef.value === null ? '' : valueRef.value + const prefix = value.split('@')[0] + return { + label: prefix + suffix, + value: prefix + suffix + } + }) + }) } } -} +}) ``` diff --git a/src/auto-complete/demos/enUS/basic.demo.md b/src/auto-complete/demos/enUS/basic.demo.md index f0fb5ecb0b4..388e949a1c7 100644 --- a/src/auto-complete/demos/enUS/basic.demo.md +++ b/src/auto-complete/demos/enUS/basic.demo.md @@ -5,22 +5,23 @@ ``` ```js -export default { - computed: { - options () { - return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { - const prefix = this.value.split('@')[0] - return { - label: prefix + suffix, - value: prefix + suffix - } - }) - } - }, - data () { +import { defineComponent, ref, computed } from 'vue' + +export default defineComponent({ + setup () { + const valueRef = ref('') return { - value: '' + value: valueRef, + options: computed(() => { + return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { + const prefix = valueRef.value.split('@')[0] + return { + label: prefix + suffix, + value: prefix + suffix + } + }) + }) } } -} +}) ``` diff --git a/src/auto-complete/demos/enUS/custom-input.demo.md b/src/auto-complete/demos/enUS/custom-input.demo.md index 764c936e77a..73e85d741ea 100644 --- a/src/auto-complete/demos/enUS/custom-input.demo.md +++ b/src/auto-complete/demos/enUS/custom-input.demo.md @@ -20,22 +20,23 @@ You can replace auto-complete's input element. ``` ```js -export default { - computed: { - options () { - return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { - const prefix = this.value.split('@')[0] - return { - label: prefix + suffix, - value: prefix + suffix - } - }) - } - }, - data () { +import { defineComponent, ref, computed } from 'vue' + +export default defineComponent({ + setup () { + const valueRef = ref('') return { - value: '' + value: valueRef, + options: computed(() => { + return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { + const prefix = valueRef.value.split('@')[0] + return { + label: prefix + suffix, + value: prefix + suffix + } + }) + }) } } -} +}) ``` diff --git a/src/auto-complete/demos/enUS/size.demo.md b/src/auto-complete/demos/enUS/size.demo.md index cb610590a78..3365f4487ee 100644 --- a/src/auto-complete/demos/enUS/size.demo.md +++ b/src/auto-complete/demos/enUS/size.demo.md @@ -24,22 +24,23 @@ ``` ```js -export default { - computed: { - options () { - return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { - const prefix = this.value.split('@')[0] - return { - label: prefix + suffix, - value: prefix + suffix - } - }) - } - }, - data () { +import { defineComponent, ref, computed } from 'vue' + +export default defineComponent({ + setup () { + const valueRef = ref('') return { - value: '' + value: valueRef, + options: computed(() => { + return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { + const prefix = valueRef.value.split('@')[0] + return { + label: prefix + suffix, + value: prefix + suffix + } + }) + }) } } -} +}) ``` diff --git a/src/auto-complete/demos/zhCN/after-select.demo.md b/src/auto-complete/demos/zhCN/after-select.demo.md index 7a1262850e1..37d07b2e717 100644 --- a/src/auto-complete/demos/zhCN/after-select.demo.md +++ b/src/auto-complete/demos/zhCN/after-select.demo.md @@ -20,23 +20,24 @@ ``` ```js -export default { - computed: { - options () { - return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { - const value = this.value === null ? '' : this.value - const prefix = value.split('@')[0] - return { - label: prefix + suffix, - value: prefix + suffix - } - }) - } - }, - data () { +import { defineComponent, ref, computed } from 'vue' + +export default defineComponent({ + setup () { + const valueRef = ref('') return { - value: null + value: valueRef, + options: computed(() => { + return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { + const value = valueRef.value === null ? '' : valueRef.value + const prefix = value.split('@')[0] + return { + label: prefix + suffix, + value: prefix + suffix + } + }) + }) } } -} +}) ``` diff --git a/src/auto-complete/demos/zhCN/basic.demo.md b/src/auto-complete/demos/zhCN/basic.demo.md index 4caaf323ff7..fe0ab6bd295 100644 --- a/src/auto-complete/demos/zhCN/basic.demo.md +++ b/src/auto-complete/demos/zhCN/basic.demo.md @@ -5,22 +5,23 @@ ``` ```js -export default { - computed: { - options () { - return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { - const prefix = this.value.split('@')[0] - return { - label: prefix + suffix, - value: prefix + suffix - } - }) - } - }, - data () { +import { defineComponent, ref, computed } from 'vue' + +export default defineComponent({ + setup () { + const valueRef = ref('') return { - value: '' + value: valueRef, + options: computed(() => { + return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { + const prefix = valueRef.value.split('@')[0] + return { + label: prefix + suffix, + value: prefix + suffix + } + }) + }) } } -} +}) ``` diff --git a/src/auto-complete/demos/zhCN/custom-input.demo.md b/src/auto-complete/demos/zhCN/custom-input.demo.md index c0516548fa0..eecc0ba707b 100644 --- a/src/auto-complete/demos/zhCN/custom-input.demo.md +++ b/src/auto-complete/demos/zhCN/custom-input.demo.md @@ -20,22 +20,23 @@ ``` ```js -export default { - computed: { - options () { - return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { - const prefix = this.value.split('@')[0] - return { - label: prefix + suffix, - value: prefix + suffix - } - }) - } - }, - data () { +import { defineComponent, ref, computed } from 'vue' + +export default defineComponent({ + setup () { + const valueRef = ref('') return { - value: '' + value: valueRef, + options: computed(() => { + return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { + const prefix = valueRef.value.split('@')[0] + return { + label: prefix + suffix, + value: prefix + suffix + } + }) + }) } } -} +}) ``` diff --git a/src/auto-complete/demos/zhCN/size.demo.md b/src/auto-complete/demos/zhCN/size.demo.md index 49207ff0603..a6255446629 100644 --- a/src/auto-complete/demos/zhCN/size.demo.md +++ b/src/auto-complete/demos/zhCN/size.demo.md @@ -24,22 +24,23 @@ ``` ```js -export default { - computed: { - options () { - return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { - const prefix = this.value.split('@')[0] - return { - label: prefix + suffix, - value: prefix + suffix - } - }) - } - }, - data () { +import { defineComponent, ref, computed } from 'vue' + +export default defineComponent({ + setup () { + const valueRef = ref('') return { - value: '' + value: valueRef, + options: computed(() => { + return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { + const prefix = valueRef.value.split('@')[0] + return { + label: prefix + suffix, + value: prefix + suffix + } + }) + }) } } -} +}) ``` diff --git a/src/auto-complete/src/AutoComplete.tsx b/src/auto-complete/src/AutoComplete.tsx index 51e347f39c7..64ff17f96ef 100644 --- a/src/auto-complete/src/AutoComplete.tsx +++ b/src/auto-complete/src/AutoComplete.tsx @@ -62,7 +62,10 @@ const autoCompleteProps = { type: Boolean, default: undefined }, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, placeholder: String, value: String, blurAfterSelect: Boolean, @@ -103,7 +106,7 @@ export default defineComponent({ const { mergedBorderedRef, namespaceRef, mergedClsPrefixRef } = useConfig(props) const formItem = useFormItem(props) - + const { mergedSizeRef, mergedDisabledRef } = formItem const triggerElRef = ref(null) const menuInstRef = ref(null) @@ -246,7 +249,8 @@ export default defineComponent({ menuInstRef, triggerElRef, treeMate: treeMateRef, - mergedSize: formItem.mergedSizeRef, + mergedSize: mergedSizeRef, + mergedDisabled: mergedDisabledRef, active: activeRef, handleClear, handleFocus, @@ -307,7 +311,7 @@ export default defineComponent({ value={this.mergedValue} placeholder={this.placeholder} size={this.mergedSize} - disabled={this.disabled} + disabled={this.mergedDisabled} clearable={this.clearable} loading={this.loading} onClear={this.handleClear} diff --git a/src/avatar/demos/enUS/badge.demo.md b/src/avatar/demos/enUS/badge.demo.md index 01ebe68949f..f4b327c6574 100644 --- a/src/avatar/demos/enUS/badge.demo.md +++ b/src/avatar/demos/enUS/badge.demo.md @@ -1,6 +1,6 @@ # Badge -Using it with badge would be nice (if you like tons of notifications). +Using it with `badge` would be nice (if you like tons of notifications). ```html diff --git a/src/avatar/demos/enUS/icon.demo.md b/src/avatar/demos/enUS/icon.demo.md index 9def81e7917..0fad6f67d2d 100644 --- a/src/avatar/demos/enUS/icon.demo.md +++ b/src/avatar/demos/enUS/icon.demo.md @@ -11,13 +11,12 @@ I like using icon in avatar. ``` ```js -import { MdCash, MdContacts, IosContacts } from '@vicons/ionicons4' +import { MdCash } from '@vicons/ionicons4' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { - MdCash, - MdContacts, - IosContacts + MdCash } -} +}) ``` diff --git a/src/avatar/demos/enUS/index.demo-entry.md b/src/avatar/demos/enUS/index.demo-entry.md index 9d891a61efd..97cb15b5f22 100644 --- a/src/avatar/demos/enUS/index.demo-entry.md +++ b/src/avatar/demos/enUS/index.demo-entry.md @@ -13,17 +13,20 @@ icon name-size ``` -## Props +## API + +### Avatar Props | Name | Type | Default | Description | | --- | --- | --- | --- | -| object-fit | `'fill' \| 'contain' \| 'cover' \| 'none' \| 'scale-down'` | `fill` | Object-fit type of the image in the container. | +| color | `string` | `undefined` | The background color of the avatar. | +| object-fit | `'fill' \| 'contain' \| 'cover' \| 'none' \| 'scale-down'` | `'fill'` | Object-fit type of the image in the container. | | size | `'small' \| 'medium' \| 'large' \| number` | `'medium'` | Avatar's size. | | src | `string` | `undefined` | Avatar's source. | | round | `boolean` | `false` | Whether to display a rounded avatar. | | on-error | `(e: Event) => void` | `undefined` | Callback executed when the avatar image fails to load. | -## Slots +### Avatar Slots | Name | Parameters | Description | | ------- | ---------- | --------------------------------- | diff --git a/src/avatar/demos/enUS/name-size.demo.md b/src/avatar/demos/enUS/name-size.demo.md index faf4516d483..272bcde0681 100644 --- a/src/avatar/demos/enUS/name-size.demo.md +++ b/src/avatar/demos/enUS/name-size.demo.md @@ -6,18 +6,20 @@ Words' sizing would be auto adjusted in avatar. {{ value }} - {{ value }} + {{ value }} ``` ```js -export default { - data () { +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { return { - value: 'Oasis' + value: ref('Oasis') } } -} +}) ``` diff --git a/src/avatar/demos/enUS/shape.demo.md b/src/avatar/demos/enUS/shape.demo.md index 78b9bb29fe7..052368b3fa8 100644 --- a/src/avatar/demos/enUS/shape.demo.md +++ b/src/avatar/demos/enUS/shape.demo.md @@ -5,22 +5,22 @@ Avatar can be circle shaped. ```html diff --git a/src/avatar/demos/zhCN/badge.demo.md b/src/avatar/demos/zhCN/badge.demo.md index 31abfbc36b4..49bf4ce7da7 100644 --- a/src/avatar/demos/zhCN/badge.demo.md +++ b/src/avatar/demos/zhCN/badge.demo.md @@ -1,6 +1,6 @@ # 标记 -和 Badge 一起用也挺好的 (如果你喜欢看到一堆一堆的推送)。 +和 `Badge` 一起用也挺好的 (如果你喜欢看到一堆一堆的推送)。 ```html diff --git a/src/avatar/demos/zhCN/icon.demo.md b/src/avatar/demos/zhCN/icon.demo.md index 978d317a00e..3b699393627 100644 --- a/src/avatar/demos/zhCN/icon.demo.md +++ b/src/avatar/demos/zhCN/icon.demo.md @@ -11,13 +11,12 @@ ``` ```js -import { MdCash, MdContacts, IosContacts } from '@vicons/ionicons4' +import { MdCash } from '@vicons/ionicons4' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { - MdCash, - MdContacts, - IosContacts + MdCash } -} +}) ``` diff --git a/src/avatar/demos/zhCN/index.demo-entry.md b/src/avatar/demos/zhCN/index.demo-entry.md index 1f71da587ac..e61a09b25f2 100644 --- a/src/avatar/demos/zhCN/index.demo-entry.md +++ b/src/avatar/demos/zhCN/index.demo-entry.md @@ -11,19 +11,23 @@ color badge icon name-size +v-show-debug ``` -## Props +## API + +### Avatar Props | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| object-fit | `'fill' \| 'contain' \| 'cover' \| 'none' \| 'scale-down'` | `fill` | 头像的图片在容器内的的适应类型 | +| color | `string` | `undefined` | 头像的背景色 | +| object-fit | `'fill' \| 'contain' \| 'cover' \| 'none' \| 'scale-down'` | `'fill'` | 头像的图片在容器内的的适应类型 | | size | `'small' \| 'medium' \| 'large' \| number` | `'medium'` | 头像的尺寸 | | src | `string` | `undefined` | 头像的地址 | | round | `boolean` | `false` | 头像是否圆形 | | on-error | `(e: Event) => void` | `undefined` | 头像的图片加载失败执行的回调 | -## Slots +### Avatar Slots | 名称 | 参数 | 说明 | | ------- | ---- | ---------------- | diff --git a/src/avatar/demos/zhCN/name-size.demo.md b/src/avatar/demos/zhCN/name-size.demo.md index 3cc9b0e77a7..cff053cb5d0 100644 --- a/src/avatar/demos/zhCN/name-size.demo.md +++ b/src/avatar/demos/zhCN/name-size.demo.md @@ -6,18 +6,20 @@ {{ value }} - {{ value }} + {{ value }} ``` ```js -export default { - data () { +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { return { - value: 'Oasis' + value: ref('Oasis') } } -} +}) ``` diff --git a/src/avatar/demos/zhCN/shape.demo.md b/src/avatar/demos/zhCN/shape.demo.md index 2db3cc3eb8f..52ed6a2b966 100644 --- a/src/avatar/demos/zhCN/shape.demo.md +++ b/src/avatar/demos/zhCN/shape.demo.md @@ -5,22 +5,22 @@ ```html diff --git a/src/avatar/demos/zhCN/v-show-debug.demo.md b/src/avatar/demos/zhCN/v-show-debug.demo.md new file mode 100644 index 00000000000..c789db0c0f0 --- /dev/null +++ b/src/avatar/demos/zhCN/v-show-debug.demo.md @@ -0,0 +1,37 @@ +# v-show debug + +```html + + + + {{ value }} + {{ value }} + + + +``` + +```js +import { defineComponent, onMounted, onUpdated, ref } from 'vue' + +export default defineComponent({ + setup () { + const isShow = ref(false) + const toggle = () => { + isShow.value = !isShow.value + } + onMounted(() => { + console.log('world') + }) + + onUpdated(() => { + console.log('hello') + }) + return { + value: ref('Oasis111'), + toggle, + isShow + } + } +}) +``` diff --git a/src/avatar/src/Avatar.tsx b/src/avatar/src/Avatar.tsx index 4956b133166..985b05f84a7 100644 --- a/src/avatar/src/Avatar.tsx +++ b/src/avatar/src/Avatar.tsx @@ -1,12 +1,5 @@ -import { - h, - ref, - computed, - onUpdated, - onMounted, - defineComponent, - PropType -} from 'vue' +import { h, ref, computed, defineComponent, PropType } from 'vue' +import { VResizeObserver } from 'vueuc' import { useConfig, useTheme } from '../../_mixins' import type { ThemeProps } from '../../_mixins' import { avatarLight } from '../styles' @@ -22,10 +15,7 @@ const avatarProps = { default: 'medium' }, src: String, - circle: { - type: Boolean, - default: false - }, + circle: Boolean, color: String, objectFit: { type: String as PropType< @@ -33,10 +23,7 @@ const avatarProps = { >, default: 'fill' }, - round: { - type: Boolean, - default: false - }, + round: Boolean, onError: Function as PropType<(e: Event) => void> } as const @@ -51,7 +38,7 @@ export default defineComponent({ let memoedTextHtml: string | null = null const textRef = ref(null) const selfRef = ref(null) - const adjustText = (): void => { + const fitTextTransform = (): void => { const { value: textEl } = textRef if (textEl) { if (memoedTextHtml === null || memoedTextHtml !== textEl.innerHTML) { @@ -71,11 +58,6 @@ export default defineComponent({ } } } - // Not Good Impl - onMounted(() => adjustText()) - onUpdated(() => { - adjustText() - }) const themeRef = useTheme( 'Avatar', 'Avatar', @@ -88,6 +70,7 @@ export default defineComponent({ textRef, selfRef, mergedClsPrefix: mergedClsPrefixRef, + fitTextTransform, cssVars: computed(() => { const { size, round, circle } = props const { @@ -125,13 +108,19 @@ export default defineComponent({ style={{ objectFit: this.objectFit }} /> ) : ( - - {$slots} - + + {{ + default: () => ( + + {$slots} + + ) + }} + )} ) diff --git a/src/avatar/tests/Avatar.spec.tsx b/src/avatar/tests/Avatar.spec.tsx index 22e01f80063..f29fa20be4f 100644 --- a/src/avatar/tests/Avatar.spec.tsx +++ b/src/avatar/tests/Avatar.spec.tsx @@ -4,6 +4,8 @@ import { h, nextTick } from 'vue' import { CashOutline as CashIcon } from '@vicons/ionicons5' import { NIcon } from '../../icon' +// Please note that resize observer doesn't work in JSDOM, so text transfrom +// can't be tested. describe('n-avatar', () => { // mock offsetHeight offsetWidth const originalOffsetHeight = Object.getOwnPropertyDescriptor( @@ -113,9 +115,6 @@ describe('n-avatar', () => { await wrapper.setData({ text: 'adjust text' }) await nextTick() expect(textNode.exists()).toBe(true) - expect(textNode.attributes('style')).toContain( - 'transform: translateX(-50%) translateY(-50%) scale(1);' - ) expect(wrapper.html()).toMatchSnapshot() }) diff --git a/src/avatar/tests/__snapshots__/Avatar.spec.tsx.snap b/src/avatar/tests/__snapshots__/Avatar.spec.tsx.snap index 430ed0c4051..a05e70284d5 100644 --- a/src/avatar/tests/__snapshots__/Avatar.spec.tsx.snap +++ b/src/avatar/tests/__snapshots__/Avatar.spec.tsx.snap @@ -1,15 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`n-avatar avatar adjust text 1`] = `"adjust text"`; +exports[`n-avatar avatar adjust text 1`] = `"adjust text"`; -exports[`n-avatar custom style 1`] = `""`; +exports[`n-avatar custom style 1`] = `""`; -exports[`n-avatar icon avatar 1`] = `""`; +exports[`n-avatar icon avatar 1`] = `""`; exports[`n-avatar image avatar 1`] = `""`; -exports[`n-avatar round avatar 1`] = `""`; +exports[`n-avatar round avatar 1`] = `""`; -exports[`n-avatar size is number 1`] = `""`; +exports[`n-avatar size is number 1`] = `""`; -exports[`n-avatar size is string 1`] = `""`; +exports[`n-avatar size is string 1`] = `""`; diff --git a/src/badge/tests/Badge.spec.ts b/src/badge/tests/Badge.spec.ts index 74e598a493d..d6a810926a6 100644 --- a/src/badge/tests/Badge.spec.ts +++ b/src/badge/tests/Badge.spec.ts @@ -5,4 +5,53 @@ describe('n-badge', () => { it('should work with import on demand', () => { mount(NBadge) }) + + it('should work with `dot` prop', async () => { + const wrapper = mount(NBadge, { props: { value: 5 } }) + expect(wrapper.find('.n-badge').classes('n-badge--dot')).not.toBe(true) + expect(wrapper.find('.n-base-slot-machine').exists()).toBe(true) + + await wrapper.setProps({ dot: true }) + expect(wrapper.find('.n-badge').classes('n-badge--dot')).toBe(true) + expect(wrapper.find('.n-base-slot-machine').exists()).not.toBe(true) + }) + + it('should work with `color` prop', async () => { + const wrapper = mount(NBadge, { props: { value: 5, color: 'grey' } }) + expect(wrapper.find('.n-badge').attributes('style')).toContain( + '--color: grey;' + ) + }) + + it('should work with `max` prop', async () => { + const wrapper = mount(NBadge, { props: { value: 5, max: 5 } }) + expect( + wrapper + .find('.n-base-slot-machine-current-number__inner--not-number') + .exists() + ).not.toBe(true) + + await wrapper.setProps({ value: 6 }) + expect( + wrapper + .find('.n-base-slot-machine-current-number__inner--not-number') + .exists() + ).toBe(true) + }) + + it('should work with `processing` prop', async () => { + const wrapper = mount(NBadge, { props: { value: 5 } }) + expect(wrapper.find('.n-base-wave').exists()).not.toBe(true) + + await wrapper.setProps({ processing: true }) + expect(wrapper.find('.n-base-wave').exists()).toBe(true) + }) + + it('should work with `show-zero` prop', async () => { + const wrapper = mount(NBadge, { props: { value: 0 } }) + expect(wrapper.find('.n-badge-sup').exists()).not.toBe(true) + + await wrapper.setProps({ 'show-zero': true }) + expect(wrapper.find('.n-badge-sup').exists()).toBe(true) + }) }) diff --git a/src/breadcrumb/tests/Breadcrumb.spec.ts b/src/breadcrumb/tests/Breadcrumb.spec.ts index 1f9f7a272ac..20261a7d080 100644 --- a/src/breadcrumb/tests/Breadcrumb.spec.ts +++ b/src/breadcrumb/tests/Breadcrumb.spec.ts @@ -1,8 +1,64 @@ +import { h } from 'vue' import { mount } from '@vue/test-utils' -import { NBreadcrumb } from '../index' +import { NBreadcrumb, NBreadcrumbItem } from '../index' describe('n-breadcrumb', () => { it('should work with import on demand', () => { mount(NBreadcrumb) }) + + it('should work with Breadcrumb, BreadcrumbItem slots', async () => { + const wrapper = mount(NBreadcrumb, { + slots: { + default: () => [ + h(NBreadcrumbItem, null, { default: () => 'test-item1' }), + h(NBreadcrumbItem, null, { default: () => 'test-item2' }) + ] + } + }) + + expect(wrapper.find('.n-breadcrumb').element.children.length).toBe(2) + expect( + wrapper.find('.n-breadcrumb').element.children[0].getAttribute('class') + ).toContain('n-breadcrumb-item') + const itemList = wrapper.findAll('.n-breadcrumb-item__link') + expect(itemList[0].text()).toBe('test-item1') + expect(itemList[1].text()).toBe('test-item2') + }) + + it("should work with Breadcrumb's `separator` prop", async () => { + const wrapper = mount(NBreadcrumb, { + props: { separator: '@' }, + slots: { + default: () => [ + h(NBreadcrumbItem, null, { default: () => 'test-item1' }), + h(NBreadcrumbItem, null, { default: () => 'test-item2' }) + ] + } + }) + + expect( + wrapper + .findAll('.n-breadcrumb-item__separator') + .every((i) => i.text() === '@') + ).toBe(true) + }) + + it("should work with BreadcrumbItem's `separator` prop", async () => { + const wrapper = mount(NBreadcrumb, { + slots: { + default: () => [ + h( + NBreadcrumbItem, + { separator: '@' }, + { default: () => 'test-item1' } + ), + h(NBreadcrumbItem, null, { default: () => 'test-item2' }) + ] + } + }) + + expect(wrapper.findAll('.n-breadcrumb-item__separator')[0].text()).toBe('@') + expect(wrapper.findAll('.n-breadcrumb-item__separator')[1].text()).toBe('/') + }) }) diff --git a/src/button/demos/enUS/color.demo.md b/src/button/demos/enUS/color.demo.md index 617dcf3138d..748cc759f15 100644 --- a/src/button/demos/enUS/color.demo.md +++ b/src/button/demos/enUS/color.demo.md @@ -57,10 +57,11 @@ The two colors look like toadstool. ```js import { CashOutline as CashIcon } from '@vicons/ionicons5' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { CashIcon } -} +}) ``` diff --git a/src/button/demos/enUS/group.demo.md b/src/button/demos/enUS/group.demo.md index 50a68b6ad78..357a1189151 100644 --- a/src/button/demos/enUS/group.demo.md +++ b/src/button/demos/enUS/group.demo.md @@ -89,10 +89,11 @@ Button can be grouped. ```js import { LogInOutline as LogInIcon } from '@vicons/ionicons5' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { LogInIcon } -} +}) ``` diff --git a/src/button/demos/enUS/icon.demo.md b/src/button/demos/enUS/icon.demo.md index c0ec460cfc5..94765ea0aa3 100644 --- a/src/button/demos/enUS/icon.demo.md +++ b/src/button/demos/enUS/icon.demo.md @@ -25,10 +25,11 @@ Use icon in button. ```js import { CashOutline as CashIcon } from '@vicons/ionicons5' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { CashIcon } -} +}) ``` diff --git a/src/button/demos/enUS/index.demo-entry.md b/src/button/demos/enUS/index.demo-entry.md index 23231222ccd..4977510ff77 100644 --- a/src/button/demos/enUS/index.demo-entry.md +++ b/src/button/demos/enUS/index.demo-entry.md @@ -27,12 +27,12 @@ icon-button | Name | Type | Default | Description | | --- | --- | --- | --- | -| attr-type | `'button' \| 'submit' \| 'reset'` | `'button'` | The DOM `type` attribute of the button. | +| attr-type | `'button' \| 'submit' \| 'reset'` | `'button'` | The `type` attribute of the button's DOM. | | block | `boolean` | `false` | Whether the button is displayed as block. | | bordered | `boolean` | `true` | Whether the button shows the border. | | circle | `boolean` | `false` | Whether the button is round. | -| color | `string` | `undefined` | Button color(support `#FFF`, `#FFFFFF`, `yellow`,`rgb(0, 0, 0)` formatted colors). | -| dashed | `boolean` | `false` | Whether the button border is a dashed line. | +| color | `string` | `undefined` | Button color (support `#FFF`, `#FFFFFF`, `yellow`,`rgb(0, 0, 0)` formatted colors). | +| dashed | `boolean` | `false` | Whether the button's border is a dashed line. | | disabled | `boolean` | `false` | Whether the button is disabled. | | ghost | `boolean` | `false` | Whether the button is ghost. | | icon-placement | `'left' \| 'right'` | `'left'` | The position of the icon in the button. | @@ -41,6 +41,7 @@ icon-button | round | `boolean` | `false` | Whether the button shows rounded corners. | | size | `'tiny' \| 'small' \| 'medium' \| 'large'` | `'medium'` | Button size. | | text | `boolean` | `false` | Whether to display as a text button. | +| text-color | `string` | `undefined` | Button text color (support `#FFF`, `#FFFFFF`, `yellow`,`rgb(0, 0, 0)` formatted colors). | | type | `'default' \| 'primary' \| 'success' \| 'info' \| 'warning' \| 'error'` | `'default'` | Button type. | | tag | `string` | `'button'` | What tag need the button be rendered as. | @@ -58,7 +59,7 @@ icon-button | Name | Parameters | Description | | ------- | ---------- | ---------------------------------- | | default | `()` | The default content of the button. | -| icon | `()` | Icon fill content in the button. | +| icon | `()` | The icon of the button. | ### Button Group Slots diff --git a/src/button/demos/enUS/loading.demo.md b/src/button/demos/enUS/loading.demo.md index 17d916787ff..8f0fd116e44 100644 --- a/src/button/demos/enUS/loading.demo.md +++ b/src/button/demos/enUS/loading.demo.md @@ -24,15 +24,16 @@ Button has loading status. ```js import { CashOutline as CashIcon } from '@vicons/ionicons5' +import { defineComponent, ref } from 'vue' -export default { +export default defineComponent({ components: { CashIcon }, - data () { + setup () { return { - loading: false + loading: ref(false) } } -} +}) ``` diff --git a/src/button/demos/enUS/shape.demo.md b/src/button/demos/enUS/shape.demo.md index d9a16f7128b..acb7921efb6 100644 --- a/src/button/demos/enUS/shape.demo.md +++ b/src/button/demos/enUS/shape.demo.md @@ -16,10 +16,11 @@ Button has different shapes. ```js import { CashOutline as CashIcon } from '@vicons/ionicons5' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { CashIcon } -} +}) ``` diff --git a/src/button/demos/enUS/text.demo.md b/src/button/demos/enUS/text.demo.md index b9a2478ee34..b7a4d147794 100644 --- a/src/button/demos/enUS/text.demo.md +++ b/src/button/demos/enUS/text.demo.md @@ -15,10 +15,11 @@ Just look like text. ```js import { TrainOutline as TrainIcon } from '@vicons/ionicons5' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { TrainIcon } -} +}) ``` diff --git a/src/button/demos/zhCN/color.demo.md b/src/button/demos/zhCN/color.demo.md index a978355a463..d91aa39728e 100644 --- a/src/button/demos/zhCN/color.demo.md +++ b/src/button/demos/zhCN/color.demo.md @@ -57,10 +57,11 @@ ```js import { CashOutline as CashIcon } from '@vicons/ionicons5' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { CashIcon } -} +}) ``` diff --git a/src/button/demos/zhCN/debug.demo.md b/src/button/demos/zhCN/debug.demo.md index 2e47e9eb4e5..495c86f172a 100644 --- a/src/button/demos/zhCN/debug.demo.md +++ b/src/button/demos/zhCN/debug.demo.md @@ -1,6 +1,6 @@ # debug -Debug 用的。 +`Debug` 用的。 ```html
), color: String, + textColor: String, text: Boolean, block: Boolean, loading: Boolean, @@ -214,7 +215,8 @@ const Button = defineComponent({ fontWeight } = self const size = mergedSizeRef.value - const { dashed, type, ghost, text, color, round, circle } = props + const { dashed, type, ghost, text, color, round, circle, textColor } = + props // font const fontProps = { fontWeight: text @@ -239,8 +241,9 @@ const Button = defineComponent({ } if (text) { const { depth } = props - const textColor = - color || + const propTextColor = textColor || color + const mergedTextColor = + propTextColor || (type === 'default' && depth !== undefined ? self[ createKey( @@ -256,20 +259,21 @@ const Button = defineComponent({ '--color-focus': '#0000', '--color-disabled': '#0000', '--ripple-color': '#0000', - '--text-color': textColor, - '--text-color-hover': color - ? createHoverColor(color) + '--text-color': mergedTextColor, + '--text-color-hover': propTextColor + ? createHoverColor(propTextColor) : self[createKey('textColorTextHover', type)], - '--text-color-pressed': color - ? createPressedColor(color) + '--text-color-pressed': propTextColor + ? createPressedColor(propTextColor) : self[createKey('textColorTextPressed', type)], - '--text-color-focus': color - ? createHoverColor(color) + '--text-color-focus': propTextColor + ? createHoverColor(propTextColor) : self[createKey('textColorTextHover', type)], '--text-color-disabled': - color || self[createKey('textColorTextDisabled', type)] + propTextColor || self[createKey('textColorTextDisabled', type)] } } else if (ghost || dashed) { + const mergedTextColor = textColor || color colorProps = { '--color': '#0000', '--color-hover': '#0000', @@ -277,18 +281,19 @@ const Button = defineComponent({ '--color-focus': '#0000', '--color-disabled': '#0000', '--ripple-color': color || self[createKey('rippleColor', type)], - '--text-color': color || self[createKey('textColorGhost', type)], - '--text-color-hover': color - ? createHoverColor(color) + '--text-color': + mergedTextColor || self[createKey('textColorGhost', type)], + '--text-color-hover': mergedTextColor + ? createHoverColor(mergedTextColor) : self[createKey('textColorGhostHover', type)], - '--text-color-pressed': color - ? createPressedColor(color) + '--text-color-pressed': mergedTextColor + ? createPressedColor(mergedTextColor) : self[createKey('textColorGhostPressed', type)], - '--text-color-focus': color - ? createHoverColor(color) + '--text-color-focus': mergedTextColor + ? createHoverColor(mergedTextColor) : self[createKey('textColorGhostHover', type)], '--text-color-disabled': - color || self[createKey('textColorGhostDisabled', type)] + mergedTextColor || self[createKey('textColorGhostDisabled', type)] } } else { colorProps = { @@ -304,21 +309,31 @@ const Button = defineComponent({ : self[createKey('colorFocus', type)], '--color-disabled': color || self[createKey('colorDisabled', type)], '--ripple-color': color || self[createKey('rippleColor', type)], - '--text-color': color - ? self.textColorPrimary - : self[createKey('textColor', type)], - '--text-color-hover': color - ? self.textColorHoverPrimary - : self[createKey('textColorHover', type)], - '--text-color-pressed': color - ? self.textColorPressedPrimary - : self[createKey('textColorPressed', type)], - '--text-color-focus': color - ? self.textColorFocusPrimary - : self[createKey('textColorFocus', type)], - '--text-color-disabled': color - ? self.textColorDisabledPrimary - : self[createKey('textColorDisabled', type)] + '--text-color': + textColor || + (color + ? self.textColorPrimary + : self[createKey('textColor', type)]), + '--text-color-hover': + textColor || + (color + ? self.textColorHoverPrimary + : self[createKey('textColorHover', type)]), + '--text-color-pressed': + textColor || + (color + ? self.textColorPressedPrimary + : self[createKey('textColorPressed', type)]), + '--text-color-focus': + textColor || + (color + ? self.textColorFocusPrimary + : self[createKey('textColorFocus', type)]), + '--text-color-disabled': + textColor || + (color + ? self.textColorDisabledPrimary + : self[createKey('textColorDisabled', type)]) } } // border diff --git a/src/button/tests/Button.spec.tsx b/src/button/tests/Button.spec.tsx index 4971f8aef4e..0c17da5992d 100644 --- a/src/button/tests/Button.spec.tsx +++ b/src/button/tests/Button.spec.tsx @@ -233,6 +233,24 @@ describe('n-button', () => { expect(colorStyle.every((i) => buttonStyle.includes(i))).toBe(true) }) + it('should work with `text-color` prop', () => { + const wrapper = mount(NButton, { + props: { + 'text-color': '#8a2be2' + }, + slots: { + default: () => 'test' + } + }) + + const buttonStyle = wrapper.find('button').attributes('style') + expect( + ( + ['--text-color: #8a2be2;', '--text-color-disabled: #8a2be2;'] as const + ).every((i) => buttonStyle.includes(i)) + ).toBe(true) + }) + it('should work with `button group`', async () => { const wrapper = mount(NButtonGroup, { slots: { diff --git a/src/card/demos/enUS/index.demo-entry.md b/src/card/demos/enUS/index.demo-entry.md index 48b34ae5692..f20ef6ad02c 100644 --- a/src/card/demos/enUS/index.demo-entry.md +++ b/src/card/demos/enUS/index.demo-entry.md @@ -21,11 +21,11 @@ no-title | Name | Type | Default | Description | | --- | --- | --- | --- | -| bordered | `boolean` | `true` | Whether the card shows the border. | -| closable | `boolean` | `false` | Whether the card displays the close icon. | -| content-style | `Object \| string` | `undefined` | Style of the card content. | -| footer-style | `Object \| string` | `undefined` | Style of the card footer. | -| header-style | `Object \| string` | `undefined` | Style of the card header. | +| bordered | `boolean` | `true` | Whether to show the card border. | +| closable | `boolean` | `false` | Is it allowed to close. | +| content-style | `Object \| string` | `undefined` | The style of the card content area. | +| footer-style | `Object \| string` | `undefined` | The style of the bottom area of the card. | +| header-style | `Object \| string` | `undefined` | The style of the card head area. | | hoverable | `boolean` | `false` | Whether to show shadow when hovering on the card. | | segmented | `boolean \| { [part in 'content' \| 'footer' \| 'action']?: boolean \| 'soft' \| 'hard' }` | `false` | Segment divider settings of the card. | | size | `'small' \| 'medium' \| 'large' \| 'huge'` | `'medium'` | Card size. | @@ -34,11 +34,11 @@ no-title ## Slots -| Name | Parameters | Description | -| ------------ | ---------- | ------------------------------------- | -| cover | `()` | The content of the cover part. | -| header | `()` | The content of the header part. | -| header-extra | `()` | The content of the header-extra part. | -| default | `()` | The default content of the card. | -| footer | `()` | The content of the footer part. | -| action | `()` | The content of the action part. | +| Name | Parameters | Description | +| ------------ | ---------- | ----------------------- | +| cover | `()` | Cover content. | +| header | `()` | Header content. | +| header-extra | `()` | Header extra content. | +| default | `()` | Card content. | +| footer | `()` | Footer content. | +| action | `()` | Operating area content. | diff --git a/src/card/demos/zhCN/index.demo-entry.md b/src/card/demos/zhCN/index.demo-entry.md index 8ac1a6ff80c..63844be75ce 100644 --- a/src/card/demos/zhCN/index.demo-entry.md +++ b/src/card/demos/zhCN/index.demo-entry.md @@ -23,11 +23,11 @@ rtl-debug | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| bordered | `boolean` | `true` | 卡片是否显示 border | -| closable | `boolean` | `false` | 卡片是否显示 close 图标 | -| content-style | `Object \| string` | `undefined` | 卡片 content 的样式设置 | -| footer-style | `Object \| string` | `undefined` | 卡片 footer 的样式设置 | -| header-style | `Object \| string` | `undefined` | 卡片 header 的样式设置 | +| bordered | `boolean` | `true` | 是否显示卡片边框 | +| closable | `boolean` | `false` | 是否允许关闭 | +| content-style | `Object \| string` | `undefined` | 卡片内容区域的样式 | +| footer-style | `Object \| string` | `undefined` | 卡片底部区域的样式 | +| header-style | `Object \| string` | `undefined` | 卡片头部区域的样式 | | hoverable | `boolean` | `false` | 卡片是否可悬浮 | | segmented | `boolean \| { [part in 'content' \| 'footer' \| 'action']?: boolean \| 'soft' \| 'hard' }` | `false` | 卡片的分段区域设置 | | size | `'small' \| 'medium' \| 'large' \| 'huge'` | `'medium'` | 卡片的尺寸 | @@ -36,11 +36,11 @@ rtl-debug ## Slots -| 名称 | 参数 | 说明 | -| ------------ | ---- | --------------------------- | -| cover | `()` | cover 部分填充的内容 | -| header | `()` | header 部分填充的内容 | -| header-extra | `()` | header-extra 部分填充的内容 | -| default | `()` | card 默认填充的内容 | -| footer | `()` | footer 部分填充的内容 | -| action | `()` | action 部分填充的内容 | +| 名称 | 参数 | 说明 | +| ------------ | ---- | ------------ | +| cover | `()` | 覆盖内容 | +| header | `()` | 头部内容 | +| header-extra | `()` | 头部额外内容 | +| default | `()` | 卡片内容 | +| footer | `()` | 底部内容 | +| action | `()` | 操作区域内容 | diff --git a/src/cascader/demos/zhCN/index.demo-entry.md b/src/cascader/demos/zhCN/index.demo-entry.md index 3ff31864599..00226fa3d01 100644 --- a/src/cascader/demos/zhCN/index.demo-entry.md +++ b/src/cascader/demos/zhCN/index.demo-entry.md @@ -24,7 +24,7 @@ virtual | disabled | `boolean` | `false` | 是否禁用 | | expand-trigger | `'click' \| 'hover'` | `'click'` | 在 `remote` 被设定时 `'hover'` 不生效 | | filterable | `boolean` | `false` | `remote` 被设定时不生效 | -| filter | `(pattern: string, option: CascaderOption, path: Array) => boolean` | 一个基于字符串的过滤算法 | 过滤选项的函数 | +| filter | `(pattern: string, option: CascaderOption, path: Array) => boolean` | 一个基于字符串的过滤算法 | 过滤选项的函数 | | leaf-only | `boolean` | `false` | 是否只允许 `value` 出现叶节点的值 | | max-tag-count | `number \| 'responsive'` | `undefined` | 多选标签的最大显示数量,`responsive` 会将所有标签保持在一行 | | multiple | `boolean` | `false` | 是否支持多选 | diff --git a/src/cascader/src/Cascader.tsx b/src/cascader/src/Cascader.tsx index 814ab7492b5..275976c0618 100644 --- a/src/cascader/src/Cascader.tsx +++ b/src/cascader/src/Cascader.tsx @@ -65,7 +65,10 @@ const cascaderProps = { multiple: Boolean, size: String as PropType<'small' | 'medium' | 'large'>, filterable: Boolean, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, expandTrigger: { type: String as PropType, default: 'click' @@ -144,6 +147,7 @@ export default defineComponent({ ) const patternRef = ref('') const formItem = useFormItem(props) + const { mergedSizeRef, mergedDisabledRef } = formItem const cascaderMenuInstRef = ref(null) const selectMenuInstRef = ref(null) const triggerInstRef = ref(null) @@ -370,7 +374,7 @@ export default defineComponent({ triggerInstRef.value?.focus() } function openMenu (): void { - if (!props.disabled) { + if (!mergedDisabledRef.value) { patternRef.value = '' uncontrolledShowRef.value = true if (props.filterable) { @@ -694,7 +698,8 @@ export default defineComponent({ showSelectMenu: showSelectMenuRef, pattern: patternRef, treeMate: treeMateRef, - mergedSize: formItem.mergedSizeRef, + mergedSize: mergedSizeRef, + mergedDisabled: mergedDisabledRef, localizedPlaceholder: localizedPlaceholderRef, selectedOption: selectedOptionRef, selectedOptions: selectedOptionsRef, @@ -732,7 +737,8 @@ export default defineComponent({ optionColorHover, optionHeight, optionFontSize, - loadingColor + loadingColor, + columnWidth }, common: { cubicBezierEaseInOut } } = themeRef.value @@ -741,6 +747,7 @@ export default defineComponent({ '--menu-border-radius': menuBorderRadius, '--menu-box-shadow': menuBoxShadow, '--menu-height': menuHeight, + '--column-width': columnWidth, '--menu-color': menuColor, '--menu-divider-color': menuDividerColor, '--option-height': optionHeight, @@ -785,7 +792,7 @@ export default defineComponent({ multiple={this.multiple} filterable={this.filterable} clearable={this.clearable} - disabled={this.disabled} + disabled={this.mergedDisabled} focused={this.focused} onFocus={this.handleTriggerFocus} onBlur={this.handleTriggerBlur} diff --git a/src/cascader/src/CascaderSubmenu.tsx b/src/cascader/src/CascaderSubmenu.tsx index 41f93da3bd8..f7749b92800 100644 --- a/src/cascader/src/CascaderSubmenu.tsx +++ b/src/cascader/src/CascaderSubmenu.tsx @@ -68,7 +68,12 @@ export default defineComponent({ render () { const { mergedClsPrefix, mergedTheme, virtualScroll } = this return ( -
+
{ optionTextColorActive: primaryColor, optionTextColorDisabled: textColorDisabled, optionCheckMarkColor: primaryColor, - loadingColor: primaryColor + loadingColor: primaryColor, + columnWidth: '180px' } } diff --git a/src/checkbox/demos/enUS/basic.demo.md b/src/checkbox/demos/enUS/basic.demo.md index 3f3e2816325..1ac734e97b4 100644 --- a/src/checkbox/demos/enUS/basic.demo.md +++ b/src/checkbox/demos/enUS/basic.demo.md @@ -10,12 +10,14 @@ ``` ```js -export default { - data () { +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { return { - value: false, - disabled: true + value: ref(false), + disabled: ref(true) } } -} +}) ``` diff --git a/src/checkbox/demos/enUS/controlled.demo.md b/src/checkbox/demos/enUS/controlled.demo.md index 17eee183c60..be6473bce8c 100644 --- a/src/checkbox/demos/enUS/controlled.demo.md +++ b/src/checkbox/demos/enUS/controlled.demo.md @@ -8,11 +8,13 @@ ``` ```js -export default { - data () { +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { return { - value: false + value: ref(false) } } -} +}) ``` diff --git a/src/checkbox/demos/enUS/group.demo.md b/src/checkbox/demos/enUS/group.demo.md index 7fe332760ca..45188ead2d0 100644 --- a/src/checkbox/demos/enUS/group.demo.md +++ b/src/checkbox/demos/enUS/group.demo.md @@ -12,11 +12,13 @@ ``` ```js -export default { - data () { +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { return { - cities: null + cities: ref(null) } } -} +}) ``` diff --git a/src/checkbox/demos/enUS/indeterminate.demo.md b/src/checkbox/demos/enUS/indeterminate.demo.md index 6b7a4448e22..79f0e00e048 100644 --- a/src/checkbox/demos/enUS/indeterminate.demo.md +++ b/src/checkbox/demos/enUS/indeterminate.demo.md @@ -15,12 +15,14 @@ ``` ```js -export default { - data () { +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { return { - value: false, - indeterminate: false + value: ref(false), + indeterminate: ref(false) } } -} +}) ``` diff --git a/src/checkbox/demos/zhCN/basic.demo.md b/src/checkbox/demos/zhCN/basic.demo.md index f86892bbac1..483ba578519 100644 --- a/src/checkbox/demos/zhCN/basic.demo.md +++ b/src/checkbox/demos/zhCN/basic.demo.md @@ -10,12 +10,14 @@ ``` ```js -export default { - data () { +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { return { - value: false, - disabled: true + value: ref(false), + disabled: ref(true) } } -} +}) ``` diff --git a/src/checkbox/demos/zhCN/controlled.demo.md b/src/checkbox/demos/zhCN/controlled.demo.md index b699db9a6c1..444e95e2b65 100644 --- a/src/checkbox/demos/zhCN/controlled.demo.md +++ b/src/checkbox/demos/zhCN/controlled.demo.md @@ -8,11 +8,13 @@ ``` ```js -export default { - data () { +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { return { - value: false + value: ref(false) } } -} +}) ``` diff --git a/src/checkbox/demos/zhCN/group.demo.md b/src/checkbox/demos/zhCN/group.demo.md index af34402862b..09d913c60f5 100644 --- a/src/checkbox/demos/zhCN/group.demo.md +++ b/src/checkbox/demos/zhCN/group.demo.md @@ -12,11 +12,13 @@ ``` ```js -export default { - data () { +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { return { - cities: null + cities: ref(null) } } -} +}) ``` diff --git a/src/checkbox/demos/zhCN/indeterminate.demo.md b/src/checkbox/demos/zhCN/indeterminate.demo.md index 93369ac3078..828cf6a7d7c 100644 --- a/src/checkbox/demos/zhCN/indeterminate.demo.md +++ b/src/checkbox/demos/zhCN/indeterminate.demo.md @@ -15,12 +15,14 @@ ``` ```js -export default { - data () { +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { return { - value: false, - indeterminate: false + value: ref(false), + indeterminate: ref(false) } } -} +}) ``` diff --git a/src/checkbox/src/Checkbox.tsx b/src/checkbox/src/Checkbox.tsx index aa0a9003bd2..ebfbef49e2e 100644 --- a/src/checkbox/src/Checkbox.tsx +++ b/src/checkbox/src/Checkbox.tsx @@ -36,7 +36,10 @@ const checkboxProps = { }, defaultChecked: Boolean, value: [String, Number] as PropType, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, indeterminate: Boolean, label: String, focusable: { @@ -75,6 +78,56 @@ export default defineComponent({ props: checkboxProps, setup (props) { const { mergedClsPrefixRef } = useConfig(props) + const formItem = useFormItem(props, { + mergedSize (NFormItem) { + const { size } = props + if (size !== undefined) return size + if (NCheckboxGroup) { + const { value: mergedSize } = NCheckboxGroup.mergedSizeRef + if (mergedSize !== undefined) { + return mergedSize + } + } + if (NFormItem) { + const { mergedSize } = NFormItem + if (mergedSize !== undefined) return mergedSize.value + } + return 'medium' + }, + mergedDisabled (NFormItem) { + const { disabled } = props + if (disabled !== undefined) return disabled + if (NCheckboxGroup) { + if (NCheckboxGroup.disabledRef.value) return true + const { + maxRef: { value: max }, + checkedCountRef + } = NCheckboxGroup + if ( + max !== undefined && + checkedCountRef.value >= max && + !renderedCheckedRef.value + ) { + return true + } + const { + minRef: { value: min } + } = NCheckboxGroup + if ( + min !== undefined && + checkedCountRef.value <= min && + renderedCheckedRef.value + ) { + return true + } + } + if (NFormItem) { + return NFormItem.disabled.value + } + return false + } + }) + const { mergedDisabledRef, mergedSizeRef } = formItem const NCheckboxGroup = inject(checkboxGroupInjectionKey, null) const uncontrolledCheckedRef = ref(props.defaultChecked) const controlledCheckedRef = toRef(props, 'checked') @@ -93,52 +146,6 @@ export default defineComponent({ return mergedCheckedRef.value } }) - const mergedDisabledRef = useMemo(() => { - if (props.disabled) return true - if (NCheckboxGroup) { - if (NCheckboxGroup.disabledRef.value) return true - const { - maxRef: { value: max }, - checkedCountRef - } = NCheckboxGroup - if ( - max !== undefined && - checkedCountRef.value >= max && - !renderedCheckedRef.value - ) { - return true - } - const { - minRef: { value: min } - } = NCheckboxGroup - if ( - min !== undefined && - checkedCountRef.value <= min && - renderedCheckedRef.value - ) { - return true - } - } - return false - }) - const formItem = useFormItem(props, { - mergedSize (NFormItem) { - const { size } = props - if (size !== undefined) return size - if (NCheckboxGroup) { - const { value: mergedSize } = NCheckboxGroup.mergedSizeRef - if (mergedSize !== undefined) { - return mergedSize - } - } - if (NFormItem) { - const { mergedSize } = NFormItem - if (mergedSize !== undefined) return mergedSize.value - } - return 'medium' - } - }) - const { mergedSizeRef } = formItem const themeRef = useTheme( 'Checkbox', 'Checkbox', diff --git a/src/checkbox/src/CheckboxGroup.tsx b/src/checkbox/src/CheckboxGroup.tsx index ca32adc2b0e..5c1ac71285a 100644 --- a/src/checkbox/src/CheckboxGroup.tsx +++ b/src/checkbox/src/CheckboxGroup.tsx @@ -37,7 +37,10 @@ const checkboxGroupProps = { type: Array as PropType | null>, default: null }, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, // eslint-disable-next-line vue/prop-name-casing 'onUpdate:value': [Function, Array] as PropType< MaybeArray<(value: Array) => void> @@ -73,6 +76,7 @@ export default defineComponent({ setup (props) { const { mergedClsPrefixRef } = useConfig(props) const formItem = useFormItem(props) + const { mergedSizeRef, mergedDisabledRef } = formItem const uncontrolledValueRef = ref(props.defaultValue) const controlledValueRef = computed(() => props.value) const mergedValueRef = useMergedState( @@ -148,8 +152,8 @@ export default defineComponent({ maxRef: toRef(props, 'max'), minRef: toRef(props, 'min'), valueSetRef: valueSetRef, - disabledRef: toRef(props, 'disabled'), - mergedSizeRef: formItem.mergedSizeRef, + disabledRef: mergedDisabledRef, + mergedSizeRef: mergedSizeRef, toggleCheckbox }) return { diff --git a/src/collapse/demos/enUS/index.demo-entry.md b/src/collapse/demos/enUS/index.demo-entry.md index fb6f1c7ef81..29eddc2d79c 100644 --- a/src/collapse/demos/enUS/index.demo-entry.md +++ b/src/collapse/demos/enUS/index.demo-entry.md @@ -22,9 +22,9 @@ customize-icon | --- | --- | --- | --- | | accordion | `boolean` | `false` | Whether to only allow on panel open. | | arrow-placement | `'left' \| 'right'` | `'left'` | Arrow placement. | -| default-expanded-names | `string \| number \| Array \| null` | `null` | If set to `accrodion`, it will be a non-array value. | +| default-expanded-names | `string \| number \| Array \| null` | `null` | Panel expanded in uncontrolled mode. If `accrodion` is set, it should be a non-array value. | | display-directive | `'if' \| 'show'` | `'if'` | The display directive to use when its inner `n-collapse-item` render content. `'if'` corresponds to `v-if` and `'show'` corresponds to `v-show`. | -| expanded-names | `string \| number \| Array \| null` | `undefined` | If set to `accrodion`, it will be a non-array value. | +| expanded-names | `string \| number \| Array \| null` | `undefined` | Expanded panel in controlled mode. If `accrodion` is set, it should be a non-array value. | | on-update:expanded-names | `(expandedNames: Array \| string \| number \| null) => void` | `undefined` | Callback function triggered when expanded-names changes. | | on-item-header-click | `(data: { name: string \| number, expanded: boolean, event: MouseEvent }) => void` | `undefined` | Callback function triggered when the title is clicked. | @@ -33,22 +33,22 @@ customize-icon | Name | Type | Default | Description | | --- | --- | --- | --- | | display-directive | `'if' \| 'show'` | `undefined` | The display directive to use when it is rendering its content. `'if'` corresponds to `v-if` and `'show'` corresponds to `v-show`. When it is set to `undefined` the value will follow its outer `n-collapse`. | -| name | `string \| number` | random string | Name of the collapse item. | +| name | `string \| number` | random string | Name. | | title | `string` | `undefined` | Title. | ## Slots ### Collapse Slots -| Name | Parameters | Description | -| ------- | ----------------------------------- | ------------------- | -| default | `()` | Collapse's content. | -| arrow | `(options: { collapsed: boolean })` | Collapse's icon. | +| Name | Parameters | Description | +| --- | --- | --- | +| default | `()` | The contents of the collapsible panel. | +| arrow | `(options: { collapsed: boolean })` | Custom icons for folding panels. | ### Collapse Item Slots | Name | Parameters | Description | | --- | --- | --- | -| default | `()` | Collapse item's content. | -| header | `()` | Collapse item's header. | -| arrow | `(options: { collapsed: boolean })` | Collapse item's icon. | +| default | `()` | The contents of the collapsible panel node. | +| header | `()` | The content of the header of the collapsed panel node. | +| arrow | `(options: { 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 6617ff1ee67..fb7f7e2071a 100644 --- a/src/collapse/demos/zhCN/index.demo-entry.md +++ b/src/collapse/demos/zhCN/index.demo-entry.md @@ -22,9 +22,9 @@ customize-icon | --- | --- | --- | --- | | accordion | `boolean` | `false` | 是否只允许展开一个面板 | | arrow-placement | `'left' \| 'right'` | `'left'` | 箭头位置 | -| default-expanded-names | `string \| number \| Array \| null` | `null` | `accordion` 模式时不为数组 | +| default-expanded-names | `string \| number \| Array \| null` | `null` | 非受控模式下展开的面板 `name`。`accordion` 模式时不为数组 | | display-directive | `'if' \| 'show'` | `'if'` | 内部 `n-collapse-item` 在控制内容是否渲染时使用的指令,`'if'` 对应 `v-if`,`'show'` 对应 `v-show` | -| expanded-names | `string \| number \| Array \| null` | `undefined` | `accordion` 模式时不为数组 | +| expanded-names | `string \| number \| Array \| null` | `undefined` | 受控模式下展开的面板的 `name`,`accordion` 模式时不为数组 | | on-update:expanded-names | `(expandedNames: Array \| string \| number \| null) => void` | `undefined` | 展开内容改变时触发的回调函数 | | on-item-header-click | `(data: { name: string \| number, expanded: boolean, event: MouseEvent }) => void` | `undefined` | 点击标题时触发的回调函数 | @@ -33,22 +33,22 @@ customize-icon | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | display-directive | `'if' \| 'show'` | `undefined` | 自身在控制内容是否渲染时使用的指令,`'if'` 对应 `v-if`,`'show'` 对应 `v-show`。在设定为 `undefined` 的时候跟随外层的 `n-collapse` | -| name | `string \| number` | 随机字符串 | name 值,会在选中时被用到 | +| name | `string \| number` | 随机字符串 | 名称 | | title | `string` | `undefined` | 标题 | ## Slots ### Collapse Slots -| 名称 | 参数 | 说明 | -| ------- | ----------------------------------- | ------------------- | -| default | `()` | Collapse 的内容 | -| arrow | `(options: { collapsed: boolean })` | Collapse 自定义图标 | +| 名称 | 参数 | 说明 | +| ------- | ----------------------------------- | -------------------- | +| default | `()` | 折叠面板的内容 | +| arrow | `(options: { collapsed: boolean })` | 折叠面板的自定义图标 | ### Collapse Item Slots -| 名称 | 参数 | 说明 | -| --- | --- | --- | -| default | `()` | Collapse Item 的内容 | -| header | `()` | Collapse Item 的 header | -| arrow | `(options: { collapsed: boolean })` | Collapse Item 自定义图标 | +| 名称 | 参数 | 说明 | +| ------- | ----------------------------------- | ---------------------------- | +| default | `()` | 折叠面板节点的内容 | +| header | `()` | 折叠面板节点头部的内容 | +| arrow | `(options: { collapsed: boolean })` | 折叠面板节点头部的自定义图标 | diff --git a/src/data-table/demos/enUS/ajax-usage.demo.md b/src/data-table/demos/enUS/ajax-usage.demo.md index 7b00abcef46..4ecadcffdf3 100644 --- a/src/data-table/demos/enUS/ajax-usage.demo.md +++ b/src/data-table/demos/enUS/ajax-usage.demo.md @@ -59,12 +59,14 @@ function query (page, pageSize = 10, order = 'ascend', filterValues = []) { ? orderedData.filter((row) => filterValues.includes(row.column2)) : orderedData const pagedData = filteredData.slice((page - 1) * pageSize, page * pageSize) + const total = filteredData.length const pageCount = Math.ceil(filteredData.length / pageSize) setTimeout( () => resolve({ pageCount, - data: pagedData + data: pagedData, + total }), 1500 ) @@ -81,7 +83,10 @@ export default { pagination: { page: 1, pageCount: 1, - pageSize: 10 + pageSize: 10, + prefix ({ itemCount }) { + return `Total is ${itemCount}.` + } }, loading: true } @@ -95,6 +100,7 @@ export default { ).then((data) => { this.data = data.data this.pagination.pageCount = data.pageCount + this.pagination.itemCount = data.total this.loading = false }) }, @@ -115,6 +121,7 @@ export default { this.Column1.sortOrder = !sorter ? false : sorter.order this.data = data.data this.pagination.pageCount = data.pageCount + this.pagination.itemCount = data.total this.loading = false }) } @@ -133,6 +140,7 @@ export default { this.Column2.filterOptionValues = filterValues this.data = data.data this.pagination.pageCount = data.pageCount + this.pagination.itemCount = data.total this.loading = false }) } @@ -151,6 +159,7 @@ export default { console.log(data.data) this.pagination.page = currentPage this.pagination.pageCount = data.pageCount + this.pagination.itemCount = data.total this.loading = false }) } diff --git a/src/data-table/demos/enUS/flex-height.demo.md b/src/data-table/demos/enUS/flex-height.demo.md index 9d37870432b..b9c70c9903b 100644 --- a/src/data-table/demos/enUS/flex-height.demo.md +++ b/src/data-table/demos/enUS/flex-height.demo.md @@ -1,6 +1,6 @@ -# 弹性高度 +# Flex height -如果你想设定表格的整体高度,你可以在设定好表格高度的情况下设定 `flex-height` 属性。 +If you want to set the overall height of the table, you can set the `flex-height` property. ```html diff --git a/src/data-table/demos/enUS/index.demo-entry.md b/src/data-table/demos/enUS/index.demo-entry.md index 3cbaa95dd20..7afefdd2730 100644 --- a/src/data-table/demos/enUS/index.demo-entry.md +++ b/src/data-table/demos/enUS/index.demo-entry.md @@ -110,7 +110,7 @@ flex-height | title | `string \| (() => VNodeChild)` | `undefined` | Column title, Can be a render function. | | titleRowSpan | `number` | `undefined` | The number of cells occupied by the title row. | | type | `'selection' \| 'expand'` | `undefined` | Column type. | -| width | `number \| string` | `undefined` | Width of the column, **required** when fixed. | +| width | `number` | `undefined` | Width of the column, **required** when fixed. | #### CreateSummary Type diff --git a/src/data-table/demos/zhCN/ajax-usage.demo.md b/src/data-table/demos/zhCN/ajax-usage.demo.md index d61e4cbffe5..6b328951a3e 100644 --- a/src/data-table/demos/zhCN/ajax-usage.demo.md +++ b/src/data-table/demos/zhCN/ajax-usage.demo.md @@ -59,12 +59,14 @@ function query (page, pageSize = 10, order = 'ascend', filterValues = []) { ? orderedData.filter((row) => filterValues.includes(row.column2)) : orderedData const pagedData = filteredData.slice((page - 1) * pageSize, page * pageSize) + const total = filteredData.length const pageCount = Math.ceil(filteredData.length / pageSize) setTimeout( () => resolve({ pageCount, - data: pagedData + data: pagedData, + total }), 1500 ) @@ -81,7 +83,11 @@ export default { pagination: { page: 1, pageCount: 1, - pageSize: 10 + pageSize: 10, + itemCount: 0, + prefix ({ itemCount }) { + return `共 ${itemCount} 项` + } }, loading: true } @@ -95,6 +101,7 @@ export default { ).then((data) => { this.data = data.data this.pagination.pageCount = data.pageCount + this.pagination.itemCount = data.total this.loading = false }) }, @@ -115,6 +122,7 @@ export default { this.Column1.sortOrder = !sorter ? false : sorter.order this.data = data.data this.pagination.pageCount = data.pageCount + this.pagination.itemCount = data.total this.loading = false }) } @@ -133,6 +141,7 @@ export default { this.Column2.filterOptionValues = filterValues this.data = data.data this.pagination.pageCount = data.pageCount + this.pagination.itemCount = data.total this.loading = false }) } @@ -151,6 +160,7 @@ export default { console.log(data.data) this.pagination.page = currentPage this.pagination.pageCount = data.pageCount + this.pagination.itemCount = data.total this.loading = false }) } diff --git a/src/data-table/demos/zhCN/index.demo-entry.md b/src/data-table/demos/zhCN/index.demo-entry.md index dc27439dca1..ae3c175c785 100644 --- a/src/data-table/demos/zhCN/index.demo-entry.md +++ b/src/data-table/demos/zhCN/index.demo-entry.md @@ -111,7 +111,7 @@ scroll-debug | title | `string \| (() => VNodeChild)` | `undefined` | 列的 title 信息,可以是渲染函数 | | titleRowSpan | `number` | `undefined` | title 行所占的单元格的个数 | | type | `'selection' \| 'expand'` | `undefined` | 列的类型 | -| width | `number \| string` | `undefined` | 列的宽度,在列固定时是**必需**的 | +| width | `number` | `undefined` | 列的宽度,在列固定时是**必需**的 | #### CreateSummary Type diff --git a/src/data-table/src/styles/index.cssr.ts b/src/data-table/src/styles/index.cssr.ts index 91845b34259..7f3f15ac0d4 100644 --- a/src/data-table/src/styles/index.cssr.ts +++ b/src/data-table/src/styles/index.cssr.ts @@ -82,7 +82,8 @@ export default c([ ]) ]) ]), - cB('base-loading', ` + c('>', [ + cB('base-loading', ` color: var(--loading-color); font-size: var(--loading-size); position: absolute; @@ -91,9 +92,10 @@ export default c([ transform: translateX(-50%) translateY(-50%); transition: color .3s var(--bezier); `, [ - fadeInScaleUpTransition({ - originalTransform: 'translateX(-50%) translateY(-50%)' - }) + fadeInScaleUpTransition({ + originalTransform: 'translateX(-50%) translateY(-50%)' + }) + ]) ]), cB('data-table-expand-trigger', 'cursor: pointer;'), cB('data-table-expand-placeholder', ` diff --git a/src/data-table/src/use-table-data.ts b/src/data-table/src/use-table-data.ts index 933d2f6d6a2..7d89229e665 100644 --- a/src/data-table/src/use-table-data.ts +++ b/src/data-table/src/use-table-data.ts @@ -123,10 +123,7 @@ export function useTableData ( const { pageCount } = pagination if (pageCount !== undefined) return pageCount } - const { value: filteredData } = filteredDataRef - if (filteredData.length === 0) return 1 - const { value: pageSize } = mergedPageSizeRef - return Math.ceil(filteredData.length / pageSize) + return undefined }) const mergedSortStateRef = computed(() => { @@ -318,7 +315,17 @@ export function useTableData ( doUpdatePageSize(pageSize) } } - + const mergedItemCountRef = computed(() => { + if (props.remote) { + const { pagination } = props + if (pagination) { + const { itemCount } = pagination + if (itemCount !== undefined) return itemCount + } + return undefined + } + return filteredDataRef.value.length + }) const mergedPaginationRef = computed(() => { return { ...props.pagination, @@ -332,20 +339,30 @@ export function useTableData ( // key still exists but value is undefined page: mergedCurrentPageRef.value, pageSize: mergedPageSizeRef.value, - pageCount: mergedPageCountRef.value + pageCount: + mergedItemCountRef.value === undefined + ? mergedPageCountRef.value + : undefined, + itemCount: mergedItemCountRef.value } }) function doUpdatePage (page: number): void { - const { 'onUpdate:page': onUpdatePage, onPageChange } = props + const { 'onUpdate:page': _onUpdatePage, onPageChange, onUpdatePage } = props if (onUpdatePage) call(onUpdatePage, page) if (onPageChange) call(onPageChange, page) + if (_onUpdatePage) call(_onUpdatePage, page) uncontrolledCurrentPageRef.value = page } function doUpdatePageSize (pageSize: number): void { - const { 'onUpdate:pageSize': onUpdatePageSize, onPageSizeChange } = props + const { + 'onUpdate:pageSize': _onUpdatePageSize, + onPageSizeChange, + onUpdatePageSize + } = props if (onPageSizeChange) call(onPageSizeChange, pageSize) if (onUpdatePageSize) call(onUpdatePageSize, pageSize) + if (_onUpdatePageSize) call(_onUpdatePageSize, pageSize) uncontrolledPageSizeRef.value = pageSize } function doUpdateSorter (sortState: SortState | null): void { diff --git a/src/date-picker/src/DatePicker.tsx b/src/date-picker/src/DatePicker.tsx index 545756ec522..7e57a57699f 100644 --- a/src/date-picker/src/DatePicker.tsx +++ b/src/date-picker/src/DatePicker.tsx @@ -77,8 +77,8 @@ const datePickerProps = { default: null }, disabled: { - type: Boolean, - default: false + type: Boolean as PropType, + default: undefined }, placement: { type: String as PropType, @@ -143,6 +143,7 @@ export default defineComponent({ setup (props, { slots }) { const { localeRef, dateLocaleRef } = useLocale('DatePicker') const formItem = useFormItem(props) + const { mergedSizeRef, mergedDisabledRef } = formItem const { NConfigProvider, mergedClsPrefixRef, @@ -375,7 +376,7 @@ export default defineComponent({ } } function handleInputDeactivate (): void { - if (props.disabled) return + if (mergedDisabledRef.value) return deriveInputState() closeCalendar({ returnFocus: false @@ -429,7 +430,7 @@ export default defineComponent({ } // --- Click function handleTriggerClick (e: MouseEvent): void { - if (props.disabled) return + if (mergedDisabledRef.value) return if (happensIn(e, 'clear')) return if (!mergedShowRef.value) { openCalendar() @@ -437,12 +438,12 @@ export default defineComponent({ } // --- Focus function handleInputFocus (e: FocusEvent): void { - if (props.disabled) return + if (mergedDisabledRef.value) return doFocus(e) } // --- Calendar function openCalendar (): void { - if (props.disabled || mergedShowRef.value) return + if (mergedDisabledRef.value || mergedShowRef.value) return doUpdateShow(true) } function closeCalendar ({ @@ -519,7 +520,8 @@ export default defineComponent({ isRange: isRangeRef, localizedStartPlaceholder: localizedStartPlaceholderRef, localizedEndPlaceholder: localizedEndPlaceholderRef, - mergedSize: formItem.mergedSizeRef, + mergedSize: mergedSizeRef, + mergedDisabled: mergedDisabledRef, localizedPlacehoder: localizedPlacehoderRef, isValueInvalid: uniVaidation.isValueInvalidRef, isStartValueInvalid: dualValidation.isStartValueInvalidRef, @@ -659,8 +661,8 @@ export default defineComponent({ bordered: this.mergedBordered, size: this.mergedSize, passivelyActivated: true, - disabled: this.disabled, - readonly: this.disabled, + disabled: this.mergedDisabled, + readonly: this.mergedDisabled, clearable, onClear: this.handleClear, onClick: this.handleTriggerClick, @@ -687,7 +689,7 @@ export default defineComponent({ ref="triggerElRef" class={[ `${mergedClsPrefix}-date-picker`, - this.disabled && `${mergedClsPrefix}-date-picker--disabled`, + this.mergedDisabled && `${mergedClsPrefix}-date-picker--disabled`, this.isRange && `${mergedClsPrefix}-date-picker--range` ]} style={this.triggerCssVars as CSSProperties} diff --git a/src/descriptions/demos/enUS/index.demo-entry.md b/src/descriptions/demos/enUS/index.demo-entry.md index 33a835a862c..62528d1de43 100644 --- a/src/descriptions/demos/enUS/index.demo-entry.md +++ b/src/descriptions/demos/enUS/index.demo-entry.md @@ -23,8 +23,10 @@ size | --- | --- | --- | --- | | bordered | `boolean` | `false` | Whether to display border. | | column | `number` | `3` | Total columns. | +| content-style | `Object \| string` | `undefined` | Style of the item content. | | label-align | `'center' \| 'left' \| 'right'` | `'left'` | Label align. | | label-placement | `'top' \| 'left'` | `'top'` | Label placement. | +| label-style | `Object \| string` | `undefined` | Style of the item label. | | size | `'small' \| 'medium' \| 'large'` | `'medium'` | Size of the description. | | title | `string` | `undefined` | Title of the description. | @@ -32,7 +34,9 @@ size | Name | Type | Default | Description | | ----- | -------- | ----------- | ------------------------ | +| content-style | `Object \| string` | `undefined` | Style of the item content. | | label | `string` | `undefined` | Label of the item. | +| label-style | `Object \| string` | `undefined` | Style of the item label. | | span | `number` | `1` | Column span of the item. | ## Slots diff --git a/src/descriptions/demos/zhCN/index.demo-entry.md b/src/descriptions/demos/zhCN/index.demo-entry.md index 668c1eb09ab..07d3cf0f8d8 100644 --- a/src/descriptions/demos/zhCN/index.demo-entry.md +++ b/src/descriptions/demos/zhCN/index.demo-entry.md @@ -23,8 +23,10 @@ size | --- | --- | --- | --- | | bordered | `boolean` | `false` | 是否显示 border | | column | `number` | `3` | 设置的总列数 | +| content-style | `Object \| string` | `undefined` | 内容的样式 | | label-align | `'center' \| 'left' \| 'right'` | `'left'` | label 对齐方式 | | label-placement | `'top' \| 'left'` | `'top'` | label 显示位置 | +| label-style | `Object \| string` | `undefined` | label的样式 | | size | `'small' \| 'medium' \| 'large'` | `'medium'` | 尺寸 | | title | `string` | `undefined` | 标题 | @@ -32,7 +34,9 @@ size | 名称 | 类型 | 默认值 | 说明 | | ----- | -------- | ----------- | --------------- | +| content-style | `Object \| string` | `undefined` | 内容的样式 | | label | `string` | `undefined` | 显示的 label 值 | +| label-style | `Object \| string` | `undefined` | label的样式 | | span | `number` | `1` | 所占的单元格数 | ## Slots diff --git a/src/descriptions/src/Descriptions.tsx b/src/descriptions/src/Descriptions.tsx index 583aebf30e8..bab1fbbb50c 100644 --- a/src/descriptions/src/Descriptions.tsx +++ b/src/descriptions/src/Descriptions.tsx @@ -48,7 +48,9 @@ const descriptionProps = { bordered: { type: Boolean, default: false - } + }, + labelStyle: [Object, String] as PropType, + contentStyle: [Object, String] as PropType } as const export type DescriptionProps = ExtractPublicPropTypes @@ -162,12 +164,15 @@ export default defineComponent({ const itemSpan = (props.span as number) || 1 const memorizedSpan = state.span state.span += itemSpan + const labelStyle = props.labelStyle || props['label-style'] || this.labelStyle + const contentStyle = props.contentStyle || props['content-style'] || this.contentStyle if (labelPlacement === 'left') { if (bordered) { state.row.push( {itemLabel} , @@ -178,6 +183,7 @@ export default defineComponent({ ? (compitableColumn - memorizedSpan) * 2 + 1 : itemSpan * 2 - 1 } + style={contentStyle} > {itemChildren} @@ -194,6 +200,7 @@ export default defineComponent({ > {itemChildren} @@ -218,6 +226,7 @@ export default defineComponent({ {itemLabel} @@ -226,6 +235,7 @@ export default defineComponent({ {itemChildren} diff --git a/src/descriptions/src/DescriptionsItem.ts b/src/descriptions/src/DescriptionsItem.ts index 265751c4d57..54c7edb4fff 100644 --- a/src/descriptions/src/DescriptionsItem.ts +++ b/src/descriptions/src/DescriptionsItem.ts @@ -1,4 +1,8 @@ -import { defineComponent } from 'vue' +import { + defineComponent, + PropType, + CSSProperties +} from 'vue' import type { ExtractPublicPropTypes } from '../../_utils' import { DESCRIPTION_ITEM_FLAG } from './utils' @@ -7,7 +11,9 @@ const descriptionItemProps = { span: { type: Number, default: 1 - } + }, + labelStyle: [Object, String] as PropType, + contentStyle: [Object, String] as PropType } as const export type DescriptionItemProps = ExtractPublicPropTypes< diff --git a/src/dialog/demos/enUS/index.demo-entry.md b/src/dialog/demos/enUS/index.demo-entry.md index 7db96a5403c..2c8deaefdc1 100644 --- a/src/dialog/demos/enUS/index.demo-entry.md +++ b/src/dialog/demos/enUS/index.demo-entry.md @@ -5,6 +5,7 @@ Before taking action, please confirm. If you want use dialog, you need to wrap the component where you call related methods inside n-dialog-provider and use useDialog to get the API. + For example: ```html @@ -48,31 +49,31 @@ action | --- | --- | --- | | destroyAll | `() => void` | Destroy all popup dialogs. | | create | `(options: DialogOptions) => DialogReactive` | Create a dialog. | -| error | `(options: DialogOptions) => DialogReactive` | Use error type dialog. | -| info | `(options: DialogOptions) => DialogReactive` | Use info type dialog. | -| success | `(options: DialogOptions) => DialogReactive` | Use success type dialog. | -| warning | `(options: DialogOptions) => DialogReactive` | Use warning type dialog. | +| error | `(options: DialogOptions) => DialogReactive` | Use `error` type dialog. | +| info | `(options: DialogOptions) => DialogReactive` | Use `info` type dialog. | +| success | `(options: DialogOptions) => DialogReactive` | Use `success` type dialog. | +| warning | `(options: DialogOptions) => DialogReactive` | Use `warning` type dialog. | ### DialogOptions Properties | Name | Type | Default | Description | | --- | --- | --- | --- | -| action | `() => VNodeChild` | `undefined` | Content of the operation area, must be a render function. | -| bordered | `boolean` | `false` | Whether to show border. | -| closable | `boolean` | `true` | Whether to show close icon. | -| content | `string \| (() => VNodeChild)` | `undefined` | Content, can be a render function. | +| action | `() => VNodeChild` | `undefined` | Content of the operation area, must be a `render` function. | +| bordered | `boolean` | `false` | Whether to show `border`. | +| closable | `boolean` | `true` | Whether to show `close` icon. | +| content | `string \| (() => VNodeChild)` | `undefined` | Content, can be a `render` function. | | iconPlacement | `'left' \| 'top'` | `'left'` | Icon placement. | -| icon | `() => VNodeChild` | `undefined` | Render function of icon. | -| loading | `boolean` | `false` | Whether to display loading status. | -| maskClosable | `boolean` | `true` | Whether the dialog can be closed by clicking the mask. | +| icon | `() => VNodeChild` | `undefined` | `Render` function of `icon`. | +| loading | `boolean` | `false` | Whether to display `loading` status. | +| maskClosable | `boolean` | `true` | Whether the dialog can be closed by clicking the `mask`. | | negativeText | `string` | `undefined` | Corresponding button won't show if not set. | | positiveText | `string` | `undefined` | Corresponding button won't show if not set. | -| show-icon | `boolean` | `true` | Whether to show icon. | -| title | `string \| (() => VNodeChild)` | `undefined` | Title, can be a render function. | +| show-icon | `boolean` | `true` | Whether to show `icon`. | +| title | `string \| (() => VNodeChild)` | `undefined` | Title, can be a `render` function. | | type | `'error \| 'success' \| 'warning'` | `'warning'` | Dialog type. | -| onClose | `() => boolean \| Promise \| any` | `undefined` | The default behavior is closing the confirm. Return `false` or resolve `false` or Promise rejected will prevent the default behavior. | -| onNegativeClick | `() => boolean \| Promise \| any` | `undefined` | The default behavior is closing the confirm. Return `false` or resolve `false` or Promise rejected will prevent the default behavior. | -| onPositiveClick | `() => boolean \| Promise \| any` | `undefined` | The default behavior is closing the confirm. Return `false` or resolve `false` or Promise rejected will prevent the default behavior. | +| onClose | `() => boolean \| Promise \| any` | `undefined` | The default behavior is closing the confirm. Return `false` or resolve `false` or `Promise rejected` will prevent the default behavior. | +| onNegativeClick | `() => boolean \| Promise \| any` | `undefined` | The default behavior is closing the confirm. Return `false` or resolve `false` or `Promise rejected` will prevent the default behavior. | +| onPositiveClick | `() => boolean \| Promise \| any` | `undefined` | The default behavior is closing the confirm. Return `false` or resolve `false` or `Promise rejected` will prevent the default behavior. | | onMaskClick | `() => void` | `undefined` | Callback triggered when click the mask. | ### DialogReactive API @@ -83,27 +84,27 @@ All the properties can be modified dynamically. | Name | Type | Description | | --- | --- | --- | -| bordered | `boolean` | Whether to show border. | -| closable | `boolean` | Whether to show close icon. | -| content | `string \| (() => VNodeChild)` | Content, can be a render function. | +| bordered | `boolean` | Whether to show `border`. | +| closable | `boolean` | Whether to show `close` icon. | +| content | `string \| (() => VNodeChild)` | Content, can be a `render` function. | | iconPlacement | `'left' \| 'top'` | Icon placement. | -| icon | `() => VNodeChild` | Render function of icon. | -| loading | `boolean` | Whether to display loading status. | -| maskClosable | `boolean` | Whether the dialog can be closed by clicking the mask. | +| icon | `() => VNodeChild` | `Render` function of `icon`. | +| loading | `boolean` | Whether to display `loading` status. | +| maskClosable | `boolean` | Whether the dialog can be closed by clicking the `mask`. | | negativeText | `string` | Corresponding button won't show if not set. | | positiveText | `string` | Corresponding button won't show if not set. | -| show-icon | `boolean` | Whether to show icon. | -| title | `string \| (() => VNodeChild)` | Can be a render function. | +| show-icon | `boolean` | Whether to show `icon`. | +| title | `string \| (() => VNodeChild)` | Can be a `render` function. | | type | `'error \| 'success' \| 'warning'` | Dialog type. | -| onClose | `() => boolean \| Promise \| any` | The default behavior is closing the confirm. Return `false` or resolve `false` or Promise rejected will prevent the default behavior. | -| onNegativeClick | `() => boolean \| Promise \| any` | The default behavior is closing the confirm. Return `false` or resolve `false` or Promise rejected will prevent the default behavior. | -| onPositiveClick | `() => boolean \| Promise \| any` | The default behavior is closing the confirm. Return `false` or resolve `false` or Promise rejected will prevent the default behavior. | +| onClose | `() => boolean \| Promise \| any` | The default behavior is closing the confirm. Return `false` or `resolve false` or `Promise rejected` will prevent the default behavior. | +| onNegativeClick | `() => boolean \| Promise \| any` | The default behavior is closing the confirm. Return `false` or `resolve false` or `Promise rejected` will prevent the default behavior. | +| onPositiveClick | `() => boolean \| Promise \| any` | The default behavior is closing the confirm. Return `false` or `resolve false` or `Promise rejected` will prevent the default behavior. | #### DialogReactive Methods -| Name | Type | Description | -| ------- | ---- | ------------- | -| destroy | `()` | Close dialog. | +| Name | Type | Description | +| ------- | ---- | --------------- | +| destroy | `()` | Close `dialog`. | ## Props @@ -111,28 +112,28 @@ All the properties can be modified dynamically. | Name | Type | Default | Description | | --- | --- | --- | --- | -| bordered | `boolean` | `false` | Whether to show border. | -| closable | `boolean` | `true` | Whether to show close icon. | -| content | `string \| (() => VNodeChild)` | `undefined` | Can be a render function. | +| bordered | `boolean` | `false` | Whether to show `border`. | +| closable | `boolean` | `true` | Whether to show `close` icon. | +| content | `string \| (() => VNodeChild)` | `undefined` | Can be a `render` function. | | icon-placement | `'left' \| 'top'` | `'left'` | Icon placement. | -| icon | `() => VNodeChild` | `undefined` | Render function of icon. | -| loading | `boolean` | `false` | Whether to display loading status. | +| icon | `() => VNodeChild` | `undefined` | `Render` function of icon. | +| loading | `boolean` | `false` | Whether to display `loading` status. | | negative-text | `string` | `undefined` | Corresponding button won't show if not set. | | positive-text | `string` | `undefined` | Corresponding button won't show if not set. | -| show-icon | `boolean` | `true` | | -| title | `string \| (() => VNodeChild)` | `undefined` | Title, can be a render function. | +| show-icon | `boolean` | `true` | Whether to display the `icon`. | +| title | `string \| (() => VNodeChild)` | `undefined` | Title, can be a `render` function. | | type | `'error \| 'success' \| 'warning'` | `'warning'` | Dialog type. | -| on-close | `() => void` | Callback function triggered when close dialog, the default behavior is closing the confirm. | -| on-negative-click | `() => void` | Callback function triggered when click negative text, the default behavior is closing the confirm. | -| on-positive-click | `() => void` | Callback function triggered when click positive text, the default behavior is closing the confirm. | +| on-close | `() => void` | `undefined` | The default behavior is closing the confirm. Return `false` or `resolve false` or `Promise rejected` will prevent the default behavior. | +| on-negative-click | `() => void` | `undefined` | The default behavior is closing the confirm. Return `false` or `resolve false` or `Promise rejected` will prevent the default behavior. | +| on-positive-click | `() => void` | `undefined` | The default behavior is closing the confirm. Return `false` or `resolve false` or `Promise rejected` will prevent the default behavior. | ## Slots ### Dialog Slots -| Name | Parameters | Description | -| ------- | ---------- | --------------- | -| action | `()` | Action content. | -| default | `()` | Dialog content. | -| header | `()` | Header content. | -| icon | `()` | Icon content. | +| Name | Parameters | Description | +| ------- | ---------- | ----------------- | +| action | `()` | `Action` content. | +| default | `()` | Dialog content. | +| header | `()` | `Header` content. | +| icon | `()` | `Icon` content. | diff --git a/src/dialog/demos/zhCN/index.demo-entry.md b/src/dialog/demos/zhCN/index.demo-entry.md index 64ca7e49b61..0c1b1468d22 100644 --- a/src/dialog/demos/zhCN/index.demo-entry.md +++ b/src/dialog/demos/zhCN/index.demo-entry.md @@ -49,31 +49,31 @@ action | --- | --- | --- | | destroyAll | `() => void` | 销毁所有弹出的对话框 | | create | `(options: DialogOptions) => DialogReactive` | 创建对话框 | -| error | `(options: DialogOptions) => DialogReactive` | 调用 error 类型的对话框 | -| info | `(options: DialogOptions) => DialogReactive` | 调用 info 类型的对话框 | -| success | `(options: DialogOptions) => DialogReactive` | 调用 success 类型的对话框 | -| warning | `(options: DialogOptions) => DialogReactive` | 调用 warning 类型的对话框 | +| error | `(options: DialogOptions) => DialogReactive` | 调用 `error` 类型的对话框 | +| info | `(options: DialogOptions) => DialogReactive` | 调用 `info` 类型的对话框 | +| success | `(options: DialogOptions) => DialogReactive` | 调用 `success` 类型的对话框 | +| warning | `(options: DialogOptions) => DialogReactive` | 调用 `warning` 类型的对话框 | ### DialogOptions Properties | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| action | `() => VNodeChild` | `undefined` | 操作区域的内容,需要是 render 函数 | -| bordered | `boolean` | `false` | 是否显示 border | -| closable | `boolean` | `true` | 是否显示 close 图标 | -| content | `string \| (() => VNodeChild)` | `undefined` | 对话框内容,可以是 render 函数 | +| action | `() => VNodeChild` | `undefined` | 操作区域的内容,需要是 `render` 函数 | +| bordered | `boolean` | `false` | 是否显示 `border` | +| closable | `boolean` | `true` | 是否显示 `close` 图标 | +| content | `string \| (() => VNodeChild)` | `undefined` | 对话框内容,可以是 `render` 函数 | | icon-placement | `'left' \| 'top'` | `'left'` | 图标的位置 | -| icon | `() => VNodeChild` | `undefined` | 对话框 icon, 需要是 render 函数 | -| loading | `boolean` | `false` | 是否显示 loading 状态 | -| mask-closable | `boolean` | `true` | 是否可以通过点击 mask 关闭对话框 | +| icon | `() => VNodeChild` | `undefined` | 对话框 `icon`, 需要是 `render` 函数 | +| loading | `boolean` | `false` | 是否显示 `loading` 状态 | +| mask-closable | `boolean` | `true` | 是否可以通过点击 `mask` 关闭对话框 | | negative-text | `string` | `undefined` | 不填对应的按钮不会出现 | | positive-text | `string` | `undefined` | 不填对应的按钮不会出现 | -| show-icon | `boolean` | `true` | 是否显示 icon | -| title | `string \| (() => VNodeChild)` | `undefined` | 标题,可以是 render 函数 | +| show-icon | `boolean` | `true` | 是否显示 `icon` | +| title | `string \| (() => VNodeChild)` | `undefined` | 标题,可以是 `render` 函数 | | type | `'error \| 'success' \| 'warning'` | `'warning'` | 对话框类型 | -| onClose | `() => boolean \| Promise \| any` | `undefined` | 默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 Promise 被 reject 会避免默认行为 | -| onNegativeClick | `() => boolean \| Promise \| any` | `undefined` | 默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 Promise 被 reject 会避免默认行为 | -| onPositiveClick | `() => boolean \| Promise \| any` | `undefined` | 默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 Promise 被 reject 会避免默认行为 | +| onClose | `() => boolean \| Promise \| any` | `undefined` | 默认行为是关闭确认框。返回 `false` 或者 `resolve false` 或者 `Promise` 被 `reject` 会避免默认行为 | +| onNegativeClick | `() => boolean \| Promise \| any` | `undefined` | 默认行为是关闭确认框。返回 `false` 或者 `resolve false` 或者 `Promise` 被 `reject` 会避免默认行为 | +| onPositiveClick | `() => boolean \| Promise \| any` | `undefined` | 默认行为是关闭确认框。返回 `false` 或者 `resolve false` 或者 `Promise` 被 `reject` 会避免默认行为 | | onMaskClick | `() => void` | `undefined` | 点击蒙层后执行的回调 | ### DialogReactive API @@ -84,27 +84,27 @@ action | 名称 | 类型 | 说明 | | --- | --- | --- | -| bordered | `boolean` | 是否显示 border | -| closable | `boolean` | 是否显示 close 图标 | -| content | `string \| (() => VNodeChild)` | 对话框内容,可以是 render 函数 | +| bordered | `boolean` | 是否显示 `border` | +| closable | `boolean` | 是否显示 `close` 图标 | +| content | `string \| (() => VNodeChild)` | 对话框内容,可以是 `render` 函数 | | iconPlacement | `'left' \| 'top'` | 图标的位置 | -| icon | `() => VNodeChild` | 对话框 icon,需要是 render 函数 | -| loading | `boolean` | 是否显示 loading 状态 | -| maskClosable | `boolean` | 是否可以通过点击 mask 关闭对话框 | +| icon | `() => VNodeChild` | 对话框 `icon`,需要是 `render` 函数 | +| loading | `boolean` | 是否显示 `loading` 状态 | +| maskClosable | `boolean` | 是否可以通过点击 `mask` 关闭对话框 | | negativeText | `string` | 不填对应的按钮不会出现 | | positiveText | `string` | 不填对应的按钮不会出现 | -| showIcon | `boolean` | 是否显示 icon | -| title | `string \| (() => VNodeChild)` | 可以是 render 函数 | +| showIcon | `boolean` | 是否显示 `icon` | +| title | `string \| (() => VNodeChild)` | 可以是 `render` 函数 | | type | `'error \| 'success' \| 'warning'` | 对话框类型 | -| onClose | `() => boolean \| Promise \| any` | 默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 Promise 被 reject 会避免默认行为 | -| onNegativeClick | `() => boolean \| Promise \| any` | 默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 Promise 被 reject 会避免默认行为 | -| onPositiveClick | `() => boolean \| Promise \| any` | 默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 Promise 被 reject 会避免默认行为 | +| onClose | `() => boolean \| Promise \| any` | 默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 `Promise` 被 `reject` 会避免默认行为 | +| onNegativeClick | `() => boolean \| Promise \| any` | 默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 `Promise` 被 `reject` 会避免默认行为 | +| onPositiveClick | `() => boolean \| Promise \| any` | 默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 `Promise` 被 `reject` 会避免默认行为 | #### DialogReactive Methods -| 名称 | 类型 | 说明 | -| ------- | ---- | ----------- | -| destroy | `()` | 关闭 Dialog | +| 名称 | 类型 | 说明 | +| ------- | ---- | ------------- | +| destroy | `()` | 关闭 `Dialog` | ## Props @@ -112,27 +112,28 @@ action | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| bordered | `boolean` | `false` | 是否显示 border | -| closable | `boolean` | `true` | 是否显示 close 图标 | -| content | `string \| (() => VNodeChild)` | `undefined` | 对话框内容,可以是 render 函数 | -| icon | `() => VNodeChild` | `undefined` | 需要是 render 函数 | -| loading | `boolean` | `false` | 是否显示 loading 状态 | +| bordered | `boolean` | `false` | 是否显示 `border` | +| closable | `boolean` | `true` | 是否显示 `close` 图标 | +| content | `string \| (() => VNodeChild)` | `undefined` | 对话框内容,可以是 `render` 函数 | +| icon-placement | `'left' \| 'top'` | `'left'` | 图标放置的位置 | +| icon | `() => VNodeChild` | `undefined` | 需要是 `render` 函数 | +| loading | `boolean` | `false` | 是否显示 `loading` 状态 | | negative-text | `string` | `undefined` | 不填对应的按钮不会出现 | | positive-text | `string` | `undefined` | 不填对应的按钮不会出现 | -| show-icon | `boolean` | `true` | 是否显示 icon | -| title | `string \| (() => VNodeChild)` | `undefined` | 对话框标题,可以是 render 函数 | +| show-icon | `boolean` | `true` | 是否显示 `icon` | +| title | `string \| (() => VNodeChild)` | `undefined` | 对话框标题,可以是 `render` 函数 | | type | `'error \| 'success' \| 'warning'` | `'warning'` | 对话框类型 | -| on-close | `() => void` | 关闭时执行的回调函数,默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 Promise 被 reject 会避免默认行为 | -| on-negative-click | `() => void` | 执行 negative 时执行的回调函数,默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 Promise 被 reject 会避免默认行为 | -| on-positive-click | `() => void` | 执行 positive 时执行的回调函数,默认行为是关闭确认框。返回 `false` 或者 resolve `false` 或者 Promise 被 reject 会避免默认行为 | +| on-close | `() => void` | `undefined` | 关闭时执行的回调函数,默认行为是关闭确认框。返回 `false` 或者 `resolve false` 或者 `Promise` 被 `reject` 会避免默认行为 | +| on-negative-click | `() => void` | `undefined` | 执行 `negative` 时执行的回调函数,默认行为是关闭确认框。返回 `false` 或者 `resolve false` 或者 `Promise` 被 `reject` 会避免默认行为 | +| on-positive-click | `() => void` | `undefined` | 执行 `positive` 时执行的回调函数,默认行为是关闭确认框。返回 `false` 或者 `resolve false` 或者 `Promise` 被 `reject` 会避免默认行为 | ## Slots ### Dialog Slots -| 名称 | 参数 | 说明 | -| ------- | ---- | ----------- | -| action | `()` | action 内容 | -| default | `()` | 对话框内容 | -| header | `()` | header 内容 | -| icon | `()` | icon 内容 | +| 名称 | 参数 | 说明 | +| ------- | ---- | ------------- | +| action | `()` | `action` 内容 | +| default | `()` | 对话框内容 | +| header | `()` | `header` 内容 | +| icon | `()` | `icon` 内容 | diff --git a/src/divider/demos/zhCN/index.demo-entry.md b/src/divider/demos/zhCN/index.demo-entry.md index 287919a0199..af90f26a221 100644 --- a/src/divider/demos/zhCN/index.demo-entry.md +++ b/src/divider/demos/zhCN/index.demo-entry.md @@ -15,7 +15,7 @@ vertical | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | dashed | `boolean` | `false` | 是否使用虚线分割 | -| title-placement | `'left' \| 'right' \| 'center'` | `'center'` | title 的位置 | +| title-placement | `'left' \| 'right' \| 'center'` | `'center'` | 标题的位置 | | vertical | `boolean` | `false` | 是否垂直分隔 | ## Slots diff --git a/src/drawer/demos/enUS/index.demo-entry.md b/src/drawer/demos/enUS/index.demo-entry.md index be0d5dd10d6..f0aac4e7ee8 100644 --- a/src/drawer/demos/enUS/index.demo-entry.md +++ b/src/drawer/demos/enUS/index.demo-entry.md @@ -9,6 +9,7 @@ basic multiple target closable +slot ``` ## Props diff --git a/src/drawer/demos/enUS/slot.demo.md b/src/drawer/demos/enUS/slot.demo.md new file mode 100644 index 00000000000..90b56eb7073 --- /dev/null +++ b/src/drawer/demos/enUS/slot.demo.md @@ -0,0 +1,32 @@ +# Custom Header and Bottom Content + +```html + + Open + + + + + + + +``` + +```js +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { + const active = ref(false) + const activate = () => { + active.value = true + } + return { + active, + activate + } + } +}) +``` diff --git a/src/drawer/demos/zhCN/index.demo-entry.md b/src/drawer/demos/zhCN/index.demo-entry.md index ba35d9f2e2d..74dcfeb96f7 100644 --- a/src/drawer/demos/zhCN/index.demo-entry.md +++ b/src/drawer/demos/zhCN/index.demo-entry.md @@ -9,6 +9,7 @@ basic multiple target closable +slot custom-style-debug dark-1-debug dark-2-debug diff --git a/src/drawer/demos/zhCN/slot.demo.md b/src/drawer/demos/zhCN/slot.demo.md new file mode 100644 index 00000000000..de1935b04ea --- /dev/null +++ b/src/drawer/demos/zhCN/slot.demo.md @@ -0,0 +1,32 @@ +# 自定义头部和底部内容 + +```html + + 打开 + + + + + + + +``` + +```js +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { + const active = ref(false) + const activate = () => { + active.value = true + } + return { + active, + activate + } + } +}) +``` diff --git a/src/drawer/styles/light.ts b/src/drawer/styles/light.ts index cf8bf0c31c7..aa27fe79f1a 100644 --- a/src/drawer/styles/light.ts +++ b/src/drawer/styles/light.ts @@ -23,7 +23,7 @@ export const self = (vars: ThemeCommonVars) => { color: modalColor, textColor: textColor2, titleTextColor: textColor1, - titleFontSize: '20px', + titleFontSize: '18px', titleFontWeight: fontWeightStrong, boxShadow: boxShadow3, lineHeight, diff --git a/src/dropdown/demos/enUS/arrow.demo.md b/src/dropdown/demos/enUS/arrow.demo.md new file mode 100644 index 00000000000..f718b225c3e --- /dev/null +++ b/src/dropdown/demos/enUS/arrow.demo.md @@ -0,0 +1,49 @@ +# Basic + +Show Arrow + +```html + + Go For a Trip + +``` + +```js +import { defineComponent } from 'vue' +import { useMessage } from 'naive-ui' + +export default defineComponent({ + setup () { + const message = useMessage() + return { + options: [ + { + label: 'Marina Bay Sands', + key: 'marina bay sands', + disabled: true + }, + { + label: "Brown's Hotel, London", + key: "brown's hotel, london" + }, + { + label: 'Atlantis Bahamas, Nassau', + key: 'atlantis nahamas, nassau' + }, + { + label: 'The Beverly Hills Hotel, Los Angeles', + key: 'the beverly hills hotel, los angeles' + } + ], + handleSelect (key) { + message.info(key) + } + } + } +}) +``` diff --git a/src/dropdown/demos/enUS/icon.demo.md b/src/dropdown/demos/enUS/icon.demo.md new file mode 100644 index 00000000000..77edf3ddb19 --- /dev/null +++ b/src/dropdown/demos/enUS/icon.demo.md @@ -0,0 +1,49 @@ +# Icon + +```html + + User profile + +``` + +```js +import { h, defineComponent } from 'vue' +import { NIcon } from 'naive-ui' +import { + PersonCircleOutline as UserIcon, + Pencil as EditIcon, + LogOutOutline as LogoutIcon +} from '@vicons/ionicons5' + +const renderIcon = (icon) => { + return () => { + return h(NIcon, null, { + default: () => h(icon) + }) + } +} + +export default defineComponent({ + setup () { + return { + options: [ + { + label: 'Profile', + key: 'profile', + icon: renderIcon(UserIcon) + }, + { + label: 'Edit Profile', + key: 'editProfile', + icon: renderIcon(EditIcon) + }, + { + label: 'Logout', + key: 'logout', + icon: renderIcon(LogoutIcon) + } + ] + } + } +}) +``` diff --git a/src/dropdown/demos/enUS/index.demo-entry.md b/src/dropdown/demos/enUS/index.demo-entry.md index 3d7bdf2d287..457f1d2be6f 100644 --- a/src/dropdown/demos/enUS/index.demo-entry.md +++ b/src/dropdown/demos/enUS/index.demo-entry.md @@ -6,8 +6,10 @@ When you have some functions to trigger. ```demo basic +icon trigger cascade +arrow placement size manual-position @@ -28,7 +30,7 @@ batch-render | on-clickoutside | `(e: MouseEvent) => void` | `undefined` | Callback function triggered when clickoutside. | | on-select | `(key: string \| number) => void` | `undefined` | Callback function triggered on blur. | -For other props, for example `placement`, please see [Popover Props](popover#Props). Note that `arrow`, `raw` is not available. +For other props, for example `placement`, please see [Popover Props](popover#Props). Note that `raw` is not available. ### DropdownOption Type diff --git a/src/dropdown/demos/enUS/manual-position.demo.md b/src/dropdown/demos/enUS/manual-position.demo.md index 511e17166ba..7133b480b26 100644 --- a/src/dropdown/demos/enUS/manual-position.demo.md +++ b/src/dropdown/demos/enUS/manual-position.demo.md @@ -2,6 +2,8 @@ For some special case, you may want to manually position the dropdown. For example, right click to activate dropdown in some area. +Warn: when manually positioned, the `trigger` prop must be `'manual'`. + ```html
+ 找个地方休息 + +``` + +```js +import { defineComponent } from 'vue' +import { useMessage } from 'naive-ui' + +export default defineComponent({ + setup () { + const message = useMessage() + return { + options: [ + { + label: '滨海湾金沙,新加坡', + key: 'marina bay sands', + disabled: true + }, + { + label: '布朗酒店,伦敦', + key: "brown's hotel, london" + }, + { + label: '亚特兰蒂斯巴哈马,拿骚', + key: 'atlantis nahamas, nassau' + }, + { + label: '比佛利山庄酒店,洛杉矶', + key: 'the beverly hills hotel, los angeles' + } + ], + handleSelect (key) { + message.info(key) + } + } + } +}) +``` diff --git a/src/dropdown/demos/zhCN/icon.demo.md b/src/dropdown/demos/zhCN/icon.demo.md new file mode 100644 index 00000000000..81b0e613a59 --- /dev/null +++ b/src/dropdown/demos/zhCN/icon.demo.md @@ -0,0 +1,49 @@ +# 图标 + +```html + + 用户资料 + +``` + +```js +import { h, defineComponent } from 'vue' +import { NIcon } from 'naive-ui' +import { + PersonCircleOutline as UserIcon, + Pencil as EditIcon, + LogOutOutline as LogoutIcon +} from '@vicons/ionicons5' + +const renderIcon = (icon) => { + return () => { + return h(NIcon, null, { + default: () => h(icon) + }) + } +} + +export default defineComponent({ + setup () { + return { + options: [ + { + label: '用户资料', + key: 'profile', + icon: renderIcon(UserIcon) + }, + { + label: '编辑用户资料', + key: 'editProfile', + icon: renderIcon(EditIcon) + }, + { + label: '退出登录', + key: 'logout', + icon: renderIcon(LogoutIcon) + } + ] + } + } +}) +``` diff --git a/src/dropdown/demos/zhCN/index.demo-entry.md b/src/dropdown/demos/zhCN/index.demo-entry.md index f5f257e9666..d60f9ed2a97 100644 --- a/src/dropdown/demos/zhCN/index.demo-entry.md +++ b/src/dropdown/demos/zhCN/index.demo-entry.md @@ -6,8 +6,10 @@ ```demo basic +icon trigger cascade +arrow placement size manual-position @@ -29,7 +31,7 @@ batch-render | on-clickoutside | `(e: MouseEvent) => void` | `undefined` | clickoutside 的时候触发的回调函数 | | on-select | `(key: string \| number) => void` | `undefined` | select 选中时触发的回调函数 | -对于其他 Props,例如 `placement`,请参考 [Popover Props](popover#Props)。注意 `arrow`、`raw` 属性不可用。 +对于其他 Props,例如 `placement`,请参考 [Popover Props](popover#Props)。注意 `raw` 属性不可用。 ### DropdownOption Type diff --git a/src/dropdown/demos/zhCN/manual-position.demo.md b/src/dropdown/demos/zhCN/manual-position.demo.md index 94b391596db..36011509727 100644 --- a/src/dropdown/demos/zhCN/manual-position.demo.md +++ b/src/dropdown/demos/zhCN/manual-position.demo.md @@ -2,6 +2,8 @@ 在特殊情况下,你可能想手动定位下拉菜单。比如在一块区域右击以弹出下拉菜单。 +注意:手动定位时,`trigger` 属性必须为 `'manual'` + ```html
, default: () => [] }, + showArrow: Boolean, // for menu, not documented value: [String, Number] as PropType, renderLabel: Function as PropType, @@ -389,10 +390,16 @@ export default defineComponent({ const { mergedClsPrefix } = this const dropdownProps = { ref: createRefSetter(ref), - class: [className, `${mergedClsPrefix}-dropdown`], + class: [ + className, + `${mergedClsPrefix}-dropdown`, + this.showArrow && `${mergedClsPrefix}-popover--show-arrow` + ], clsPrefix: mergedClsPrefix, tmNodes: this.tmNodes, style: [style, this.cssVars as CSSProperties], + showArrow: this.showArrow, + arrowStyle: this.arrowStyle, onMouseenter, onMouseleave } diff --git a/src/dropdown/src/DropdownMenu.tsx b/src/dropdown/src/DropdownMenu.tsx index 0eebb05a4f2..764579c39c6 100644 --- a/src/dropdown/src/DropdownMenu.tsx +++ b/src/dropdown/src/DropdownMenu.tsx @@ -6,9 +6,11 @@ import { InjectionKey, PropType, provide, - Ref + Ref, + CSSProperties } from 'vue' import { TreeNode } from 'treemate' +import { renderArrow } from '../../popover/src/PopoverBody' import NDropdownOption from './DropdownOption' import NDropdownDivider from './DropdownDivider' import NDropdownGroup from './DropdownGroup' @@ -31,6 +33,8 @@ export const dropdownMenuInjectionKey: InjectionKey = export default defineComponent({ name: 'DropdownMenu', props: { + showArrow: Boolean, + arrowStyle: [String, Object] as PropType, clsPrefix: { type: String, required: true @@ -104,6 +108,12 @@ export default defineComponent({ /> ) })} + {this.showArrow + ? renderArrow({ + clsPrefix, + arrowStyle: this.arrowStyle + }) + : null}
) } diff --git a/src/dropdown/tests/Dropdown.spec.ts b/src/dropdown/tests/Dropdown.spec.ts index 21e302548d5..ffca1c1912f 100644 --- a/src/dropdown/tests/Dropdown.spec.ts +++ b/src/dropdown/tests/Dropdown.spec.ts @@ -98,6 +98,17 @@ describe('n-dropdown', () => { wrapper.unmount() }) + it('shows arrow', async () => { + const wrapper = mountDropdown() + + const triggerNodeWrapper = wrapper.find('span') + expect(triggerNodeWrapper.exists()).toBe(true) + await triggerNodeWrapper.trigger('click') + + expect(document.querySelector('.n-popover-arrow-wrapper')).toMatchSnapshot() + wrapper.unmount() + }) + it('inverted style', async () => { const wrapper = mountDropdown({ inverted: true }) diff --git a/src/dropdown/tests/__snapshots__/Dropdown.spec.ts.snap b/src/dropdown/tests/__snapshots__/Dropdown.spec.ts.snap index 97847e04866..8bcf13f7a86 100644 --- a/src/dropdown/tests/__snapshots__/Dropdown.spec.ts.snap +++ b/src/dropdown/tests/__snapshots__/Dropdown.spec.ts.snap @@ -6,6 +6,7 @@ exports[`n-dropdown dropdown clickoutside 1`] = ` style="--box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05); --bezier: cubic-bezier(.4, 0, .2, 1); --bezier-ease-in: cubic-bezier(.4, 0, 1, 1); --bezier-ease-out: cubic-bezier(0, 0, .2, 1); --font-size: 14px; --text-color: rgb(51, 54, 57); --color: #fff; --divider-color: rgb(239, 239, 245); --border-radius: 3px; --arrow-height: 6px; --arrow-offset: 10px; --arrow-offset-vertical: 10px; --padding: 4px 0; --space: 6px; --space-arrow: 10px; --option-height: 34px; --option-prefix-width: 14px; --option-icon-prefix-width: 36px; --option-suffix-width: 14px; --option-icon-suffix-width: 32px; --option-icon-size: 16px; --option-opacity-disabled: 0.5; --option-color-hover: rgb(243, 243, 245); --option-color-active: rgba(24, 160, 88, 0.1); --option-text-color: rgb(51, 54, 57); --option-text-color-hover: rgb(51, 54, 57); --option-text-color-active: #18a058; --option-text-color-child-active: #18a058; --prefix-color: rgb(51, 54, 57); --suffix-color: rgb(51, 54, 57); --group-header-text-color: rgb(158, 164, 170);" > +
@@ -232,6 +233,8 @@ exports[`n-dropdown dropdown clickoutside 1`] = `
+ +
`; @@ -241,6 +244,7 @@ exports[`n-dropdown inverted style 1`] = ` style="--box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05); --bezier: cubic-bezier(.4, 0, .2, 1); --bezier-ease-in: cubic-bezier(.4, 0, 1, 1); --bezier-ease-out: cubic-bezier(0, 0, .2, 1); --font-size: 14px; --text-color: rgb(51, 54, 57); --color: rgb(0, 20, 40); --divider-color: rgb(239, 239, 245); --border-radius: 3px; --arrow-height: 6px; --arrow-offset: 10px; --arrow-offset-vertical: 10px; --padding: 4px 0; --space: 6px; --space-arrow: 10px; --option-height: 34px; --option-prefix-width: 14px; --option-icon-prefix-width: 36px; --option-suffix-width: 14px; --option-icon-suffix-width: 32px; --option-icon-size: 16px; --option-opacity-disabled: 0.5; --option-color-hover: #18a058; --option-color-active: #18a058; --option-text-color: #BBB; --option-text-color-hover: #FFF; --option-text-color-active: #FFF; --option-text-color-child-active: #FFF; --prefix-color: #BBB; --suffix-color: #BBB; --group-header-text-color: #AAA;" > +
@@ -448,6 +452,8 @@ exports[`n-dropdown inverted style 1`] = `
+ +
`; @@ -457,6 +463,7 @@ exports[`n-dropdown should work with \`render-icon\` props 1`] = ` style="--box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05); --bezier: cubic-bezier(.4, 0, .2, 1); --bezier-ease-in: cubic-bezier(.4, 0, 1, 1); --bezier-ease-out: cubic-bezier(0, 0, .2, 1); --font-size: 14px; --text-color: rgb(51, 54, 57); --color: #fff; --divider-color: rgb(239, 239, 245); --border-radius: 3px; --arrow-height: 6px; --arrow-offset: 10px; --arrow-offset-vertical: 10px; --padding: 4px 0; --space: 6px; --space-arrow: 10px; --option-height: 34px; --option-prefix-width: 14px; --option-icon-prefix-width: 36px; --option-suffix-width: 14px; --option-icon-suffix-width: 32px; --option-icon-size: 16px; --option-opacity-disabled: 0.5; --option-color-hover: rgb(243, 243, 245); --option-color-active: rgba(24, 160, 88, 0.1); --option-text-color: rgb(51, 54, 57); --option-text-color-hover: rgb(51, 54, 57); --option-text-color-active: #18a058; --option-text-color-child-active: #18a058; --prefix-color: rgb(51, 54, 57); --suffix-color: rgb(51, 54, 57); --group-header-text-color: rgb(158, 164, 170);" > +
@@ -832,6 +839,8 @@ exports[`n-dropdown should work with \`render-icon\` props 1`] = `
+ + `; @@ -841,6 +850,7 @@ exports[`n-dropdown should work with \`render-label\` props 1`] = ` style="--box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05); --bezier: cubic-bezier(.4, 0, .2, 1); --bezier-ease-in: cubic-bezier(.4, 0, 1, 1); --bezier-ease-out: cubic-bezier(0, 0, .2, 1); --font-size: 14px; --text-color: rgb(51, 54, 57); --color: #fff; --divider-color: rgb(239, 239, 245); --border-radius: 3px; --arrow-height: 6px; --arrow-offset: 10px; --arrow-offset-vertical: 10px; --padding: 4px 0; --space: 6px; --space-arrow: 10px; --option-height: 34px; --option-prefix-width: 14px; --option-icon-prefix-width: 36px; --option-suffix-width: 14px; --option-icon-suffix-width: 32px; --option-icon-size: 16px; --option-opacity-disabled: 0.5; --option-color-hover: rgb(243, 243, 245); --option-color-active: rgba(24, 160, 88, 0.1); --option-text-color: rgb(51, 54, 57); --option-text-color-hover: rgb(51, 54, 57); --option-text-color-active: #18a058; --option-text-color-child-active: #18a058; --prefix-color: rgb(51, 54, 57); --suffix-color: rgb(51, 54, 57); --group-header-text-color: rgb(158, 164, 170);" > +
@@ -1064,15 +1074,20 @@ exports[`n-dropdown should work with \`render-label\` props 1`] = `
+ + `; +exports[`n-dropdown shows arrow 1`] = `null`; + exports[`n-dropdown shows menu after click 1`] = `
+
@@ -1280,5 +1295,7 @@ exports[`n-dropdown shows menu after click 1`] = `
+ + `; diff --git a/src/dynamic-input/demos/enUS/custom.demo.md b/src/dynamic-input/demos/enUS/custom.demo.md index 794b0b94af1..9840dda60f5 100644 --- a/src/dynamic-input/demos/enUS/custom.demo.md +++ b/src/dynamic-input/demos/enUS/custom.demo.md @@ -13,7 +13,7 @@ v-model:value="value.num" style="margin-right: 12px; width: 160px;" /> - + diff --git a/src/dynamic-input/demos/zhCN/custom.demo.md b/src/dynamic-input/demos/zhCN/custom.demo.md index 7f6acb21944..14d60331bfa 100644 --- a/src/dynamic-input/demos/zhCN/custom.demo.md +++ b/src/dynamic-input/demos/zhCN/custom.demo.md @@ -13,7 +13,7 @@ v-model:value="value.num" style="margin-right: 12px; width: 160px;" /> - + diff --git a/src/dynamic-tags/demos/enUS/index.demo-entry.md b/src/dynamic-tags/demos/enUS/index.demo-entry.md index 04b2de4488a..caeb096d693 100644 --- a/src/dynamic-tags/demos/enUS/index.demo-entry.md +++ b/src/dynamic-tags/demos/enUS/index.demo-entry.md @@ -6,6 +6,7 @@ Make tags inputable. ```demo basic +max form ``` @@ -14,9 +15,11 @@ form | Name | Type | Default | Description | | --- | --- | --- | --- | | closable | `boolean` | `true` | Whether the tag is closable. | +| color | `{ color?: string, borderColor?: string, textColor?: string }` | `undefined` | Color of the tag, it will overrides type's color. | | default-value | `string[]` | `[]` | Default value in uncontrolled mode. | | disabled | `boolean` | `false` | Whether the tag is disabled. | | input-style | `string \| Object` | `undefined` | Customize the style of the input. | +| max | `number` | `undefined` | Maximum number of tags. | | round | `boolean` | `false` | Whether the tag has round corner. | | size | `'small' \| 'medium' \| 'large'` | `'medium'` | Size of the tag. | | tag-style | `string \| Object` | `undefined` | Customize the style of the tag. | diff --git a/src/dynamic-tags/demos/enUS/max.demo.md b/src/dynamic-tags/demos/enUS/max.demo.md new file mode 100644 index 00000000000..3605704d0d3 --- /dev/null +++ b/src/dynamic-tags/demos/enUS/max.demo.md @@ -0,0 +1,17 @@ +# Max Tag Count + +```html + +``` + +```js +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { + return { + tags: ref(['teacher', 'programmer']) + } + } +}) +``` diff --git a/src/dynamic-tags/demos/zhCN/index.demo-entry.md b/src/dynamic-tags/demos/zhCN/index.demo-entry.md index 2f17c654068..2a974a92a0c 100644 --- a/src/dynamic-tags/demos/zhCN/index.demo-entry.md +++ b/src/dynamic-tags/demos/zhCN/index.demo-entry.md @@ -6,6 +6,7 @@ ```demo basic +max form ``` @@ -14,9 +15,11 @@ form | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | closable | `boolean` | `true` | 是否可关闭 | +| color | `{ color?: string, borderColor?: string, textColor?: string }` | `undefined` | 标签颜色,设置该项后 `type` 无效 | | default-value | `string[]` | `[]` | 非受控模式下的默认值 | | disabled | `boolean` | `false` | 是否禁用 | | input-style | `string \| Object` | `undefined` | 自定义输入框的样式 | +| max | `number` | `undefined` | tag 的最大数量 | | round | `boolean` | `false` | 是否圆角 | | size | `'small' \| 'medium' \| 'large'` | `'medium'` | 尺寸大小 | | tag-style | `string \| Object` | `undefined` | 自定义标签的样式 | diff --git a/src/dynamic-tags/demos/zhCN/max.demo.md b/src/dynamic-tags/demos/zhCN/max.demo.md new file mode 100644 index 00000000000..bfe00ed6eae --- /dev/null +++ b/src/dynamic-tags/demos/zhCN/max.demo.md @@ -0,0 +1,17 @@ +# 最大标签数量 + +```html + +``` + +```js +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { + return { + tags: ref(['教师', '程序员']) + } + } +}) +``` diff --git a/src/dynamic-tags/src/DynamicTags.tsx b/src/dynamic-tags/src/DynamicTags.tsx index 0988392dfee..20a4e6bd58b 100644 --- a/src/dynamic-tags/src/DynamicTags.tsx +++ b/src/dynamic-tags/src/DynamicTags.tsx @@ -39,6 +39,7 @@ const dynamicTagsProps = { }, value: Array as PropType, inputStyle: [String, Object] as PropType, + max: Number as PropType, tagStyle: [String, Object] as PropType, // eslint-disable-next-line vue/prop-name-casing 'onUpdate:value': [Function, Array] as PropType>, @@ -68,6 +69,7 @@ export default defineComponent({ const { mergedClsPrefixRef } = useConfig(props) const { localeRef } = useLocale('DynamicTags') const formItem = useFormItem(props) + const { mergedDisabledRef } = formItem const inputValueRef = ref('') const showInputRef = ref(false) const inputForceFocusedRef = ref(true) @@ -148,6 +150,7 @@ export default defineComponent({ showInput: showInputRef, inputForceFocused: inputForceFocusedRef, mergedValue: mergedValueRef, + mergedDisabled: mergedDisabledRef, handleInputKeyUp, handleAddClick, handleInputBlur, @@ -182,8 +185,9 @@ export default defineComponent({ type, round, size, + color, closable, - disabled, + mergedDisabled, showInput, inputValue, inputStyle, @@ -204,8 +208,9 @@ export default defineComponent({ type={type} round={round} size={size} + color={color} closable={closable} - disabled={disabled} + disabled={mergedDisabled} onClose={() => handleCloseClick(index)} > {{ default: () => tag }} @@ -232,6 +237,7 @@ export default defineComponent({ ) : ( = this.max} theme={mergedTheme.peers.Button} themeOverrides={mergedTheme.peerOverrides.Button} size={inputSize} diff --git a/src/form/demos/enUS/disabled.demo.md b/src/form/demos/enUS/disabled.demo.md new file mode 100644 index 00000000000..c94b587b1af --- /dev/null +++ b/src/form/demos/enUS/disabled.demo.md @@ -0,0 +1,207 @@ +# Form Disabled + +```html + + + + + + + + + + + + Upload file + + + + + + + + + + + + + + + + + + + + + + Checkbox + + + + + Option 1 + Option 2 + Option 3 + + + + + + Definitely Maybe + + + + + Radio 1 + Radio 2 + Radio 3 + + + + + Radio 1 + Radio 2 + Radio 3 + + + + + + + + + + + + + + + + +``` + +```js +import { defineComponent, computed, ref } from 'vue' + +function genOptions (depth = 2, iterator = 1, prefix = '') { + const length = 12 + const options = [] + for (let i = 1; i <= length; ++i) { + if (iterator === 1) { + options.push({ + value: `${i}`, + label: `${i}`, + disabled: i % 5 === 0, + children: genOptions(depth, iterator + 1, '' + i) + }) + } else if (iterator === depth) { + options.push({ + value: `${prefix}-${i}`, + label: `${prefix}-${i}`, + disabled: i % 5 === 0 + }) + } else { + options.push({ + value: `${prefix}-${i}`, + label: `${prefix}-${i}`, + disabled: i % 5 === 0, + children: genOptions(depth, iterator + 1, `${prefix}-${i}`) + }) + } + } + return options +} +export default defineComponent({ + setup () { + const formRef = ref(null) + const model = ref({ + inputValue: null, + selectValue: null, + autoCompleteValue: '', + dynamicTagsValue: ['teacher', 'frontend'], + cascaderValue: null, + datetimeValue: null, + switchValue: false, + checkboxValue: null, + checkboxGroupValue: null, + radioValue: 'Definitely Maybe', + radioGroupValue: null, + radioButtonGroupValue: null, + inputNumberValue: null, + timePickerValue: null, + sliderValue: 0, + transferValue: null + }) + return { + updateDisabled: ref(false), + formRef, + model, + generalOptions: ['groode', 'veli good', 'emazing', 'lidiculous'].map( + (v) => ({ + label: v, + value: v + }) + ), + options: genOptions(), + treeSelectOptions: [ + { + label: 'Rubber Soul', + key: 'Rubber Soul', + children: [ + { + label: 'Drive My Car', + key: 'Drive My Car' + }, + { + label: 'Michelle', + key: 'Michelle' + } + ] + } + ], + autoCompleteOptions: computed(() => { + return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { + const prefix = model.value.autoCompleteValue.split('@')[0] + return { + label: prefix + suffix, + value: prefix + suffix + } + }) + }) + } + } +}) +``` diff --git a/src/form/demos/enUS/index.demo-entry.md b/src/form/demos/enUS/index.demo-entry.md index abe18923dc4..b146f268118 100644 --- a/src/form/demos/enUS/index.demo-entry.md +++ b/src/form/demos/enUS/index.demo-entry.md @@ -14,6 +14,7 @@ top left item-only async +disabled ``` ## Props @@ -22,6 +23,7 @@ async | Name | Type | Default | Description | | --- | --- | --- | --- | +| disabled | `boolean` | `false` | Whether to disable. | | inline | `boolean` | `false` | Whether to display as an inline form. | | label-width | `number \| string` | `undefined` | The width of label. Can be useful when `label-placement` is `'left'`. | | label-align | `'left' \| 'right'` | `-` | Text align in label. | @@ -77,14 +79,14 @@ Accept all props from FormItem & [GridItem](grid#GridItem-Props) | Name | Type | Description | | --- | --- | --- | -| validate | `(validateCallback?: (errors?: Array) => void, shouldRuleBeApplied?: FormItemRule => boolean) => Promise` | Validate the form. The rejection value type of returned promise is `Array`. | +| validate | `(validateCallback?: (errors?: Array) => void, shouldRuleBeApplied?: FormItemRule => boolean) => Promise` | Validate the form. The rejection value type of returned promise is `Array`. | | restoreValidation | `() => void` | Restore validate. | ### FormItem, FormItemGi Methods | Name | Type | Description | | --- | --- | --- | -| validate | `(options: { trigger?: string, callback?: (errors?: Array) => void, shouldRuleBeApplied?: FormItemRule => boolean, options?: AsyncValidatorOptions }) => Promise` | Validate the form item. The rejection value type of returned promise is `Array`. If trigger is not set, all rules of the item will be applied. `shouldRuleBeApplied` can filter rules after they are filtered by the trigger. | +| validate | `(options: { trigger?: string, callback?: (errors?: Array) => void, shouldRuleBeApplied?: FormItemRule => boolean, options?: AsyncValidatorOptions }) => Promise` | Validate the form item. The rejection value type of returned promise is `Array`. If trigger is not set, all rules of the item will be applied. `shouldRuleBeApplied` can filter rules after they are filtered by the trigger. | | restoreValidation | `() => void` | Restore validate. | About AsyncValidatorOptions, see async-validator. diff --git a/src/form/demos/zhCN/disabled.demo.md b/src/form/demos/zhCN/disabled.demo.md new file mode 100644 index 00000000000..c7f9504f43e --- /dev/null +++ b/src/form/demos/zhCN/disabled.demo.md @@ -0,0 +1,207 @@ +# 表单禁用 + +```html + + + + + + + + + + + + Upload file + + + + + + + + + + + + + + + + + + + + + + Checkbox + + + + + Option 1 + Option 2 + Option 3 + + + + + + Definitely Maybe + + + + + Radio 1 + Radio 2 + Radio 3 + + + + + Radio 1 + Radio 2 + Radio 3 + + + + + + + + + + + + + + + + +``` + +```js +import { defineComponent, computed, ref } from 'vue' + +function genOptions (depth = 2, iterator = 1, prefix = '') { + const length = 12 + const options = [] + for (let i = 1; i <= length; ++i) { + if (iterator === 1) { + options.push({ + value: `${i}`, + label: `${i}`, + disabled: i % 5 === 0, + children: genOptions(depth, iterator + 1, '' + i) + }) + } else if (iterator === depth) { + options.push({ + value: `${prefix}-${i}`, + label: `${prefix}-${i}`, + disabled: i % 5 === 0 + }) + } else { + options.push({ + value: `${prefix}-${i}`, + label: `${prefix}-${i}`, + disabled: i % 5 === 0, + children: genOptions(depth, iterator + 1, `${prefix}-${i}`) + }) + } + } + return options +} +export default defineComponent({ + setup () { + const formRef = ref(null) + const model = ref({ + inputValue: null, + selectValue: null, + autoCompleteValue: '', + dynamicTagsValue: ['teacher', 'frontend'], + cascaderValue: null, + datetimeValue: null, + switchValue: false, + checkboxValue: null, + checkboxGroupValue: null, + radioValue: 'Definitely Maybe', + radioGroupValue: null, + radioButtonGroupValue: null, + inputNumberValue: null, + timePickerValue: null, + sliderValue: 0, + transferValue: null + }) + return { + updateDisabled: ref(false), + formRef, + model, + generalOptions: ['groode', 'veli good', 'emazing', 'lidiculous'].map( + (v) => ({ + label: v, + value: v + }) + ), + options: genOptions(), + treeSelectOptions: [ + { + label: 'Rubber Soul', + key: 'Rubber Soul', + children: [ + { + label: 'Drive My Car', + key: 'Drive My Car' + }, + { + label: 'Michelle', + key: 'Michelle' + } + ] + } + ], + autoCompleteOptions: computed(() => { + return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => { + const prefix = model.value.autoCompleteValue.split('@')[0] + return { + label: prefix + suffix, + value: prefix + suffix + } + }) + }) + } + } +}) +``` diff --git a/src/form/demos/zhCN/index.demo-entry.md b/src/form/demos/zhCN/index.demo-entry.md index 34712a9f95d..f88a137600c 100644 --- a/src/form/demos/zhCN/index.demo-entry.md +++ b/src/form/demos/zhCN/index.demo-entry.md @@ -14,6 +14,7 @@ top left item-only async +disabled height-debug validator-debug ``` @@ -24,6 +25,7 @@ validator-debug | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| disabled | `boolean` | `false` | 是否禁用 | | inline | `boolean` | `false` | 是否展示为行内表单 | | label-width | `number \| string` | `undefined` | 标签的宽度,在 `label-placement` 是 `'left'` 的时候可能会有用 | | label-align | `'left' \| 'right'` | `-` | 标签的文本对齐方式 | @@ -78,14 +80,14 @@ validator-debug | 名称 | 类型 | 说明 | | --- | --- | --- | -| validate | `(validateCallback?: (errors?: Array) => void, shouldRuleBeApplied?: FormItemRule => boolean) => Promise` | 验证表单,Promise rejection 的返回值类型是 `Array` | +| validate | `(validateCallback?: (errors?: Array) => void, shouldRuleBeApplied?: FormItemRule => boolean) => Promise` | 验证表单,Promise rejection 的返回值类型是 `Array` | | restoreValidation | `() => void` | 还原到未校验的状态 | ### FormItem, FormItemGi Methods | 名称 | 类型 | 说明 | | --- | --- | --- | -| validate | `(options: { trigger?: string, callback?: (errors?: Array) => void, shouldRuleBeApplied?: FormItemRule => boolean, options?: AsyncValidatorOptions }) => Promise` | 验证表项,Promise rejection 的返回值类型是 `Array`。如果设定 `trigger`,这一个表项全部的规则都会被使用。`shouldRuleBeApplied` 可以用来进一步过滤已经经过 `trigger` 筛选的规则 | +| validate | `(options: { trigger?: string, callback?: (errors?: Array) => void, shouldRuleBeApplied?: FormItemRule => boolean, options?: AsyncValidatorOptions }) => Promise` | 验证表项,Promise rejection 的返回值类型是 `Array`。如果设定 `trigger`,这一个表项全部的规则都会被使用。`shouldRuleBeApplied` 可以用来进一步过滤已经经过 `trigger` 筛选的规则 | | restoreValidation | `() => void` | 还原到未校验的状态 | 关于 AsyncValidatorOptions,参考 async-validator。 diff --git a/src/form/index.ts b/src/form/index.ts index 8ea7c2932c9..c774c99bcf7 100644 --- a/src/form/index.ts +++ b/src/form/index.ts @@ -14,7 +14,8 @@ export type { FormInst, FormItemInst, FormItemRule, - FormRules + FormRules, + FormValidationError } from './src/interface' // deprecated diff --git a/src/form/src/Form.tsx b/src/form/src/Form.tsx index a8b7f03dec2..1dcfe7c0c83 100644 --- a/src/form/src/Form.tsx +++ b/src/form/src/Form.tsx @@ -35,6 +35,7 @@ const formProps = { default: () => {} }, rules: Object as PropType, + disabled: Boolean, size: String as PropType<'small' | 'medium' | 'large'>, showRequireMark: { type: [Boolean, String] as PropType<'left' | 'right' | boolean>, diff --git a/src/form/src/FormItem.tsx b/src/form/src/FormItem.tsx index ec33e2ee831..5304fe37150 100644 --- a/src/form/src/FormItem.tsx +++ b/src/form/src/FormItem.tsx @@ -166,6 +166,7 @@ export default defineComponent({ if (feedback !== undefined && feedback !== null) return true return explainsRef.value.length }) + const mergedDisabledRef = NForm ? toRef(NForm, 'disabled') : ref(false) const themeRef = useTheme( 'Form', 'FormItem', @@ -322,6 +323,7 @@ export default defineComponent({ } provide(formItemInjectionKey, { path: toRef(props, 'path'), + disabled: mergedDisabledRef, mergedSize: formItemSizeRefs.mergedSize, restoreValidation, handleContentBlur, diff --git a/src/form/src/interface.ts b/src/form/src/interface.ts index d61818837cf..3781176daed 100644 --- a/src/form/src/interface.ts +++ b/src/form/src/interface.ts @@ -75,6 +75,8 @@ export type FormValidateCallback = (errors?: ErrorList[]) => void export type FormValidate = ((callback?: FormValidateCallback) => void) & (() => Promise) +export type FormValidationError = ErrorList + export interface FormInst { validate: FormValidate restoreValidation: () => void diff --git a/src/icon/demos/enUS/basic.demo.md b/src/icon/demos/enUS/basic.demo.md index 925e1f23513..eb5d469b0b0 100644 --- a/src/icon/demos/enUS/basic.demo.md +++ b/src/icon/demos/enUS/basic.demo.md @@ -11,11 +11,12 @@ ```js import { GameControllerOutline, GameController } from '@vicons/ionicons5' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { GameControllerOutline, GameController } -} +}) ``` diff --git a/src/icon/demos/enUS/depth.demo.md b/src/icon/demos/enUS/depth.demo.md index d33e0b99c12..48917d84c07 100644 --- a/src/icon/demos/enUS/depth.demo.md +++ b/src/icon/demos/enUS/depth.demo.md @@ -22,10 +22,11 @@ To match different level text colors, icon provides `depth` prop. ```js import { CashOutline } from '@vicons/ionicons5' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { CashOutline } -} +}) ``` diff --git a/src/icon/demos/zhCN/basic.demo.md b/src/icon/demos/zhCN/basic.demo.md index 4f006a369a2..235cd4a56dc 100644 --- a/src/icon/demos/zhCN/basic.demo.md +++ b/src/icon/demos/zhCN/basic.demo.md @@ -11,11 +11,12 @@ ```js import { GameControllerOutline, GameController } from '@vicons/ionicons5' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { GameControllerOutline, GameController } -} +}) ``` diff --git a/src/icon/demos/zhCN/depth.demo.md b/src/icon/demos/zhCN/depth.demo.md index f901b3490d0..19171df05db 100644 --- a/src/icon/demos/zhCN/depth.demo.md +++ b/src/icon/demos/zhCN/depth.demo.md @@ -22,10 +22,11 @@ ```js import { CashOutline } from '@vicons/ionicons5' +import { defineComponent } from 'vue' -export default { +export default defineComponent({ components: { CashOutline } -} +}) ``` diff --git a/src/icon/src/Icon.ts b/src/icon/src/Icon.ts index 6b146f160f4..412bfe18a16 100644 --- a/src/icon/src/Icon.ts +++ b/src/icon/src/Icon.ts @@ -13,18 +13,9 @@ export default defineComponent({ name: 'Icon', props: { ...(useTheme.props as ThemeProps), - depth: { - type: [String, Number] as PropType, - default: undefined - }, - size: { - type: [Number, String], - default: undefined - }, - color: { - type: String, - default: undefined - } + depth: [String, Number] as PropType, + size: [Number, String], + color: String }, setup (props) { const { mergedClsPrefixRef } = useConfig(props) diff --git a/src/image/demos/enUS/index.demo-entry.md b/src/image/demos/enUS/index.demo-entry.md index c09d098da46..1bab288020b 100644 --- a/src/image/demos/enUS/index.demo-entry.md +++ b/src/image/demos/enUS/index.demo-entry.md @@ -26,7 +26,7 @@ group ### ImageGroup Props -| 名称 | 类型 | 默认值 | 说明 | +| Name | Type | Default | Description | | --- | --- | --- | --- | | show-toolbar | `boolean` | `true` | Whether to show the bottom toolbar when the image enlarge. | diff --git a/src/input-number/demos/enUS/icon.demo.md b/src/input-number/demos/enUS/icon.demo.md new file mode 100644 index 00000000000..6ca405bee92 --- /dev/null +++ b/src/input-number/demos/enUS/icon.demo.md @@ -0,0 +1,29 @@ +# Prefix & Suffix + +Add content to prefix and suffix. + +```html + + + + + + + + + + + +``` + +```js +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { + return { + value: ref(0) + } + } +}) +``` diff --git a/src/input-number/demos/enUS/index.demo-entry.md b/src/input-number/demos/enUS/index.demo-entry.md index 74b4f9890b9..ed311249b2f 100644 --- a/src/input-number/demos/enUS/index.demo-entry.md +++ b/src/input-number/demos/enUS/index.demo-entry.md @@ -8,6 +8,7 @@ If you want just input number, use it. basic disabled event +icon min-max size step @@ -24,7 +25,7 @@ show-button | disabled | `boolean` | `false` | Whether to disable the input. | | max | `number` | `undefined` | The max value. | | min | `number` | `undefined` | The min value. | -| placeholder | `string` | `'Please Input'` | | +| placeholder | `string` | `'Please Input'` | Placeholder of input number. | | show-button | `boolean` | `true` | Whether to show buttons. | | size | `'small' \| 'medium' \| 'large'` | `'medium'` | The size of input box. | | step | `number` | `1` | The number to which the current value is increased or decreased. It can be an integer or decimal. | @@ -33,3 +34,10 @@ show-button | on-blur | `(event: FocusEvent) => void` | `undefined` | Callback when blur. | | on-focus | `(event: FocusEvent) => void` | `undefined` | Callback when focused. | | on-update:value | `(value: number \| null) => void` | `undefined` | Callback when the component's value changes. | + +### Input Slots + +| Name | Parameters | Description | +| ------ | ---------- | ------------------------------ | +| prefix | `()` | Input box prefix content slot. | +| suffix | `()` | Input box suffix content slot. | diff --git a/src/input-number/demos/zhCN/icon.demo.md b/src/input-number/demos/zhCN/icon.demo.md new file mode 100644 index 00000000000..4bc73f7d2e8 --- /dev/null +++ b/src/input-number/demos/zhCN/icon.demo.md @@ -0,0 +1,29 @@ +# 前缀 & 后缀 + +在前缀后缀添加内容 + +```html + + + + + + + + + + + +``` + +```js +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { + return { + value: ref(0) + } + } +}) +``` diff --git a/src/input-number/demos/zhCN/index.demo-entry.md b/src/input-number/demos/zhCN/index.demo-entry.md index ec0ad1645b9..9986aae84d0 100644 --- a/src/input-number/demos/zhCN/index.demo-entry.md +++ b/src/input-number/demos/zhCN/index.demo-entry.md @@ -8,6 +8,7 @@ basic disabled event +icon min-max size step @@ -25,7 +26,7 @@ debug | disabled | `boolean` | `false` | 是否禁用 | | max | `number` | `undefined` | 最大值 | | min | `number` | `undefined` | 最小值 | -| placeholder | `string` | `'请输入'` | | +| placeholder | `string` | `'请输入'` | 提示信息 | | show-button | `boolean` | `true` | 是否有按钮 | | size | `'small' \| 'medium' \| 'large'` | `'medium'` | 输入框大小 | | step | `number` | `1` | 每次改变步数,可以为小数 | @@ -34,3 +35,10 @@ debug | on-blur | `(event: FocusEvent) => void` | `undefined` | 移除焦点的回调 | | on-focus | `(event: FocusEvent) => void` | `undefined` | 获取焦点的回调 | | on-update:value | `(value: number \| null) => void` | `undefined` | 组件值发生变化的回调 | + +### Slots + +| 属性 | 类型 | 说明 | +| ------ | ---- | ------------------ | +| prefix | `()` | 输入框头部内容插槽 | +| suffix | `()` | 输入框尾部内容插槽 | diff --git a/src/input-number/src/InputNumber.tsx b/src/input-number/src/InputNumber.tsx index f7ea52dc895..7c9b5b088ae 100644 --- a/src/input-number/src/InputNumber.tsx +++ b/src/input-number/src/InputNumber.tsx @@ -12,6 +12,7 @@ import { warn, call, MaybeArray, ExtractPublicPropTypes } from '../../_utils' import { inputNumberLight, InputNumberTheme } from '../styles' import { parse, validator, format, parseNumber } from './utils' import type { OnUpdateValue } from './interface' +import style from './styles/input-number.cssr' const inputNumberProps = { ...(useTheme.props as ThemeProps), @@ -28,7 +29,10 @@ const inputNumberProps = { min: [Number, String], max: [Number, String], size: String as PropType<'small' | 'medium' | 'large'>, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, validator: Function as PropType<(value: number) => boolean>, bordered: { type: Boolean as PropType, @@ -68,13 +72,14 @@ export default defineComponent({ const themeRef = useTheme( 'InputNumber', 'InputNumber', - undefined, + style, inputNumberLight, props, mergedClsPrefixRef ) const { localeRef } = useLocale('InputNumber') const formItem = useFormItem(props) + const { mergedSizeRef, mergedDisabledRef } = formItem // dom ref const inputInstRef = ref(null) const minusButtonInstRef = ref<{ $el: HTMLElement } | null>(null) @@ -315,7 +320,8 @@ export default defineComponent({ mergedValue: mergedValueRef, mergedPlaceholder: mergedPlaceholderRef, displayedValueInvalid: displayedValueInvalidRef, - mergedSize: formItem.mergedSizeRef, + mergedSize: mergedSizeRef, + mergedDisabled: mergedDisabledRef, displayedValue: displayedValueRef, addable: addableRef, minusable: minusableRef, @@ -359,7 +365,7 @@ export default defineComponent({ builtinThemeOverrides={this.inputThemeOverrides} size={this.mergedSize} placeholder={this.mergedPlaceholder} - disabled={this.disabled} + disabled={this.mergedDisabled} textDecoration={ this.displayedValueInvalid ? 'line-through' : undefined } @@ -368,12 +374,19 @@ export default defineComponent({ onKeydown={this.handleKeyDown} onMousedown={this.handleMouseDown} > - {this.showButton - ? { - suffix: () => [ + {{ + _: 2, // input number has dynamic slots + prefix: this.$slots.prefix, + suffix: this.showButton + ? () => [ + this.$slots.suffix && ( + + {{ default: this.$slots.suffix }} + + ), , ] - } - : null} + : this.$slots.suffix + }} ) diff --git a/src/input-number/src/styles/input-number.cssr.ts b/src/input-number/src/styles/input-number.cssr.ts new file mode 100644 index 00000000000..ebda2d89fd4 --- /dev/null +++ b/src/input-number/src/styles/input-number.cssr.ts @@ -0,0 +1,8 @@ +import { cB, c } from '../../../_utils/cssr' + +export default c([ + cB('input-number-suffix', ` + display: inline-block; + margin-right: 10px; + `) +]) diff --git a/src/input-number/tests/InputNumber.spec.tsx b/src/input-number/tests/InputNumber.spec.tsx index 91ffea93acc..52121bef2aa 100644 --- a/src/input-number/tests/InputNumber.spec.tsx +++ b/src/input-number/tests/InputNumber.spec.tsx @@ -8,9 +8,10 @@ describe('n-input-number', () => { }) it('should work with `show-button` prop', async () => { + // Here is a strange case, we must make input number's slots flag to 2 + // (dynamic) to make it work. const wrapper = mount(NInputNumber) expect(wrapper.findComponent(NButton).exists()).toBe(true) - await wrapper.setProps({ showButton: false }) expect(wrapper.findComponent(NButton).exists()).toBe(false) }) @@ -55,4 +56,15 @@ describe('n-input-number', () => { expect(onBlur).toHaveBeenCalledTimes(1) wrapper.unmount() }) + + it('should work with `prefix` & `suffix` slots', async () => { + const wrapper = mount(NInputNumber, { + slots: { prefix: () => '$', suffix: () => '%' } + }) + expect(wrapper.find('.n-input__prefix').exists()).toBe(true) + expect(wrapper.find('.n-input__prefix').text()).toBe('$') + expect(wrapper.find('.n-input__suffix').exists()).toBe(true) + expect(wrapper.find('.n-input-number-suffix').exists()).toBe(true) + expect(wrapper.find('.n-input-number-suffix').text()).toBe('%') + }) }) diff --git a/src/input/demos/enUS/basic.demo.md b/src/input/demos/enUS/basic.demo.md index 0cd7a618bd4..bf4d0a3baca 100644 --- a/src/input/demos/enUS/basic.demo.md +++ b/src/input/demos/enUS/basic.demo.md @@ -4,17 +4,19 @@ Basic usage of input. ```html - + ``` ```js -export default { - data () { +import { defineComponent, ref } from 'vue' + +export default defineComponent({ + setup () { return { - value: null + value: ref(null) } } -} +}) ``` diff --git a/src/input/demos/enUS/clearable.demo.md b/src/input/demos/enUS/clearable.demo.md index 08ac5dc5554..dbf46599a61 100644 --- a/src/input/demos/enUS/clearable.demo.md +++ b/src/input/demos/enUS/clearable.demo.md @@ -4,7 +4,7 @@ Make input clearable when value is set. ```html - + diff --git a/src/input/demos/enUS/disabled.demo.md b/src/input/demos/enUS/disabled.demo.md index dd013d9a853..f453ac1564b 100644 --- a/src/input/demos/enUS/disabled.demo.md +++ b/src/input/demos/enUS/disabled.demo.md @@ -5,7 +5,7 @@ Input can be disabled. ```html - - - + + + ``` diff --git a/src/input/demos/zhCN/basic.demo.md b/src/input/demos/zhCN/basic.demo.md index 9653e8da235..76b3beb2cad 100644 --- a/src/input/demos/zhCN/basic.demo.md +++ b/src/input/demos/zhCN/basic.demo.md @@ -4,7 +4,7 @@ ```html - + - + diff --git a/src/input/demos/zhCN/disabled.demo.md b/src/input/demos/zhCN/disabled.demo.md index c13b735cf80..8e66d37d59e 100644 --- a/src/input/demos/zhCN/disabled.demo.md +++ b/src/input/demos/zhCN/disabled.demo.md @@ -5,7 +5,7 @@ ```html - - - + + + ``` diff --git a/src/input/src/Input.tsx b/src/input/src/Input.tsx index c320beda16b..bad0c604c31 100644 --- a/src/input/src/Input.tsx +++ b/src/input/src/Input.tsx @@ -46,8 +46,8 @@ const inputProps = { default: undefined }, type: { - type: String as PropType<'input' | 'textarea' | 'password'>, - default: 'input' + type: String as PropType<'text' | 'textarea' | 'password'>, + default: 'text' }, placeholder: [Array, String] as PropType, defaultValue: { @@ -55,7 +55,10 @@ const inputProps = { default: null }, value: [String, Array] as PropType, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, size: String as PropType, rows: { type: [Number, String] as PropType, @@ -163,7 +166,7 @@ export default defineComponent({ ) // form-item const formItem = useFormItem(props) - const { mergedSizeRef } = formItem + const { mergedSizeRef, mergedDisabledRef } = formItem // states const focusedRef = ref(false) const hoverRef = ref(false) @@ -209,7 +212,7 @@ export default defineComponent({ // clear const showClearButton = computed(() => { if ( - props.disabled || + mergedDisabledRef.value || props.readonly || !props.clearable || (!mergedFocusRef.value && !hoverRef.value) @@ -508,15 +511,15 @@ export default defineComponent({ hoverRef.value = false } function handlePasswordToggleClick (): void { - if (props.disabled) return + if (mergedDisabledRef.value) return passwordVisibleRef.value = !passwordVisibleRef.value } function handlePasswordToggleMousedown (e: MouseEvent): void { - if (props.disabled) return + if (mergedDisabledRef.value) return e.preventDefault() } function handlePasswordToggleMouseup (e: MouseEvent): void { - if (props.disabled) return + if (mergedDisabledRef.value) return e.preventDefault() } function handleWrapperKeyDown (e: KeyboardEvent): void { @@ -557,7 +560,7 @@ export default defineComponent({ } } function focus (): void { - if (props.disabled) return + if (mergedDisabledRef.value) return if (props.passivelyActivated) { wrapperElRef.value?.focus() } else { @@ -571,7 +574,7 @@ export default defineComponent({ } } function activate (): void { - if (props.disabled) return + if (mergedDisabledRef.value) return if (textareaElRef.value) textareaElRef.value.focus() else if (inputElRef.value) inputElRef.value.focus() } @@ -664,6 +667,7 @@ export default defineComponent({ activated: activatedRef, showClearButton, mergedSize: mergedSizeRef, + mergedDisabled: mergedDisabledRef, textDecorationStyle: textDecorationStyleRef, mergedClsPrefix: mergedClsPrefixRef, mergedBordered: mergedBorderedRef, @@ -803,7 +807,7 @@ export default defineComponent({ class={[ `${mergedClsPrefix}-input`, { - [`${mergedClsPrefix}-input--disabled`]: this.disabled, + [`${mergedClsPrefix}-input--disabled`]: this.mergedDisabled, [`${mergedClsPrefix}-input--textarea`]: this.type === 'textarea', [`${mergedClsPrefix}-input--resizable`]: this.resizable && !this.autosize, @@ -817,7 +821,7 @@ export default defineComponent({ ]} style={this.cssVars as CSSProperties} tabindex={ - !this.disabled && this.passivelyActivated && !this.activated + !this.mergedDisabled && this.passivelyActivated && !this.activated ? 0 : undefined } @@ -851,7 +855,7 @@ export default defineComponent({ rows={Number(this.rows)} placeholder={this.placeholder as string | undefined} value={this.mergedValue as string | undefined} - disabled={this.disabled} + disabled={this.mergedDisabled} maxlength={this.maxlength as any} minlength={this.minlength as any} readonly={this.readonly as any} @@ -903,7 +907,7 @@ export default defineComponent({ this.passivelyActivated && !this.activated ? -1 : undefined } placeholder={this.mergedPlaceholder[0]} - disabled={this.disabled} + disabled={this.mergedDisabled} maxlength={this.maxlength as any} minlength={this.minlength as any} value={ @@ -1003,7 +1007,7 @@ export default defineComponent({ this.passivelyActivated && !this.activated ? -1 : undefined } placeholder={this.mergedPlaceholder[1]} - disabled={this.disabled} + disabled={this.mergedDisabled} maxlength={this.maxlength as any} minlength={this.minlength as any} value={ diff --git a/src/input/src/styles/input.cssr.ts b/src/input/src/styles/input.cssr.ts index 12a6b6377ad..543d3be6d58 100644 --- a/src/input/src/styles/input.cssr.ts +++ b/src/input/src/styles/input.cssr.ts @@ -299,7 +299,7 @@ export default c([ transition: color .3s var(--bezier); flex-wrap: nowrap; flex-shrink: 0; - line-height: 1.5; + line-height: var(--height); white-space: nowrap; display: inline-flex; align-items: center; diff --git a/src/input/tests/Input.spec.tsx b/src/input/tests/Input.spec.tsx index b6c22c44d75..534f2a2a97e 100644 --- a/src/input/tests/Input.spec.tsx +++ b/src/input/tests/Input.spec.tsx @@ -27,4 +27,13 @@ describe('n-input', () => { expect(wrapper.find('.n-base-loading__icon').exists()).toBe(true) wrapper.unmount() }) + it('should work with `type` prop', async () => { + const wrapper = mount(NInput) + await wrapper.setProps({ type: 'text' }) + expect(wrapper.find('input').exists()).toBe(true) + + await wrapper.setProps({ type: 'textarea' }) + expect(wrapper.find('.n-input').classes()).toContain('n-input--textarea') + expect(wrapper.find('textarea').exists()).toBe(true) + }) }) diff --git a/src/loading-bar/demos/enUS/basic.demo.md b/src/loading-bar/demos/enUS/basic.demo.md index ff2235bf876..08821d11968 100644 --- a/src/loading-bar/demos/enUS/basic.demo.md +++ b/src/loading-bar/demos/enUS/basic.demo.md @@ -3,28 +3,36 @@ ```html start - finish + finish error ``` ```js +import { defineComponent, ref } from 'vue' import { useLoadingBar } from 'naive-ui' -export default { +export default defineComponent({ setup () { const loadingBar = useLoadingBar() + const disabledRef = ref(true) + function handleStart () { + loadingBar.start() + disabledRef.value = false + } + function handleFinish () { + loadingBar.finish() + disabledRef.value = true + } + function handleError () { + loadingBar.error() + } return { - handleStart () { - loadingBar.start() - }, - handleFinish () { - loadingBar.finish() - }, - handleError () { - loadingBar.error() - } + disabled: disabledRef, + handleStart, + handleFinish, + handleError } } -} +}) ``` diff --git a/src/loading-bar/demos/enUS/index.demo-entry.md b/src/loading-bar/demos/enUS/index.demo-entry.md index 27f09fe5a68..6f516c157ba 100644 --- a/src/loading-bar/demos/enUS/index.demo-entry.md +++ b/src/loading-bar/demos/enUS/index.demo-entry.md @@ -41,6 +41,12 @@ basic ## API +### LoadingBarProvider Props + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| loading-bar-style | `{ loading?: string \| object, error?: string \| object }` | `undefined` | Style of the loading bar. | + ### `loadingBar` Injection Methods | Name | Type | Description | diff --git a/src/loading-bar/demos/zhCN/basic.demo.md b/src/loading-bar/demos/zhCN/basic.demo.md index db277acd4cf..893404bd785 100644 --- a/src/loading-bar/demos/zhCN/basic.demo.md +++ b/src/loading-bar/demos/zhCN/basic.demo.md @@ -3,28 +3,36 @@ ```html 开始 - 结束 + 结束 报个错 ``` ```js +import { defineComponent, ref } from 'vue' import { useLoadingBar } from 'naive-ui' -export default { +export default defineComponent({ setup () { const loadingBar = useLoadingBar() + const disabledRef = ref(true) + function handleStart () { + loadingBar.start() + disabledRef.value = false + } + function handleFinish () { + loadingBar.finish() + disabledRef.value = true + } + function handleError () { + loadingBar.error() + } return { - handleStart () { - loadingBar.start() - }, - handleFinish () { - loadingBar.finish() - }, - handleError () { - loadingBar.error() - } + disabled: disabledRef, + handleStart, + handleFinish, + handleError } } -} +}) ``` diff --git a/src/loading-bar/demos/zhCN/index.demo-entry.md b/src/loading-bar/demos/zhCN/index.demo-entry.md index c2a680e26b8..357f448fbbf 100644 --- a/src/loading-bar/demos/zhCN/index.demo-entry.md +++ b/src/loading-bar/demos/zhCN/index.demo-entry.md @@ -41,6 +41,12 @@ basic ## API +### LoadingBarProvider Props + +| 名称 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| loading-bar-style | `{ loading?: string \| object, error?: string \| object }` | `undefined` | 加载条样式 | + ### `loadingBar` Injection Methods | 名称 | 类型 | 说明 | diff --git a/src/loading-bar/src/LoadingBar.tsx b/src/loading-bar/src/LoadingBar.tsx index 8284ac5ada0..7cb80a149ba 100644 --- a/src/loading-bar/src/LoadingBar.tsx +++ b/src/loading-bar/src/LoadingBar.tsx @@ -6,7 +6,6 @@ import { inject, withDirectives, vShow, - CSSProperties, ref, nextTick } from 'vue' @@ -35,12 +34,17 @@ export default defineComponent({ const loadingRef = ref(false) const transitionDisabledRef = ref(false) let finishing = false - let erroring = false + const erroringRef = ref(false) + const mergedLoadingBarStyle = computed(() => { + const { loadingBarStyle } = providerProps + if (!loadingBarStyle) return '' + return loadingBarStyle[erroringRef.value ? 'error' : 'loading'] + }) async function init (): Promise { enteringRef.value = false loadingRef.value = false finishing = false - erroring = false + erroringRef.value = false transitionDisabledRef.value = true await nextTick() transitionDisabledRef.value = false @@ -63,31 +67,20 @@ export default defineComponent({ el.style.maxWidth = `${toProgress}%` } function finish (): void { - if (finishing || erroring) return - if (!loadingRef.value) { - void start(100, 100).then(() => { - finishing = true - const el = loadingBarRef.value - if (!el) return - el.className = createClassName('finishing', mergedClsPrefixRef.value) - void el.offsetWidth - loadingRef.value = false - }) - } else { - finishing = true - const el = loadingBarRef.value - if (!el) return - el.className = createClassName('finishing', mergedClsPrefixRef.value) - el.style.maxWidth = '100%' - void el.offsetWidth - loadingRef.value = false - } + if (finishing || erroringRef.value) return + finishing = true + const el = loadingBarRef.value + if (!el) return + el.className = createClassName('finishing', mergedClsPrefixRef.value) + el.style.maxWidth = '100%' + void el.offsetWidth + loadingRef.value = false } function error (): void { - if (finishing || erroring) return + if (finishing || erroringRef.value) return if (!loadingRef.value) { void start(100, 100, 'error').then(() => { - erroring = true + erroringRef.value = true const el = loadingBarRef.value if (!el) return el.className = createClassName('error', mergedClsPrefixRef.value) @@ -95,7 +88,7 @@ export default defineComponent({ loadingRef.value = false }) } else { - erroring = true + erroringRef.value = true const el = loadingBarRef.value if (!el) return el.className = createClassName('error', mergedClsPrefixRef.value) @@ -133,6 +126,7 @@ export default defineComponent({ handleEnter, handleAfterEnter, handleAfterLeave, + mergedLoadingBarStyle, cssVars: computed(() => { const { self: { height, colorError, colorLoading } @@ -168,7 +162,10 @@ export default defineComponent({
, [[vShow, this.loading || (!this.loading && this.entering)]] diff --git a/src/loading-bar/src/LoadingBarProvider.tsx b/src/loading-bar/src/LoadingBarProvider.tsx index 0fced2c94f4..b83bf693950 100644 --- a/src/loading-bar/src/LoadingBarProvider.tsx +++ b/src/loading-bar/src/LoadingBarProvider.tsx @@ -10,7 +10,8 @@ import { ExtractPropTypes, InjectionKey, renderSlot, - Ref + Ref, + CSSProperties } from 'vue' import { useIsMounted } from 'vooks' import { useConfig, useTheme } from '../../_mixins' @@ -33,6 +34,12 @@ const loadingBarProps = { to: { type: [String, Object] as PropType, default: undefined + }, + loadingBarStyle: { + type: [String, Object, Function] as PropType<{ + loading?: string | CSSProperties + error?: string | CSSProperties + }> } } @@ -49,9 +56,8 @@ export const loadingBarProviderInjectionKey: InjectionKey<{ mergedClsPrefixRef: Ref }> = Symbol('loadingBar') -export const loadingBarApiInjectionKey: InjectionKey = Symbol( - 'loadingBarApi' -) +export const loadingBarApiInjectionKey: InjectionKey = + Symbol('loadingBarApi') export default defineComponent({ name: 'LoadingBarProvider', diff --git a/src/loading-bar/src/styles/index.cssr.ts b/src/loading-bar/src/styles/index.cssr.ts index b81c8a1817d..0e6e49d8804 100644 --- a/src/loading-bar/src/styles/index.cssr.ts +++ b/src/loading-bar/src/styles/index.cssr.ts @@ -21,23 +21,23 @@ export default cB('loading-bar-container', ` width: 100%; transition: max-width 4s linear, - background-color .2s linear; + background .2s linear; height: var(--height); `, [ cM('starting', ` - background-color: var(--color-loading); + background: var(--color-loading); `), cM('finishing', ` - background-color: var(--color-loading); + background: var(--color-loading); transition: max-width .2s linear, - background-color .2s linear; + background .2s linear; `), cM('error', ` - background-color: var(--color-error); + background: var(--color-error); transition: max-width .2s linear, - background-color .2s linear; + background .2s linear; `) ]) ]) diff --git a/src/mention/demos/enUS/index.demo-entry.md b/src/mention/demos/enUS/index.demo-entry.md index 9f699180725..ab747f94c78 100644 --- a/src/mention/demos/enUS/index.demo-entry.md +++ b/src/mention/demos/enUS/index.demo-entry.md @@ -22,7 +22,7 @@ Mention is provided after `v2.2.0`. | --- | --- | --- | --- | | autosize | `boolean \| { maxRows?: number, minRows?: number }` | `false` | Autosize. | | options | `MentionOption[]` | `[]` | Mention Options list. | -| type | `'input' \| 'textarea'` | `'input'` | Input type. | +| type | `'text' \| 'textarea'` | `'text'` | Input type. | | separator | `string` | `' '` | Char to split mentions whose length must be 1. | | bordered | `boolean` | `true` | Whether to display the border of the input box. | | disabled | `boolean` | `false` | Whether to set the input box to be disabled. | diff --git a/src/mention/demos/zhCN/index.demo-entry.md b/src/mention/demos/zhCN/index.demo-entry.md index c2db90acb7a..1e2c1e8f148 100644 --- a/src/mention/demos/zhCN/index.demo-entry.md +++ b/src/mention/demos/zhCN/index.demo-entry.md @@ -22,7 +22,7 @@ Mention 在 `v2.2.0` 及以后可用。 | --- | --- | --- | --- | | autosize | `boolean \| { maxRows?: number, minRows?: number }` | `false` | 自动换行 | | options | `MentionOption[]` | `[]` | 选项列表 | -| type | `'input' \| 'textarea'` | `'input'` | 输入框类型 | +| type | `'text' \| 'textarea'` | `'text'` | 输入框类型 | | separator | `string` | `' '` | 切分提及使用的字符,长度必须为 1 | | bordered | `boolean` | `true` | 是否显示输入框边框 | | disabled | `boolean` | `false` | 是否设置输入框为禁用状态 | diff --git a/src/mention/src/Mention.tsx b/src/mention/src/Mention.tsx index d2e058577a7..2e065eace92 100644 --- a/src/mention/src/Mention.tsx +++ b/src/mention/src/Mention.tsx @@ -45,8 +45,8 @@ const mentionProps = { default: [] }, type: { - type: String as PropType<'input' | 'textarea'>, - default: 'input' + type: String as PropType<'text' | 'textarea'>, + default: 'text' }, separator: { type: String, @@ -181,7 +181,7 @@ export default defineComponent({ uncontrolledValueRef.value = value } function getInputEl (): HTMLInputElement | HTMLTextAreaElement { - return props.type === 'input' + return props.type === 'text' ? inputInstRef.value!.inputElRef! : inputInstRef.value!.textareaElRef! } diff --git a/src/mention/tests/Mention.spec.ts b/src/mention/tests/Mention.spec.ts index 2fb8c56423c..1e269981c31 100644 --- a/src/mention/tests/Mention.spec.ts +++ b/src/mention/tests/Mention.spec.ts @@ -63,6 +63,9 @@ describe('n-mention', () => { 'n-input--textarea' ) expect(wrapper.find('input').exists()).toBe(true) + + await wrapper.setProps({ type: 'text' }) + expect(wrapper.find('input').exists()).toBe(true) await wrapper.setProps({ type: 'textarea' }) expect(wrapper.find('.n-input').classes()).toContain('n-input--textarea') diff --git a/src/menu/demos/enUS/index.demo-entry.md b/src/menu/demos/enUS/index.demo-entry.md index 7f7ec79eff7..4f43b9e7784 100644 --- a/src/menu/demos/enUS/index.demo-entry.md +++ b/src/menu/demos/enUS/index.demo-entry.md @@ -31,11 +31,13 @@ long-label | default-value | `string \| null` | `null` | Whether selected by default in uncontrolled mode. | | dropdown-placement | `'top-start' \| 'top' \| 'top-end' \| 'right-start' \| 'right' \| 'right-end' \| 'bottom-start' \| 'bottom' \| 'bottom-end' \| 'left-start' \| 'left' \| 'left-end' \| ` | `'top'` | Only effective in horizontal mode. | | expanded-keys | `Array` | `undefined` | The expanded submenu keys. If set, menu will work in controlled manner and `default-expanded-names` won't work. | +| expand-icon | `(option: MenuOption) => VNodeChild` | `undefined` | Render function that renders all expand icon. | | icon-size | `number` | `20` | The icon size when menu is not collapsed. | | indent | `number` | `32` | The indent of menu. | | inverted | `boolean` | `false` | Use inverted style. | | options | `Array` | `[]` | Items data of menu. | | mode | `'vertical' \| 'horizontal'` | `'vertical'` | Menu layout. | +| render-extra | `(option: MenuOption \| MenuGroupOption) => VNodeChild` | `undefined` | Render function that renders all extras. | | render-icon | `(option: MenuOption) => VNodeChild` | `undefined` | Render function that renders all icons. | | render-label | `(option: MenuOption \| MenuGroupOption) => VNodeChild` | `undefined` | Render function that renders all labels. | | root-indent | `number` | `undefined` | The indent of menu's first level children. If not set, menu will use `indent` in place of it. | diff --git a/src/menu/demos/enUS/render-label.demo.md b/src/menu/demos/enUS/render-label.demo.md index 7c8782cdc9d..2f2a67880c9 100644 --- a/src/menu/demos/enUS/render-label.demo.md +++ b/src/menu/demos/enUS/render-label.demo.md @@ -1,6 +1,6 @@ # Batch Customizing Menu Options -The `render-label`, `render-icon` can be used to batch render menu options. +The `render-label`, `render-icon`, `expand-icon` can be used to batch render menu options. ```html @@ -23,6 +23,7 @@ The `render-label`, `render-icon` can be used to batch render menu options. :options="menuOptions" :render-label="renderMenuLabel" :render-icon="renderMenuIcon" + :expand-icon="expandIcon" /> @@ -35,7 +36,7 @@ The `render-label`, `render-icon` can be used to batch render menu options. ```js import { h, ref, defineComponent } from 'vue' import { NIcon } from 'naive-ui' -import { BookmarkOutline } from '@vicons/ionicons5' +import { BookmarkOutline, CaretDownOutline } from '@vicons/ionicons5' const menuOptions = [ { @@ -118,8 +119,13 @@ export default defineComponent({ } return option.label }, - renderMenuIcon () { + renderMenuIcon (option) { + if (option.key === 'beverage') return true + if (option.key === 'food') return null // falsy return h(NIcon, null, { default: () => h(BookmarkOutline) }) + }, + expandIcon () { + return h(NIcon, null, { default: () => h(CaretDownOutline) }) } } } diff --git a/src/menu/demos/zhCN/index.demo-entry.md b/src/menu/demos/zhCN/index.demo-entry.md index 1d9296f501c..5941352c261 100644 --- a/src/menu/demos/zhCN/index.demo-entry.md +++ b/src/menu/demos/zhCN/index.demo-entry.md @@ -31,11 +31,13 @@ long-label | default-value | `string \| null` | `null` | 非受控模式下的默认值 | | dropdown-placement | `'top-start' \| 'top' \| 'top-end' \| 'right-start' \| 'right' \| 'right-end' \| 'bottom-start' \| 'bottom' \| 'bottom-end' \| 'left-start' \| 'left' \| 'left-end' \| ` | `'top'` | 仅在 `mode='horizontal'` 模式下生效 | | expanded-keys | `Array` | `undefined` | 展开的子菜单标识符数组,如果设定了,菜单的展开将会进入受控状态,`default-expanded-keys` 不会生效 | +| expand-icon | `(option: MenuOption) => VNodeChild` | `undefined` | 批量处理菜单展开图标的渲染 | | icon-size | `number` | `20` | 菜单未折叠时图标的大小 | | indent | `number` | `32` | 菜单每级的缩进 | | inverted | `boolean` | `false` | 使用反转样式 | | options | `Array` | `[]` | 菜单的数据 | | mode | `'vertical' \| 'horizontal'` | `'vertical'` | 菜单的布局方式 | +| render-extra | `(option: MenuOption \| MenuGroupOption) => VNodeChild` | `undefined` | 批量处理菜单额外部分渲染 | | render-icon | `(option: MenuOption) => VNodeChild` | `undefined` | 批量处理菜单图标渲染 | | render-label | `(option: MenuOption \| MenuGroupOption) => VNodeChild` | `undefined` | 批量处理菜单标签渲染 | | root-indent | `number` | `32` | 菜单第一级的缩进,如果没有设定,使用 `indent` 代替 | diff --git a/src/menu/demos/zhCN/render-label.demo.md b/src/menu/demos/zhCN/render-label.demo.md index 14229de3f0b..77f313d4298 100644 --- a/src/menu/demos/zhCN/render-label.demo.md +++ b/src/menu/demos/zhCN/render-label.demo.md @@ -1,6 +1,6 @@ # 批量处理菜单渲染 -使用 `render-label`、`render-icon` 可以批量控制菜单的选项渲染。 +使用 `render-label`、`render-icon`、`expand-icon` 可以批量控制菜单的选项渲染。 ```html @@ -23,6 +23,7 @@ :options="menuOptions" :render-label="renderMenuLabel" :render-icon="renderMenuIcon" + :expand-icon="expandIcon" /> @@ -35,7 +36,7 @@ ```js import { h, ref, defineComponent } from 'vue' import { NIcon } from 'naive-ui' -import { BookmarkOutline } from '@vicons/ionicons5' +import { BookmarkOutline, CaretDownOutline } from '@vicons/ionicons5' const menuOptions = [ { @@ -118,8 +119,13 @@ export default defineComponent({ } return option.label }, - renderMenuIcon () { + renderMenuIcon (option) { + if (option.key === 'sheep-man') return true + if (option.key === 'food') return null // falsy return h(NIcon, null, { default: () => h(BookmarkOutline) }) + }, + expandIcon () { + return h(NIcon, null, { default: () => h(CaretDownOutline) }) } } } diff --git a/src/menu/src/Menu.tsx b/src/menu/src/Menu.tsx index c937ec73e90..1552d497eaf 100644 --- a/src/menu/src/Menu.tsx +++ b/src/menu/src/Menu.tsx @@ -127,6 +127,7 @@ const menuProps = { }, default: undefined }, + expandIcon: Function as PropType<(option: MenuOption) => VNodeChild>, expandedNames: { type: Array as PropType, validator: () => { @@ -153,6 +154,9 @@ const menuProps = { renderLabel: Function as PropType< (option: MenuOption | MenuGroupOption) => VNodeChild >, + renderExtra: Function as PropType< + (option: MenuOption | MenuGroupOption) => VNodeChild + >, dropdownPlacement: { type: String as PropType, default: 'bottom' diff --git a/src/menu/src/MenuOptionContent.tsx b/src/menu/src/MenuOptionContent.tsx index 45234e8efbf..95dcc112833 100644 --- a/src/menu/src/MenuOptionContent.tsx +++ b/src/menu/src/MenuOptionContent.tsx @@ -63,8 +63,9 @@ export default defineComponent({ const { clsPrefix, tmNode, - menuProps: { renderIcon, renderLabel } + menuProps: { renderIcon, renderLabel, renderExtra, expandIcon } } = this + const icon = renderIcon ? renderIcon(tmNode.rawNode) : render(this.icon) return (
- {renderIcon || this.icon ? ( + {icon && (
- {renderIcon ? renderIcon(tmNode.rawNode) : render(this.icon)} + {icon}
- ) : null} + )}
{renderLabel ? renderLabel(tmNode.rawNode) : render(this.title)} - {this.extra ? ( + {this.extra || renderExtra ? ( {' '} - {render(this.extra)} + {renderExtra ? renderExtra(tmNode.rawNode) : render(this.extra)} ) : null}
@@ -105,7 +106,12 @@ export default defineComponent({ clsPrefix={clsPrefix} > {{ - default: () => + default: () => + expandIcon ? ( + expandIcon(tmNode.rawNode) + ) : ( + + ) }} ) : null} diff --git a/src/menu/tests/Menu.spec.ts b/src/menu/tests/Menu.spec.ts index 6e6425c5950..a40c9046b7e 100644 --- a/src/menu/tests/Menu.spec.ts +++ b/src/menu/tests/Menu.spec.ts @@ -9,6 +9,7 @@ describe('n-menu', () => { it('should work with import on demand', () => { mount(NMenu) }) + it('props.onUpdateValue type', () => { const stringCb = (v: string): void => {} const numberCb = (v: number): void => {} @@ -35,6 +36,42 @@ describe('n-menu', () => { } }) }) + + it('should work with `render-icon` props', async () => { + const options = [ + { + label: 'yeh', + key: 'yeh' + }, + { + label: 'fantasy', + key: 'fantasy' + }, + { + label: 'mojito', + key: 'mojito' + }, + { + label: 'initialj', + key: 'initialj' + } + ] + function renderMenuIcon (option: any): any { + if (option.key === 'fantasy') return true // render indent + if (option.key === 'mojito') return true // render indent + if (option.key === 'initialj') return null // don't render + return h(NIcon, null, { default: () => h(HappyOutline) }) // render this + } + const wrapper = mount(NMenu, { + props: { + options: options, + renderIcon: renderMenuIcon + } + }) + expect(wrapper.findAll('.n-menu-item-content__icon').length).toBe(3) + expect(wrapper.findAll('.n-icon').length).toBe(1) + }) + it('should tooltip work with `render-label` props', async () => { const options = [ { @@ -81,6 +118,7 @@ describe('n-menu', () => { expect(wrapper.find('[target="_blank"]').exists()).toBe(true) expect(wrapper.find('[href="test2"]').exists()).toBe(true) }) + it('should dropdown work with `render-label` props', async () => { const options = [ { @@ -181,4 +219,87 @@ describe('n-menu', () => { expect(document.body.querySelector('.n-dropdown')).not.toEqual(null) expect(document.querySelectorAll('.n-icon').length).toEqual(2) }) + + it('should dropdown work with `expand-icon` props', () => { + const options = [ + { + label: 'jj', + key: 'jj' + }, + { + label: 'jay', + key: 'jay', + children: [ + { + type: 'group', + label: 'song-group', + key: 'group', + children: [ + { + label: 'fantasy', + key: 'fantasy' + }, + { + label: 'mojito', + key: 'mojito' + } + ] + } + ] + } + ] + function renderExpandIcon (): any { + return h('span', { class: 'expand-icon' }, '1') + } + const wrapper = mount(NMenu, { + props: { + options: options, + expandIcon: renderExpandIcon + } + }) + expect(wrapper.find('.expand-icon').text()).toEqual('1') + }) +}) + +it('should dropdown work with `render-extra` props', async () => { + const options = [ + { + label: 'jj', + key: 'jj' + }, + { + label: 'jay', + key: 'jay', + children: [ + { + type: 'group', + label: 'song-group', + key: 'group', + children: [ + { + label: 'fantasy', + key: 'fantasy' + }, + { + label: 'mojito', + key: 'mojito' + } + ] + } + ] + } + ] + function renderMenuExtra (): any { + return 'test' + } + const wrapper = mount(NMenu, { + props: { + defaultExpandAll: true, + options: options, + renderExtra: renderMenuExtra + } + }) + expect(wrapper.findAll('.n-menu-item-content-header__extra').length).toEqual( + 4 + ) }) diff --git a/src/message/demos/enUS/index.demo-entry.md b/src/message/demos/enUS/index.demo-entry.md index 4e198031ebc..f7b73871640 100644 --- a/src/message/demos/enUS/index.demo-entry.md +++ b/src/message/demos/enUS/index.demo-entry.md @@ -17,9 +17,10 @@ For example: ```js import { useMessage } from 'naive-ui' +import { defineComponent } from 'vue' // content -export default { +export default defineComponent({ setup () { const message = useMessage() return { @@ -28,7 +29,7 @@ export default { } } } -} +}) ``` @@ -52,6 +53,7 @@ multiple-line | Name | Type | Default | Description | | --- | --- | --- | --- | +| closable | `boolean` | All messages whether to show close icon. | | duration | `number` | `3000` | All messages's default duration. | | max | `number` | `undefined` | Limit the number of message to display. | | to | `string \| HTMLElement` | `'body'` | Container node of message container. | @@ -62,6 +64,7 @@ multiple-line | Name | Type | Description | | --- | --- | --- | +| destroyAll | `() => void` | Destroy all popup messages. | | error | `(content: string \| (() => VNodeChild), option?: MessageOption) => MessageReactive` | Use error type message. | | info | `(content: string \| (() => VNodeChild), option?: MessageOption) => MessageReactive` | Use info type message. | | loading | `(content: string \| (() => VNodeChild), option?: MessageOption) => MessageReactive` | Use loading type message. | @@ -119,13 +122,14 @@ multiple-line ``` diff --git a/src/message/demos/zhCN/index.demo-entry.md b/src/message/demos/zhCN/index.demo-entry.md index c9ead27758e..a2c5ed2006b 100644 --- a/src/message/demos/zhCN/index.demo-entry.md +++ b/src/message/demos/zhCN/index.demo-entry.md @@ -17,9 +17,10 @@ ```js import { useMessage } from 'naive-ui' +import { defineComponent } from 'vue' // content -export default { +export default defineComponent({ setup () { const message = useMessage() return { @@ -28,7 +29,7 @@ export default { } } } -} +}) ``` @@ -52,6 +53,7 @@ multiple-line | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| closable | `boolean` | 所有 Message 是否显示 close 图标 | | duration | `number` | `3000` | 所有 Message 默认的持续时长 | | max | `number` | `undefined` | 限制提示信息显示的个数 | | to | `string \| HTMLElement` | `'body'` | Message 容器节点的位置 | @@ -62,6 +64,7 @@ multiple-line | 名称 | 类型 | 说明 | | --- | --- | --- | +| destroyAll | `() => void` | 销毁所有弹出的信息 | | error | `(content: string \| (() => VNodeChild), option?: MessageOption) => MessageReactive` | 调用 error 类型的信息 | | info | `(content: string \| (() => VNodeChild), option?: MessageOption) => MessageReactive` | 调用 info 类型的信息 | | loading | `(content: string \| (() => VNodeChild), option?: MessageOption) => MessageReactive` | 调用 loading 类型的信息 | @@ -119,13 +122,14 @@ multiple-line ``` diff --git a/src/message/src/MessageProvider.tsx b/src/message/src/MessageProvider.tsx index 2d830032651..e0d483c6efd 100644 --- a/src/message/src/MessageProvider.tsx +++ b/src/message/src/MessageProvider.tsx @@ -10,7 +10,8 @@ import { InjectionKey, ExtractPropTypes, renderSlot, - Ref + Ref, + PropType } from 'vue' import { createId } from 'seemly' import { ExtractPublicPropTypes, omit } from '../../_utils' @@ -36,6 +37,7 @@ export interface MessageApiInjection { warning: (content: ContentType, options?: MessageOptions) => MessageReactive error: (content: ContentType, options?: MessageOptions) => MessageReactive loading: (content: ContentType, options?: MessageOptions) => MessageReactive + destroyAll: () => void } export const messageApiInjectionKey: InjectionKey = @@ -63,15 +65,13 @@ export type MessageProviderInst = MessageApiInjection const messageProviderProps = { ...(useTheme.props as ThemeProps), - to: { - type: [String, Object], - default: undefined - }, + to: [String, Object] as PropType, duration: { type: Number, default: 3000 }, - max: Number + max: Number, + closable: Boolean } export type MessageProviderProps = ExtractPublicPropTypes< @@ -107,7 +107,8 @@ export default defineComponent({ }, loading (content: ContentType, options?: MessageOptions) { return create(content, { ...options, type: 'loading' }) - } + }, + destroyAll } provide(messageProviderInjectionKey, { props, @@ -136,6 +137,13 @@ export default defineComponent({ messageListRef.value.findIndex((message) => message.key === key), 1 ) + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete messageRefs.value[key] + } + function destroyAll (): void { + Object.values(messageRefs.value).forEach((messageInstRef) => { + messageInstRef.hide() + }) } return Object.assign( { @@ -162,13 +170,24 @@ export default defineComponent({ { - this.messageRefs[message.key] = inst + if (inst) { + this.messageRefs[message.key] = inst + } }) as () => void } internalKey={message.key} onInternalAfterLeave={this.handleAfterLeave} {...omit(message, ['destroy'], undefined)} - duration={this.duration} + duration={ + message.duration === undefined + ? this.duration + : message.duration + } + closable={ + message.closable === undefined + ? this.closable + : message.closable + } /> ) })} diff --git a/src/modal/demos/enUS/index.demo-entry.md b/src/modal/demos/enUS/index.demo-entry.md index f90c3f18e96..04acae06292 100644 --- a/src/modal/demos/enUS/index.demo-entry.md +++ b/src/modal/demos/enUS/index.demo-entry.md @@ -22,7 +22,7 @@ preset-confirm-slot | --- | --- | --- | --- | | display-directive | `'if' \| 'show'` | `'if'` | Use which directive to control the rendering of modal body. | | mask-closable | `boolean` | `true` | Whether to emit `hide` event when click mask. | -| preset | `'card' \| 'confirm'` | `undefined` | | +| preset | `'card' \| 'confirm'` | `undefined` | The preset of `n-modal`. | | show | `boolean` | `false` | Whether to show modal. | | on-update:show | `(value: boolean) => void` | `undefined` | Callback when modal's display status is changed. | @@ -38,9 +38,9 @@ See [Dialog props](dialog#Props) ### Modal without Preset -| Name | Parameters | Description | -| ------- | ---------- | ----------- | -| default | `()` | | +| Name | Parameters | Description | +| ------- | ---------- | ------------------------- | +| default | `()` | The content of the modal. | ### Modal with Preset Card diff --git a/src/modal/demos/enUS/preset-card.demo.md b/src/modal/demos/enUS/preset-card.demo.md index 93eef143907..1ff50cac801 100644 --- a/src/modal/demos/enUS/preset-card.demo.md +++ b/src/modal/demos/enUS/preset-card.demo.md @@ -5,6 +5,7 @@ Modal has some presets, which means you can use props & slots of the preset afte ```html Start Me up 来吧 来吧 + +
+
123
+
+
+``` + +```js +export default { + data () { + return { + showModal: false + } + } +} +``` diff --git a/src/modal/src/BodyWrapper.tsx b/src/modal/src/BodyWrapper.tsx index 2a579d0eee8..22b22fcc913 100644 --- a/src/modal/src/BodyWrapper.tsx +++ b/src/modal/src/BodyWrapper.tsx @@ -13,7 +13,8 @@ import { Transition, VNode, ComponentPublicInstance, - mergeProps + mergeProps, + cloneVNode } from 'vue' import { clickoutside } from 'vdirs' import { dialogPropKeys } from '../../dialog/src/Dialog' @@ -173,6 +174,7 @@ export default defineComponent({ warn('modal', 'default slot is empty') return } + childNode = cloneVNode(childNode) childNode.props = mergeProps( { class: `${mergedClsPrefix}-modal` @@ -206,7 +208,10 @@ export default defineComponent({ this.preset === 'dialog' ? ( ), - show: { - type: Boolean, - default: false - }, + show: Boolean, unstableShowMask: { type: Boolean, default: true diff --git a/src/notification/demos/enUS/index.demo-entry.md b/src/notification/demos/enUS/index.demo-entry.md index 5327999442f..a632e945ffb 100644 --- a/src/notification/demos/enUS/index.demo-entry.md +++ b/src/notification/demos/enUS/index.demo-entry.md @@ -50,37 +50,37 @@ duration ### NotificationProvider Props -| Name | Type | Default | Description | -| ---------- | ----------------------- | -------- | ----------- | -| scrollable | `boolean` | `true` | | -| to | `string \| HTMLElement` | `'body'` | | +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| scrollable | `boolean` | `true` | Whether notification can be scroll. | +| to | `string \| HTMLElement` | `'body'` | Container node of notification container. | ### `notification` Injection Methods | Name | Type | Description | | --- | --- | --- | -| create | `(option: NotificationOption) => NotificationReactive` | | -| error | `(option: NotificationOption) => NotificationReactive` | | -| info | `(option: NotificationOption) => NotificationReactive` | | -| success | `(option: NotificationOption) => NotificationReactive` | | -| warning | `(option: NotificationOption) => NotificationReactive` | | +| create | `(option: NotificationOption) => NotificationReactive` | Create a notification. | +| error | `(option: NotificationOption) => NotificationReactive` | Use `error` type notification. | +| info | `(option: NotificationOption) => NotificationReactive` | Use `info` type notification. | +| success | `(option: NotificationOption) => NotificationReactive` | Use `success` type notification. | +| warning | `(option: NotificationOption) => NotificationReactive` | Use `warning` type notification. | ### NotificationOption Properties | Name | Type | Default | Description | | --- | --- | --- | --- | -| action | `string \| (() => VNodeChild)` | `undefined` | Can be a render function. | -| avatar | `() => VNodeChild` | `undefined` | Can be a render function. | -| closable | `boolean` | `true` | | -| content | `string \| (() => VNodeChild)` | `undefined` | Can be a render function. | -| description | `string \| (() => VNodeChild)` | `undefined` | Can be a render function. | +| action | `string \| (() => VNodeChild)` | `undefined` | Content of the operation area,, can be a render function. | +| avatar | `() => VNodeChild` | `undefined` | Content of the `avatar`, can be a render function. | +| closable | `boolean` | `true` | Whether to show close icon. | +| content | `string \| (() => VNodeChild)` | `undefined` | Content, can be a render function. | +| description | `string \| (() => VNodeChild)` | `undefined` | Content of the `description`, can be a render function. | | duration | `number` | `undefined` | If not set, it won't automatically close. Unit is millisecond. | -| meta | `string \| (() => VNodeChild)` | `undefined` | Can be a render function. | -| title | `string \| (() => VNodeChild)` | `undefined` | Can be a render function. | -| onAfterEnter | `Function` | `undefined` | | -| onAfterLeave | `Function` | `undefined` | | +| meta | `string \| (() => VNodeChild)` | `undefined` | Content of the `meta`, can be a render function. | +| title | `string \| (() => VNodeChild)` | `undefined` | Content of the `title`, can be a render function. | +| onAfterEnter | `Function` | `undefined` | Callback triggered after Transition's enter animation executed. | +| onAfterLeave | `Function` | `undefined` | Callback triggered after Transition's leave animation executed. | | onClose | `() => boolean \| Promise` | `undefined` | The callback of notification closing. Returning `false`, promise resolve `false` or promise reject will cancel this closing. | -| onLeave | `Function` | | | +| onLeave | `Function` | `undefined` | Callback triggered when Transition's leave animation executed. | ### NotificationReactive API @@ -90,20 +90,20 @@ Properties of NotificationReactive can be dynamically changed. | Name | Type | Description | | --- | --- | --- | -| action | `string \| (() => VNodeChild)` | Can be a render function. | -| avatar | `() => VNodeChild` | Can be a render function. | -| closable | `boolean` | | -| content | `string \| (() => VNodeChild)` | Can be a render function. | -| description | `string \| (() => VNodeChild)` | Can be a render function. | -| meta | `string \| (() => VNodeChild)` | Can be a render function. | -| title | `string \| (() => VNodeChild)` | Can be a render function. | -| onAfterEnter | `Function` | | -| onAfterLeave | `Function` | | +| action | `string \| (() => VNodeChild)` | Content of the operation area,, can be a render function. | +| avatar | `() => VNodeChild` | Content of the `avatar`, can be a render function. | +| closable | `boolean` | Whether to show close icon. | +| content | `string \| (() => VNodeChild)` | Content, can be a render function. | +| description | `string \| (() => VNodeChild)` | Content of the `description`, can be a render function. | +| meta | `string \| (() => VNodeChild)` | Content of the `meta`, can be a render function. | +| title | `string \| (() => VNodeChild)` | Content of the `title`, can be a render function | +| onAfterEnter | `Function` | Callback triggered after Transition's enter animation executed. | +| onAfterLeave | `Function` | Callback triggered after Transition's leave animation executed. | | onClose | `() => boolean \| Promise` | The callback of notification closing. Returning `false`, promise resolve `false` or promise reject will cancel this closing. | -| onLeave | `Function` | | +| onLeave | `Function` | Callback triggered when Transition's leave animation executed. | #### NotificationReactive Methods -| Name | Type | Description | -| ------- | ---- | ------------------------ | -| destroy | `()` | Destroy the notification | +| Name | Type | Description | +| ------- | ---- | ------------------------- | +| destroy | `()` | Destroy the notification. | diff --git a/src/notification/demos/zhCN/index.demo-entry.md b/src/notification/demos/zhCN/index.demo-entry.md index 9dd027599ea..0511120fcc6 100644 --- a/src/notification/demos/zhCN/index.demo-entry.md +++ b/src/notification/demos/zhCN/index.demo-entry.md @@ -50,37 +50,37 @@ duration ### NotificationProvider Props -| 名称 | 类型 | 默认值 | 说明 | -| ---------- | ----------------------- | -------- | ---- | -| scrollable | `boolean` | `true` | | -| to | `string \| HTMLElement` | `'body'` | | +| 名称 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| scrollable | `boolean` | `true` | 通知是否可滚动 | +| to | `string \| HTMLElement` | `'body'` | `Notification` 容器节点的位置 | ### `notification` Injection Methods -| 名称 | 类型 | 说明 | -| ------- | ------------------------------------------------------ | ---- | -| create | `(option: NotificationOption) => NotificationReactive` | | -| error | `(option: NotificationOption) => NotificationReactive` | | -| info | `(option: NotificationOption) => NotificationReactive` | | -| success | `(option: NotificationOption) => NotificationReactive` | | -| warning | `(option: NotificationOption) => NotificationReactive` | | +| 名称 | 类型 | 说明 | +| --- | --- | --- | +| create | `(option: NotificationOption) => NotificationReactive` | 创建通知框 | +| error | `(option: NotificationOption) => NotificationReactive` | 调用 `error` 类型的通知框 | +| info | `(option: NotificationOption) => NotificationReactive` | 调用 `info` 类型的通知框 | +| success | `(option: NotificationOption) => NotificationReactive` | 调用 `success` 类型的通知框 | +| warning | `(option: NotificationOption) => NotificationReactive` | 调用 `warning` 类型的通知框 | ### NotificationOption Properties | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| action | `string \| (() => VNodeChild)` | `undefined` | 可以是 render 函数 | -| avatar | `() => VNodeChild` | `undefined` | 可以是 render 函数 | -| closable | `boolean` | `true` | | -| content | `string \| (() => VNodeChild)` | `undefined` | 可以是 render 函数 | -| description | `string \| (() => VNodeChild)` | `undefined` | 可以是 render 函数 | +| action | `string \| (() => VNodeChild)` | `undefined` | 操作区域的内容,可以是 render 函数 | +| avatar | `() => VNodeChild` | `undefined` | 头像区域的内容,可以是 render 函数 | +| closable | `boolean` | `true` | 是否显示 close 图标 | +| content | `string \| (() => VNodeChild)` | `undefined` | 通知框内容,可以是 render 函数 | +| description | `string \| (() => VNodeChild)` | `undefined` | 描述的内容,可以是 render 函数 | | duration | `number` | `undefined` | 如果没有设定则不会自动关闭,单位毫秒 | -| meta | `string \| (() => VNodeChild)` | `undefined` | 可以是 render 函数 | -| title | `string \| (() => VNodeChild)` | `undefined` | 可以是 render 函数 | -| onAfterEnter | `Function` | `undefined` | | -| onAfterLeave | `Function` | `undefined` | | -| onClose | `() => boolean \| Promise` | `undefined` | 关闭通知的回调。返回 `false`、Promise resolve `false` 或者 reject 会取消这次关闭 | -| onLeave | `Function` | `undefined` | | +| meta | `string \| (() => VNodeChild)` | `undefined` | `meta` 信息,可以是 render 函数 | +| title | `string \| (() => VNodeChild)` | `undefined` | `title` 信息,可以是 render 函数 | +| onAfterEnter | `Function` | `undefined` | 过渡动画进入执行完后执行的回调 | +| onAfterLeave | `Function` | `undefined` | 过渡动画离开执行完后执行的回调 | +| onClose | `() => boolean \| Promise` | `undefined` | 关闭通知的回调,返回 `false`、Promise resolve `false` 或者 reject 会取消这次关闭 | +| onLeave | `Function` | `undefined` | 过渡动画离开时执行的回调 | ### NotificationReactive API @@ -90,17 +90,17 @@ NotificationReactive 实例的属性可以被动态改变。 | 名称 | 类型 | 说明 | | --- | --- | --- | -| action | `string \| (() => VNodeChild)` | 可以是 render 函数 | -| avatar | `() => VNodeChild` | 可以是 render 函数 | -| closable | `boolean` | | -| content | `string \| (() => VNodeChild)` | 可以是 render 函数 | -| description | `string \| (() => VNodeChild)` | 可以是 render 函数 | -| meta | `string \| (() => VNodeChild)` | 可以是 render 函数 | -| title | `string \| (() => VNodeChild)` | 可以是 render 函数 | -| onAfterEnter | `Function` | | -| onAfterLeave | `Function` | | +| action | `string \| (() => VNodeChild)` | 操作区域的内容,可以是 render 函数 | +| avatar | `() => VNodeChild` | 头像区域的内容,可以是 render 函数 | +| closable | `boolean` | 是否显示 close 图标 | +| content | `string \| (() => VNodeChild)` | 通知框内容,可以是 render 函数 | +| description | `string \| (() => VNodeChild)` | 描述的内容,可以是 render 函数 | +| meta | `string \| (() => VNodeChild)` | `meta` 信息,可以是 render 函数 | +| title | `string \| (() => VNodeChild)` | `title` 信息,可以是 render 函数 | +| onAfterEnter | `Function` | 过渡动画进入执行完后执行的回调 | +| onAfterLeave | `Function` | 过渡动画离开进入执行完后执行的回调 | | onClose | `() => boolean \| Promise` | 关闭通知的回调。返回 `false`、Promise resolve `false` 或者 reject 会取消这次关闭 | -| onLeave | `Function` | | +| onLeave | `Function` | 过渡动画离开动画执行时的回调 | #### NotificationReactive Methods diff --git a/src/pagination/demos/enUS/index.demo-entry.md b/src/pagination/demos/enUS/index.demo-entry.md index d384d8f9fe0..bf756dad572 100644 --- a/src/pagination/demos/enUS/index.demo-entry.md +++ b/src/pagination/demos/enUS/index.demo-entry.md @@ -13,6 +13,7 @@ quick-jumper size-picker disabled item-count +prev prefix ``` @@ -22,6 +23,8 @@ prefix | --- | --- | --- | --- | | default-page | `number` | `1` | Current page in uncontrolled mode. | | default-page-size | `number` | `10` | Page size in uncontrolled mode. | +| next | `(info: PaginationInfo) => VNodeChild` | `undefined` | Next page. | +| prev | `(info: PaginationInfo) => VNodeChild` | `undefined` | Previous page. | | item-count | `number` | `undefined` | Total number. | | page-count | `number` | `1` | Total pages. | | page-sizes | `Array` | `['10']` | Number of items per page. | @@ -37,10 +40,12 @@ prefix ## Slots -| Name | Parameters | Description | -| ------ | ------------------------ | ------------ | -| prefix | `(info: PaginationInfo)` | Page prefix. | -| suffix | `(info: PaginationInfo)` | Page suffix. | +| Name | Parameters | Description | +| ------ | ------------------------ | -------------- | +| next | `(info: PaginationInfo)` | Next page. | +| prev | `(info: PaginationInfo)` | Previous page. | +| prefix | `(info: PaginationInfo)` | Page prefix. | +| suffix | `(info: PaginationInfo)` | Page suffix. | ## API @@ -53,5 +58,6 @@ interface PaginationInfo { page: number pageSize: number pageCount: number + itemCount: number | undefined } ``` diff --git a/src/pagination/demos/enUS/prefix.demo.md b/src/pagination/demos/enUS/prefix.demo.md index 72821aeeeac..772e052e618 100644 --- a/src/pagination/demos/enUS/prefix.demo.md +++ b/src/pagination/demos/enUS/prefix.demo.md @@ -4,7 +4,9 @@ You may want to add something before and after. ```html - + ``` diff --git a/src/pagination/demos/enUS/prev.demo.md b/src/pagination/demos/enUS/prev.demo.md new file mode 100644 index 00000000000..eee80931c5c --- /dev/null +++ b/src/pagination/demos/enUS/prev.demo.md @@ -0,0 +1,8 @@ +# Customize the Previous and Next Button + +```html + + + + +``` diff --git a/src/pagination/demos/zhCN/index.demo-entry.md b/src/pagination/demos/zhCN/index.demo-entry.md index 322037585df..51f17f44bde 100644 --- a/src/pagination/demos/zhCN/index.demo-entry.md +++ b/src/pagination/demos/zhCN/index.demo-entry.md @@ -13,6 +13,7 @@ quick-jumper size-picker disabled item-count +prev prefix ``` @@ -22,6 +23,8 @@ prefix | --- | --- | --- | --- | | default-page | `number` | `1` | 非受控模式下的当前页 | | default-page-size | `number` | `10` | 非受控模式下的分页大小 | +| next | `(info: PaginationInfo) => VNodeChild` | `undefined` | 下一页 | +| prev | `(info: PaginationInfo) => VNodeChild` | `undefined` | 上一页 | | item-count | `number` | `undefined` | 总条数 | | page-count | `number` | `1` | 总页数 | | page-sizes | `Array` | `['10']` | 每页条数 | @@ -39,6 +42,8 @@ prefix | 名称 | 参数 | 说明 | | ------ | ------------------------ | -------- | +| next | `(info: PaginationInfo)` | 下一页 | +| prev | `(info: PaginationInfo)` | 上一页 | | prefix | `(info: PaginationInfo)` | 分页前缀 | | suffix | `(info: PaginationInfo)` | 分页后缀 | @@ -53,5 +58,6 @@ interface PaginationInfo { page: number pageSize: number pageCount: number + itemCount: number | undefined } ``` diff --git a/src/pagination/demos/zhCN/prefix.demo.md b/src/pagination/demos/zhCN/prefix.demo.md index ac3fde0386f..596fd4b8356 100644 --- a/src/pagination/demos/zhCN/prefix.demo.md +++ b/src/pagination/demos/zhCN/prefix.demo.md @@ -4,7 +4,9 @@ ```html - + ``` diff --git a/src/pagination/demos/zhCN/prev.demo.md b/src/pagination/demos/zhCN/prev.demo.md new file mode 100644 index 00000000000..b59f0c7a9fc --- /dev/null +++ b/src/pagination/demos/zhCN/prev.demo.md @@ -0,0 +1,8 @@ +# 自定义上一步和下一步 + +```html + + + + +``` diff --git a/src/pagination/src/Pagination.tsx b/src/pagination/src/Pagination.tsx index 4e1ff7e47fc..7d579ed4a5b 100644 --- a/src/pagination/src/Pagination.tsx +++ b/src/pagination/src/Pagination.tsx @@ -30,7 +30,7 @@ import style from './styles/index.cssr' import { call, ExtractPublicPropTypes, MaybeArray, warn } from '../../_utils' import type { Size as InputSize } from '../../input/src/interface' import type { Size as SelectSize } from '../../select/src/interface' -import { RenderPrefix, RenderSuffix } from './interface' +import { RenderPrefix, RenderSuffix, RenderPrev, RenderNext } from './interface' const paginationProps = { ...(useTheme.props as ThemeProps), @@ -50,7 +50,9 @@ const paginationProps = { defaultPageSize: Number, pageSizes: { type: Array as PropType, - default: () => [10] + default () { + return [10] + } }, showQuickJumper: Boolean, disabled: Boolean, @@ -58,6 +60,8 @@ const paginationProps = { type: Number, default: 9 }, + prev: Function as PropType, + next: Function as PropType, prefix: Function as PropType, suffix: Function as PropType, 'onUpdate:page': [Function, Array] as PropType< @@ -426,6 +430,8 @@ export default defineComponent({ mergedPageSize, pageSizeOptions, jumperValue, + prev, + next, prefix, suffix, handleJumperInput, @@ -437,6 +443,8 @@ export default defineComponent({ handleForwardClick, handleQuickJumperKeyUp } = this + const renderPrev = prev || $slots.prev + const renderNext = next || $slots.next return (
) : null}
mergedPageCount || disabled) && `${mergedClsPrefix}-pagination-item--disabled` ]} onClick={handleBackwardClick} > - - {{ default: () => }} - + {renderPrev ? ( + renderPrev({ + page: mergedPage, + pageSize: mergedPageSize, + pageCount: mergedPageCount, + startIndex: this.startIndex, + endIndex: this.endIndex, + itemCount: this.itemCount + }) + ) : ( + + {{ default: () => }} + + )}
{pageItems.map((pageItem, index) => { return ( @@ -515,7 +536,8 @@ export default defineComponent({ })}
= mergedPageCount || disabled @@ -523,9 +545,20 @@ export default defineComponent({ ]} onClick={handleForwardClick} > - - {{ default: () => }} - + {renderNext ? ( + renderNext({ + page: mergedPage, + pageSize: mergedPageSize, + pageCount: mergedPageCount, + itemCount: this.itemCount, + startIndex: this.startIndex, + endIndex: this.endIndex + }) + ) : ( + + {{ default: () => }} + + )}
{showSizePicker ? ( ) : null} diff --git a/src/pagination/src/interface.ts b/src/pagination/src/interface.ts index 5a620167ba8..330ade82245 100644 --- a/src/pagination/src/interface.ts +++ b/src/pagination/src/interface.ts @@ -1,4 +1,4 @@ -import { VNode } from 'vue' +import { VNodeChild } from 'vue' export type RenderPrefix = (info: { startIndex: number @@ -6,6 +6,9 @@ export type RenderPrefix = (info: { page: number pageSize: number pageCount: number -}) => VNode + itemCount: number | undefined +}) => VNodeChild export type RenderSuffix = RenderPrefix +export type RenderNext = RenderPrefix +export type RenderPrev = RenderPrefix diff --git a/src/pagination/src/styles/index.cssr.ts b/src/pagination/src/styles/index.cssr.ts index 5e016f5e9fc..a3c69847dbd 100644 --- a/src/pagination/src/styles/index.cssr.ts +++ b/src/pagination/src/styles/index.cssr.ts @@ -95,13 +95,15 @@ export default cB('pagination', ` background-color .3s var(--bezier), fill .3s var(--bezier); `, [ - - cM('button', { - background: 'var(--item-color)', - color: 'var(--button-icon-color)', - border: 'var(--button-border)', - fontSize: 'var(--button-icon-size)' - }), + cM('button', ` + background: var(--item-color); + color: var(--button-icon-color); + border: var(--button-border); + `, [ + cB('base-icon', ` + font-size: var(--button-icon-size); + `) + ]), cNotM('disabled', [ c('&:hover', { background: 'var(--item-color-hover)', diff --git a/src/pagination/tests/Pagination.spec.ts b/src/pagination/tests/Pagination.spec.ts index 66fb8759db7..9a6122b8d52 100644 --- a/src/pagination/tests/Pagination.spec.ts +++ b/src/pagination/tests/Pagination.spec.ts @@ -18,4 +18,12 @@ describe('n-pagination', () => { }) expect(wrapper.findAll('.n-pagination-item').length).toEqual(4) }) + it('should work with prev slot', async () => { + const wrapper = mount(NPagination, { + slots: { + prev: () => 'Prev' + } + }) + expect(wrapper.find('.n-pagination-item').text()).toContain('Prev') + }) }) diff --git a/src/popconfirm/demos/enUS/actions.demo.md b/src/popconfirm/demos/enUS/actions.demo.md new file mode 100644 index 00000000000..082ec734f70 --- /dev/null +++ b/src/popconfirm/demos/enUS/actions.demo.md @@ -0,0 +1,59 @@ +# Actions + +```html + + + + Things pass us by. Nobody can catch them. That's the way we live our lives. + + + + Things pass us by. Nobody can catch them. That's the way we live our lives. + + + + Things pass us by. Nobody can catch them. That's the way we live our lives. + + + + Things pass us by. Nobody can catch them. That's the way we live our lives. + + + + + Things pass us by. Nobody can catch them. That's the way we live our lives. + + +``` + +```js +import { defineComponent } from 'vue' +import { useMessage } from 'naive-ui' + +export default defineComponent({ + setup () { + const message = useMessage() + return { + handlePositiveClick () { + message.info('Yes') + }, + handleNegativeClick () { + message.info('No') + } + } + } +}) +``` diff --git a/src/popconfirm/demos/enUS/index.demo-entry.md b/src/popconfirm/demos/enUS/index.demo-entry.md index e86314bc147..90ec5912dbc 100644 --- a/src/popconfirm/demos/enUS/index.demo-entry.md +++ b/src/popconfirm/demos/enUS/index.demo-entry.md @@ -10,24 +10,25 @@ custom-action custom-icon event no-icon +actions ``` ## Props | Name | Type | Default | Description | | --- | --- | --- | --- | -| negative-text | `string` | `'Cancel'` | | -| positive-text | `string` | `'Confirm'` | | -| show-icon | `boolean` | `true` | | -| on-positive-click | `() => boolean \| Promise \| any` | `undefined` | | -| on-negative-click | `() => boolean \| Promise \| any` | `undefined` | | +| negative-text | `string` | `'Cancel'` | Cancel button text. | +| positive-text | `string` | `'Confirm'` | Confirm button text. | +| show-icon | `boolean` | `true` | Whether to show icon. | +| on-positive-click | `() => boolean \| Promise \| any` | `undefined` | Callback of confirmation. | +| on-negative-click | `() => boolean \| Promise \| any` | `undefined` | Callback of cancel. | For more props, see [popover](popover#Props). ## Slots -| Name | Parameters | Description | -| ------- | ---------- | ----------- | -| action | `()` | | -| default | `()` | | -| icon | `()` | | +| Name | Parameters | Description | +| ------- | ---------- | ------------------- | +| action | `()` | Custom action. | +| default | `()` | Popconfirm content. | +| icon | `()` | Popconfirm icon. | diff --git a/src/popconfirm/demos/zhCN/actions.demo.md b/src/popconfirm/demos/zhCN/actions.demo.md new file mode 100644 index 00000000000..4e9499283fb --- /dev/null +++ b/src/popconfirm/demos/zhCN/actions.demo.md @@ -0,0 +1,59 @@ +# 操作 + +```html + + + + 一切都将一去杳然,任何人都无法将其捕获。 + + + + 一切都将一去杳然,任何人都无法将其捕获。 + + + + 一切都将一去杳然,任何人都无法将其捕获。 + + + + 一切都将一去杳然,任何人都无法将其捕获。 + + + + + 一切都将一去杳然,任何人都无法将其捕获。 + + +``` + +```js +import { defineComponent } from 'vue' +import { useMessage } from 'naive-ui' + +export default defineComponent({ + setup () { + const message = useMessage() + return { + handlePositiveClick () { + message.info('是的') + }, + handleNegativeClick () { + message.info('并不') + } + } + } +}) +``` diff --git a/src/popconfirm/demos/zhCN/index.demo-entry.md b/src/popconfirm/demos/zhCN/index.demo-entry.md index 077ef143a60..400998ef63c 100644 --- a/src/popconfirm/demos/zhCN/index.demo-entry.md +++ b/src/popconfirm/demos/zhCN/index.demo-entry.md @@ -10,24 +10,25 @@ custom-action custom-icon event no-icon +actions ``` ## Props | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| negative-text | `string` | `'取消'` | | -| positive-text | `string` | `'确认'` | | -| show-icon | `boolean` | `true` | | -| on-positive-click | `() => boolean \| Promise \| any` | `undefined` | | -| on-negative-click | `() => boolean \| Promise \| any` | `undefined` | | +| negative-text | `string` | `'取消'` | 取消按钮文字 | +| positive-text | `string` | `'确认'` | 确定按钮文字 | +| show-icon | `boolean` | `true` | 是否显示图标 | +| on-positive-click | `() => boolean \| Promise \| any` | `undefined` | 点击确定的回调函数 | +| on-negative-click | `() => boolean \| Promise \| any` | `undefined` | 点击取消的回调函数 | 更多 props 请参考 [Popover](popover#Props). ## Slots -| 名称 | 参数 | 说明 | -| ------- | ---- | ---- | -| action | `()` | | -| default | `()` | | -| icon | `()` | | +| 名称 | 参数 | 说明 | +| ------- | ---- | -------------- | +| action | `()` | 自定义操作 | +| default | `()` | 弹出确认的内容 | +| icon | `()` | 弹出确认的图标 | diff --git a/src/popconfirm/src/PopconfirmPanel.tsx b/src/popconfirm/src/PopconfirmPanel.tsx index ae2be55d282..db1ef56c9d8 100644 --- a/src/popconfirm/src/PopconfirmPanel.tsx +++ b/src/popconfirm/src/PopconfirmPanel.tsx @@ -73,6 +73,26 @@ export default defineComponent({ }, render () { const { mergedClsPrefix, $slots } = this + const actionContentNode = $slots.action + ? $slots.action() + : this.negativeText === null && this.positiveText === null + ? null + : [ + this.negativeText !== null && ( + + {{ default: () => this.localizedNegativeText }} + + ), + this.positiveText !== null && ( + + {{ default: () => this.localizedPositiveText }} + + ) + ] return (
@@ -87,20 +107,11 @@ export default defineComponent({ ) : null} {renderSlot($slots, 'default')}
-
- {renderSlot($slots, 'action', undefined, () => [ - - {{ default: () => this.localizedNegativeText }} - , - - {{ default: () => this.localizedPositiveText }} - - ])} -
+ {actionContentNode ? ( +
+ {actionContentNode} +
+ ) : null}
) } diff --git a/src/popover/demos/enUS/index.demo-entry.md b/src/popover/demos/enUS/index.demo-entry.md index c2ad7ff229a..f73bb12f4ae 100644 --- a/src/popover/demos/enUS/index.demo-entry.md +++ b/src/popover/demos/enUS/index.demo-entry.md @@ -41,6 +41,7 @@ header | width | `number \| 'trigger'` | `undefined` | `'trigger'` means popover's witdh will follow its trigger's width. | | x | `number` | `undefined` | The CSS `left` pixel value when popover manually positioned (x, y need to be set together). | | y | `number` | `undefined` | The CSS `top` pixel value when popover manually positioned (x, y need to be set together). | +| on-clickoutside | `(e: MouseEvent) => void` | `undefined` | Callback function triggered when clickoutside. | | on-update:show | `(value: boolean) => void` | `undefined` | Callback on show status changes. | ## Slots diff --git a/src/popover/demos/enUS/manual-position.demo.md b/src/popover/demos/enUS/manual-position.demo.md index 725ad97bf3c..b7c9e975c86 100644 --- a/src/popover/demos/enUS/manual-position.demo.md +++ b/src/popover/demos/enUS/manual-position.demo.md @@ -2,12 +2,16 @@ Click it. +Warn: when manually positioned, the `trigger` prop must be `'manual'`. + ```html
- Cool! + + Cool! + ``` ```js diff --git a/src/popover/demos/zhCN/index.demo-entry.md b/src/popover/demos/zhCN/index.demo-entry.md index 246b7a48910..3a694da30f4 100644 --- a/src/popover/demos/zhCN/index.demo-entry.md +++ b/src/popover/demos/zhCN/index.demo-entry.md @@ -42,6 +42,7 @@ header | width | `number \| 'trigger'` | `undefined` | `'trigger'` 表示 popover 的宽度会和它的触发元素一致 | | x | `number` | `undefined` | 手动控制位置时弹出内容的 CSS `left` 的像素值(x,y 都设置才能生效) | | y | `number` | `undefined` | 手动控制位置时弹出内容的 CSS `top` 的像素值(x,y 都设置才能生效) | +| on-clickoutside | `(e: MouseEvent) => void` | `undefined` | clickoutside 时触发的回调函数 | | on-update:show | `(value: boolean) => void` | `undefined` | 显示状态改变的回调函数 | ## Slots diff --git a/src/popover/demos/zhCN/manual-position.demo.md b/src/popover/demos/zhCN/manual-position.demo.md index 2a3fda55bd2..9783e427f38 100644 --- a/src/popover/demos/zhCN/manual-position.demo.md +++ b/src/popover/demos/zhCN/manual-position.demo.md @@ -2,12 +2,16 @@ 点它 +注意:手动定位时,`trigger` 属性必须为 `'manual'` + ```html
- 厉害! + + 厉害! ``` ```js diff --git a/src/popover/src/Popover.ts b/src/popover/src/Popover.ts index 1976da7ee73..1b15b338ca4 100644 --- a/src/popover/src/Popover.ts +++ b/src/popover/src/Popover.ts @@ -10,7 +10,8 @@ import { CSSProperties, ComputedRef, Ref, - toRef + toRef, + cloneVNode } from 'vue' import { VBinder, VTarget, FollowerPlacement } from 'vueuc' import { useMergedState, useCompitable, useIsMounted, useMemo } from 'vooks' @@ -396,11 +397,12 @@ export default defineComponent({ triggerVNode = getFirstSlotVNode(slots, 'trigger') } if (triggerVNode) { + triggerVNode = cloneVNode(triggerVNode) triggerVNode = triggerVNode.type === textVNodeType ? h('span', [triggerVNode]) : triggerVNode - appendEvents(triggerVNode, this.trigger, { + appendEvents(triggerVNode, positionManually ? 'manual' : this.trigger, { onClick: this.handleClick, onMouseenter: this.handleMouseEnter, onMouseleave: this.handleMouseLeave, diff --git a/src/popover/src/PopoverBody.tsx b/src/popover/src/PopoverBody.tsx index 0190326e30b..dafc23f8294 100644 --- a/src/popover/src/PopoverBody.tsx +++ b/src/popover/src/PopoverBody.tsx @@ -58,6 +58,22 @@ export const popoverBodyProps = { maxWidth: Number } +interface RenderArrowProps { + arrowStyle: string | CSSProperties | undefined + clsPrefix: string +} + +export const renderArrow = ({ + arrowStyle, + clsPrefix +}: RenderArrowProps): VNode | null => { + return ( +
+
+
+ ) +} + export default defineComponent({ name: 'PopoverBody', inheritAttrs: false, @@ -238,17 +254,12 @@ export default defineComponent({ ) : ( renderSlot(slots, 'default') ), - props.showArrow ? ( -
-
-
- ) : null + props.showArrow + ? renderArrow({ + arrowStyle: props.arrowStyle, + clsPrefix: mergedClsPrefix + }) + : null ] ) } else { diff --git a/src/progress/demos/enUS/index.demo-entry.md b/src/progress/demos/enUS/index.demo-entry.md index f2ff62a9a0e..8fd07d71380 100644 --- a/src/progress/demos/enUS/index.demo-entry.md +++ b/src/progress/demos/enUS/index.demo-entry.md @@ -20,24 +20,24 @@ processing | Name | Type | Default | Description | | --- | --- | --- | --- | | border-radius | `number \| string` | `undefined` | `'line'` typed progress's border-radius. Keep half of default height if not passed. | -| circle-gap | `number` | `1` | The gap bewteen circles when type is `'multiple-circle'`, suppose viewbox size is `100` | -| color | `string \| string[]` | `undefined` | | +| circle-gap | `number` | `1` | The gap between circles when type is `'multiple-circle'`, suppose `viewbox` size is `100`. | +| color | `string \| string[]` | `undefined` | Progress color. | | fill-border-radius | `number \| string` | `undefined` | `'line'` typed progress's fill's border-radius. Keep `border-radius` if not passed. | | height | `number` | `undefined` | `'line'` typed progress's height. Keep default height if not passed. | -| indicator-placement | `'inside' \| 'inside-label' \| 'outside'` | `'outside'` | | -| indicator-text-color | `string` | `undefined` | | -| percentage | `number \| Array` | `0` | | -| processing | `boolean` | `false` | | -| rail-color | `string \| string[]` | `undefined` | | -| rail-style | `string \| CSS \| Array` | `undefined` | | -| show-indicator | `boolean` | `true` | | -| status | `'default' \| 'success' \| 'error' \| 'warning' \| 'info'` | `'default'` | | -| stroke-width | `number` | `7` | | -| type | `'line' \| 'circle' \| 'multiple-circle'` | `line` | | -| unit | `string` | `%` | | +| indicator-placement | `'inside' \| 'inside-label' \| 'outside'` | `'outside'` | Indicator placement. | +| indicator-text-color | `string` | `undefined` | Indicator text color. | +| percentage | `number \| Array` | `0` | Percentage value. | +| processing | `boolean` | `false` | Processing status. | +| rail-color | `string \| string[]` | `undefined` | Rail color. | +| rail-style | `string \| CSS \| Array` | `undefined` | Rail style. | +| show-indicator | `boolean` | `true` | Whether to display indicators. | +| status | `'default' \| 'success' \| 'error' \| 'warning' \| 'info'` | `'default'` | Progress status. | +| stroke-width | `number` | `7` | Progress width. | +| type | `'line' \| 'circle' \| 'multiple-circle'` | `line` | Progress type. | +| unit | `string` | `%` | Progress unit. | ## Slots | Name | Parameters | Description | | ------- | ---------- | ----------------------------------------------- | -| default | `()` | Content will replace default indicatior content | +| default | `()` | Content will replace default indicator content. | diff --git a/src/progress/demos/zhCN/index.demo-entry.md b/src/progress/demos/zhCN/index.demo-entry.md index 53cefd0d9d0..5a0f3e32ab9 100644 --- a/src/progress/demos/zhCN/index.demo-entry.md +++ b/src/progress/demos/zhCN/index.demo-entry.md @@ -20,21 +20,21 @@ processing | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | border-radius | `number \| string` | `undefined` | `'line'` 类型进度条的圆角半径,不填写则维持高度的一半 | -| circle-gap | `number` | `1` | 当类型是 `'multiple-circle'` 的时候圈之间的距离,假设 viewbox 的尺寸是 100 | -| color | `string \| string[]` | `undefined` | | +| circle-gap | `number` | `1` | 当类型是 `'multiple-circle'` 的时候圈之间的距离,假设 `viewbox` 的尺寸是 `100` | +| color | `string \| string[]` | `undefined` | 进度条颜色 | | fill-border-radius | `number \| string` | `undefined` | `'line'` 类型进度条填充的圆角半径,不填写则维持 `border-radius` | | height | `number` | `undefined` | `'line'` 类型进度条的高度,不填写则维持默认高度 | -| indicator-placement | `'inside' \| 'inside-label' \| 'outside'` | `'outside'` | | -| indicator-text-color | `string` | `undefined` | | -| percentage | `number \| Array` | `0` | | -| processing | `boolean` | `false` | | -| rail-color | `string \| string[]` | `undefined` | | -| rail-style | `string \| CSS \| Array` | `undefined` | | -| show-indicator | `boolean` | `true` | | -| status | `'default' \| 'success' \| 'error' \| 'warning' \| 'info'` | `'default'` | | -| stroke-width | `number` | `7` | | -| type | `'line' \| 'circle' \| 'multiple-circle'` | `line` | | -| unit | `string` | `%` | | +| indicator-placement | `'inside' \| 'inside-label' \| 'outside'` | `'outside'` | 设置指标位置 | +| indicator-text-color | `string` | `undefined` | 指标文本颜色 | +| percentage | `number \| Array` | `0` | 百分比的值 | +| processing | `boolean` | `false` | 处理中状态 | +| rail-color | `string \| string[]` | `undefined` | 轨道的颜色 | +| rail-style | `string \| CSS \| Array` | `undefined` | 轨道的对象 | +| show-indicator | `boolean` | `true` | 是否显示指标 | +| status | `'default' \| 'success' \| 'error' \| 'warning' \| 'info'` | `'default'` | 进度条状态 | +| stroke-width | `number` | `7` | 进度条宽度 | +| type | `'line' \| 'circle' \| 'multiple-circle'` | `line` | 进度条类型 | +| unit | `string` | `%` | 进度条单位 | ## Slots diff --git a/src/progress/src/Circle.tsx b/src/progress/src/Circle.tsx index 1ad928ee709..8e5414e534e 100644 --- a/src/progress/src/Circle.tsx +++ b/src/progress/src/Circle.tsx @@ -35,7 +35,7 @@ export default defineComponent({ railStyle: [String, Object] as PropType, percentage: { type: Number, - required: true + default: 0 }, showIndicator: { type: Boolean, diff --git a/src/progress/src/Line.tsx b/src/progress/src/Line.tsx index fd1d9d19ff9..9ed72c998b1 100644 --- a/src/progress/src/Line.tsx +++ b/src/progress/src/Line.tsx @@ -25,7 +25,7 @@ export default defineComponent({ }, percentage: { type: Number, - required: true + default: 0 }, railColor: String, railStyle: [String, Object] as PropType, diff --git a/src/progress/src/MultipleCircle.tsx b/src/progress/src/MultipleCircle.tsx index 7ee878cc22f..33438bab88c 100644 --- a/src/progress/src/MultipleCircle.tsx +++ b/src/progress/src/MultipleCircle.tsx @@ -19,7 +19,7 @@ export default defineComponent({ }, percentage: { type: Array as PropType, - required: true + default: [0] }, strokeWidth: { type: Number, diff --git a/src/progress/src/Progress.tsx b/src/progress/src/Progress.tsx index 66ef1902378..34da34d4d41 100644 --- a/src/progress/src/Progress.tsx +++ b/src/progress/src/Progress.tsx @@ -34,10 +34,7 @@ const progressProps = { type: Number, default: 7 }, - percentage: { - type: [Number, Array] as PropType, - default: 0 - }, + percentage: [Number, Array] as PropType, unit: { type: String, default: '%' diff --git a/src/progress/tests/Progress.spec.ts b/src/progress/tests/Progress.spec.ts index 1df3314fc66..885df4d0182 100644 --- a/src/progress/tests/Progress.spec.ts +++ b/src/progress/tests/Progress.spec.ts @@ -5,4 +5,85 @@ describe('n-progress', () => { it('should work with import on demand', () => { mount(NProgress) }) + + it('should work with `type` prop', async () => { + ;(['line', 'circle', 'multiple-circle'] as const).forEach((item) => { + const wrapper = mount(NProgress, { props: { type: item } }) + expect(wrapper.find('.n-progress').classes()).toContain( + `n-progress--${item}` + ) + }) + }) + + it('should work with `color`, `rail-color`, `indicator-text-color` prop', async () => { + const wrapper = mount(NProgress, { + props: { + color: 'rgb(51, 51, 51)', + 'rail-color': 'rgb(68, 68, 68)', + 'indicator-text-color': 'rgb(85, 85, 85)' + }, + slots: { + default: () => 'test' + } + }) + expect( + wrapper.find('.n-progress-graph-line-fill').attributes('style') + ).toContain('background-color: rgb(51, 51, 51);') + expect( + wrapper.find('.n-progress-graph-line-rail').attributes('style') + ).toContain('background-color: rgb(68, 68, 68);') + expect( + wrapper.find('.n-progress-custom-content').attributes('style') + ).toContain('color: rgb(85, 85, 85);') + }) + + it('should work with `border-radius`, `fill-border-radius` prop', async () => { + const wrapper = mount(NProgress, { + props: { + 'border-radius': '12px', + 'fill-border-radius': '13px' + }, + slots: { + default: () => 'test' + } + }) + expect( + wrapper.find('.n-progress-graph-line-rail').attributes('style') + ).toContain('border-radius: 12px') + expect( + wrapper.find('.n-progress-graph-line-fill').attributes('style') + ).toContain('border-radius: 13px') + }) + + it('should work with `height` prop', async () => { + const wrapper = mount(NProgress, { + props: { + height: 24 + } + }) + expect( + wrapper.find('.n-progress-graph-line-rail').attributes('style') + ).toContain('height: 24') + }) + + it('should work with `processing` prop', async () => { + const wrapper = mount(NProgress, { + props: { + processing: true + } + }) + expect(wrapper.find('.n-progress-graph-line-fill').classes()).toContain( + 'n-progress-graph-line-fill--processing' + ) + }) + + it('should work with slot', async () => { + const wrapper = mount(NProgress, { + slots: { + default: () => 'test' + } + }) + expect(wrapper.find('.n-progress-custom-content').exists()).toBe(true) + expect(wrapper.find('.n-progress-custom-content').text()).toBe('test') + }) }) diff --git a/src/radio/src/RadioGroup.tsx b/src/radio/src/RadioGroup.tsx index b30f68a6f7e..7ff03ceb3bd 100644 --- a/src/radio/src/RadioGroup.tsx +++ b/src/radio/src/RadioGroup.tsx @@ -55,7 +55,8 @@ function mapSlot ( const lastInstanceProps: RadioProps = children[children.length - 1] .props as any const lastInstanceChecked = value === lastInstanceProps.value - const lastInstanceDisabled: boolean = lastInstanceProps.disabled + const lastInstanceDisabled: boolean | undefined = + lastInstanceProps.disabled const currentInstanceChecked = value === instanceProps.value const currentInstanceDisabled = instanceProps.disabled /** @@ -109,8 +110,8 @@ const radioGroupProps = { default: undefined }, disabled: { - type: Boolean, - default: false + type: Boolean as PropType, + default: undefined }, // eslint-disable-next-line vue/prop-name-casing 'onUpdate:value': Function as PropType<(value: string | number) => void>, @@ -142,6 +143,7 @@ export default defineComponent({ const selfElRef = ref(null) const { mergedSizeRef, + mergedDisabledRef, nTriggerFormChange, nTriggerFormInput, nTriggerFormBlur, @@ -197,7 +199,7 @@ export default defineComponent({ mergedClsPrefixRef, nameRef: toRef(props, 'name'), valueRef: mergedValueRef, - disabledRef: toRef(props, 'disabled'), + disabledRef: mergedDisabledRef, mergedSizeRef, doUpdateValue }) diff --git a/src/radio/src/use-radio.ts b/src/radio/src/use-radio.ts index b1bff99909e..114f1e9c4e0 100644 --- a/src/radio/src/use-radio.ts +++ b/src/radio/src/use-radio.ts @@ -23,13 +23,10 @@ const radioProps = { type: Boolean as PropType, default: undefined }, - defaultChecked: { - type: Boolean, - default: false - }, + defaultChecked: Boolean, disabled: { - type: Boolean, - default: false + type: Boolean as PropType, + default: undefined }, size: String as PropType<'small' | 'medium' | 'large'>, 'onUpdate:checked': [Function, Array] as PropType< @@ -96,8 +93,15 @@ function setup (props: ExtractPropTypes): UseRadio { return NFormItem.mergedSize.value } return 'medium' + }, + mergedDisabled (NFormItem) { + if (props.disabled) return true + if (NRadioGroup?.disabledRef.value) return true + if (NFormItem?.disabled.value) return true + return false } }) + const { mergedSizeRef, mergedDisabledRef } = formItem const inputRef = ref(null) const labelRef = ref(null) const NRadioGroup = inject(radioGroupInjectionKey, null) @@ -116,9 +120,6 @@ function setup (props: ExtractPropTypes): UseRadio { if (name !== undefined) return name if (NRadioGroup) return NRadioGroup.nameRef.value }) - const mergedDisabledRef = useMemo(() => { - return NRadioGroup?.disabledRef.value || props.disabled - }) const focusRef = ref(false) function doUpdateChecked (): void { if (NRadioGroup) { @@ -178,7 +179,7 @@ function setup (props: ExtractPropTypes): UseRadio { uncontrolledChecked: uncontrolledCheckedRef, renderSafeChecked: renderSafeCheckedRef, focus: focusRef, - mergedSize: formItem.mergedSizeRef, + mergedSize: mergedSizeRef, handleRadioInputChange, handleRadioInputBlur, handleRadioInputFocus, diff --git a/src/result/tests/Result.spec.ts b/src/result/tests/Result.spec.ts index 603935985da..50746617337 100644 --- a/src/result/tests/Result.spec.ts +++ b/src/result/tests/Result.spec.ts @@ -5,4 +5,41 @@ describe('n-result', () => { it('should work with import on demand', () => { mount(NResult) }) + + it('should work with `description` prop', async () => { + const wrapper = mount(NResult, { + props: { description: 'test-description' } + }) + expect(wrapper.find('.n-result-header__description').exists()).toBe(true) + expect(wrapper.find('.n-result-header__description').text()).toBe( + 'test-description' + ) + }) + + it('should work with `title` prop', async () => { + const wrapper = mount(NResult, { + props: { title: 'test-title' } + }) + expect(wrapper.find('.n-result-header__title').exists()).toBe(true) + expect(wrapper.find('.n-result-header__title').text()).toBe('test-title') + }) + + it('should work with `size` prop', async () => { + ;(['small', 'medium', 'large', 'huge'] as const).forEach((item) => { + const wrapper = mount(NResult, { + props: { size: item } + }) + expect(wrapper.find('.n-result').attributes('style')).toMatchSnapshot() + }) + }) + + it('should work with slots', async () => { + const wrapper = mount(NResult, { + slots: { default: () => 'test-default', footer: () => 'test-footer' } + }) + expect(wrapper.find('.n-result-content').exists()).toBe(true) + expect(wrapper.find('.n-result-content').text()).toBe('test-default') + expect(wrapper.find('.n-result-footer').exists()).toBe(true) + expect(wrapper.find('.n-result-footer').text()).toBe('test-footer') + }) }) diff --git a/src/result/tests/__snapshots__/Result.spec.ts.snap b/src/result/tests/__snapshots__/Result.spec.ts.snap new file mode 100644 index 00000000000..c8596f8189d --- /dev/null +++ b/src/result/tests/__snapshots__/Result.spec.ts.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`n-result should work with \`size\` prop 1`] = `"--bezier: cubic-bezier(.4, 0, .2, 1); --font-size: 14px; --icon-size: 64px; --line-height: 1.6; --text-color: rgb(51, 54, 57); --title-font-size: 26px; --title-font-weight: 500; --title-text-color: rgb(31, 34, 37); --icon-color: #2080f0;"`; + +exports[`n-result should work with \`size\` prop 2`] = `"--bezier: cubic-bezier(.4, 0, .2, 1); --font-size: 14px; --icon-size: 80px; --line-height: 1.6; --text-color: rgb(51, 54, 57); --title-font-size: 32px; --title-font-weight: 500; --title-text-color: rgb(31, 34, 37); --icon-color: #2080f0;"`; + +exports[`n-result should work with \`size\` prop 3`] = `"--bezier: cubic-bezier(.4, 0, .2, 1); --font-size: 15px; --icon-size: 100px; --line-height: 1.6; --text-color: rgb(51, 54, 57); --title-font-size: 40px; --title-font-weight: 500; --title-text-color: rgb(31, 34, 37); --icon-color: #2080f0;"`; + +exports[`n-result should work with \`size\` prop 4`] = `"--bezier: cubic-bezier(.4, 0, .2, 1); --font-size: 16px; --icon-size: 125px; --line-height: 1.6; --text-color: rgb(51, 54, 57); --title-font-size: 48px; --title-font-weight: 500; --title-text-color: rgb(31, 34, 37); --icon-color: #2080f0;"`; diff --git a/src/scrollbar/src/ScrollBar.tsx b/src/scrollbar/src/ScrollBar.tsx index e2ce14e6f83..6317c5110c8 100644 --- a/src/scrollbar/src/ScrollBar.tsx +++ b/src/scrollbar/src/ScrollBar.tsx @@ -212,9 +212,6 @@ const Scrollbar = defineComponent({ const xBarLeftPxRef = computed(() => { return `${xBarLeftRef.value}px` }) - const sizePxRef = computed(() => { - return `${props.size}px` - }) const needYBarRef = computed(() => { const { value: containerHeight } = containerHeightRef const { value: contentHeight } = contentHeightRef @@ -561,7 +558,6 @@ const Scrollbar = defineComponent({ xRailRef, needYBar: needYBarRef, needXBar: needXBarRef, - sizePx: sizePxRef, yBarSizePx: yBarSizePxRef, xBarSizePx: xBarSizePxRef, yBarTopPx: yBarTopPxRef, @@ -578,13 +574,21 @@ const Scrollbar = defineComponent({ handleXScrollMouseDown, cssVars: computed(() => { const { - common: { cubicBezierEaseInOut }, + common: { + cubicBezierEaseInOut, + scrollbarBorderRadius, + scrollbarHeight, + scrollbarWidth + }, self: { color, colorHover } } = themeRef.value return { '--scrollbar-bezier': cubicBezierEaseInOut, '--scrollbar-color': color, - '--scrollbar-color-hover': colorHover + '--scrollbar-color-hover': colorHover, + '--scrollbar-border-radius': scrollbarBorderRadius, + '--scrollbar-width': scrollbarWidth, + '--scrollbar-height': scrollbarHeight } }) } @@ -640,7 +644,7 @@ const Scrollbar = defineComponent({
{{ @@ -650,9 +654,7 @@ const Scrollbar = defineComponent({ class={`${mergedClsPrefix}-scrollbar-rail__scrollbar`} style={{ height: this.yBarSizePx, - top: this.yBarTopPx, - width: this.sizePx, - borderRadius: this.sizePx + top: this.yBarTopPx }} onMousedown={this.handleYScrollMouseDown} /> @@ -663,7 +665,7 @@ const Scrollbar = defineComponent({
{{ @@ -672,10 +674,8 @@ const Scrollbar = defineComponent({
diff --git a/src/scrollbar/src/styles/index.cssr.ts b/src/scrollbar/src/styles/index.cssr.ts index 6130bb474f1..78af5f969a4 100644 --- a/src/scrollbar/src/styles/index.cssr.ts +++ b/src/scrollbar/src/styles/index.cssr.ts @@ -5,6 +5,9 @@ import fadeInTransition from '../../../_styles/transitions/fade-in.cssr' // --scrollbar-bezier // --scrollbar-color // --scrollbar-color-hover +// --scrollbar-width +// --scrollbar-height +// --scrollbar-border-radius export default cB('scrollbar', ` overflow: hidden; position: relative; @@ -41,22 +44,28 @@ export default cB('scrollbar', ` left: 2px; right: 2px; bottom: 4px; + height: var(--scrollbar-height); `, [ c('>', [ - cE('scrollbar', { - right: 0 - }) + cE('scrollbar', ` + height: var(--scrollbar-height); + border-radius: var(--scrollbar-border-radius); + right: 0; + `) ]) ]), cM('vertical', ` right: 4px; top: 2px; bottom: 2px; + width: var(--scrollbar-width); `, [ c('>', [ - cE('scrollbar', { - bottom: 0 - }) + cE('scrollbar', ` + width: var(--scrollbar-width); + border-radius: var(--scrollbar-border-radius); + bottom: 0; + `) ]) ]), cM('disabled', [ diff --git a/src/select/demos/enUS/index.demo-entry.md b/src/select/demos/enUS/index.demo-entry.md index 1ea28dbfd24..08df441efa6 100644 --- a/src/select/demos/enUS/index.demo-entry.md +++ b/src/select/demos/enUS/index.demo-entry.md @@ -38,12 +38,12 @@ render-tag | disabled | `boolean` | `false` | Whether to disable the select. | | fallback-option | `false \| (value: string \| number) => SelectOption` | `value => ({ label: '' + value, value })` | The option to be created according the value which has no corresponding option in the options of the component. If set to `false`, the fallback option won't be created and displayed and the value has no corresponding option will be viewed as a invalid value and it will be removed in the operations of the component. | | filterable | `boolean` | `false` | Whether it can filter options. | -| filter | `(pattern: string, option: Object) => boolean` | A basic string based search method. | | +| filter | `(pattern: string, option: Object) => boolean` | A basic string based search method. | Filter function. | | loading | `boolean` | `false` | Whether to show loading status. | | max-tag-count | `number \| 'responsive'` | `undefined` | Max tag count in multiple mode. `responsive` will keep all the tags in single line. | | multiple | `boolean` | `false` | Whether to select multiple values. | | options | `Array` | `[]` | For details of configuration options, see SelectOption Properties. | -| placeholder | `string` | `'Please Select'` | | +| placeholder | `string` | `'Please Select'` | Placeholder of select. | | remote | `boolean` | `false` | If you want to async get options. Note that if remote is set, `filter` & `tag` won't work on `options`. At that time, you are taking all control of `options`. | | render-label | `(option: SelectOption \| SelectGroupOption, selected: boolean) => VNodeChild` | `undefined` | Render function of all the options' label. | | render-option | `(info: { node: VNode, option: SelectOption \| SelectGroupOption, selected: boolean } }` | `undefined` | Render function of all the options. | @@ -54,12 +54,13 @@ render-tag | tag | `boolean` | `false` | Whether it can create new option, should be used with `filterable`. | | value | `Array \| string \| number \| null` | `undefined` | Value in controlled mode. | | virtual-scroll | `boolean` | `true` | Whether to enable virtual scrolling. | -| on-blur | `() => void` | `undefined` | Selection blur. | +| on-blur | `() => void` | `undefined` | Callback triggered when selection blur. | +| on-clear | `() => void` | `undefined` | Callback triggered when selection clear. | | on-create | `(label: string) => SelectOption` | `label => ({ label, value: label })` | How to create a option when you input a string to create a option. Note that `filter` will be applied to the created option too. And make sure the value of the created option is not the same as any other option. | -| on-focus | `() => void` | `undefined` | Selection focus. | -| on-scroll | `(e: ScrollEvent) => void` | `undefined` | Menu scroll. | -| on-search | `(value: string) => void` | `undefined` | | -| on-update:value | `(value: Array \| string \| number \| null) => void` | `undefined` | Callback of value updating. | +| on-focus | `() => void` | `undefined` | Callback triggered when selection focus. | +| on-scroll | `(e: ScrollEvent) => void` | `undefined` | Callback triggered when menu scroll. | +| on-search | `(value: string) => void` | `undefined` | Callback triggered when search. | +| on-update:value | `(value: Array \| string \| number \| null) => void` | `undefined` | Callback triggered when value updating. | ### SelectOption Properties @@ -76,11 +77,11 @@ render-tag | Name | Type | Description | | --- | --- | --- | -| children | `Array` | | +| children | `Array` | Child select options. | | label | `string \| ((option: SelectGroupOption) => VNodeChild)` | Label of the group option. | | key | `string \| number` | Should be unique in options. | | render | `(info: { node: VNode }) => VNodeChild` | Render the entire option. | -| type | `'group'` | | +| type | `'group'` | Type of the group option. | ### Select Slots diff --git a/src/select/demos/enUS/multiple.demo.md b/src/select/demos/enUS/multiple.demo.md index 368f8684f70..cbb07bf574d 100644 --- a/src/select/demos/enUS/multiple.demo.md +++ b/src/select/demos/enUS/multiple.demo.md @@ -15,7 +15,7 @@ import { defineComponent, ref } from 'vue' export default defineComponent({ setup () { return { - value: ref(null), + value: ref(['song3']), options: [ { label: "Everybody's Got Something to Hide Except Me and My Monkey", diff --git a/src/select/demos/zhCN/index.demo-entry.md b/src/select/demos/zhCN/index.demo-entry.md index e8ba27510c8..be43f4f6291 100644 --- a/src/select/demos/zhCN/index.demo-entry.md +++ b/src/select/demos/zhCN/index.demo-entry.md @@ -49,7 +49,7 @@ options-change-debug | max-tag-count | `number \| 'responsive'` | `undefined` | 多选标签的最大显示数量,`responsive` 会将所有标签保持在一行 | | multiple | `boolean` | `false` | 是否为多选 | | options | `Array` | `[]` | 配置选项内容,详情见 SelectOption Properties | -| placeholder | `string` | `'请选择'` | | +| placeholder | `string` | `'请选择'` | 提示信息 | | remote | `boolean` | `false` | 是否要异步获取选项。注意如果设定了,那么 `filter` 和 `tag` 都不会对 `options` 生效。这个时候你在全权控制 `options` | | render-label | `(option: SelectOption \| SelectGroupOption, selected: boolean) => VNodeChild` | `undefined` | 选项标签渲染函数 | | render-option | `(info: { node: VNode, option: SelectOption \| SelectGroupOption, selected: boolean } }` | `undefined` | 选项的渲染函数 | @@ -60,12 +60,13 @@ options-change-debug | tag | `boolean` | `false` | 是否可以创建新的选项,需要和 `filterable` 一起使用 | | value | `Array \| string \| number \| null` | `undefined` | 受控模式下的值 | | virtual-scroll | `boolean` | `true` | 是否启用虚拟滚动 | -| on-blur | `() => void` | `undefined` | 选择器 Blur 时发出 | +| on-blur | `() => void` | `undefined` | `blur` 时执行的回调 | +| on-clear | `() => void` | `undefined` | `clear` 时执行的回调 | | on-create | `(label: string) => SelectOption` | `label => ({ label, value: label })` | 在输入内容时如何创建一个选项。注意 `filter` 对这个生成的选项同样会生效。同时确保这个选项和其他选项的 `value` 不要有重复 | -| on-focus | `() => void` | `undefined` | 选择器 Focus 时发出 | -| on-scroll | `(e: ScrollEvent) => void` | `undefined` | 选择菜单在滚动 | -| on-search | `(value: string) => void` | `undefined` | | -| on-update:value | `(value: Array \| string \| number \| null) => void` | `undefined` | 值更新的回调 | +| on-focus | `() => void` | `undefined` | `focus` 时执行的回调 | +| on-scroll | `(e: ScrollEvent) => void` | `undefined` | 滚动时执行的回调 | +| on-search | `(value: string) => void` | `undefined` | 搜索时执行的回调 | +| on-update:value | `(value: Array \| string \| number \| null) => void` | `undefined` | 值更新时执行的回调 | ### SelectOption Properties @@ -74,7 +75,7 @@ options-change-debug | class | `string` | 自定义一个选项的类名 | | disabled | `boolean` | 是否禁用一个选项 | | label | `string \| ((option: SelectOption, selected: boolean) => VNodeChild)` | 选项的标签,注意如果你使用了渲染函数,默认的过滤器将会过滤该选项 | -| render | `(info: { node: VNode, option: SelectOption, selected: boolean }) => VNodeChild` | Render the entire option. | +| render | `(info: { node: VNode, option: SelectOption, selected: boolean }) => VNodeChild` | 渲染整个选项 | | style | `string \| object` | 自定义一个选项的样式 | | value | `string \| number` | 在选项中应该是唯一的 | @@ -82,11 +83,11 @@ options-change-debug | 名称 | 类型 | 说明 | | --- | --- | --- | -| children | `Array` | | +| children | `Array` | 子选项组 | | label | `string \| ((option: SelectGroupOption) => VNodeChild)` | 选项组的标签 | | key | `string \| number` | 在选项中应该是唯一的 | -| render | `(info: { node: VNode, option: SelectOption, selected: boolean } }) => VNodeChild` | Render the entire option. | -| type | `'group'` | | +| render | `(info: { node: VNode, option: SelectOption, selected: boolean } }) => VNodeChild` | 渲染整个选项 | +| type | `'group'` | 选项组的类型 | ### Select Slots diff --git a/src/select/demos/zhCN/multiple.demo.md b/src/select/demos/zhCN/multiple.demo.md index 07a2d60a333..d9403ffe30c 100644 --- a/src/select/demos/zhCN/multiple.demo.md +++ b/src/select/demos/zhCN/multiple.demo.md @@ -15,7 +15,7 @@ import { defineComponent, ref } from 'vue' export default defineComponent({ setup () { return { - value: ref(null), + value: ref(['song3']), options: [ { label: "Everybody's Got Something to Hide Except Me and My Monkey", diff --git a/src/select/src/Select.tsx b/src/select/src/Select.tsx index cd70eb68779..8483a3c1efd 100644 --- a/src/select/src/Select.tsx +++ b/src/select/src/Select.tsx @@ -77,7 +77,10 @@ const selectProps = { multiple: Boolean, size: String as PropType, filterable: Boolean, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, remote: Boolean, loading: Boolean, filter: { @@ -141,6 +144,7 @@ const selectProps = { onBlur: [Function, Array] as PropType< MaybeArray<(e: FocusEvent) => void> | undefined >, + onClear: [Function, Array] as PropType void> | undefined>, onFocus: [Function, Array] as PropType< MaybeArray<(e: FocusEvent) => void> | undefined >, @@ -309,6 +313,7 @@ export default defineComponent({ }) const formItem = useFormItem(props) + const { mergedSizeRef, mergedDisabledRef } = formItem function doUpdateValue ( value: string | number | Array | null ): void { @@ -331,6 +336,10 @@ export default defineComponent({ if (onBlur) call(onBlur, e) nTriggerFormBlur() } + function doClear (): void { + const { onClear } = props + if (onClear) call(onClear) + } function doFocus (e: FocusEvent): void { const { onFocus } = props const { nTriggerFormFocus } = formItem @@ -364,7 +373,7 @@ export default defineComponent({ } // menu related methods function openMenu (): void { - if (!props.disabled) { + if (!mergedDisabledRef.value) { patternRef.value = '' uncontrolledShowRef.value = true if (props.filterable) { @@ -379,7 +388,7 @@ export default defineComponent({ patternRef.value = '' } function handleTriggerClick (): void { - if (props.disabled) return + if (mergedDisabledRef.value) return if (!mergedShowRef.value) { openMenu() } else { @@ -444,7 +453,7 @@ export default defineComponent({ } } function handleToggleOption (option: SelectBaseOption): void { - if (props.disabled) return + if (mergedDisabledRef.value) return const { tag, remote } = props if (tag && !remote) { const { value: beingCreatedOptions } = beingCreatedOptionsRef @@ -500,6 +509,9 @@ export default defineComponent({ ) } function handlePatternInput (e: InputEvent): void { + if (!mergedShowRef.value) { + openMenu() + } const { value } = e.target as unknown as HTMLInputElement patternRef.value = value const { tag, remote } = props @@ -530,6 +542,7 @@ export default defineComponent({ if (!multiple && props.filterable) { closeMenu() } + doClear() if (multiple) { doUpdateValue([]) } else { @@ -637,7 +650,8 @@ export default defineComponent({ localizedPlaceholder: localizedPlaceholderRef, selectedOption: selectedOptionRef, selectedOptions: selectedOptionsRef, - mergedSize: formItem.mergedSizeRef, + mergedSize: mergedSizeRef, + mergedDisabled: mergedDisabledRef, focused: focusedRef, handleMenuFocus, handleMenuBlur, @@ -694,7 +708,7 @@ export default defineComponent({ renderLabel={this.renderLabel} filterable={this.filterable} clearable={this.clearable} - disabled={this.disabled} + disabled={this.mergedDisabled} size={this.mergedSize} theme={this.mergedTheme.peers.InternalSelection} themeOverrides={ diff --git a/src/skeleton/tests/Skeleton.spec.tsx b/src/skeleton/tests/Skeleton.spec.tsx new file mode 100644 index 00000000000..3f0cb3e44dd --- /dev/null +++ b/src/skeleton/tests/Skeleton.spec.tsx @@ -0,0 +1,58 @@ +import { mount } from '@vue/test-utils' +import { NSkeleton } from '../index' + +describe('n-skeleton', () => { + it('should work with import on demand', () => { + mount(NSkeleton) + }) + + it('should work with `text` prop', async () => { + const wrapper = mount(NSkeleton, { + props: { + text: true + } + }) + expect(wrapper.find('.n-skeleton').attributes('style')).toMatchSnapshot() + }) + + it('should work with box', async () => { + const wrapper = mount(NSkeleton) + expect(wrapper.find('.n-skeleton').attributes('style')).toMatchSnapshot() + }) + + it('should work with `sharp` prop', async () => { + const wrapper = mount(NSkeleton, { + props: { + sharp: false + } + }) + expect(wrapper.find('.n-skeleton').attributes('style')).toMatchSnapshot() + }) + + it('should work with `round` prop', async () => { + const wrapper = mount(NSkeleton, { + props: { + round: true + } + }) + expect(wrapper.find('.n-skeleton').attributes('style')).toMatchSnapshot() + }) + + it('should work with `circle` prop', async () => { + const wrapper = mount(NSkeleton, { + props: { + circle: true + } + }) + expect(wrapper.find('.n-skeleton').attributes('style')).toMatchSnapshot() + }) + + it('should work with `repeat` prop', async () => { + const wrapper = mount(NSkeleton, { + props: { + repeat: 2 + } + }) + expect(wrapper.findAll('.n-skeleton').length).toBe(2) + }) +}) diff --git a/src/skeleton/tests/__snapshots__/Skeleton.spec.tsx.snap b/src/skeleton/tests/__snapshots__/Skeleton.spec.tsx.snap new file mode 100644 index 00000000000..d1b6ce1b761 --- /dev/null +++ b/src/skeleton/tests/__snapshots__/Skeleton.spec.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`n-skeleton should work with \`circle\` prop 1`] = `"border-radius: 50%; --bezier: cubic-bezier(.4, 0, .2, 1); --color-start: #eee; --color-end: #ddd;"`; + +exports[`n-skeleton should work with \`round\` prop 1`] = `"border-radius: 4096px; --bezier: cubic-bezier(.4, 0, .2, 1); --color-start: #eee; --color-end: #ddd;"`; + +exports[`n-skeleton should work with \`sharp\` prop 1`] = `"border-radius: 3px; --bezier: cubic-bezier(.4, 0, .2, 1); --color-start: #eee; --color-end: #ddd;"`; + +exports[`n-skeleton should work with \`text\` prop 1`] = `"display: inline-block; vertical-align: -0.125em; --bezier: cubic-bezier(.4, 0, .2, 1); --color-start: #eee; --color-end: #ddd;"`; + +exports[`n-skeleton should work with box 1`] = `"--bezier: cubic-bezier(.4, 0, .2, 1); --color-start: #eee; --color-end: #ddd;"`; diff --git a/src/slider/src/Slider.tsx b/src/slider/src/Slider.tsx index 83af4e8ba0c..d1768d7046b 100644 --- a/src/slider/src/Slider.tsx +++ b/src/slider/src/Slider.tsx @@ -37,7 +37,10 @@ const sliderProps = { default: 0 }, marks: Object as PropType>, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, formatTooltip: Function as PropType<(value: number) => string | number>, min: { type: Number, @@ -105,12 +108,19 @@ export default defineComponent({ mergedClsPrefixRef ) const formItem = useFormItem(props) - + const { mergedDisabledRef } = formItem const handleRef1 = ref(null) const handleRef2 = ref(null) const railRef = ref(null) const followerRef1 = ref(null) const followerRef2 = ref(null) + const precisionRef = computed(() => { + const precisions = [props.min, props.max, props.step].map((item) => { + const fraction = String(item).split('.')[1] + return fraction ? fraction.length : 0 + }) + return Math.max(...precisions) + }) const uncontrolledValueRef = ref(props.defaultValue) const controlledValueRef = toRef(props, 'value') @@ -264,7 +274,7 @@ export default defineComponent({ doUpdateShow(false, false) } function handleRailClick (e: MouseEvent): void { - if (props.disabled) return + if (mergedDisabledRef.value) return const { value: railEl } = railRef if (!railEl) return const railRect = railEl.getBoundingClientRect() @@ -319,7 +329,7 @@ export default defineComponent({ } } function handleKeyDown (e: KeyboardEvent): void { - if (props.disabled) return + if (mergedDisabledRef.value) return switch (e.code) { case 'ArrowRight': handleKeyDownRight() @@ -456,6 +466,7 @@ export default defineComponent({ justifiedValue = Math.max(min, justifiedValue) justifiedValue = Math.min(max, justifiedValue) justifiedValue = Math.round((justifiedValue - min) / step) * step + min + justifiedValue = parseFloat(justifiedValue.toFixed(precisionRef.value)) if (marks) { const closestMarkValue = getClosestMarkValue(value) if ( @@ -468,7 +479,7 @@ export default defineComponent({ return justifiedValue } function handleFirstHandleMouseDown (e: MouseEvent | TouchEvent): void { - if (props.disabled) return + if (mergedDisabledRef.value) return if (isTouchEvent(e)) e.preventDefault() if (props.range) { memoziedOtherValueRef.value = handleValue2Ref.value @@ -481,7 +492,7 @@ export default defineComponent({ on('mousemove', document, handleFirstHandleMouseMove) } function handleSecondHandleMouseDown (e: MouseEvent | TouchEvent): void { - if (props.disabled) return + if (mergedDisabledRef.value) return if (isTouchEvent(e)) e.preventDefault() if (props.range) { memoziedOtherValueRef.value = handleValue1Ref.value @@ -681,6 +692,7 @@ export default defineComponent({ namespace: namespaceRef, uncontrolledValue: uncontrolledValueRef, mergedValue: mergedValueRef, + mergedDisabled: mergedDisabledRef, isMounted: useIsMounted(), adjustedTo: useAdjustedTo(props), handleValue1: handleValue1Ref, @@ -799,7 +811,7 @@ export default defineComponent({ class={[ `${mergedClsPrefix}-slider`, { - [`${mergedClsPrefix}-slider--disabled`]: this.disabled, + [`${mergedClsPrefix}-slider--disabled`]: this.mergedDisabled, [`${mergedClsPrefix}-slider--active`]: this.active, [`${mergedClsPrefix}-slider--with-mark`]: this.marks } @@ -847,7 +859,7 @@ export default defineComponent({
{ it('should work with import on demand', () => { mount(NStatistic) }) + + it('should work with `label` prop', async () => { + const wrapper = mount(NStatistic, { props: { label: 'test' } }) + + expect(wrapper.find('.n-statistic__label').exists()).toBe(true) + expect(wrapper.find('.n-statistic__label').text()).toBe('test') + }) + + it('should work with `value` prop', async () => { + const wrapper = mount(NStatistic, { props: { value: 'test' } }) + + expect(wrapper.find('.n-statistic-value__content').exists()).toBe(true) + expect(wrapper.find('.n-statistic-value__content').text()).toBe('test') + }) + + it('should work with `default` slot', async () => { + const wrapper = mount(NStatistic, { slots: { default: () => 'test' } }) + + expect(wrapper.find('.n-statistic-value__content').exists()).toBe(true) + expect(wrapper.find('.n-statistic-value__content').text()).toBe('test') + }) + + it('should work with `label` slot', async () => { + const wrapper = mount(NStatistic, { slots: { label: () => 'test' } }) + + expect(wrapper.find('.n-statistic__label').exists()).toBe(true) + expect(wrapper.find('.n-statistic__label').text()).toBe('test') + }) + + it('should work with `prefix` slot', async () => { + const wrapper = mount(NStatistic, { slots: { prefix: () => 'test' } }) + + expect(wrapper.find('.n-statistic-value__prefix').exists()).toBe(true) + expect(wrapper.find('.n-statistic-value__prefix').text()).toBe('test') + }) + + it('should work with `suffix` slot', async () => { + const wrapper = mount(NStatistic, { slots: { suffix: () => 'test' } }) + + expect(wrapper.find('.n-statistic-value__suffix').exists()).toBe(true) + expect(wrapper.find('.n-statistic-value__suffix').text()).toBe('test') + }) }) diff --git a/src/steps/tests/Steps.spec.ts b/src/steps/tests/Steps.spec.ts index d77756e34cc..cc9964ed932 100644 --- a/src/steps/tests/Steps.spec.ts +++ b/src/steps/tests/Steps.spec.ts @@ -8,6 +8,94 @@ describe('n-steps', () => { mount(NSteps) }) + it('should work with `current` prop', async () => { + const processStyle = + '--description-text-color: rgb(51, 54, 57); --header-text-color: rgb(31, 34, 37); --indicator-border-color: #18a058; --indicator-color: #18a058;' + const waitStyle = + '--description-text-color: rgba(194, 194, 194, 1); --header-text-color: rgba(194, 194, 194, 1); --indicator-border-color: rgba(194, 194, 194, 1); --indicator-color: #0000;' + const finishStyle = + ' --description-text-color: rgba(194, 194, 194, 1); --header-text-color: rgba(194, 194, 194, 1); --indicator-border-color: #18a058; --indicator-color: #0000;' + const wrapper = mount(NSteps, { + props: { + current: 1 + }, + slots: { + default: () => [ + h(NStep, { title: 'test1', description: 'test1', internalIndex: 1 }), + h(NStep, { title: 'test2', description: 'test2', internalIndex: 2 }), + h(NStep, { title: 'test3', description: 'test3', internalIndex: 3 }) + ] + } + }) + expect(wrapper.findAll('.n-step')[0].attributes('style')).toContain( + processStyle + ) + expect(wrapper.findAll('.n-step')[1].attributes('style')).toContain( + waitStyle + ) + expect(wrapper.findAll('.n-step')[2].attributes('style')).toContain( + waitStyle + ) + + await wrapper.setProps({ current: 2 }) + expect(wrapper.findAll('.n-step')[0].attributes('style')).toContain( + finishStyle + ) + expect(wrapper.findAll('.n-step')[1].attributes('style')).toContain( + processStyle + ) + expect(wrapper.findAll('.n-step')[2].attributes('style')).toContain( + waitStyle + ) + + await wrapper.setProps({ current: 3 }) + expect(wrapper.findAll('.n-step')[0].attributes('style')).toContain( + finishStyle + ) + expect(wrapper.findAll('.n-step')[1].attributes('style')).toContain( + finishStyle + ) + expect(wrapper.findAll('.n-step')[2].attributes('style')).toContain( + processStyle + ) + }) + + it('should work with `size` prop', async () => { + const mediumStyle = + '--indicator-icon-size: 18px; --indicator-index-font-size: 16px; --indicator-size: 28px;' + const smallStyle = + '--indicator-icon-size: 14px; --indicator-index-font-size: 14px; --indicator-size: 22px;' + const wrapper = mount(NSteps, { + props: { + current: 1 + }, + slots: { + default: () => + h(NStep, { title: 'test1', description: 'test1', internalIndex: 1 }) + } + }) + expect(wrapper.find('.n-step').attributes('style')).toContain(mediumStyle) + await wrapper.setProps({ size: 'small' }) + expect(wrapper.find('.n-step').attributes('style')).toContain(smallStyle) + }) + + it('should work with `vertical` prop', async () => { + const wrapper = mount(NSteps, { + props: { + current: 1 + }, + slots: { + default: () => + h(NStep, { title: 'test1', description: 'test1', internalIndex: 1 }) + } + }) + expect(wrapper.find('.n-steps').classes()).not.toContain( + 'n-steps--vertical' + ) + await wrapper.setProps({ vertical: true }) + expect(wrapper.find('.n-steps').classes()).toContain('n-steps--vertical') + }) + it('should work with `finish-icon` and `error-icon` slots', async () => { const wrapper = mount(NSteps, { props: { diff --git a/src/switch/src/Switch.tsx b/src/switch/src/Switch.tsx index 55b8c82281b..5c9eba6f8b4 100644 --- a/src/switch/src/Switch.tsx +++ b/src/switch/src/Switch.tsx @@ -31,7 +31,10 @@ const switchProps = { }, loading: Boolean, defaultValue: Boolean, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, round: { type: Boolean, default: true @@ -76,7 +79,7 @@ export default defineComponent({ mergedClsPrefixRef ) const formItem = useFormItem(props) - const { mergedSizeRef } = formItem + const { mergedSizeRef, mergedDisabledRef } = formItem const uncontrolledValueRef = ref(props.defaultValue) const controlledValueRef = toRef(props, 'value') const mergedValueRef = useMergedState( @@ -107,7 +110,7 @@ export default defineComponent({ nTriggerFormBlur() } function handleClick (): void { - if (!props.disabled) { + if (!mergedDisabledRef.value) { doUpdateValue(!mergedValueRef.value) } } @@ -139,6 +142,7 @@ export default defineComponent({ pressed: pressedRef, mergedClsPrefix: mergedClsPrefixRef, mergedValue: mergedValueRef, + mergedDisabled: mergedDisabledRef, cssVars: computed(() => { const { value: size } = mergedSizeRef const { @@ -202,12 +206,12 @@ export default defineComponent({ `${mergedClsPrefix}-switch`, { [`${mergedClsPrefix}-switch--active`]: mergedValue, - [`${mergedClsPrefix}-switch--disabled`]: this.disabled, + [`${mergedClsPrefix}-switch--disabled`]: this.mergedDisabled, [`${mergedClsPrefix}-switch--round`]: this.round, [`${mergedClsPrefix}-switch--pressed`]: this.pressed } ]} - tabindex={!this.disabled ? 0 : undefined} + tabindex={!this.mergedDisabled ? 0 : undefined} style={this.cssVars as CSSProperties} onClick={this.handleClick} onFocus={this.handleFocus} diff --git a/src/tag/demos/enUS/color.demo.md b/src/tag/demos/enUS/color.demo.md new file mode 100644 index 00000000000..f880837bc9a --- /dev/null +++ b/src/tag/demos/enUS/color.demo.md @@ -0,0 +1,9 @@ +# Color + +Use a color object to customize color. + +```html + + Farewell to the night, waiting for dawn + +``` diff --git a/src/tag/demos/enUS/index.demo-entry.md b/src/tag/demos/enUS/index.demo-entry.md index f56cd6a9754..d2892f7b5e3 100644 --- a/src/tag/demos/enUS/index.demo-entry.md +++ b/src/tag/demos/enUS/index.demo-entry.md @@ -11,6 +11,7 @@ disabled size checkable shape +color ``` ## Props @@ -23,6 +24,7 @@ shape | checkable | `boolean` | `false` | Whether the tag is checkable, use checkable the type property invalid. | | checked | `boolean` | `false` | Whether the tag is checked, use with checkable. | | closable | `boolean` | `false` | Whether the tag is closable. | +| color | `{ color?: string, borderColor?: string, textColor?: string }` | `undefined` | Color of the tag, it will overrides type's color. | | disabled | `boolean` | `false` | Whether the tag is disabled. | | round | `boolean` | `false` | Whether the tag has round corner. | | size | `'small' \| 'medium' \| 'large'` | `'medium'` | Size of the tag. | diff --git a/src/tag/demos/zhCN/color.demo.md b/src/tag/demos/zhCN/color.demo.md new file mode 100644 index 00000000000..435e728c81a --- /dev/null +++ b/src/tag/demos/zhCN/color.demo.md @@ -0,0 +1,9 @@ +# 颜色 + +使用一个颜色对象定制标签的颜色。 + +```html + + 告别夜晚 等待天亮 + +``` diff --git a/src/tag/demos/zhCN/index.demo-entry.md b/src/tag/demos/zhCN/index.demo-entry.md index 95240d0f87b..b90b312dcc4 100644 --- a/src/tag/demos/zhCN/index.demo-entry.md +++ b/src/tag/demos/zhCN/index.demo-entry.md @@ -11,6 +11,7 @@ disabled size checkable shape +color rtl-debug ``` @@ -24,6 +25,7 @@ rtl-debug | checkable | `boolean` | `false` | 是否可以选择,使用后 type 将不生效 | | checked | `boolean` | `false` | 是否被选中,配合 checkable 一起使用 | | closable | `boolean` | `false` | 是否可关闭 | +| color | `{ color?: string, borderColor?: string, textColor?: string }` | `undefined` | 标签颜色,设置该项后 `type` 无效 | | disabled | `boolean` | `false` | 是否禁用 | | round | `boolean` | `false` | 是否圆角 | | size | `'small' \| 'medium' \| 'large'` | `'medium'` | 尺寸 | diff --git a/src/tag/src/Tag.tsx b/src/tag/src/Tag.tsx index a3b89c71391..31716d8298b 100644 --- a/src/tag/src/Tag.tsx +++ b/src/tag/src/Tag.tsx @@ -111,7 +111,7 @@ export default defineComponent({ handleClick, handleCloseClick, cssVars: computed(() => { - const { type, size } = props + const { type, size, color: { color, textColor } = {} } = props const { common: { cubicBezierEaseInOut }, self: { @@ -133,8 +133,8 @@ export default defineComponent({ [createKey('closeSize', size)]: closeSize, [createKey('fontSize', size)]: fontSize, [createKey('height', size)]: height, - [createKey('color', type)]: color, - [createKey('textColor', type)]: textColor, + [createKey('color', type)]: typedColor, + [createKey('textColor', type)]: typeTextColor, [createKey('border', type)]: border, [createKey('closeColor', type)]: closeColor, [createKey('closeColorHover', type)]: closeColorHover, @@ -151,7 +151,7 @@ export default defineComponent({ '--close-margin': closeMargin, '--close-margin-rtl': closeMarginRtl, '--close-size': closeSize, - '--color': color, + '--color': color || typedColor, '--color-checkable': colorCheckable, '--color-checked': colorChecked, '--color-checked-hover': colorCheckedHover, @@ -162,7 +162,7 @@ export default defineComponent({ '--height': height, '--opacity-disabled': opacityDisabled, '--padding': padding, - '--text-color': textColor, + '--text-color': textColor || typeTextColor, '--text-color-checkable': textColorCheckable, '--text-color-checked': textColorChecked, '--text-color-hover-checkable': textColorHoverCheckable, @@ -172,7 +172,7 @@ export default defineComponent({ } }, render () { - const { mergedClsPrefix, rtlEnabled } = this + const { mergedClsPrefix, rtlEnabled, color: { borderColor } = {} } = this return (
) : null} {!this.checkable && this.mergedBordered ? ( -
+
) : null}
) diff --git a/src/tag/src/common-props.ts b/src/tag/src/common-props.ts index 74e80bf1194..d4f4cc5a135 100644 --- a/src/tag/src/common-props.ts +++ b/src/tag/src/common-props.ts @@ -1,6 +1,13 @@ import { PropType } from 'vue' +export interface TagColor { + color?: string + borderColor?: string + textColor?: string +} + export default { + color: Object as PropType, type: { type: String as PropType< 'default' | 'primary' | 'success' | 'info' | 'warning' | 'error' @@ -13,5 +20,8 @@ export default { default: 'medium' }, closable: Boolean, - disabled: Boolean + disabled: { + type: Boolean as PropType, + default: undefined + } } as const diff --git a/src/thing/demos/enUS/index.demo-entry.md b/src/thing/demos/enUS/index.demo-entry.md index bf285aa14bc..dfd71f24c4a 100644 --- a/src/thing/demos/enUS/index.demo-entry.md +++ b/src/thing/demos/enUS/index.demo-entry.md @@ -23,10 +23,12 @@ indent ## Slots -| Name | Parameters | Description | -| ------------ | ---------- | ------------------------------------- | -| action | `()` | Operating area slot. | -| default | `()` | Content information. | -| description | `()` | Description information. | -| header-extra | `()` | Additional information in the header. | -| header | `()` | Header information. | +| Name | Parameters | Description | +| ------------ | ---------- | -------------------- | +| action | `()` | Action's slot. | +| avatar | `()` | Avatar's slot. | +| default | `()` | Content's slot. | +| description | `()` | Description's slot. | +| footer | `()` | Footer's slot. | +| header-extra | `()` | Header extra's slot. | +| header | `()` | Header's slot. | diff --git a/src/thing/demos/zhCN/index.demo-entry.md b/src/thing/demos/zhCN/index.demo-entry.md index ea7afed71f1..7ad29fade9a 100644 --- a/src/thing/demos/zhCN/index.demo-entry.md +++ b/src/thing/demos/zhCN/index.demo-entry.md @@ -23,10 +23,12 @@ indent ## Slots -| 名称 | 参数 | 说明 | -| ------------ | ---- | -------------- | -| action | `()` | 操作区域插槽 | -| default | `()` | 内容信息 | -| description | `()` | 描述信息 | -| header-extra | `()` | 头部的附加信息 | -| header | `()` | 头部信息 | +| 名称 | 参数 | 说明 | +| ------------ | ---- | ---------------- | +| action | `()` | 操作区域插槽 | +| avatar | `()` | 头像区域插槽 | +| default | `()` | 内容区域插槽 | +| description | `()` | 描述区域插槽 | +| footer | `()` | 尾部区域插槽 | +| header-extra | `()` | 头部附加区域插槽 | +| header | `()` | 头部区域插槽 | diff --git a/src/thing/tests/Thing.spec.ts b/src/thing/tests/Thing.spec.ts index ac8f277d88b..1be6c7f2f64 100644 --- a/src/thing/tests/Thing.spec.ts +++ b/src/thing/tests/Thing.spec.ts @@ -5,4 +5,83 @@ describe('n-thing', () => { it('should work with import on demand', () => { mount(NThing) }) + + it('should work with `content`, `description`, `title-extra`, `title` props', async () => { + const wrapper = mount(NThing, { + props: { + content: 'test-content', + description: 'test-description', + 'title-extra': 'test-title-extra', + title: 'test-title' + } + }) + + expect(wrapper.find('.n-thing-main__content').exists()).toBe(true) + expect(wrapper.find('.n-thing-main__content').text()).toBe('test-content') + expect(wrapper.find('.n-thing-main__description').exists()).toBe(true) + expect(wrapper.find('.n-thing-main__description').text()).toBe( + 'test-description' + ) + expect(wrapper.find('.n-thing-header__title').exists()).toBe(true) + expect(wrapper.find('.n-thing-header__title').text()).toBe('test-title') + expect(wrapper.find('.n-thing-header__extra').exists()).toBe(true) + expect(wrapper.find('.n-thing-header__extra').text()).toBe( + 'test-title-extra' + ) + }) + + it('should work with `content-indented` prop', async () => { + const wrapper = mount(NThing, { + props: { + content: 'test-content', + description: 'test-description', + 'title-extra': 'test-title-extra', + title: 'test-title' + }, + slots: { avatar: () => 'test-avatar' } + }) + expect(wrapper.find('.n-thing').element.children.length).toBe(1) + expect( + wrapper.find('.n-thing').element.children[0].getAttribute('class') + ).toContain('n-thing-main') + + await wrapper.setProps({ contentIndented: true }) + expect(wrapper.find('.n-thing').element.children.length).toBe(2) + expect( + wrapper.find('.n-thing').element.children[0].getAttribute('class') + ).toContain('n-thing-avatar') + expect( + wrapper.find('.n-thing').element.children[1].getAttribute('class') + ).toContain('n-thing-main') + }) + + it('should work with Slots', async () => { + const wrapper = mount(NThing, { + slots: { + avatar: () => 'test-avatar', + action: () => 'test-action', + default: () => 'test-default', + description: () => 'test-description', + 'header-extra': () => 'test-header-extra', + header: () => 'test-header' + } + }) + + expect(wrapper.find('.n-thing-avatar').exists()).toBe(true) + expect(wrapper.find('.n-thing-avatar').text()).toBe('test-avatar') + expect(wrapper.find('.n-thing-main__action').exists()).toBe(true) + expect(wrapper.find('.n-thing-main__action').text()).toBe('test-action') + expect(wrapper.find('.n-thing-main__content').exists()).toBe(true) + expect(wrapper.find('.n-thing-main__content').text()).toBe('test-default') + expect(wrapper.find('.n-thing-main__description').exists()).toBe(true) + expect(wrapper.find('.n-thing-main__description').text()).toBe( + 'test-description' + ) + expect(wrapper.find('.n-thing-header__extra').exists()).toBe(true) + expect(wrapper.find('.n-thing-header__extra').text()).toBe( + 'test-header-extra' + ) + expect(wrapper.find('.n-thing-header__title').exists()).toBe(true) + expect(wrapper.find('.n-thing-header__title').text()).toBe('test-header') + }) }) diff --git a/src/time-picker/src/TimePicker.tsx b/src/time-picker/src/TimePicker.tsx index 13cfc00cb3b..a7821e4e488 100644 --- a/src/time-picker/src/TimePicker.tsx +++ b/src/time-picker/src/TimePicker.tsx @@ -58,7 +58,7 @@ import { timePickerInjectionKey } from './interface' import { happensIn } from 'seemly' -import { isTimeInStep } from './utils' +import { findSimilarTime, isTimeInStep } from './utils' // validate hours, minutes, seconds prop function validateUnits (value: MaybeArray, max: number): boolean { @@ -113,7 +113,10 @@ const timePickerProps = { type: Boolean, default: true }, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, // deprecated onChange: { type: [Function, Array] as PropType | undefined>, @@ -152,6 +155,7 @@ export default defineComponent({ useConfig(props) const { localeRef, dateLocaleRef } = useLocale('TimePicker') const formItem = useFormItem(props) + const { mergedSizeRef, mergedDisabledRef } = formItem const themeRef = useTheme( 'TimePicker', 'TimePicker', @@ -322,7 +326,7 @@ export default defineComponent({ }) } function handleTriggerClick (e: MouseEvent): void { - if (props.disabled || happensIn(e, 'clear')) return + if (mergedDisabledRef.value || happensIn(e, 'clear')) return if (!activeRef.value) { openPanel() } @@ -377,13 +381,13 @@ export default defineComponent({ } function handleTimeInputActivate (): void { - if (props.disabled) return + if (mergedDisabledRef.value) return if (!activeRef.value) { openPanel() } } function handleTimeInputDeactivate (): void { - if (props.disabled) return + if (mergedDisabledRef.value) return deriveInputValue() closePanel({ returnFocus: false @@ -479,17 +483,29 @@ export default defineComponent({ } function handleNowClick (): void { const now = new Date() - if (!mergedValueRef.value) doChange(getTime(now)) - else { - const newValue = setSeconds( - setMinutes( - setHours(mergedValueRef.value, getHours(now)), - getMinutes(now) - ), - getSeconds(now) - ) - doChange(getTime(newValue)) + const getNowTime = { + hours: getHours, + minutes: getMinutes, + seconds: getSeconds } + const [mergeHours, mergeMinutes, mergeSeconds] = ( + ['hours', 'minutes', 'seconds'] as const + ).map((i) => + !props[i] || isTimeInStep(getNowTime[i](now), i, props[i]) + ? getNowTime[i](now) + : findSimilarTime(getNowTime[i](now), i, props[i]) + ) + const newValue = setSeconds( + setMinutes( + setHours( + mergedValueRef.value ? mergedValueRef.value : getTime(now), + mergeHours + ), + mergeMinutes + ), + mergeSeconds + ) + doChange(getTime(newValue)) } function handleConfirmClick (): void { deriveInputValue() @@ -538,7 +554,8 @@ export default defineComponent({ secondInFormat: secondInFormatRef, mergedAttrSize: mergedAttrSizeRef, displayTimeString: displayTimeStringRef, - mergedSize: formItem.mergedSizeRef, + mergedSize: mergedSizeRef, + mergedDisabled: mergedDisabledRef, isValueInvalid: isValueInvalidRef, isHourInvalid: isHourInvalidRef, isMinuteInvalid: isMinuteInvalidRef, @@ -637,7 +654,7 @@ export default defineComponent({ size={this.mergedSize} placeholder={this.localizedPlaceholder} clearable={this.clearable} - disabled={this.disabled} + disabled={this.mergedDisabled} textDecoration={ this.isValueInvalid ? 'line-through' : undefined } diff --git a/src/time-picker/src/utils.ts b/src/time-picker/src/utils.ts index 615112834c9..641bfc0461f 100644 --- a/src/time-picker/src/utils.ts +++ b/src/time-picker/src/utils.ts @@ -1,4 +1,4 @@ -import { MaybeArray } from '../../_utils' +import { MaybeArray, throwError } from '../../_utils' export const time = { hours: [ @@ -184,3 +184,34 @@ export function isTimeInStep ( return stepOrList.includes(value) } } + +export function findSimilarTime ( + value: number, + type: 'hours' | 'minutes' | 'seconds', + stepOrList: MaybeArray | undefined +): number { + const list = getTimeUnits(time[type], stepOrList).map(Number) + let lowerBound, upperBound + for (let i = 0; i < list.length; ++i) { + const v = list[i] + if (v === value) return v + else if (v > value) { + upperBound = v + break + } + lowerBound = v + } + if (lowerBound === undefined) { + if (!upperBound) { + throwError( + 'time-picker', + "Please set 'hours' or 'minutes' or 'seconds' props" + ) + } + return upperBound + } + if (upperBound === undefined) { + return lowerBound + } + return upperBound - value > value - lowerBound ? lowerBound : upperBound +} diff --git a/src/timeline/src/TimelineItem.tsx b/src/timeline/src/TimelineItem.tsx index f6d1517db33..c7073ff9560 100644 --- a/src/timeline/src/TimelineItem.tsx +++ b/src/timeline/src/TimelineItem.tsx @@ -88,7 +88,7 @@ export default defineComponent({
- {this.title ? ( + {this.title || this.$slots.header ? (
{renderSlot(this.$slots, 'header', undefined, () => [this.title])}
diff --git a/src/timeline/tests/Timeline.spec.ts b/src/timeline/tests/Timeline.spec.ts index eff92412ed5..1614f78ab5d 100644 --- a/src/timeline/tests/Timeline.spec.ts +++ b/src/timeline/tests/Timeline.spec.ts @@ -1,8 +1,114 @@ +import { h } from 'vue' import { mount } from '@vue/test-utils' -import { NTimeline } from '../index' +import { NTimeline, NTimelineItem } from '../index' describe('n-timeline', () => { it('should work with import on demand', () => { mount(NTimeline) }) + + it('should work with `item-placement` prop', async () => { + const wrapper = mount(NTimeline) + expect(wrapper.find('.n-timeline').classes()).toContain( + 'n-timeline--left-placement' + ) + await wrapper.setProps({ 'item-placement': 'right' }) + expect(wrapper.find('.n-timeline').classes()).toContain( + 'n-timeline--right-placement' + ) + }) + + it('should work with `size` prop', async () => { + const wrapper = mount(NTimeline) + expect(wrapper.find('.n-timeline').classes()).toContain( + 'n-timeline--medium-size' + ) + await wrapper.setProps({ size: 'large' }) + expect(wrapper.find('.n-timeline').classes()).toContain( + 'n-timeline--large-size' + ) + }) + + it('should work with `default` slot', async () => { + const wrapper = mount(NTimeline, { + slots: { + default: () => h(NTimelineItem) + } + }) + expect(wrapper.find('.n-timeline').element.children.length).toBe(1) + expect( + wrapper.find('.n-timeline').element.children[0].getAttribute('class') + ).toContain('n-timeline-item') + }) +}) + +describe('n-timeline-item', () => { + it('should work with `content`, `time`, `title` props', async () => { + const wrapper = mount(NTimeline, { + slots: { + default: () => + h(NTimelineItem, { + title: 'test-title', + content: 'test-content', + time: '2021-07-28' + }) + } + }) + expect(wrapper.find('.n-timeline-item-content__title').exists()).toBe(true) + expect(wrapper.find('.n-timeline-item-content__title').text()).toBe( + 'test-title' + ) + expect(wrapper.find('.n-timeline-item-content__content').exists()).toBe( + true + ) + expect(wrapper.find('.n-timeline-item-content__content').text()).toBe( + 'test-content' + ) + expect(wrapper.find('.n-timeline-item-content__meta').exists()).toBe(true) + expect(wrapper.find('.n-timeline-item-content__meta').text()).toBe( + '2021-07-28' + ) + }) + + it('should work with `type` prop', async () => { + ;(['default', 'success', 'info', 'warning', 'error'] as const).forEach( + (item) => { + const wrapper = mount(NTimeline, { + slots: { + default: () => h(NTimelineItem, { title: 'test-title', type: item }) + } + }) + expect(wrapper.find('.n-timeline-item').classes()).toContain( + `n-timeline-item--${item}-type` + ) + } + ) + }) + + it('should work with `default`, `footer`, `header` slots', async () => { + const wrapper = mount(NTimeline, { + slots: { + default: () => + h(NTimelineItem, null, { + header: () => 'test-header', + default: () => 'test-default', + footer: () => 'test-footer' + }) + } + }) + expect(wrapper.find('.n-timeline-item-content__title').exists()).toBe(true) + expect(wrapper.find('.n-timeline-item-content__title').text()).toBe( + 'test-header' + ) + expect(wrapper.find('.n-timeline-item-content__content').exists()).toBe( + true + ) + expect(wrapper.find('.n-timeline-item-content__content').text()).toBe( + 'test-default' + ) + expect(wrapper.find('.n-timeline-item-content__meta').exists()).toBe(true) + expect(wrapper.find('.n-timeline-item-content__meta').text()).toBe( + 'test-footer' + ) + }) }) diff --git a/src/transfer/src/Transfer.tsx b/src/transfer/src/Transfer.tsx index c3e7dc10ba9..4161dce5769 100644 --- a/src/transfer/src/Transfer.tsx +++ b/src/transfer/src/Transfer.tsx @@ -4,7 +4,6 @@ import { h, provide, PropType, - toRef, CSSProperties } from 'vue' import { useIsMounted } from 'vooks' @@ -44,8 +43,8 @@ const transferProps = { default: () => [] }, disabled: { - type: Boolean, - default: false + type: Boolean as PropType, + default: undefined }, virtualScroll: { type: Boolean, @@ -106,7 +105,7 @@ export default defineComponent({ mergedClsPrefixRef ) const formItem = useFormItem(props) - const { mergedSizeRef } = formItem + const { mergedSizeRef, mergedDisabledRef } = formItem const itemSizeRef = computed(() => { const { value: size } = mergedSizeRef const { @@ -136,7 +135,7 @@ export default defineComponent({ handleInputBlur, handleTgtFilterUpdateValue, handleSrcFilterUpdateValue - } = useTransferData(props) + } = useTransferData(props, mergedDisabledRef) function doUpdateValue (value: OptionValue[]): void { const { onUpdateValue, @@ -217,7 +216,7 @@ export default defineComponent({ provide(transferInjectionKey, { mergedClsPrefixRef, mergedSizeRef, - disabledRef: toRef(props, 'disabled'), + disabledRef: mergedDisabledRef, mergedThemeRef: themeRef, srcCheckedValuesRef, tgtCheckedValuesRef, @@ -232,6 +231,7 @@ export default defineComponent({ return { locale: localeRef, mergedClsPrefix: mergedClsPrefixRef, + mergedDisabled: mergedDisabledRef, itemSize: itemSizeRef, isMounted: useIsMounted(), isInputing: isInputingRef, @@ -311,7 +311,7 @@ export default defineComponent({
void } -export const transferInjectionKey: InjectionKey = Symbol( - 'transfer' -) +export const transferInjectionKey: InjectionKey = + Symbol('transfer') export type OnUpdateValue = (value: OptionValue[]) => void diff --git a/src/transfer/src/use-transfer-data.ts b/src/transfer/src/use-transfer-data.ts index 4320687b780..0961f5928d2 100644 --- a/src/transfer/src/use-transfer-data.ts +++ b/src/transfer/src/use-transfer-data.ts @@ -1,4 +1,4 @@ -import { ref, computed, toRef } from 'vue' +import { ref, computed, toRef, Ref } from 'vue' import { useMemo, useMergedState } from 'vooks' import type { Option, OptionValue, Filter, CheckedStatus } from './interface' @@ -7,12 +7,14 @@ interface UseTransferDataProps { value?: OptionValue[] | null options: Option[] filterable: boolean - disabled: boolean filter: Filter } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function useTransferData (props: UseTransferDataProps) { +export function useTransferData ( + props: UseTransferDataProps, + mergedDisabledRef: Ref +) { const uncontrolledValueRef = ref(props.defaultValue) const controlledValueRef = toRef(props, 'value') const mergedValueRef = useMergedState( @@ -123,11 +125,11 @@ export function useTransferData (props: UseTransferDataProps) { } }) const fromButtonDisabledRef = useMemo(() => { - if (props.disabled) return true + if (mergedDisabledRef.value) return true return tgtCheckedValuesRef.value.length === 0 }) const toButtonDisabledRef = useMemo(() => { - if (props.disabled) return true + if (mergedDisabledRef.value) return true return srcCheckedValuesRef.value.length === 0 }) const isInputingRef = ref(false) diff --git a/src/tree-select/demos/enUS/index.demo-entry.md b/src/tree-select/demos/enUS/index.demo-entry.md index 1e0b283838f..28da095dd79 100644 --- a/src/tree-select/demos/enUS/index.demo-entry.md +++ b/src/tree-select/demos/enUS/index.demo-entry.md @@ -33,6 +33,7 @@ debug | multiple | `boolean` | `false` | Whether to support multiple select. | | options | `TreeSelectOption[]` | `[]` | Options. | | placeholder | `string` | `'Please Select'` | Placeholder. | +| size | `'small' \| 'medium' \| 'large'` | `'medium'` | Component size. | | value | `string \| number \| Array \| null>` | `undefined` | Selected key (or keys when multiple). | | virtual-scroll | `boolean` | `true` | Whether to enable virtual scroll. | | on-blur | `(e: FocusEvent) => void` | `undefined` | Callback on blur. | diff --git a/src/tree-select/demos/zhCN/index.demo-entry.md b/src/tree-select/demos/zhCN/index.demo-entry.md index aaea712e7b7..9d8859691f6 100644 --- a/src/tree-select/demos/zhCN/index.demo-entry.md +++ b/src/tree-select/demos/zhCN/index.demo-entry.md @@ -34,6 +34,7 @@ debug | multiple | `boolean` | `false` | 是否支持多选 | | options | `TreeSelectOption[]` | `[]` | 选项 | | placeholder | `string` | `'请选择'` | 占位信息 | +| size | `'small' \| 'medium' \| 'large'` | `'medium'` | 组件尺寸 | | value | `string \| number \| Array \| null>` | `undefined` | 选中的 key | | virtual-scroll | `boolean` | `true` | 是否开启虚拟滚动 | | on-blur | `(e: FocusEvent) => void` | `undefined` | Blur 时的回调 | diff --git a/src/tree-select/src/TreeSelect.tsx b/src/tree-select/src/TreeSelect.tsx index 7d54278567e..9e4fa210de3 100644 --- a/src/tree-select/src/TreeSelect.tsx +++ b/src/tree-select/src/TreeSelect.tsx @@ -71,7 +71,10 @@ const props = { >, default: null }, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, filterable: Boolean, leafOnly: Boolean, maxTagCount: [String, Number] as PropType, @@ -125,6 +128,7 @@ export default defineComponent({ const { localeRef } = useLocale('Select') const { mergedSizeRef, + mergedDisabledRef, nTriggerFormBlur, nTriggerFormChange, nTriggerFormFocus, @@ -301,7 +305,7 @@ export default defineComponent({ doUpdateShow(false) } function openMenu (): void { - if (!props.disabled) { + if (!mergedDisabledRef.value) { patternRef.value = '' doUpdateShow(true) if (props.filterable) { @@ -321,7 +325,7 @@ export default defineComponent({ } } function handleTriggerClick (): void { - if (props.disabled) return + if (mergedDisabledRef.value) return if (!mergedShowRef.value) { openMenu() } else { @@ -529,6 +533,7 @@ export default defineComponent({ treeSelectedKeys: treeSelectedKeysRef, treeCheckedKeys: treeCheckedKeysRef, mergedSize: mergedSizeRef, + mergedDisabled: mergedDisabledRef, selectedOption: selectedOptionRef, selectedOptions: selectedOptionsRef, pattern: patternRef, @@ -590,7 +595,7 @@ export default defineComponent({ size={this.mergedSize} bordered={this.bordered} placeholder={this.mergedPlaceholder} - disabled={this.disabled} + disabled={this.mergedDisabled} active={this.mergedShow} multiple={this.multiple} maxTagCount={this.maxTagCount} diff --git a/src/typography/demos/enUS/index.demo-entry.md b/src/typography/demos/enUS/index.demo-entry.md index ba761a70f93..11534bd41be 100644 --- a/src/typography/demos/enUS/index.demo-entry.md +++ b/src/typography/demos/enUS/index.demo-entry.md @@ -58,6 +58,6 @@ router-link ### All Typography Components -| Name | Parameters | Description | -| ------- | ---------- | ----------- | -| default | `()` | | +| Name | Parameters | Description | +| ------- | ---------- | -------------------------- | +| default | `()` | The content of typography. | diff --git a/src/typography/demos/zhCN/index.demo-entry.md b/src/typography/demos/zhCN/index.demo-entry.md index 7faaa02115f..8dde3db69c6 100644 --- a/src/typography/demos/zhCN/index.demo-entry.md +++ b/src/typography/demos/zhCN/index.demo-entry.md @@ -56,8 +56,8 @@ router-link ## Slots -### 全部排版组件 +### All Typography Components -| 名称 | 参数 | 说明 | -| ------- | ---- | ---- | -| default | `()` | | +| 名称 | 参数 | 说明 | +| ------- | ---- | ---------- | +| default | `()` | 排印的内容 | diff --git a/src/upload/demos/enUS/index.demo-entry.md b/src/upload/demos/enUS/index.demo-entry.md index 30b5b0def62..68f659b5c5d 100644 --- a/src/upload/demos/enUS/index.demo-entry.md +++ b/src/upload/demos/enUS/index.demo-entry.md @@ -25,7 +25,7 @@ before-upload | data | `Object \| ({ file: UploadFile }) => Object` | `undefined` | The additional fileds data of HTTP request's form data. | | default-file-list | `Array` | `[]` | The default file list in uncontrolled manner. | | default-upload | `boolean` | `false` | If file uploaded immediatelly after file is selected. | -| disabled | `boolean` | `false` | | +| disabled | `boolean` | `false` | Whether to disable the upload. | | file-list-style | `Object` | `undefined` | The style of file list area | | file-list | `Array` | `undefined` | The file list of component. If set, the component will work in controlled manner. | | headers | `Object \| ({ file: UploadFile }) => Object` | `undefined` | The additional HTTP Headers of request. | @@ -66,12 +66,12 @@ before-upload ### Upload Slots -| Name | Parameters | Description | -| ------- | ---------- | ----------- | -| default | `()` | | +| Name | Parameters | Description | +| ------- | ---------- | -------------------------- | +| default | `()` | The content of the upload. | ### Upload Dragger Slots -| Name | Parameters | Description | -| ------- | ---------- | ----------- | -| default | `()` | | +| Name | Parameters | Description | +| --- | --- | --- | +| default | `()` | The content of the upload dragger, use can refer to [Drag to Upload](#drag). | diff --git a/src/upload/demos/zhCN/index.demo-entry.md b/src/upload/demos/zhCN/index.demo-entry.md index a77e51d6d1c..2563b28254d 100644 --- a/src/upload/demos/zhCN/index.demo-entry.md +++ b/src/upload/demos/zhCN/index.demo-entry.md @@ -25,7 +25,7 @@ before-upload | data | `Object \| ({ file: UploadFile }) => Object` | `undefined` | 提交表单需要附加的数据 | | default-file-list | `Array` | `[]` | 非受控状态下默认的文件列表 | | default-upload | `boolean` | `false` | 选择文件时候是否默认上传 | -| disabled | `boolean` | `false` | | +| disabled | `boolean` | `false` | 是否禁用 | | file-list-style | `Object` | `undefined` | 文件列表区域的样式 | | file-list | `Array` | `undefined` | 文件列表,如果传入组件会处于受控状态 | | headers | `Object \| ({ file: UploadFile }) => Object` | `undefined` | HTTP 请求需要附加的 Headers | @@ -65,12 +65,12 @@ before-upload ### Upload Slots -| 名称 | 参数 | 说明 | -| ------- | ---- | ---- | -| default | `()` | | +| 名称 | 参数 | 说明 | +| ------- | ---- | ---------- | +| default | `()` | 上传的内容 | ### Upload Dragger Slots -| 名称 | 参数 | 说明 | -| ------- | ---- | ---- | -| default | `()` | | +| 名称 | 参数 | 说明 | +| ------- | ---- | ------------------------------------ | +| default | `()` | 上传拖动器的内容,使用可参考[拖拽上传](#drag) | diff --git a/src/upload/src/Upload.tsx b/src/upload/src/Upload.tsx index 98f9af0ccd5..76edb6006b1 100644 --- a/src/upload/src/Upload.tsx +++ b/src/upload/src/Upload.tsx @@ -9,7 +9,7 @@ import { CSSProperties } from 'vue' import { createId } from 'seemly' -import { useConfig, useTheme } from '../../_mixins' +import { useConfig, useTheme, useFormItem } from '../../_mixins' import type { ThemeProps } from '../../_mixins' import { ExtractPublicPropTypes, @@ -201,7 +201,10 @@ const uploadProps = { data: [Object, Function] as PropType, headers: [Object, Function] as PropType, withCredentials: Boolean, - disabled: Boolean, + disabled: { + type: Boolean as PropType, + default: undefined + }, onChange: Function as PropType, onRemove: Function as PropType, onFinish: Function as PropType, @@ -255,6 +258,8 @@ export default defineComponent({ props, mergedClsPrefixRef ) + const formItem = useFormItem(props) + const { mergedDisabledRef } = formItem const uncontrolledFileListRef = ref(props.defaultFileList) const controlledFileListRef = toRef(props, 'fileList') const inputElRef = ref(null) @@ -271,7 +276,7 @@ export default defineComponent({ inputElRef.value?.click() } function handleTriggerClick (): void { - if (props.disabled) return + if (mergedDisabledRef.value) return openFileDialog() } function handleTriggerDragOver (e: DragEvent): void { @@ -288,7 +293,7 @@ export default defineComponent({ } function handleTriggerDrop (e: DragEvent): void { e.preventDefault() - if (!draggerInsideRef.value || props.disabled) return + if (!draggerInsideRef.value || mergedDisabledRef.value) return const dataTransfer = e.dataTransfer const files = dataTransfer?.files if (files) { @@ -415,6 +420,7 @@ export default defineComponent({ provide(uploadInjectionKey, { mergedClsPrefixRef, mergedThemeRef: themeRef, + disabledRef: mergedDisabledRef, showCancelButtonRef: toRef(props, 'showCancelButton'), showDownloadButtonRef: toRef(props, 'showDownloadButton'), showRemoveButtonRef: toRef(props, 'showRemoveButton'), @@ -431,6 +437,7 @@ export default defineComponent({ draggerInsideRef, inputElRef, mergedFileList: mergedFileListRef, + mergedDisabled: mergedDisabledRef, mergedTheme: themeRef, dragOver: dragOverRef, handleTriggerDrop, @@ -496,7 +503,7 @@ export default defineComponent({ [`${mergedClsPrefix}-upload--dragger-inside`]: draggerInsideRef.value, [`${mergedClsPrefix}-upload--drag-over`]: this.dragOver, - [`${mergedClsPrefix}-upload--disabled`]: this.disabled + [`${mergedClsPrefix}-upload--disabled`]: this.mergedDisabled } ]} style={this.cssVars as CSSProperties} diff --git a/src/upload/src/UploadFile.tsx b/src/upload/src/UploadFile.tsx index 3a50e263af6..3d3ba4cc308 100644 --- a/src/upload/src/UploadFile.tsx +++ b/src/upload/src/UploadFile.tsx @@ -126,6 +126,7 @@ export default defineComponent({ progressStatus: progressStatusRef, buttonType: buttonTypeRef, showProgress: showProgressRef, + disabled: NUpload.disabledRef, showCancelButton: showCancelButtonRef, showRemoveButton: showRemoveButtonRef, showDownloadButton: showDownloadButtonRef, @@ -156,36 +157,37 @@ export default defineComponent({ {this.file.name}
- {this.showRemoveButton || this.showCancelButton ? ( - - {{ - icon: () => ( - - {{ - default: () => - this.showRemoveButton ? ( - - {{ default: () => }} - - ) : ( - - {{ default: () => }} - - ) - }} - - ) - }} - - ) : null} - {this.showRetryButton ? ( + {(this.showRemoveButton || this.showCancelButton) && + !this.disabled && ( + + {{ + icon: () => ( + + {{ + default: () => + this.showRemoveButton ? ( + + {{ default: () => }} + + ) : ( + + {{ default: () => }} + + ) + }} + + ) + }} + + )} + {this.showRetryButton && !this.disabled && ( - ) : null} + )} {this.showDownloadButton ? ( void export interface UploadInjection { mergedClsPrefixRef: Ref mergedThemeRef: Ref> + disabledRef: Ref showCancelButtonRef: Ref showRemoveButtonRef: Ref showDownloadButtonRef: Ref diff --git a/src/upload/src/styles/index.cssr.ts b/src/upload/src/styles/index.cssr.ts index 5a0e17a14a2..4457d67ecff 100644 --- a/src/upload/src/styles/index.cssr.ts +++ b/src/upload/src/styles/index.cssr.ts @@ -145,6 +145,9 @@ export default cB('upload', [ cE('trigger', ` cursor: not-allowed; `), + cB('upload-file', ` + cursor: not-allowed; + `), cB('upload-file-list', ` cursor: not-allowed; `), diff --git a/src/version.ts b/src/version.ts index 98b1cb01234..4cf1a859e22 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export default '2.15.7' +export default '2.16.1'