Merge pull request #118 from TuSimple/main

chore: sync doc with main
This commit is contained in:
07akioni 2021-06-15 01:15:01 +08:00 committed by GitHub
commit 0678245cab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 1487 additions and 371 deletions

View File

@ -1,25 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
### Environment Info
- Naive UI version: (eg. 2.11.0)
- Vue version: (eg. 3.0.11)
- Browser Info: (eg. Chome 91)
- System Info: (eg. Mac OS 11.2.3, Windows)
### Reproduction link
<!-- A CodeSandbox reproduction link is recommended. -->
<!-- If CodeSandbox is not easy to reproduce your issue, please link a GitHub repo. -->
### Steps to reproduce
### What is expected?
### What is actually happening?

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Create new issue
url: https://naive-ui.github.io/issue-helper/
about: The issue which is not created via https://naive-ui.github.io/issue-helper/ will be closed immediately.

View File

@ -1,13 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
## What problem does the feature solve?
## What does the proposed API look like?
## Some pictures that can demonstrate the feature.

View File

@ -1,6 +1,25 @@
# CHANGELOG
## 2.11.8
## 2.11.9 (2021-06-15)
### Feats
- `n-space` supports wai-aria.
- `n-button-group` supports wai-aria.
- `n-progress` supports wai-aria.
- `n-menu` supports use `<a />` and `<router-link />` as label, closes [#84](https://github.com/TuSimple/naive-ui/issues/84).
- `n-input-number` add `show-button` prop.
- `n-rate` support `default` slot for icon customizing.
- `n-rate` add color prop.
- `n-rate` add size prop.
### Fixes
- Fix `n-card`'s `header-style` it not applied to header. [#103](https://github.com/TuSimple/naive-ui/issues/103)
- Fix `n-dialog` misses `destroyAll` method.
- Fix `n-data-table` misses `on-update-sorter`, `on-update-filters`, `on-update-page` and `on-update-page-size` props.
## 2.11.8 (2021-06-13)
### Feats
@ -12,7 +31,7 @@
- Fix `n-form-item`'s style attribute `grid-template-columns` influence on the layout of child elements. [#93](https://github.com/TuSimple/naive-ui/pull/93)
- Fix `n-data-table`'s prop types of `rowKey`, `rowClassName`, `rowProps`, `summary` aren't compatible with expected value.
## 2.11.7
## 2.11.7 (2021-06-12)
### Fixes
@ -49,8 +68,8 @@
- Fix `n-popover` sometimes won't sync position in manual mode.
- Fix `n-transfer`'s empty icon is no toggling transition.
- Fix `n-message` API option is not optional.
- Fix `n-calendar` date calculate incorrectly
- Fix `n-input` missing the `password` type declaration.
- Fix `n-calendar` date calculate incorrectly.
- Fix `n-input` misses the `password` type declaration.
- Fix `n-menu` the type definition of `extra` property of menu and submenu.
- Fix `n-dropdown` mouse cursor is not pointer.
@ -808,3 +827,7 @@ See vue3.md
### Fixes
- Rails of `n-scrollbar` shadow mouse event.
### Features
- `n-date-table` add `empty` slot. [#86](https://github.com/TuSimple/naive-ui/issues/86)

View File

@ -1,6 +1,25 @@
# CHANGELOG
## 2.11.8
## 2.11.9 (2021-06-15)
### Feats
- `n-space` 支持 wai-aria
- `n-button-group` 支持 wai-aria
- `n-progress` 支持 wai-aria
- `n-menu` 支持使用 `<a />``<router-link />` 作为 label关闭 [#84](https://github.com/TuSimple/naive-ui/issues/84)
- `n-input-number` 新增 `show-button` 属性
- `n-rate` 支持使用 default slot 自定义图标
- `n-rate` 新增 color 属性
- `n-rate` 新增 size 属性
### Fixes
- 修复 `n-card``header-style` 没有应用于 header 上 [#103](https://github.com/TuSimple/naive-ui/issues/103)
- 修复 `n-dialog``destroyAll` 方法缺失
- 修复 `n-data-table` 缺少 `on-update-sorter`、`on-update-filters`、`on-update-page`、`on-update-page-size` 属性
## 2.11.8 (2021-06-13)
### Feats
@ -12,7 +31,7 @@
- 修复 `n-form-item``grid-template-columns` 样式属性对子元素布局的影响 [#93](https://github.com/TuSimple/naive-ui/pull/93)
- 修复 `n-data-table``rowKey`, `rowClassName`, `rowProps`, `summary` 属性类型和期望值不兼容
## 2.11.7
## 2.11.7 (2021-06-12)
### Fixes
@ -810,3 +829,7 @@
### Fixes
- `n-scrollbar` 的轨道会挡住鼠标事件
### Features
- `n-data-table` 增加了 empty 插槽 [#86](https://github.com/TuSimple/naive-ui/issues/86)

View File

@ -26,7 +26,7 @@
text
tag="a"
target="_blank"
href="https://github.com/TuSimple/naive-ui/issues/new/choose"
href="https://naive-ui.github.io/issue-helper/"
>
{{ t('reportBug') }}
</n-button>

View File

@ -1,6 +1,6 @@
{
"name": "naive-ui",
"version": "2.11.8",
"version": "2.11.9",
"description": "A Vue 3 Component Library. Fairly Complete, Customizable Themes, Uses TypeScript, Not Too Slow",
"main": "lib/index.js",
"module": "es/index.js",
@ -22,7 +22,7 @@
"test:cov": "cross-env NODE_ENV=test jest",
"test:watch": "cross-env NODE_ENV=test jest ---watch --verbose --coverage",
"gen-version": "node scripts/gen-version",
"post-changelog": "node scripts/post-changelog",
"release-changelog": "node scripts/release-changelog",
"build:site:ts": "./scripts/pre-build-site/pre-build-site.sh && cross-env TUSIMPLE=true NODE_ENV=production NODE_OPTIONS=--max-old-space-size=4096 vite build && ./scripts/post-build-site/post-build-site.sh",
"prepare": "husky install"
},
@ -70,9 +70,9 @@
"@types/jest": "^26.0.20",
"@typescript-eslint/eslint-plugin": "^4.15.1",
"@typescript-eslint/parser": "^4.15.1",
"@vicons/fluent": "^0.8.0",
"@vicons/ionicons4": "^0.8.0",
"@vicons/ionicons5": "^0.8.0",
"@vicons/fluent": "^0.9.0",
"@vicons/ionicons4": "^0.9.0",
"@vicons/ionicons5": "^0.9.0",
"@vitejs/plugin-vue": "^1.2.1",
"@vue/compiler-sfc": "^3.0.10",
"@vue/eslint-config-standard": "^6.0.0",

View File

@ -1,46 +0,0 @@
const request = require('superagent')
const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')
const { DINGTALK_TOKEN } = process.env
if (!DINGTALK_TOKEN) {
console.log('No DINGTALK_TOKEN in your env.')
process.exit(0)
}
const changelog = fs
.readFileSync(path.resolve(__dirname, '../CHANGELOG.zh-CN.md'), 'utf-8')
.split(/^## /gm)[1]
.replace(/^##/gm, '')
const message = `变更日志 ${changelog}`
inquirer
.prompt([
{
type: 'confirm',
name: 'post-changelog',
message: `发布以下变更日志到钉钉群:\n\n${message}`
}
])
.then((ans) => {
if (ans['post-changelog']) {
request
.post('https://oapi.dingtalk.com/robot/send')
.query({
access_token: DINGTALK_TOKEN
})
.type('application/json')
.send({
msgtype: 'text',
text: {
content: message
}
})
.then((res) => {
console.log(res.text)
})
}
})

View File

@ -0,0 +1,97 @@
const request = require('superagent')
const fs = require('fs')
const path = require('path')
const inquirer = require('inquirer')
const { DINGTALK_TOKEN } = process.env
if (!DINGTALK_TOKEN) {
console.log('No DINGTALK_TOKEN in your env.')
process.exit(0)
}
const { DISCORD_TOKEN } = process.env
if (!DISCORD_TOKEN) {
console.error('No DISCORD_TOKEN in your env.')
process.exit(0)
}
async function releaseChangelogToDingTalk () {
const changelog = fs
.readFileSync(path.resolve(__dirname, '../CHANGELOG.zh-CN.md'), 'utf-8')
.split(/^## /gm)[1]
.replace(/^##/gm, '')
.replace(/\[([^\]]+)\]\([^)]+\)/g, '[$1]')
const message = `变更日志 ${changelog.trim()}\n\n完整信息见 https://github.com/TuSimple/naive-ui/blob/main/CHANGELOG.zh-CN.md\n`
await inquirer
.prompt([
{
type: 'confirm',
name: 'release-changelog',
message: `发布以下变更日志到钉钉群:\n\n${message}`
}
])
.then((ans) => {
if (ans['release-changelog']) {
request
.post('https://oapi.dingtalk.com/robot/send')
.query({
access_token: DINGTALK_TOKEN
})
.type('application/json')
.send({
msgtype: 'text',
text: {
content: message
}
})
.then((res) => {
console.log(res.text)
})
}
})
}
async function releaseChangelogToDiscord () {
const changelog = fs
.readFileSync(path.resolve(__dirname, '../CHANGELOG.en-US.md'), 'utf-8')
.split(/^## /gm)[1]
.replace(/^##/gm, '')
.replace(/\[([^\]]+)\]\([^)]+\)/g, '[$1]')
const message = `Changelog ${changelog.trim()}\n\nSee https://github.com/TuSimple/naive-ui/blob/main/CHANGELOG.en-US.md for details.\n`
await inquirer
.prompt([
{
type: 'confirm',
name: 'release-changelog',
message: `发布以下变更日志到 Discord\n\n${message}`
}
])
.then((ans) => {
if (ans['release-changelog']) {
request
.post(`https://discord.com/api/webhooks/${DISCORD_TOKEN}`)
.type('application/json')
.send({
content: message
})
.then(() => {
console.log('done')
})
.catch((e) => {
console.error(e)
console.log('Error happens.')
})
}
})
}
;(async () => {
await releaseChangelogToDingTalk()
await releaseChangelogToDiscord()
})()

View File

@ -1,8 +0,0 @@
import { mount } from '@vue/test-utils'
import { NAvatar } from '../index'
describe('n-avatar', () => {
it('should work with import on demand', () => {
mount(NAvatar)
})
})

View File

@ -0,0 +1,121 @@
import { mount } from '@vue/test-utils'
import { NAvatar } from '../index'
import { h, nextTick } from 'vue'
import { CashOutline as CashIcon } from '@vicons/ionicons5'
import { NIcon } from '../../icon'
describe('n-avatar', () => {
// mock offsetHeight offsetWidth
const originalOffsetHeight = Object.getOwnPropertyDescriptor(
HTMLElement.prototype,
'offsetHeight'
)
const originalOffsetWidth = Object.getOwnPropertyDescriptor(
HTMLElement.prototype,
'offsetWidth'
)
beforeAll(() => {
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', {
get () {
if (this.className === 'n-avatar__text') {
return 80
}
return 100
}
})
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
get () {
if (this.className === 'n-avatar__text') {
return 80
}
return 100
}
})
})
afterAll(() => {
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', {
get: () => originalOffsetHeight
})
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
get: () => originalOffsetWidth
})
})
it('should work with import on demand', () => {
mount(NAvatar)
})
it('size is string', () => {
const wrapper = mount(NAvatar, { props: { size: 'medium' } })
expect(wrapper.attributes('style')).toContain('--size')
expect(wrapper.html()).toMatchSnapshot()
})
it('size is number', () => {
const wrapper = mount(NAvatar, { props: { size: 50 } })
expect(wrapper.attributes('style')).toContain('--size: 50px;')
expect(wrapper.html()).toMatchSnapshot()
})
it('round avatar', () => {
const wrapper = mount(NAvatar, { props: { round: true } })
expect(wrapper.attributes('style')).toContain('--border-radius: 50%;')
expect(wrapper.html()).toMatchSnapshot()
})
it('custom style', () => {
const wrapper = mount(NAvatar, {
props: { style: { backgroundColor: 'red' } }
})
expect(wrapper.attributes('style')).toContain('background-color: red;')
expect(wrapper.html()).toMatchSnapshot()
})
it('image avatar', () => {
const wrapper = mount(NAvatar, {
props: {
src: 'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg'
}
})
expect(wrapper.find('img').exists()).toBe(true)
expect(wrapper.html()).toMatchSnapshot()
})
it('icon avatar', () => {
const wrapper = mount(NAvatar, {
slots: {
default: () =>
h(NIcon, null, {
default: () => h(CashIcon)
})
}
})
expect(wrapper.find('i').classes()).toContain('n-icon')
expect(wrapper.html()).toMatchSnapshot()
})
it('avatar adjust text', async () => {
const AdjustAvatar = {
data () {
return {
text: ''
}
},
render () {
const { text } = this as any
return <NAvatar size="medium">{text}</NAvatar>
}
}
const wrapper = mount(AdjustAvatar)
const textNode = wrapper.find('.n-avatar__text')
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()
})
})

View File

@ -0,0 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`n-avatar avatar adjust text 1`] = `"<span class=\\"n-avatar\\" style=\\"--font-size: 14px; --border-radius: 3px; --color: rgba(204, 204, 204, 1); --bezier: cubic-bezier(.4, 0, .2, 1); --size: 34px;\\"><span class=\\"n-avatar__text\\" style=\\"transform: translateX(-50%) translateY(-50%) scale(1);\\">adjust text</span></span>"`;
exports[`n-avatar custom style 1`] = `"<span class=\\"n-avatar\\" style=\\"--font-size: 14px; --border-radius: 3px; --color: rgba(204, 204, 204, 1); --bezier: cubic-bezier(.4, 0, .2, 1); --size: 34px; background-color: red;\\"><span class=\\"n-avatar__text\\" style=\\"transform: translateX(-50%) translateY(-50%) scale(1);\\"></span></span>"`;
exports[`n-avatar icon avatar 1`] = `"<span class=\\"n-avatar\\" style=\\"--font-size: 14px; --border-radius: 3px; --color: rgba(204, 204, 204, 1); --bezier: cubic-bezier(.4, 0, .2, 1); --size: 34px;\\"><span class=\\"n-avatar__text\\" style=\\"transform: translateX(-50%) translateY(-50%) scale(1);\\"><i role=\\"img\\" class=\\"n-icon\\" style=\\"--bezier: cubic-bezier(.4, 0, .2, 1);\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" viewBox=\\"0 0 512 512\\"><rect x=\\"32\\" y=\\"80\\" width=\\"448\\" height=\\"256\\" rx=\\"16\\" ry=\\"16\\" transform=\\"rotate(180 256 208)\\" fill=\\"none\\" stroke=\\"currentColor\\" stroke-linejoin=\\"round\\" stroke-width=\\"32\\"></rect><path fill=\\"none\\" stroke=\\"currentColor\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" stroke-width=\\"32\\" d=\\"M64 384h384\\"></path><path fill=\\"none\\" stroke=\\"currentColor\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" stroke-width=\\"32\\" d=\\"M96 432h320\\"></path><circle cx=\\"256\\" cy=\\"208\\" r=\\"80\\" fill=\\"none\\" stroke=\\"currentColor\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" stroke-width=\\"32\\"></circle><path d=\\"M480 160a80 80 0 0 1-80-80\\" fill=\\"none\\" stroke=\\"currentColor\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" stroke-width=\\"32\\"></path><path d=\\"M32 160a80 80 0 0 0 80-80\\" fill=\\"none\\" stroke=\\"currentColor\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" stroke-width=\\"32\\"></path><path d=\\"M480 256a80 80 0 0 0-80 80\\" fill=\\"none\\" stroke=\\"currentColor\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" stroke-width=\\"32\\"></path><path d=\\"M32 256a80 80 0 0 1 80 80\\" fill=\\"none\\" stroke=\\"currentColor\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\" stroke-width=\\"32\\"></path></svg></i></span></span>"`;
exports[`n-avatar image avatar 1`] = `"<span class=\\"n-avatar\\" style=\\"--font-size: 14px; --border-radius: 3px; --color: rgba(204, 204, 204, 1); --bezier: cubic-bezier(.4, 0, .2, 1); --size: 34px;\\"><img src=\\"https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg\\"></span>"`;
exports[`n-avatar round avatar 1`] = `"<span class=\\"n-avatar\\" style=\\"--font-size: 14px; --border-radius: 50%; --color: rgba(204, 204, 204, 1); --bezier: cubic-bezier(.4, 0, .2, 1); --size: 34px;\\"><span class=\\"n-avatar__text\\" style=\\"transform: translateX(-50%) translateY(-50%) scale(1);\\"></span></span>"`;
exports[`n-avatar size is number 1`] = `"<span class=\\"n-avatar\\" style=\\"--font-size: 14px; --border-radius: 3px; --color: rgba(204, 204, 204, 1); --bezier: cubic-bezier(.4, 0, .2, 1); --size: 50px;\\"><span class=\\"n-avatar__text\\" style=\\"transform: translateX(-50%) translateY(-50%) scale(1);\\"></span></span>"`;
exports[`n-avatar size is string 1`] = `"<span class=\\"n-avatar\\" style=\\"--font-size: 14px; --border-radius: 3px; --color: rgba(204, 204, 204, 1); --bezier: cubic-bezier(.4, 0, .2, 1); --size: 34px;\\"><span class=\\"n-avatar__text\\" style=\\"transform: translateX(-50%) translateY(-50%) scale(1);\\"></span></span>"`;

View File

@ -41,6 +41,7 @@ export default defineComponent({
`${mergedClsPrefix}-button-group`,
this.vertical && `${mergedClsPrefix}-button-group--vertical`
]}
role="group"
>
{this.$slots}
</div>

View File

@ -184,12 +184,11 @@ export default defineComponent({
</div>
) : null}
{$slots.header || this.title || this.closable ? (
<div class={`${mergedClsPrefix}-card-header`}>
<div
class={`${mergedClsPrefix}-card-header__main`}
style={this.headerStyle}
role="heading"
>
<div
class={`${mergedClsPrefix}-card-header`}
style={this.headerStyle}
>
<div class={`${mergedClsPrefix}-card-header__main`} role="heading">
{renderSlot($slots, 'header', {}, () => [this.title])}
</div>
{$slots['header-extra'] ? (

View File

@ -78,6 +78,7 @@ export default c([
box-sizing: border-box;
display: flex;
align-items: center;
font-size: var(--title-font-size);
padding:
var(--padding-top)
var(--padding-left)
@ -85,7 +86,6 @@ export default c([
var(--padding-left);
`, [
cE('main', `
font-size: var(--title-font-size);
font-weight: var(--title-font-weight);
transition: color .3s var(--bezier);
flex: 1;

View File

@ -84,6 +84,14 @@ These methods can help you control table in an uncontrolled manner. However, it'
| page | `(page: number) => void` | |
| sort | `(columnKey: string \| number \| null, order: 'ascend' \| 'descend' \| false) => void` | If columnKey set to `null`, it is the same as clearSorter. |
## Slots
### Slots
| Name | Type | Description |
| ----- | ---- | ---------------------------------------------- |
| empty | `()` | Custom description when data of table is empty. |
## API
### Column Properties

View File

@ -84,6 +84,14 @@ tree
| page | `(page: number) => void` | |
| sort | `(columnKey: string \| number \| null, order: 'ascend' \| 'descend' \| false) => void` | 如果 columnKey 设为 `null`,那它和 clearSorter 效果一致 |
## Slots
### Slots
| 名称 | 参数 | 说明 |
| ----- | ---- | -------------------- |
| empty | `()` | 表格数据为空时的展示 |
## API
### Column Properties

View File

@ -7,6 +7,7 @@ import {
PropType,
ExtractPropTypes,
toRef,
renderSlot,
CSSProperties
} from 'vue'
import { useConfig, useLocale, useTheme } from '../../_mixins'
@ -80,10 +81,7 @@ export const dataTableProps = {
type: Array as PropType<RowKey[]>,
default: () => []
},
checkedRowKeys: {
type: Array as PropType<RowKey[]>,
default: undefined
},
checkedRowKeys: Array as PropType<RowKey[]>,
singleLine: {
type: Boolean,
default: true
@ -116,24 +114,28 @@ export const dataTableProps = {
type: Number,
default: 16
},
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:page': [Function, Array] as PropType<
PaginationProps['onUpdate:page']
>,
// eslint-disable-next-line vue/prop-name-casing
onUpdatePage: [Function, Array] as PropType<PaginationProps['onUpdate:page']>,
'onUpdate:pageSize': [Function, Array] as PropType<
PaginationProps['onUpdate:pageSize']
>,
// eslint-disable-next-line vue/prop-name-casing
onUpdatePageSize: [Function, Array] as PropType<
PaginationProps['onUpdate:pageSize']
>,
'onUpdate:sorter': [Function, Array] as PropType<MaybeArray<OnUpdateSorter>>,
// eslint-disable-next-line vue/prop-name-casing
onUpdateSorter: [Function, Array] as PropType<MaybeArray<OnUpdateSorter>>,
'onUpdate:filters': [Function, Array] as PropType<
MaybeArray<OnUpdateFilters>
>,
// eslint-disable-next-line vue/prop-name-casing
onUpdateFilters: [Function, Array] as PropType<MaybeArray<OnUpdateFilters>>,
'onUpdate:checkedRowKeys': [Function, Array] as PropType<
MaybeArray<OnUpdateCheckedRowKeys>
>,
onUpdateCheckedRowKeys: [Function, Array] as PropType<
MaybeArray<OnUpdateCheckedRowKeys>
>,
'onUpdate:expandedRowKeys': [Function, Array] as PropType<
MaybeArray<OnUpdateExpandedRowKeys>
>,
@ -499,12 +501,14 @@ export default defineComponent({
}
]}
>
<NEmpty
theme={this.mergedTheme.peers.Empty}
themeOverrides={
this.mergedTheme.peerOverrides.Empty
}
/>
{renderSlot(this.$slots, 'empty', undefined, () => [
<NEmpty
theme={this.mergedTheme.peers.Empty}
themeOverrides={
this.mergedTheme.peerOverrides.Empty
}
/>
])}
</div>
) : null
}}

View File

@ -69,9 +69,11 @@ export function useCheck (
})
function doUpdateCheckedRowKeys (keys: RowKey[]): void {
const {
'onUpdate:checkedRowKeys': onUpdateCheckedRowKeys,
'onUpdate:checkedRowKeys': _onUpdateCheckedRowKeys,
onUpdateCheckedRowKeys,
onCheckedRowKeysChange
} = props
if (_onUpdateCheckedRowKeys) call(_onUpdateCheckedRowKeys, keys)
if (onUpdateCheckedRowKeys) call(onUpdateCheckedRowKeys, keys)
if (onCheckedRowKeysChange) call(onCheckedRowKeysChange, keys)
uncontrolledCheckedRowKeysRef.value = keys

View File

@ -297,9 +297,14 @@ export function useTableData (
function mergedOnUpdatePage (page: number): void {
const { pagination } = props
if (pagination) {
const { onChange, 'onUpdate:page': onUpdatePage } = pagination
const {
onChange,
'onUpdate:page': _onUpdatePage,
onUpdatePage
} = pagination
if (onChange) call(onChange, page)
if (onUpdatePage) call(onUpdatePage, page)
if (_onUpdatePage) call(_onUpdatePage, page)
doUpdatePage(page)
}
}
@ -344,7 +349,12 @@ export function useTableData (
uncontrolledPageSizeRef.value = pageSize
}
function doUpdateSorter (sortState: SortState | null): void {
const { 'onUpdate:sorter': onUpdateSorter, onSorterChange } = props
const {
'onUpdate:sorter': _onUpdateSorter,
onUpdateSorter,
onSorterChange
} = props
if (_onUpdateSorter) call(_onUpdateSorter, sortState)
if (onUpdateSorter) call(onUpdateSorter, sortState)
if (onSorterChange) call(onSorterChange, sortState)
uncontrolledSortStateRef.value = sortState
@ -353,8 +363,13 @@ export function useTableData (
filters: FilterState,
sourceColumn?: TableBaseColumn
): void {
const { 'onUpdate:filters': onUpdateFilters, onFiltersChange } = props
const {
onUpdateFilters,
'onUpdate:filters': _onUpdateFilters,
onFiltersChange
} = props
if (onUpdateFilters) call(onUpdateFilters, filters, sourceColumn)
if (_onUpdateFilters) call(_onUpdateFilters, filters, sourceColumn)
if (onFiltersChange) call(onFiltersChange, filters, sourceColumn)
uncontrolledFilterStateRef.value = filters
}

View File

@ -8,6 +8,22 @@ describe('n-data-table', () => {
it('should work with import on demand', () => {
mount(NDataTable)
})
it('show custom empty', () => {
const columns = [
{
title: 'Name',
key: 'name'
}
]
const wrapper = mount(() => (
<NDataTable columns={columns} data={[]}>
{{
empty: () => <div class="empty-info">empty</div>
}}
</NDataTable>
))
expect(wrapper.find('.empty-info').exists()).toEqual(true)
})
describe('props.columns', () => {
it('has correct type', () => {
interface Data {

View File

@ -26,6 +26,7 @@ export type DialogReactive = {
} & DialogOptions
export interface DialogApiInjection {
destroyAll: () => void
create: (options: DialogOptions) => DialogReactive
success: (options: DialogOptions) => DialogReactive
warning: (options: DialogOptions) => DialogReactive
@ -33,18 +34,16 @@ export interface DialogApiInjection {
info: (options: DialogOptions) => DialogReactive
}
export const dialogApiInjectionKey: InjectionKey<DialogApiInjection> = Symbol(
'dialogApi'
)
export const dialogApiInjectionKey: InjectionKey<DialogApiInjection> =
Symbol('dialogApi')
export interface DialogProviderInjection {
clickedRef: Ref<boolean>
clickPositionRef: Ref<{ x: number, y: number } | null>
}
export const dialogProviderInjectionKey: InjectionKey<DialogProviderInjection> = Symbol(
'dialogProvider'
)
export const dialogProviderInjectionKey: InjectionKey<DialogProviderInjection> =
Symbol('dialogProvider')
interface DialogInst {
hide: () => void
@ -79,11 +78,14 @@ export default defineComponent({
dialogListRef.value.push(dialogReactive)
return dialogReactive
}
const typedApi = (['info', 'success', 'warning', 'error'] as Array<
'info' | 'success' | 'warning' | 'error'
>).map((type) => (options: DialogOptions): DialogReactive => {
const typedApi = (
['info', 'success', 'warning', 'error'] as Array<
'info' | 'success' | 'warning' | 'error'
>
).map((type) => (options: DialogOptions): DialogReactive => {
return create({ ...options, type })
})
function handleAfterLeave (key: String): void {
const { value: dialogList } = dialogListRef
dialogList.splice(
@ -91,8 +93,16 @@ export default defineComponent({
1
)
}
function destroyAll (): void {
Object.values(dialogInstRefs).forEach((dialogInstRef) =>
dialogInstRef.hide()
)
}
const api = {
create,
destroyAll,
info: typedApi[0],
success: typedApi[1],
warning: typedApi[2],

View File

@ -49,7 +49,7 @@ export default defineComponent({
if (isGroupNode(child.rawNode)) {
warn(
'dropdown',
'`group` node is allowed to be put in `group` node.'
'`group` node is not allowed to be put in `group` node.'
)
return null
}

View File

@ -13,8 +13,7 @@ import {
import { VBinder, VTarget, VFollower, FollowerPlacement } from 'vueuc'
import { useMemo } from 'vooks'
import { ChevronRightIcon } from '../../_internal/icons'
import { useDeferredTrue } from '../../_utils/composable'
import { render } from '../../_utils'
import { render, useDeferredTrue } from '../../_utils'
import { NIcon } from '../../icon'
import NDropdownMenu, { dropdownMenuInjectionKey } from './DropdownMenu'
import { dropdownInjectionKey } from './Dropdown'

View File

@ -1,5 +1,72 @@
import { VueWrapper } from '@vue/test-utils/dist/vueWrapper'
import { mount } from '@vue/test-utils'
import { NDropdown } from '../index'
import { ComponentPublicInstance, h, nextTick } from 'vue'
import { NIcon } from '../../icon'
import { CashOutline as CashIcon } from '@vicons/ionicons5'
import { NDropdown, DropdownProps } from '../index'
const pendingOptionClassName = 'n-dropdown-option-body n-dropdown-option-body--pending'
const optionBodySelector = '.n-dropdown-option-body'
const options = [
{
type: 'group',
label: '主角和吃的',
key: 'main',
children: [
{
label: '杰·盖茨比',
key: 'jay gatsby'
},
{
type: 'divider',
key: 'd1'
},
{
label: '黛西·布坎南',
icon () {
return h(NIcon, null, {
default: () => h(CashIcon)
})
},
key: 'daisy buchanan',
disabled: true
}
]
},
{
type: 'divider',
key: 'd1'
},
{
label: '其他角色',
key: 'others1',
children: [
{
label: '乔丹·贝克',
key: 'jordan baker'
}
]
}
]
const mountDropdown = ({
onSelect,
inverted = false,
options: data = options
}: DropdownProps = {}): VueWrapper<ComponentPublicInstance> => {
return mount(NDropdown, {
attachTo: document.body,
props: {
options: data,
trigger: 'click',
onSelect,
inverted
},
slots: {
default: () => 'star kirby'
}
})
}
describe('n-dropdown', () => {
it('should work with import on demand', () => {
@ -10,32 +77,125 @@ describe('n-dropdown', () => {
})
})
it('dropdown disabled', async () => {
const onSelect = jest.fn()
const options = [
{
label: '滨海湾金沙,新加坡',
key: 'marina bay sands',
disabled: true
}
]
const triggerEvent = 'click'
const wrapper = mount(NDropdown, {
attachTo: document.body,
props: {
options,
trigger: triggerEvent,
onSelect: onSelect
},
slots: {
default: () => 'star kirby'
}
})
it('shows menu after click', async () => {
const wrapper = mountDropdown()
const triggerNodeWrapper = wrapper.find('span')
expect(triggerNodeWrapper.exists()).toBe(true)
await triggerNodeWrapper.trigger(triggerEvent)
await triggerNodeWrapper.trigger('click')
expect(document.querySelector('.n-dropdown')).toMatchSnapshot()
wrapper.unmount()
})
it('inverted style', async () => {
const wrapper = mountDropdown({ inverted: true })
const triggerNodeWrapper = wrapper.find('span')
expect(triggerNodeWrapper.exists()).toBe(true)
await triggerNodeWrapper.trigger('click')
expect(document.querySelector('.n-dropdown')).toMatchSnapshot()
wrapper.unmount()
})
it('keyboard event', async () => {
const onSelect = jest.fn()
let wrapper = mountDropdown({ onSelect })
let triggerNodeWrapper = wrapper.find('span')
await triggerNodeWrapper.trigger('click')
await triggerNodeWrapper.trigger('keydown', {
key: 'ArrowDown'
})
let options = document.querySelectorAll(optionBodySelector)
expect(options[1].className).toEqual(pendingOptionClassName)
await triggerNodeWrapper.trigger('keydown', {
key: 'ArrowDown'
})
await triggerNodeWrapper.trigger('keydown', {
key: 'ArrowRight'
})
options = document.querySelectorAll(optionBodySelector)
expect(options.length).toBe(5)
expect(options[3].className).toEqual(pendingOptionClassName)
expect(options[4].className).toEqual(pendingOptionClassName)
await triggerNodeWrapper.trigger('keydown', {
key: 'ArrowLeft'
})
options = document.querySelectorAll(optionBodySelector)
expect(options.length).toBe(4)
await triggerNodeWrapper.trigger('keydown', {
key: 'ArrowUp'
})
expect(options[1].className).toEqual(pendingOptionClassName)
await triggerNodeWrapper.trigger('keyup', {
key: 'Enter'
})
expect(onSelect).toHaveBeenCalledWith('jay gatsby', {
key: 'jay gatsby',
label: '杰·盖茨比'
})
wrapper = mountDropdown({ onSelect })
triggerNodeWrapper = wrapper.find('span')
await triggerNodeWrapper.trigger('click')
await triggerNodeWrapper.trigger('keydown', {
key: 'Escape'
})
expect(document.querySelector('.n-dropdown')).toBeNull()
wrapper.unmount()
})
it('option mouse event', async () => {
const onSelect = jest.fn()
const wrapper = mountDropdown({ onSelect })
const triggerNodeWrapper = wrapper.find('span')
expect(triggerNodeWrapper.exists()).toBe(true)
await triggerNodeWrapper.trigger('click')
const options = document.querySelectorAll(optionBodySelector)
const mouseEnter = new Event('mouseenter')
options[1].dispatchEvent(mouseEnter)
await nextTick(() => {
expect(options[1].className).toEqual(pendingOptionClassName)
})
const mouseMove = new Event('mousemove')
options[3].dispatchEvent(mouseMove)
await nextTick(() => {
expect(options[1].className).not.toEqual(pendingOptionClassName)
expect(options[3].className).toEqual(pendingOptionClassName)
})
await (options[3] as HTMLDivElement).click()
expect(onSelect).not.toHaveBeenCalledWith()
const mouseLeave = new Event('mouseleave')
Object.defineProperty(mouseLeave, 'relatedTarget', {
writable: false,
value: options[1]
})
options[3].dispatchEvent(mouseLeave)
await nextTick(() => {
expect(options[3].className).not.toEqual(pendingOptionClassName)
})
})
it('dropdown disabled', async () => {
const onSelect = jest.fn()
const wrapper = mountDropdown({ onSelect })
const triggerNodeWrapper = wrapper.find('span')
expect(triggerNodeWrapper.exists()).toBe(true)
await triggerNodeWrapper.trigger('click')
const disabledMenu = document.querySelector(
'.n-dropdown-option-body--disabled'

View File

@ -0,0 +1,461 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`n-dropdown inverted style 1`] = `
<div
class="n-popover n-dropdown n-popover--no-arrow n-popover--padded n-popover--raw"
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; --border-radius: 3px; --arrow-height: 6px; --arrow-offset: 10px; --arrow-offset-vertical: 10px; --padding: 8px 14px; --space: 6px; --space-arrow: 10px;"
>
<div
class="n-dropdown-menu"
style="--bezier: cubic-bezier(.4, 0, .2, 1); --font-size: 14px; --padding: 4px 0; --border-radius: 3px; --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); --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; --divider-color: rgb(239, 239, 245); --option-opacity-disabled: 0.5; --color: rgb(0, 20, 40); --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;"
>
<div
class="n-dropdown-option"
>
<div
class="n-dropdown-option-body n-dropdown-option-body--group"
>
<div
__dropdown-option="true"
class="n-dropdown-option-body__prefix n-dropdown-option-body__prefix--show-icon"
>
<!---->
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__label"
>
主角和吃的
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__suffix n-dropdown-option-body__suffix--has-submenu"
/>
</div>
</div>
<div
class="n-dropdown-option"
>
<div
class="n-dropdown-option-body"
>
<div
__dropdown-option="true"
class="n-dropdown-option-body__prefix n-dropdown-option-body__prefix--show-icon"
>
<!---->
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__label"
>
杰·盖茨比
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__suffix n-dropdown-option-body__suffix--has-submenu"
/>
</div>
<!---->
</div>
<div
class="n-dropdown-divider"
/>
<div
class="n-dropdown-option"
>
<div
class="n-dropdown-option-body n-dropdown-option-body--disabled"
>
<div
__dropdown-option="true"
class="n-dropdown-option-body__prefix n-dropdown-option-body__prefix--show-icon"
>
<i
class="n-icon"
role="img"
style="--bezier: cubic-bezier(.4, 0, .2, 1);"
>
<svg
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<rect
fill="none"
height="256"
rx="16"
ry="16"
stroke="currentColor"
stroke-linejoin="round"
stroke-width="32"
transform="rotate(180 256 208)"
width="448"
x="32"
y="80"
/>
<path
d="M64 384h384"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<path
d="M96 432h320"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<circle
cx="256"
cy="208"
fill="none"
r="80"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<path
d="M480 160a80 80 0 0 1-80-80"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<path
d="M32 160a80 80 0 0 0 80-80"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<path
d="M480 256a80 80 0 0 0-80 80"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<path
d="M32 256a80 80 0 0 1 80 80"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
</svg>
</i>
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__label"
>
黛西·布坎南
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__suffix n-dropdown-option-body__suffix--has-submenu"
/>
</div>
<!---->
</div>
<div
class="n-dropdown-divider"
/>
<div
class="n-dropdown-option"
>
<div
class="n-dropdown-option-body"
>
<div
__dropdown-option="true"
class="n-dropdown-option-body__prefix n-dropdown-option-body__prefix--show-icon"
>
<!---->
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__label"
>
其他角色
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__suffix n-dropdown-option-body__suffix--has-submenu"
>
<i
class="n-icon"
role="img"
style="--bezier: cubic-bezier(.4, 0, .2, 1);"
>
<svg
fill="none"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.64645 3.14645C5.45118 3.34171 5.45118 3.65829 5.64645 3.85355L9.79289 8L5.64645 12.1464C5.45118 12.3417 5.45118 12.6583 5.64645 12.8536C5.84171 13.0488 6.15829 13.0488 6.35355 12.8536L10.8536 8.35355C11.0488 8.15829 11.0488 7.84171 10.8536 7.64645L6.35355 3.14645C6.15829 2.95118 5.84171 2.95118 5.64645 3.14645Z"
fill="currentColor"
/>
</svg>
</i>
</div>
</div>
<div
class="n-dropdown-offset-container"
>
<!---->
</div>
</div>
</div>
<!---->
</div>
`;
exports[`n-dropdown shows menu after click 1`] = `
<div
class="n-popover n-dropdown n-popover--no-arrow n-popover--padded n-popover--raw"
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; --border-radius: 3px; --arrow-height: 6px; --arrow-offset: 10px; --arrow-offset-vertical: 10px; --padding: 8px 14px; --space: 6px; --space-arrow: 10px;"
>
<div
class="n-dropdown-menu"
style="--bezier: cubic-bezier(.4, 0, .2, 1); --font-size: 14px; --padding: 4px 0; --border-radius: 3px; --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); --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; --divider-color: rgb(239, 239, 245); --option-opacity-disabled: 0.5; --color: #fff; --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);"
>
<div
class="n-dropdown-option"
>
<div
class="n-dropdown-option-body n-dropdown-option-body--group"
>
<div
__dropdown-option="true"
class="n-dropdown-option-body__prefix n-dropdown-option-body__prefix--show-icon"
>
<!---->
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__label"
>
主角和吃的
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__suffix n-dropdown-option-body__suffix--has-submenu"
/>
</div>
</div>
<div
class="n-dropdown-option"
>
<div
class="n-dropdown-option-body"
>
<div
__dropdown-option="true"
class="n-dropdown-option-body__prefix n-dropdown-option-body__prefix--show-icon"
>
<!---->
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__label"
>
杰·盖茨比
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__suffix n-dropdown-option-body__suffix--has-submenu"
/>
</div>
<!---->
</div>
<div
class="n-dropdown-divider"
/>
<div
class="n-dropdown-option"
>
<div
class="n-dropdown-option-body n-dropdown-option-body--disabled"
>
<div
__dropdown-option="true"
class="n-dropdown-option-body__prefix n-dropdown-option-body__prefix--show-icon"
>
<i
class="n-icon"
role="img"
style="--bezier: cubic-bezier(.4, 0, .2, 1);"
>
<svg
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<rect
fill="none"
height="256"
rx="16"
ry="16"
stroke="currentColor"
stroke-linejoin="round"
stroke-width="32"
transform="rotate(180 256 208)"
width="448"
x="32"
y="80"
/>
<path
d="M64 384h384"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<path
d="M96 432h320"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<circle
cx="256"
cy="208"
fill="none"
r="80"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<path
d="M480 160a80 80 0 0 1-80-80"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<path
d="M32 160a80 80 0 0 0 80-80"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<path
d="M480 256a80 80 0 0 0-80 80"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
<path
d="M32 256a80 80 0 0 1 80 80"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="32"
/>
</svg>
</i>
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__label"
>
黛西·布坎南
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__suffix n-dropdown-option-body__suffix--has-submenu"
/>
</div>
<!---->
</div>
<div
class="n-dropdown-divider"
/>
<div
class="n-dropdown-option"
>
<div
class="n-dropdown-option-body"
>
<div
__dropdown-option="true"
class="n-dropdown-option-body__prefix n-dropdown-option-body__prefix--show-icon"
>
<!---->
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__label"
>
其他角色
</div>
<div
__dropdown-option="true"
class="n-dropdown-option-body__suffix n-dropdown-option-body__suffix--has-submenu"
>
<i
class="n-icon"
role="img"
style="--bezier: cubic-bezier(.4, 0, .2, 1);"
>
<svg
fill="none"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.64645 3.14645C5.45118 3.34171 5.45118 3.65829 5.64645 3.85355L9.79289 8L5.64645 12.1464C5.45118 12.3417 5.45118 12.6583 5.64645 12.8536C5.84171 13.0488 6.15829 13.0488 6.35355 12.8536L10.8536 8.35355C11.0488 8.15829 11.0488 7.84171 10.8536 7.64645L6.35355 3.14645C6.15829 2.95118 5.84171 2.95118 5.64645 3.14645Z"
fill="currentColor"
/>
</svg>
</i>
</div>
</div>
<div
class="n-dropdown-offset-container"
>
<!---->
</div>
</div>
</div>
<!---->
</div>
`;

View File

@ -5,11 +5,13 @@
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: 0
value: ref(0)
}
}
}
})
```

View File

@ -8,12 +8,14 @@
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: 0,
disabled: true
value: ref(0),
disabled: ref(true)
}
}
}
})
```

View File

@ -12,22 +12,24 @@ min-max
size
step
validator
show-button
```
## Props
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| bordered | `boolean` | `true` | |
| default-value | `number \| null` | `null` | |
| disabled | `boolean` | `false` | |
| max | `number` | `undefined` | |
| min | `number` | `undefined` | |
| bordered | `boolean` | `true` | Whether to show the border. |
| default-value | `number \| null` | `null` | Default value in uncontrolled mode. |
| disabled | `boolean` | `false` | Whether to disable the input. |
| max | `number` | `undefined` | The max value. |
| min | `number` | `undefined` | The min value. |
| placeholder | `string` | `'Please Input'` | |
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | |
| step | `number` | `1` | |
| validator | `(value) => boolean` | `undefined` | |
| value | `number` | `undefined` | |
| on-blur | `(event: FocusEvent) => void` | `undefined` | |
| on-focus | `(event: FocusEvent) => void` | `undefined` | |
| on-update:value | `(value: number) => void` | `undefined` | |
| 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. |
| validator | `(value) => boolean` | `undefined` | Setup custom validation. |
| value | `number` | `undefined` | Value in controlled mode. |
| on-blur | `(event: FocusEvent) => void` | `undefined` | Callback when blur. |
| on-focus | `(event: FocusEvent) => void` | `undefined` | Callback when focused. |
| on-update:value | `(value: number) => void` | `undefined` | Callback when the component's value changes. |

View File

@ -10,11 +10,13 @@ You can set min & max of it.
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: null
value: ref(null)
}
}
}
})
```

View File

@ -0,0 +1,23 @@
# Hide Button
Use `show-button` prop to control whether to show buttons.
```html
<n-space align="center">
<n-switch v-model:value="disabled" />
<n-input-number :show-button="disabled" v-model:value="value" />
</n-space>
```
```js
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: ref(0),
disabled: ref(true)
}
}
})
```

View File

@ -11,11 +11,13 @@
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: 0
value: ref(0)
}
}
}
})
```

View File

@ -5,11 +5,13 @@
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: 0
value: ref(0)
}
}
}
})
```

View File

@ -5,12 +5,14 @@
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: 0,
value: ref(0),
validator: (x) => x > 0
}
}
}
})
```

View File

@ -5,11 +5,13 @@
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: 0
value: ref(0)
}
}
}
})
```

View File

@ -8,12 +8,14 @@
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: 0,
disabled: true
value: ref(0),
disabled: ref(true)
}
}
}
})
```

View File

@ -12,22 +12,24 @@ min-max
size
step
validator
show-button
```
## Props
| 名称 | 类型 | 默认值 | 说明 |
| --------------- | -------------------------------- | ----------- | ---- |
| bordered | `boolean` | `true` | |
| default-value | `number \| null` | `null` | |
| disabled | `boolean` | `false` | |
| max | `number` | `undefined` | |
| min | `number` | `undefined` | |
| placeholder | `string` | `'请输入'` | |
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | |
| step | `number` | `1` | |
| validator | `(value) => boolean` | `undefined` | |
| value | `number \| null` | `undefined` | |
| on-blur | `(event: FocusEvent) => void` | `undefined` | |
| on-focus | `(event: FocusEvent) => void` | `undefined` | |
| on-update:value | `(value: number) => void` | `undefined` | |
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| bordered | `boolean` | `true` | 是否有边框 |
| default-value | `number \| null` | `null` | 非受控模式下的默认值 |
| disabled | `boolean` | `false` | 是否禁用 |
| max | `number` | `undefined` | 最大值 |
| min | `number` | `undefined` | 最小值 |
| placeholder | `string` | `'请输入'` | |
| show-button | `boolean` | `true` | 是否有按钮 |
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | 输入框大小 |
| step | `number` | `1` | 每次改变步数,可以为小数 |
| validator | `(value) => boolean` | `undefined` | 设置自定义验证 |
| value | `number \| null` | `undefined` | 受控模式下的值 |
| on-blur | `(event: FocusEvent) => void` | `undefined` | 移除焦点的回调 |
| on-focus | `(event: FocusEvent) => void` | `undefined` | 获取焦点的回调 |
| on-update:value | `(value: number) => void` | `undefined` | 组件值发生变化的回调 |

View File

@ -20,11 +20,13 @@
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: null
value: ref(null)
}
}
}
})
```

View File

@ -0,0 +1,23 @@
# 隐藏按钮
使用 `show-button` 属性来控制是否展示按钮。
```html
<n-space align="center">
<n-switch v-model:value="disabled" />
<n-input-number :show-button="disabled" v-model:value="value" />
</n-space>
```
```js
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: ref(0),
disabled: ref(true)
}
}
})
```

View File

@ -11,11 +11,13 @@
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: 0
value: ref(0)
}
}
}
})
```

View File

@ -5,11 +5,13 @@
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: 0
value: ref(0)
}
}
}
})
```

View File

@ -5,12 +5,14 @@
```
```js
export default {
data () {
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
value: 0,
value: ref(0),
validator: (x) => x > 0
}
}
}
})
```

View File

@ -45,6 +45,10 @@ const inputNumberProps = {
type: Boolean as PropType<boolean | undefined>,
default: undefined
},
showButton: {
type: Boolean,
default: true
},
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:value': [Function, Array] as PropType<
MaybeArray<(value: number) => void>
@ -361,46 +365,48 @@ export default defineComponent({
onKeydown={this.handleKeyDown}
onMousedown={this.handleMouseDown}
>
{{
suffix: () => [
<NButton
text
disabled={!this.minusable || this.disabled}
focusable={false}
builtinThemeOverrides={this.buttonThemeOverrides}
onClick={this.handleMinusClick}
ref="minusButtonInstRef"
>
{{
default: () => (
<NBaseIcon clsPrefix={mergedClsPrefix}>
{{
default: () => <RemoveIcon />
}}
</NBaseIcon>
)
}}
</NButton>,
<NButton
text
disabled={!this.addable || this.disabled}
focusable={false}
builtinThemeOverrides={this.buttonThemeOverrides}
onClick={this.handleAddClick}
ref="addButtonInstRef"
>
{{
default: () => (
<NBaseIcon clsPrefix={mergedClsPrefix}>
{{
default: () => <AddIcon />
}}
</NBaseIcon>
)
}}
</NButton>
]
}}
{this.showButton
? {
suffix: () => [
<NButton
text
disabled={!this.minusable || this.disabled}
focusable={false}
builtinThemeOverrides={this.buttonThemeOverrides}
onClick={this.handleMinusClick}
ref="minusButtonInstRef"
>
{{
default: () => (
<NBaseIcon clsPrefix={mergedClsPrefix}>
{{
default: () => <RemoveIcon />
}}
</NBaseIcon>
)
}}
</NButton>,
<NButton
text
disabled={!this.addable || this.disabled}
focusable={false}
builtinThemeOverrides={this.buttonThemeOverrides}
onClick={this.handleAddClick}
ref="addButtonInstRef"
>
{{
default: () => (
<NBaseIcon clsPrefix={mergedClsPrefix}>
{{
default: () => <AddIcon />
}}
</NBaseIcon>
)
}}
</NButton>
]
}
: null}
</NInput>
</div>
)

View File

@ -1,8 +1,19 @@
import { mount } from '@vue/test-utils'
import { NInputNumber } from '../index'
import { NButton } from '../../button'
describe('n-input-number', () => {
it('should work with import on demand', () => {
mount(NInputNumber)
})
})
describe('n-input-number', () => {
it('should work with `show-button` props', async () => {
const wrapper = mount(NInputNumber)
expect(wrapper.findComponent(NButton).exists()).toBe(true)
await wrapper.setProps({ showButton: false })
expect(wrapper.findComponent(NButton).exists()).toBe(false)
})
})

View File

@ -21,7 +21,16 @@ function renderIcon (icon) {
const menuOptions = [
{
label: 'Hear the Wind Sing',
label: () =>
h(
'a',
{
href: 'https://en.wikipedia.org/wiki/Hear_the_Wind_Sing',
target: '_blank',
rel: 'noopenner noreferrer'
},
'Hear the Wind Sing'
),
key: 'hear-the-wind-sing',
icon: renderIcon(BookIcon)
},

View File

@ -1,6 +1,8 @@
# Select & Routing
Use `@update:value` to listen to the select action of the menu. The firt argument of the callback is the `key` of the selected menu item. The second is the orginal data of the menu item. Usually you can use vue-router here to accomplish routing.
Use `@update:value` to listen to the select action of the menu. The firt argument of the callback is the `key` of the selected menu item. The second is the orginal data of the menu item.
Usually you can use vue-router here to accomplish routing. Also, you can render `label` as `<router-link />` or `<a />` to set route.
```html
<n-menu @update:value="handleUpdateValue" :options="menuOptions" />
@ -21,7 +23,16 @@ function renderIcon (icon) {
const menuOptions = [
{
label: 'Hear the Wind Sing',
label: () =>
h(
'a',
{
href: 'https://en.wikipedia.org/wiki/Hear_the_Wind_Sing',
target: '_blank',
rel: 'noopenner noreferrer'
},
'Hear the Wind Sing'
),
key: 'hear-the-wind-sing',
icon: renderIcon(BookIcon)
},

View File

@ -21,7 +21,16 @@ function renderIcon (icon) {
const menuOptions = [
{
label: '且听风吟',
label: () =>
h(
'a',
{
href: 'https://baike.baidu.com/item/%E4%B8%94%E5%90%AC%E9%A3%8E%E5%90%9F',
target: '_blank',
rel: 'noopenner noreferrer'
},
'且听风吟'
),
key: 'hear-the-wind-sing',
icon: renderIcon(BookIcon)
},

View File

@ -1,6 +1,8 @@
# 选中 & 路由
使用 `@update:value` 监听菜单选择变化。这个回调首个参数为选中菜单项的 `key`,第二个参数为菜单项的原数据。你通常可以在这个地方配合 vue-router 完成路由。
使用 `@update:value` 监听菜单选择变化。这个回调首个参数为选中菜单项的 `key`,第二个参数为菜单项的原数据。
你通常可以在这个地方配合 vue-router 完成路由。当然,你也可以通过将 `label` 渲染为 `<router-link />``<a />` 来改变路由。
```html
<n-menu @update:value="handleUpdateValue" :options="menuOptions" />
@ -21,7 +23,16 @@ function renderIcon (icon) {
const menuOptions = [
{
label: '且听风吟',
label: () =>
h(
'a',
{
href: 'https://baike.baidu.com/item/%E4%B8%94%E5%90%AC%E9%A3%8E%E5%90%9F',
target: '_blank',
rel: 'noopenner noreferrer'
},
'且听风吟'
),
key: 'hear-the-wind-sing',
icon: renderIcon(BookIcon)
},

View File

@ -227,6 +227,19 @@ export default cB('menu', `
text-overflow: ellipsis;
color: var(--item-text-color);
`, [
c('a', `
text-decoration: none;
color: inherit;
`, [
c('&::before', `
content: "";
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
`)
]),
cE('extra', `
font-size: .93em;
color: var(--group-text-color);

View File

@ -45,24 +45,15 @@ const paginationProps = {
type: Number,
default: 1
},
showSizePicker: {
type: Boolean,
default: false
},
showSizePicker: Boolean,
pageSize: Number as PropType<number>,
defaultPageSize: Number,
pageSizes: {
type: Array as PropType<number[]>,
default: () => [10]
},
showQuickJumper: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
showQuickJumper: Boolean,
disabled: Boolean,
pageSlot: {
type: Number,
default: 9

View File

@ -66,8 +66,8 @@ export default defineComponent({
clsPrefix
} = props
return (
<div class={`${clsPrefix}-progress-content`}>
<div class={`${clsPrefix}-progress-graph`}>
<div class={`${clsPrefix}-progress-content`} role="none">
<div class={`${clsPrefix}-progress-graph`} aria-hidden>
<div class={`${clsPrefix}-progress-graph-circle`}>
<svg viewBox="0 0 110 110">
<g>
@ -112,11 +112,11 @@ export default defineComponent({
{showIndicator ? (
<div>
{slots.default ? (
<div class={`${clsPrefix}-progress-custom-content`}>
<div class={`${clsPrefix}-progress-custom-content`} role="none">
{slots.default()}
</div>
) : status !== 'default' ? (
<div class={`${clsPrefix}-progress-icon`}>
<div class={`${clsPrefix}-progress-icon`} aria-hidden>
<NBaseIcon clsPrefix={clsPrefix}>
{{
default: () => iconMap[status]
@ -129,6 +129,7 @@ export default defineComponent({
style={{
color: indicatorTextColor
}}
role="none"
>
<span class={`${clsPrefix}-progress-text__percentage`}>
{percentage}

View File

@ -92,13 +92,14 @@ export default defineComponent({
clsPrefix
} = props
return (
<div class={`${clsPrefix}-progress-content`}>
<div class={`${clsPrefix}-progress-graph`}>
<div class={`${clsPrefix}-progress-content`} role="none">
<div class={`${clsPrefix}-progress-graph`} aria-hidden>
<div
class={[
`${clsPrefix}-progress-graph-line`,
{
[`${clsPrefix}-progress-graph-line--indicator-${indicatorPlacement}`]: true
[`${clsPrefix}-progress-graph-line--indicator-${indicatorPlacement}`]:
true
}
]}
>
@ -145,11 +146,13 @@ export default defineComponent({
style={{
color: indicatorTextColor
}}
role="none"
>
{slots.default()}
</div>
) : status === 'default' ? (
<div
role="none"
class={`${clsPrefix}-progress-icon ${clsPrefix}-progress-icon--as-text`}
style={{
color: indicatorTextColor
@ -159,7 +162,7 @@ export default defineComponent({
{unit}
</div>
) : (
<div class={`${clsPrefix}-progress-icon`}>
<div class={`${clsPrefix}-progress-icon`} aria-hidden>
<NBaseIcon clsPrefix={clsPrefix}>
{{ default: () => iconMap[status] }}
</NBaseIcon>

View File

@ -73,8 +73,8 @@ export default defineComponent({
clsPrefix
} = props
return (
<div class={`${clsPrefix}-progress-content`}>
<div class={`${clsPrefix}-progress-graph`}>
<div class={`${clsPrefix}-progress-content`} role="none">
<div class={`${clsPrefix}-progress-graph`} aria-hidden>
<div class={`${clsPrefix}-progress-graph-circle`}>
<svg viewBox={`0 0 ${viewBoxWidth} ${viewBoxWidth}`}>
{percentage.map((p, index) => {

View File

@ -12,10 +12,7 @@ import MultipleCircle from './MultipleCircle'
const progressProps = {
...(useTheme.props as ThemeProps<ProgressTheme>),
processing: {
type: Boolean,
default: false
},
processing: Boolean,
type: {
type: String as PropType<'line' | 'circle' | 'multiple-circle'>,
default: 'line'
@ -57,26 +54,14 @@ const progressProps = {
type: String as PropType<'inside' | 'outside'>,
default: 'outside'
},
indicatorTextColor: {
type: String,
default: undefined
},
indicatorTextColor: String,
circleGap: {
type: Number,
default: 1
},
height: {
type: Number,
default: undefined
},
borderRadius: {
type: [String, Number] as PropType<string | number | undefined>,
default: undefined
},
fillBorderRadius: {
type: [String, Number] as PropType<string | number | undefined>,
default: undefined
}
height: Number,
borderRadius: [String, Number] as PropType<string | number>,
fillBorderRadius: [String, Number] as PropType<string | number>
} as const
export type ProgressProps = ExtractPublicPropTypes<typeof progressProps>
@ -171,6 +156,10 @@ export default defineComponent({
`${mergedClsPrefix}-progress--${status}`
]}
style={cssVars as CSSProperties}
aria-valuemax={100}
aria-valuemin={0}
aria-valuenow={percentage as number}
role={type === 'circle' || type === 'line' ? 'progressbar' : 'none'}
>
{type === 'circle' ? (
<Circle

View File

@ -0,0 +1,7 @@
# Color
That's how disaster happens.
```html
<n-rate color="#4fb233" />
```

View File

@ -0,0 +1,22 @@
# Icon
The content in default slot will be used as icon.
```html
<n-rate>
<n-icon size="20">
<cash-icon />
</n-icon>
</n-rate>
```
```js
import { CashOutline as CashIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
CashIcon
}
})
```

View File

@ -6,13 +6,23 @@ If you not very confident, be careful about changing star's color. That will be
```demo
basic
size
color
icon
```
## Props
| 名称 | 类型 | 默认值 | 说明 |
| --------------- | ------------------------- | ----------- | ---- |
| count | `number` | `5` | |
| value | `number` | `undefined` | |
| default-value | `number` | `0` | |
| on-update:value | `(value: number) => void` | `undefined` | |
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| count | `number` | `5` | |
| value | `number` | `undefined` | |
| default-value | `number` | `0` | |
| size | `'small' \| 'medium' \| 'large' \| number` | `'medium'` | |
| on-update:value | `(value: number) => void` | `undefined` | |
## Slots
| Name | Parameters | Description |
| ------- | ---------- | --------------------- |
| default | `()` | The icon of the rate. |

View File

@ -0,0 +1,11 @@
# Size
Rate has `small`, `medium` and `large` size. Also you can use number to specify the size.
```html
<n-space align="center">
<n-rate size="small" />
<n-rate size="medium" />
<n-rate size="large" />
</n-space>
```

View File

@ -0,0 +1,7 @@
# 颜色
灾难就是这么发生的。
```html
<n-rate color="#4fb233" />
```

View File

@ -0,0 +1,22 @@
# 图标
默认插槽的内容会被用作图标。
```html
<n-rate>
<n-icon size="20">
<cash-icon />
</n-icon>
</n-rate>
```
```js
import { CashOutline as CashIcon } from '@vicons/ionicons5'
import { defineComponent } from 'vue'
export default defineComponent({
components: {
CashIcon
}
})
```

View File

@ -6,13 +6,23 @@
```demo
basic
size
color
icon
```
## Props
| 名称 | 类型 | 默认值 | 说明 |
| --------------- | ------------------------- | ----------- | ---- |
| count | `number` | `5` | |
| value | `number` | `undefined` | |
| default-value | `number` | `0` | |
| on-update:value | `(value: number) => void` | `undefined` | |
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| count | `number` | `5` | |
| value | `number` | `undefined` | |
| default-value | `number` | `0` | |
| size | `'small' \| 'medium' \| 'large' \| number` | `'medium'` | |
| on-update:value | `(value: number) => void` | `undefined` | |
## Slots
| 名称 | 参数 | 说明 |
| ------- | ---- | ---------- |
| default | `()` | 评分的图标 |

View File

@ -0,0 +1,11 @@
# 尺寸
`small`、`medium` 和 `large` 尺寸。
```html
<n-space align="center">
<n-rate size="small" />
<n-rate size="medium" />
<n-rate size="large" />
</n-space>
```

View File

@ -12,7 +12,7 @@ import { useMergedState } from 'vooks'
import { NBaseIcon } from '../../_internal'
import { useTheme, useFormItem, useConfig } from '../../_mixins'
import type { ThemeProps } from '../../_mixins'
import { call } from '../../_utils'
import { call, createKey } from '../../_utils'
import type { ExtractPublicPropTypes, MaybeArray } from '../../_utils'
import { rateLight } from '../styles'
import type { RateTheme } from '../styles'
@ -25,18 +25,16 @@ const rateProps = {
type: Number,
default: 5
},
value: {
type: Number,
default: undefined
},
value: Number,
defaultValue: {
type: Number,
default: 0
},
size: {
type: String as PropType<'small' | 'medium' | 'large'>,
type: [String, Number] as PropType<number | 'small' | 'medium' | 'large'>,
default: 'medium'
},
color: String,
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:value': [Function, Array] as PropType<
MaybeArray<(value: number) => void>
@ -95,21 +93,34 @@ export default defineComponent({
handleClick,
handleMouseLeave,
cssVars: computed(() => {
const { size } = props
const {
common: { cubicBezierEaseInOut },
self: { itemColor, itemColorActive, itemSize }
self
} = themeRef.value
const { itemColor, itemColorActive } = self
let mergedSize: string
if (typeof size === 'number') {
mergedSize = `${size}px`
} else {
mergedSize = self[createKey('size', size)]
}
return {
'--bezier': cubicBezierEaseInOut,
'--item-color': itemColor,
'--item-color-active': itemColorActive,
'--item-size': itemSize
'--item-color-active': props.color || itemColorActive,
'--item-size': mergedSize
}
})
}
},
render () {
const { hoverIndex, mergedValue, mergedClsPrefix } = this
const {
hoverIndex,
mergedValue,
mergedClsPrefix,
$slots: { default: defaultSlot }
} = this
return (
<div
class={`${mergedClsPrefix}-rate`}
@ -131,9 +142,13 @@ export default defineComponent({
onClick={() => this.handleClick(index)}
onMouseenter={() => this.handleMouseEnter(index)}
>
<NBaseIcon clsPrefix={mergedClsPrefix}>
{{ default: () => StarIcon }}
</NBaseIcon>
{defaultSlot ? (
defaultSlot()
) : (
<NBaseIcon clsPrefix={mergedClsPrefix}>
{{ default: () => StarIcon }}
</NBaseIcon>
)}
</div>
))}
</div>

View File

@ -9,7 +9,10 @@ const rateDark: RateTheme = {
return {
itemColor: railColor,
itemColorActive: '#CCAA33',
itemSize: '20px'
itemSize: '20px',
sizeSmall: '14px',
sizeMedium: '20px',
sizeLarge: '24px'
}
}
}

View File

@ -7,7 +7,9 @@ const self = (vars: ThemeCommonVars) => {
return {
itemColor: railColor,
itemColorActive: '#FFCC33',
itemSize: '20px'
sizeSmall: '16px',
sizeMedium: '20px',
sizeLarge: '24px'
}
}

View File

@ -18,22 +18,13 @@ type Align =
const spaceProps = {
...(useTheme.props as ThemeProps<SpaceTheme>),
align: {
type: String as PropType<Align>,
default: undefined
},
align: String as PropType<Align>,
justify: {
type: String as PropType<'start' | 'end'>,
default: 'start'
},
inline: {
type: Boolean,
default: false
},
vertical: {
type: Boolean,
default: false
},
inline: Boolean,
vertical: Boolean,
size: {
type: [String, Number, Array] as PropType<
'small' | 'medium' | 'large' | number | [number, number]
@ -107,6 +98,7 @@ export default defineComponent({
const lastIndex = children.length - 1
return (
<div
role="none"
class={`${mergedClsPrefix}-space`}
style={{
display: inline ? 'inline-flex' : 'flex',
@ -120,6 +112,7 @@ export default defineComponent({
>
{children.map((child, index) => (
<div
role="none"
style={[
itemStyle as any,
{

View File

@ -1 +1 @@
export default '2.11.8'
export default '2.11.9'