mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-12 12:25:16 +08:00
Merge branch 'main' of github.com:TuSimple/naive-ui into main
This commit is contained in:
commit
c89aa66f02
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -4,12 +4,12 @@ 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)
|
||||
|
||||
|
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -4,9 +4,10 @@ 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.
|
||||
|
18
.github/pull_request_template.md
vendored
18
.github/pull_request_template.md
vendored
@ -1,8 +1,18 @@
|
||||
<!--
|
||||
Please add the the changelog to `CHANGELOG.zh-CN.md` & `CHANGELOG.en-US.md` in the PR.
|
||||
You can only add detailed info in one of them and add a placeholder item in the other (for example `- ***`).
|
||||
!!! Read the following content if this is your first pull request !!!
|
||||
|
||||
1. If you are working on docs, please set `main` branch as the target branch.
|
||||
2. If you are working on bug fixes, please set `main` branch as the target branch.
|
||||
3. If you are working on new features, please set `feat` branch as the target branch.
|
||||
|
||||
For people who are working on 2 & 3, please add changelog to `CHANGELOG.zh-CN.md` & `CHANGELOG.en-US.md` in the PR. You need to add changelog to both files. You can use English in both file. The develop team will translate it later.
|
||||
-->
|
||||
<!--
|
||||
请在这个 PR 里向 `CHANGELOG.zh-CN.md` 和 `CHANGELOG.en-US.md` 添加这个 PR 的变更记录,
|
||||
你可以只在其中一个添加详细的信息,然后在另一个文件中留着一个占位的项,例如 `- ***`
|
||||
!!! 如果这是你第一次提交 PR,请阅读下面的内容 !!!
|
||||
|
||||
1. 如果你在修改文档,请提交到 `main` 分支
|
||||
2. 如果你在修复 Bug,请提交到 `main` 分支
|
||||
3. 如果你在开发新的特性,请提交到 `feat` 分支
|
||||
|
||||
对于进行工作 2 & 3 的人,请在 `CHANGELOG.zh-CN.md` & `CHANGELOG.en-US.md` 中添加变更日志,你需要在两个文件中都添加变更日志。你可以只使用中文,开发团队会在之后进行翻译。
|
||||
-->
|
||||
|
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
_
|
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
@ -1,6 +1,31 @@
|
||||
# CHANGELOG
|
||||
|
||||
## Pending
|
||||
## 2.11.7
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fix `n-slider` doesn't prevent scrolling when touchstart.
|
||||
- Fix `n-color-picker`'s default value doesn't follow modes.
|
||||
- Fix not `lodash` & `lodash-es` type.
|
||||
|
||||
## 2.11.6 (2021-06-11)
|
||||
|
||||
### Feats
|
||||
|
||||
- `n-spin`'s `size` prop support number.
|
||||
- `n-date-picker` add `footer` slot.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fix `n-slider` doesn't support touch events
|
||||
- Fix `n-button` causes crash when it's imported in script inside head tag. [#68](https://github.com/TuSimple/naive-ui/pull/68)
|
||||
- Fix `n-spin` animation shifts.
|
||||
- Fix `n-menu` lack `on-update-value` and `on-update-expanded-keys` props.
|
||||
- Fix `n-popconfirm` icon slot not working.
|
||||
- Fix `n-tabs` logs useless info.
|
||||
- Fix `n-color-picker` set `modes` not working. [#77](https://github.com/TuSimple/naive-ui/issues/77)
|
||||
|
||||
## 2.11.5 (2021-06-10)
|
||||
|
||||
### Feats
|
||||
|
||||
@ -15,6 +40,7 @@
|
||||
- Fix `n-calendar` date calculate incorrectly
|
||||
- Fix `n-input` missing 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.
|
||||
|
||||
## 2.11.4
|
||||
|
||||
|
@ -1,6 +1,31 @@
|
||||
# CHANGELOG
|
||||
|
||||
## Pending
|
||||
## 2.11.7
|
||||
|
||||
### Fixes
|
||||
|
||||
- 修复 `n-slider` 在 touchstart 发生时没有阻止滚动
|
||||
- 修复 `n-color-picker` 默认值不跟随模式设定
|
||||
- 修复缺少 `lodash` & `lodash-es` 类型
|
||||
|
||||
## 2.11.6 (2021-06-11)
|
||||
|
||||
### Feats
|
||||
|
||||
- `n-spin` 的 `size` 属性支持 number 类型
|
||||
- `n-date-picker` 支持 `footer` 插槽
|
||||
|
||||
### Fixes
|
||||
|
||||
- 修正 `n-slider` 不支持触摸事件
|
||||
- 修正 `n-button` 在 head 内部的 script 被引入造成崩溃 [#68](https://github.com/TuSimple/naive-ui/pull/68)
|
||||
- 修正 `n-spin` 动画闪烁
|
||||
- 修正 `n-menu` 缺少 `on-update-value` 和 `on-update-expanded-keys` 属性
|
||||
- 修正 `n-popconfirm` icon slot 不生效
|
||||
- 修正 `n-tabs` 在控制台输出无用信息
|
||||
- 修正 `n-color-picker` 设定 `modes` 无效 [#77](https://github.com/TuSimple/naive-ui/issues/77)
|
||||
|
||||
## 2.11.5 (2021-06-10)
|
||||
|
||||
### Feats
|
||||
|
||||
@ -15,6 +40,7 @@
|
||||
- 修复 `n-calendar` 展示日期计算错误
|
||||
- 修复 `n-input` 缺失 `password` 的声明
|
||||
- 修复 `n-menu` 的菜单和子菜单的 `extra` 属性的类型定义
|
||||
- 修复 `n-dropdown` 选项鼠标形状不是 pointer
|
||||
|
||||
## 2.11.4
|
||||
|
||||
|
14
README.md
14
README.md
@ -13,6 +13,11 @@
|
||||
|
||||
[www.naiveui.com](http://www.naiveui.com)
|
||||
|
||||
## Community
|
||||
|
||||
- [Discord](https://discord.gg/Pqv7Mev5Dd)
|
||||
- DingTalk Group 33482509
|
||||
|
||||
## Features
|
||||
|
||||
### Fairly Complete
|
||||
@ -23,13 +28,13 @@ What's more, they are all treeshakable.
|
||||
|
||||
### Customizable Themes
|
||||
|
||||
We provide an advanced type safe theme system that built Uses TypeScript. All you need is to provide a theme overrides object in JS. Then all the stuffs will be done by us.
|
||||
We provide an advanced type safe theme system built using TypeScript. All you need is to provide a theme overrides object in JS. Then all the stuff will be done by us.
|
||||
|
||||
What's more, no less/sass/css variables, no webpack loaders are required.
|
||||
|
||||
### Uses TypeScript
|
||||
|
||||
All the staff in Naive UI is written in TypeScript. It can work with your typescript project seamlessly.
|
||||
All the stuff in Naive UI is written in TypeScript. It can work with your typescript project seamlessly.
|
||||
|
||||
What's more, you don't need to import any CSS to use the components.
|
||||
|
||||
@ -63,11 +68,6 @@ Naive UI recommends using [xicons](https://www.xicons.org) as icon library.
|
||||
|
||||
Working in progress.
|
||||
|
||||
## Community
|
||||
|
||||
- [Discord](https://discord.gg/Pqv7Mev5Dd)
|
||||
- DingTalk Group 33482509
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see [CONTRIBUTING.md](https://github.com/TuSimple/naive-ui/blob/main/CONTRIBUTING.md).
|
||||
|
@ -13,6 +13,11 @@
|
||||
|
||||
[www.naiveui.com](http://www.naiveui.com)
|
||||
|
||||
## 社区
|
||||
|
||||
- [Discord](https://discord.gg/Pqv7Mev5Dd)
|
||||
- 钉钉群 33482509
|
||||
|
||||
## 特性
|
||||
|
||||
### 比较完整
|
||||
@ -63,11 +68,6 @@ naive-ui 建议使用 [xicons](https://www.xicons.org) 作为图标库。
|
||||
|
||||
正在搞。
|
||||
|
||||
## 社区
|
||||
|
||||
- [Discord](https://discord.gg/Pqv7Mev5Dd)
|
||||
- 钉钉群 33482509
|
||||
|
||||
## 贡献
|
||||
|
||||
请参考 [CONTRIBUTING.md](https://github.com/TuSimple/naive-ui/blob/main/CONTRIBUTING.md)。
|
||||
|
20
package.json
20
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "naive-ui",
|
||||
"version": "2.11.4",
|
||||
"version": "2.11.7",
|
||||
"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,9 @@
|
||||
"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",
|
||||
"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"
|
||||
"post-changelog": "node scripts/post-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"
|
||||
},
|
||||
"author": "07akioni",
|
||||
"license": "MIT",
|
||||
@ -66,7 +68,6 @@
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@types/estree": "^0.0.48",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/lodash-es": "^4.17.4",
|
||||
"@typescript-eslint/eslint-plugin": "^4.15.1",
|
||||
"@typescript-eslint/parser": "^4.15.1",
|
||||
"@vicons/fluent": "^0.8.0",
|
||||
@ -95,23 +96,27 @@
|
||||
"eslint-plugin-vue": "^7.6.0",
|
||||
"express": "^4.17.1",
|
||||
"fs-extra": "^10.0.0",
|
||||
"husky": "^4.3.5",
|
||||
"husky": "^6.0.0",
|
||||
"inquirer": "^8.1.0",
|
||||
"jest": "^27.0.4",
|
||||
"lint-staged": "^11.0.0",
|
||||
"marked": "^2.0.1",
|
||||
"prettier": "^2.2.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"superagent": "^6.1.0",
|
||||
"typescript": "^4.3.2",
|
||||
"vite": "^2.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@css-render/plugin-bem": "^0.15.2",
|
||||
"@css-render/vue3-ssr": "^0.15.2",
|
||||
"@types/lodash": "^4.14.170",
|
||||
"@types/lodash-es": "^4.17.4",
|
||||
"async-validator": "^3.5.1",
|
||||
"css-render": "^0.15.2",
|
||||
"date-fns": "^2.19.0",
|
||||
"evtd": "^0.2.2",
|
||||
"highlight.js": "^10.7.1",
|
||||
"highlight.js": "^11.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"seemly": "^0.3.1",
|
||||
@ -123,11 +128,6 @@
|
||||
"vue-router": "^4.0.5",
|
||||
"vueuc": "^0.4.7"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"sideEffects": false,
|
||||
"homepage": "https://www.naiveui.com",
|
||||
"repository": {
|
||||
|
46
scripts/post-changelog.js
Normal file
46
scripts/post-changelog.js
Normal file
@ -0,0 +1,46 @@
|
||||
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)
|
||||
})
|
||||
}
|
||||
})
|
@ -3,6 +3,10 @@ import { useStyle } from '../../../_mixins'
|
||||
import NIconSwitchTransition from '../../icon-switch-transition'
|
||||
import style from './styles/index.cssr'
|
||||
|
||||
const duration = '1.6s'
|
||||
|
||||
// The loading svg dom comes from https://codepen.io/FezVrasta/pen/oXrgdR
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BaseLoading',
|
||||
props: {
|
||||
@ -35,33 +39,62 @@ export default defineComponent({
|
||||
useStyle('BaseLoading', style, toRef(props, 'clsPrefix'))
|
||||
},
|
||||
render () {
|
||||
const { clsPrefix } = this
|
||||
const { clsPrefix, radius, strokeWidth, stroke, scale } = this
|
||||
const scaledRadius = radius / scale
|
||||
return (
|
||||
<div
|
||||
class={`${clsPrefix}-base-loading`}
|
||||
style={{ stroke: this.stroke }}
|
||||
role="img"
|
||||
aria-label="loading"
|
||||
>
|
||||
<div class={`${clsPrefix}-base-loading`} role="img" aria-label="loading">
|
||||
<NIconSwitchTransition>
|
||||
{{
|
||||
default: () =>
|
||||
this.show ? (
|
||||
<svg
|
||||
key="loading"
|
||||
class={`${clsPrefix}-base-loading-circular ${clsPrefix}-base-loading__icon`}
|
||||
viewBox={`0 0 ${(this.radius * 2) / this.scale} ${
|
||||
(this.radius * 2) / this.scale
|
||||
}`}
|
||||
class={`${clsPrefix}-base-loading__icon`}
|
||||
viewBox={`0 0 ${2 * scaledRadius} ${2 * scaledRadius}`}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style={{ color: stroke }}
|
||||
>
|
||||
<circle
|
||||
style={{ strokeWidth: this.strokeWidth }}
|
||||
class={`${clsPrefix}-base-loading-circular-path`}
|
||||
cx={this.radius / this.scale}
|
||||
cy={this.radius / this.scale}
|
||||
fill="none"
|
||||
r={this.radius - this.strokeWidth / 2}
|
||||
/>
|
||||
<g>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
type="rotate"
|
||||
values={`0 ${scaledRadius} ${scaledRadius};270 ${scaledRadius} ${scaledRadius}`}
|
||||
begin="0s"
|
||||
dur={duration}
|
||||
fill="freeze"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
<circle
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width={strokeWidth}
|
||||
stroke-linecap="round"
|
||||
cx={scaledRadius}
|
||||
cy={scaledRadius}
|
||||
r={radius - strokeWidth / 2}
|
||||
stroke-dasharray={5.67 * radius}
|
||||
stroke-dashoffset={18.48 * radius}
|
||||
>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
type="rotate"
|
||||
values={`0 ${scaledRadius} ${scaledRadius};135 ${scaledRadius} ${scaledRadius};450 ${scaledRadius} ${scaledRadius}`}
|
||||
begin="0s"
|
||||
dur={duration}
|
||||
fill="freeze"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
<animate
|
||||
attributeName="stroke-dashoffset"
|
||||
values={`${5.67 * radius};${1.42 * radius};${
|
||||
5.67 * radius
|
||||
}`}
|
||||
begin="0s"
|
||||
dur={duration}
|
||||
fill="freeze"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</circle>
|
||||
</g>
|
||||
</svg>
|
||||
) : (
|
||||
<div
|
||||
|
@ -1,65 +1,25 @@
|
||||
import { c, cB, cE } from '../../../../_utils/cssr'
|
||||
import { cB, cE } from '../../../../_utils/cssr'
|
||||
import iconSwitchTransition from '../../../../_styles/transitions/icon-switch.cssr'
|
||||
|
||||
const dashOffset = 500
|
||||
|
||||
export default c([
|
||||
cB('base-loading', `
|
||||
position: relative;
|
||||
line-height: 0;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
`, [
|
||||
cE('placeholder', {
|
||||
position: 'absolute',
|
||||
export default cB('base-loading', `
|
||||
position: relative;
|
||||
line-height: 0;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
`, [
|
||||
cE('placeholder', {
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: 'translateX(-50%) translateY(-50%)'
|
||||
}, [
|
||||
iconSwitchTransition({
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: 'translateX(-50%) translateY(-50%)'
|
||||
}, [
|
||||
iconSwitchTransition({
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
originalTransform: 'translateX(-50%) translateY(-50%)'
|
||||
})
|
||||
]),
|
||||
cE('icon', [
|
||||
iconSwitchTransition()
|
||||
]),
|
||||
cB('base-loading-circular', `
|
||||
stroke: currentColor;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
animation: n-base-loading-rotate 1.5s linear infinite;
|
||||
transform-origin: center;
|
||||
`, [
|
||||
cB('base-loading-circular-path', `
|
||||
transform-origin: center;
|
||||
animation: n-base-loading-dash 1.5s ease-in-out infinite;
|
||||
stroke-dasharray: ${dashOffset};
|
||||
stroke-dashoffset: 0;
|
||||
stroke-linecap: round;
|
||||
`)
|
||||
])
|
||||
originalTransform: 'translateX(-50%) translateY(-50%)'
|
||||
})
|
||||
]),
|
||||
c('@keyframes n-base-loading-rotate', `
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
`),
|
||||
c('@keyframes n-base-loading-dash', `
|
||||
0% {
|
||||
stroke-dashoffset: ${dashOffset};
|
||||
}
|
||||
50% {
|
||||
stroke-dashoffset: ${dashOffset / 4};
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: ${dashOffset};
|
||||
transform: rotate(450deg);
|
||||
}
|
||||
`)
|
||||
cE('icon', [
|
||||
iconSwitchTransition()
|
||||
])
|
||||
])
|
||||
|
@ -32,8 +32,8 @@ export default defineComponent({
|
||||
<NBaseLoading
|
||||
clsPrefix={clsPrefix}
|
||||
class={`${clsPrefix}-base-selection__mark`}
|
||||
strokeWidth={20}
|
||||
scale={0.8}
|
||||
strokeWidth={24}
|
||||
scale={0.85}
|
||||
show={props.loading}
|
||||
>
|
||||
{{
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { inject, computed, ComputedRef } from 'vue'
|
||||
import type { highlight, getLanguage } from 'highlight.js'
|
||||
import type { HLJSApi } from 'highlight.js'
|
||||
import { configProviderInjectionKey } from '../config-provider/src/ConfigProvider'
|
||||
import { warn } from '../_utils'
|
||||
|
||||
@ -9,8 +9,8 @@ interface UseHljsProps {
|
||||
}
|
||||
|
||||
export interface Hljs {
|
||||
highlight: typeof highlight
|
||||
getLanguage: typeof getLanguage
|
||||
highlight: HLJSApi['highlight']
|
||||
getLanguage: HLJSApi['getLanguage']
|
||||
}
|
||||
export default function useHljs (
|
||||
props: UseHljsProps
|
||||
|
51
src/_mixins/use-rtl.ts
Normal file
51
src/_mixins/use-rtl.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { Ref, onBeforeMount, inject, watchEffect, computed } from 'vue'
|
||||
import { ssrInjectionKey } from '../ssr/context'
|
||||
import {
|
||||
RtlEnabledState,
|
||||
RtlItem
|
||||
} from '../config-provider/src/internal-interface'
|
||||
|
||||
// The current implemention will take extra perf & memory usage. I just want to
|
||||
// make it work now. If we can determine whether the style is already mounted,
|
||||
// we won't need to watch effect. However, we need to make css-render support
|
||||
// it. We need to refactor ssrAdapter and expose a exists function
|
||||
export default function useRtl (
|
||||
mountId: string,
|
||||
rtlStateRef: Ref<RtlEnabledState | undefined> | undefined,
|
||||
clsPrefixRef: Ref<string>
|
||||
): Ref<RtlItem | undefined> | undefined {
|
||||
if (!rtlStateRef) return undefined
|
||||
const ssrAdapter = inject(ssrInjectionKey, undefined)
|
||||
const componentRtlStateRef = computed(() => {
|
||||
const { value: rtlState } = rtlStateRef
|
||||
if (!rtlState) {
|
||||
return undefined
|
||||
}
|
||||
const componentRtlState = rtlState[mountId as keyof RtlEnabledState]
|
||||
if (!componentRtlState) {
|
||||
return undefined
|
||||
}
|
||||
return componentRtlState
|
||||
})
|
||||
const mountStyle = (): void => {
|
||||
watchEffect(() => {
|
||||
const { value: clsPrefix } = clsPrefixRef
|
||||
const { value: componentRtlState } = componentRtlStateRef
|
||||
if (!componentRtlState) return
|
||||
componentRtlState.style.mount({
|
||||
id: `${clsPrefix}${mountId}Rtl`,
|
||||
head: true,
|
||||
props: {
|
||||
bPrefix: clsPrefix ? `.${clsPrefix}-` : undefined
|
||||
},
|
||||
ssr: ssrAdapter
|
||||
})
|
||||
})
|
||||
}
|
||||
if (ssrAdapter) {
|
||||
mountStyle()
|
||||
} else {
|
||||
onBeforeMount(mountStyle)
|
||||
}
|
||||
return componentRtlStateRef
|
||||
}
|
@ -18,6 +18,7 @@ ghost
|
||||
loading
|
||||
color
|
||||
group
|
||||
rtl-debug
|
||||
debug
|
||||
```
|
||||
|
||||
|
24
src/button/demos/zhCN/rtl-debug.demo.md
Normal file
24
src/button/demos/zhCN/rtl-debug.demo.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Rtl Debug
|
||||
|
||||
```html
|
||||
<n-space vertical>
|
||||
<n-space><n-switch v-model:value="rtlEnabled" />Rtl</n-space>
|
||||
<n-config-provider :rtl="rtlEnabled ? rtlStyles : undefined">
|
||||
<n-button>Rtl Test</n-button>
|
||||
</n-config-provider>
|
||||
</n-space>
|
||||
```
|
||||
|
||||
```js
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import { unstableButtonRtl } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {
|
||||
rtlEnabled: ref(false),
|
||||
rtlStyles: [unstableButtonRtl]
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
@ -28,6 +28,7 @@ import type { ButtonTheme } from '../styles'
|
||||
import { buttonGroupInjectionKey } from './ButtonGroup'
|
||||
import type { Type, Size } from './interface'
|
||||
import style from './styles/button.cssr'
|
||||
import useRtl from '../../_mixins/use-rtl'
|
||||
|
||||
const buttonProps = {
|
||||
...(useTheme.props as ThemeProps<ButtonTheme>),
|
||||
@ -157,7 +158,7 @@ const Button = defineComponent({
|
||||
const handleBlur = (): void => {
|
||||
enterPressedRef.value = false
|
||||
}
|
||||
const { mergedClsPrefixRef } = useConfig(props)
|
||||
const { mergedClsPrefixRef, NConfigProvider } = useConfig(props)
|
||||
const themeRef = useTheme(
|
||||
'Button',
|
||||
'Button',
|
||||
@ -166,6 +167,11 @@ const Button = defineComponent({
|
||||
props,
|
||||
mergedClsPrefixRef
|
||||
)
|
||||
const rtlEnabledRef = useRtl(
|
||||
'Button',
|
||||
NConfigProvider?.mergedRtlRef,
|
||||
mergedClsPrefixRef
|
||||
)
|
||||
return {
|
||||
selfRef,
|
||||
waveRef,
|
||||
@ -174,6 +180,7 @@ const Button = defineComponent({
|
||||
mergedSize: mergedSizeRef,
|
||||
showBorder: showBorderRef,
|
||||
enterPressed: enterPressedRef,
|
||||
rtlEnabled: rtlEnabledRef,
|
||||
handleMouseDown,
|
||||
handleKeyDown,
|
||||
handleBlur,
|
||||
@ -390,6 +397,7 @@ const Button = defineComponent({
|
||||
`${mergedClsPrefix}-button`,
|
||||
`${mergedClsPrefix}-button--${this.type}-type`,
|
||||
{
|
||||
[`${mergedClsPrefix}-button--rtl`]: this.rtlEnabled,
|
||||
[`${mergedClsPrefix}-button--disabled`]: this.disabled,
|
||||
[`${mergedClsPrefix}-button--block`]: this.block,
|
||||
[`${mergedClsPrefix}-button--pressed`]: this.enterPressed,
|
||||
@ -429,7 +437,7 @@ const Button = defineComponent({
|
||||
clsPrefix={mergedClsPrefix}
|
||||
key="loading"
|
||||
class={`${mergedClsPrefix}-icon-slot`}
|
||||
strokeWidth={24}
|
||||
strokeWidth={20}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
|
7
src/button/src/styles/button-rtl.cssr.ts
Normal file
7
src/button/src/styles/button-rtl.cssr.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { cB, cM } from '../../../_utils/cssr'
|
||||
|
||||
export default cB('button', [
|
||||
cM('rtl', `
|
||||
direction: rtl;
|
||||
`)
|
||||
])
|
@ -154,7 +154,7 @@ export default c([
|
||||
animationName: 'button-wave-spread, button-wave-opacity'
|
||||
})
|
||||
]),
|
||||
(typeof window !== 'undefined' && 'MozBoxSizing' in document.body.style)
|
||||
(typeof window !== 'undefined' && 'MozBoxSizing' in document.createElement('div').style)
|
||||
? c('&::moz-focus-inner', {
|
||||
border: 0
|
||||
})
|
||||
|
@ -1,3 +1,4 @@
|
||||
export { default as buttonDark } from './dark'
|
||||
export { default as buttonLight } from './light'
|
||||
export { default as buttonRtl } from './rtl'
|
||||
export type { ButtonThemeVars, ButtonTheme } from './light'
|
||||
|
6
src/button/styles/rtl.ts
Normal file
6
src/button/styles/rtl.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import rtlStyle from '../src/styles/button-rtl.cssr'
|
||||
|
||||
export default {
|
||||
name: 'Button',
|
||||
style: rtlStyle
|
||||
}
|
@ -16,6 +16,7 @@ closable
|
||||
no-title
|
||||
loading
|
||||
custom-style
|
||||
rtl-debug
|
||||
```
|
||||
|
||||
## Props
|
||||
|
30
src/card/demos/zhCN/rtl-debug.demo.md
Normal file
30
src/card/demos/zhCN/rtl-debug.demo.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Rtl Debug
|
||||
|
||||
```html
|
||||
<n-space vertical>
|
||||
<n-space><n-switch v-model:value="rtlEnabled" />Rtl</n-space>
|
||||
<n-config-provider :rtl="rtlEnabled ? rtlStyles : undefined">
|
||||
<n-card closable>
|
||||
<template #header>Rtl Header Test</template>
|
||||
<template #header-extra>Rtl Header Extra Test</template>
|
||||
Rtl Content Test
|
||||
<template #footer>Rtl Header Test</template>
|
||||
<template #action>Rtl Action Test</template>
|
||||
</n-card>
|
||||
</n-config-provider>
|
||||
</n-space>
|
||||
```
|
||||
|
||||
```js
|
||||
import { defineComponent, ref } from 'vue'
|
||||
import { unstableCardRtl } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
return {
|
||||
rtlEnabled: ref(false),
|
||||
rtlStyles: [unstableCardRtl]
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
@ -6,6 +6,7 @@ import {
|
||||
renderSlot,
|
||||
CSSProperties
|
||||
} from 'vue'
|
||||
import { getPadding } from 'seemly'
|
||||
import { useConfig, useTheme } from '../../_mixins'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import { call, createKey, keysOf } from '../../_utils'
|
||||
@ -14,7 +15,7 @@ import { NBaseClose } from '../../_internal'
|
||||
import { cardLight } from '../styles'
|
||||
import type { CardTheme } from '../styles'
|
||||
import style from './styles/index.cssr'
|
||||
import { getPadding } from 'seemly'
|
||||
import useRtl from '../../_mixins/use-rtl'
|
||||
|
||||
export interface Segmented {
|
||||
content?: boolean | 'soft'
|
||||
@ -64,7 +65,7 @@ export default defineComponent({
|
||||
const { onClose } = props
|
||||
if (onClose) call(onClose)
|
||||
}
|
||||
const { mergedClsPrefixRef } = useConfig(props)
|
||||
const { mergedClsPrefixRef, NConfigProvider } = useConfig(props)
|
||||
const themeRef = useTheme(
|
||||
'Card',
|
||||
'Card',
|
||||
@ -73,7 +74,13 @@ export default defineComponent({
|
||||
props,
|
||||
mergedClsPrefixRef
|
||||
)
|
||||
const rtlEnabledRef = useRtl(
|
||||
'Card',
|
||||
NConfigProvider?.mergedRtlRef,
|
||||
mergedClsPrefixRef
|
||||
)
|
||||
return {
|
||||
rtlEnabled: rtlEnabledRef,
|
||||
mergedClsPrefix: mergedClsPrefixRef,
|
||||
mergedTheme: themeRef,
|
||||
handleCloseClick,
|
||||
@ -137,12 +144,20 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { segmented, bordered, hoverable, $slots, mergedClsPrefix } = this
|
||||
const {
|
||||
segmented,
|
||||
bordered,
|
||||
hoverable,
|
||||
mergedClsPrefix,
|
||||
rtlEnabled,
|
||||
$slots
|
||||
} = this
|
||||
return (
|
||||
<div
|
||||
class={[
|
||||
`${mergedClsPrefix}-card`,
|
||||
{
|
||||
[`${mergedClsPrefix}-card--rtl`]: rtlEnabled,
|
||||
[`${mergedClsPrefix}-card--content${
|
||||
typeof segmented !== 'boolean' && segmented.content === 'soft'
|
||||
? '-soft'
|
||||
|
7
src/card/src/styles/rtl.cssr.ts
Normal file
7
src/card/src/styles/rtl.cssr.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { cB, cM } from '../../../_utils/cssr'
|
||||
|
||||
export default cB('card', [
|
||||
cM('rtl', `
|
||||
direction: rtl;
|
||||
`)
|
||||
])
|
@ -1,3 +1,4 @@
|
||||
export { default as cardDark } from './dark'
|
||||
export { default as cardLight } from './light'
|
||||
export { default as cardRtl } from './rtl'
|
||||
export type { CardTheme, CardThemeVars } from './light'
|
||||
|
6
src/card/styles/rtl.ts
Normal file
6
src/card/styles/rtl.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import rtlStyle from '../src/styles/rtl.cssr'
|
||||
|
||||
export default {
|
||||
name: 'Card',
|
||||
style: rtlStyle
|
||||
}
|
@ -176,8 +176,8 @@ export default defineComponent({
|
||||
[`${mergedClsPrefix}-cascader-option--pending`]:
|
||||
this.keyboardPending || this.hoverPending,
|
||||
[`${mergedClsPrefix}-cascader-option--disabled`]: this.disabled,
|
||||
[`${mergedClsPrefix}-cascader-option--show-prefix`]: this
|
||||
.showCheckbox
|
||||
[`${mergedClsPrefix}-cascader-option--show-prefix`]:
|
||||
this.showCheckbox
|
||||
}
|
||||
]}
|
||||
onMouseenter={this.mergedHandleMouseEnter}
|
||||
@ -206,8 +206,8 @@ export default defineComponent({
|
||||
{!this.isLeaf ? (
|
||||
<NBaseLoading
|
||||
clsPrefix={mergedClsPrefix}
|
||||
scale={0.8}
|
||||
strokeWidth={20}
|
||||
scale={0.85}
|
||||
strokeWidth={24}
|
||||
show={this.isLoading}
|
||||
class={`${mergedClsPrefix}-cascader-option-icon`}
|
||||
>
|
||||
|
@ -8,6 +8,7 @@ Compared with real world, its space is discrete.
|
||||
basic
|
||||
alpha
|
||||
size
|
||||
modes
|
||||
form
|
||||
```
|
||||
|
||||
@ -16,7 +17,7 @@ form
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| default-show | `boolean` | `undefined` | Whether to show panel by default. |
|
||||
| default-value | `string` | `#000000` | Default value of the picker. |
|
||||
| default-value | `string` | Black color value of 1st mode's corresponding value. | Default value of the picker. |
|
||||
| modes | `Array<'rgb' \| 'hex' \| 'hsl' \| 'hsv'>` | `['rgb', 'hex', 'hsl']` | The value format of the picker. Notice that value will follow the mode once you select a new value from the picker. |
|
||||
| to | `string \| HTMLElement` | `'body'` | Where to detach the panel. |
|
||||
| show | `boolean` | `undefined` | Whether to show the panel. |
|
||||
|
7
src/color-picker/demos/enUS/modes.demo.md
Normal file
7
src/color-picker/demos/enUS/modes.demo.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Set Mode
|
||||
|
||||
Use `modes` to set available modes.
|
||||
|
||||
```html
|
||||
<n-color-picker :modes="['hex']" />
|
||||
```
|
@ -8,6 +8,7 @@
|
||||
basic
|
||||
alpha
|
||||
size
|
||||
modes
|
||||
form
|
||||
```
|
||||
|
||||
@ -16,7 +17,7 @@ form
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| default-show | `boolean` | `undefined` | 默认是否展示弹出层 |
|
||||
| default-value | `string` | `#000000` | 默认的颜色值 |
|
||||
| default-value | `string \| null` | 和第一个 mode 对应的黑色值 | 默认的颜色值 |
|
||||
| modes | `Array<'rgb' \| 'hex' \| 'hsl' \| 'hsv'>` | `['rgb', 'hex', 'hsl']` | 颜色选择器支持颜色的格式,注意一旦你在某个模式下选择了值,颜色选择器值的格式将跟随这个格式 |
|
||||
| to | `string \| HTMLElement` | `'body'` | 面板的卸载位置 |
|
||||
| show | `boolean` | `undefined` | 是否展示面板 |
|
||||
|
7
src/color-picker/demos/zhCN/modes.demo.md
Normal file
7
src/color-picker/demos/zhCN/modes.demo.md
Normal file
@ -0,0 +1,7 @@
|
||||
# 设定模式
|
||||
|
||||
使用 `modes` 设定可选模式。
|
||||
|
||||
```html
|
||||
<n-color-picker :modes="['hex']" />
|
||||
```
|
@ -27,6 +27,10 @@ export default defineComponent({
|
||||
type: String as PropType<ColorPickerMode>,
|
||||
required: true
|
||||
},
|
||||
modes: {
|
||||
type: Array as PropType<ColorPickerMode[]>,
|
||||
required: true
|
||||
},
|
||||
showAlpha: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
@ -37,7 +41,7 @@ export default defineComponent({
|
||||
default: null
|
||||
},
|
||||
valueArr: {
|
||||
type: (Array as unknown) as PropType<HSVA | RGBA | HSLA | null>,
|
||||
type: Array as unknown as PropType<HSVA | RGBA | HSLA | null>,
|
||||
default: null
|
||||
},
|
||||
onUpdateValue: {
|
||||
@ -89,26 +93,32 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { clsPrefix } = this
|
||||
const { clsPrefix, modes } = this
|
||||
return (
|
||||
<div class={`${clsPrefix}-color-picker-input`}>
|
||||
<div
|
||||
class={`${clsPrefix}-color-picker-input__mode`}
|
||||
onClick={this.onUpdateMode}
|
||||
style={{
|
||||
cursor: modes.length === 1 ? '' : 'pointer'
|
||||
}}
|
||||
>
|
||||
{this.mode.toUpperCase() + (this.showAlpha ? 'A' : '')}
|
||||
</div>
|
||||
<NInputGroup>
|
||||
{{
|
||||
default: () => {
|
||||
const { mode, valueArr, value, showAlpha } = this
|
||||
const { mode, valueArr, showAlpha } = this
|
||||
if (mode === 'hex') {
|
||||
// hex and rgba shares the same value arr
|
||||
let hexValue = null
|
||||
try {
|
||||
hexValue =
|
||||
value === null
|
||||
valueArr === null
|
||||
? null
|
||||
: (showAlpha ? toHexaString : toHexString)(value)
|
||||
: (showAlpha ? toHexaString : toHexString)(
|
||||
valueArr as RGBA
|
||||
)
|
||||
} catch {}
|
||||
return (
|
||||
<ColorInputUnit
|
||||
|
@ -58,7 +58,7 @@ import AlphaSlider from './AlphaSlider'
|
||||
import Pallete from './Pallete'
|
||||
import ColorInput from './ColorInput'
|
||||
import ColorPickerTrigger from './ColorPickerTrigger'
|
||||
import { getModeFromValue } from './utils'
|
||||
import { deriveDefaultValue, getModeFromValue } from './utils'
|
||||
import type { ColorPickerMode } from './utils'
|
||||
import style from './styles/index.cssr'
|
||||
import { OnUpdateValue, OnUpdateValueImpl } from './interface'
|
||||
@ -75,10 +75,7 @@ export const colorPickerPanelProps = {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultValue: {
|
||||
type: String as PropType<string | null>,
|
||||
default: '#000000'
|
||||
},
|
||||
defaultValue: String as PropType<string | null>,
|
||||
modes: {
|
||||
type: Array as PropType<ColorPickerMode[]>,
|
||||
// no hsva by default since browser doesn't support it
|
||||
@ -145,7 +142,12 @@ export default defineComponent({
|
||||
uncontrolledShowRef.value = value
|
||||
}
|
||||
|
||||
const uncontrolledValueRef = ref(props.defaultValue)
|
||||
const { defaultValue } = props
|
||||
const uncontrolledValueRef = ref(
|
||||
defaultValue === undefined
|
||||
? deriveDefaultValue(props.modes, props.showAlpha)
|
||||
: defaultValue
|
||||
)
|
||||
const mergedValueRef = useMergedState(
|
||||
toRef(props, 'value'),
|
||||
uncontrolledValueRef
|
||||
@ -163,33 +165,11 @@ export default defineComponent({
|
||||
function handleUpdateDisplayedMode (): void {
|
||||
const { modes } = props
|
||||
const { value: displayedMode } = displayedModeRef
|
||||
switch (displayedMode) {
|
||||
case 'rgb':
|
||||
if (modes.includes('hex')) {
|
||||
displayedModeRef.value = 'hex'
|
||||
break
|
||||
}
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
case 'hex':
|
||||
if (modes.includes('hsv')) {
|
||||
displayedModeRef.value = 'hsv'
|
||||
break
|
||||
}
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
case 'hsv':
|
||||
if (modes.includes('hsl')) {
|
||||
displayedModeRef.value = 'hsl'
|
||||
break
|
||||
}
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
case 'hsl':
|
||||
if (modes.includes('rgb')) {
|
||||
displayedModeRef.value = 'rgb'
|
||||
break
|
||||
}
|
||||
// eslint-disable-next-line no-fallthrough
|
||||
default:
|
||||
displayedModeRef.value = 'rgb'
|
||||
const currentModeIndex = modes.findIndex((mode) => mode === displayedMode)
|
||||
if (~currentModeIndex) {
|
||||
displayedModeRef.value = modes[(currentModeIndex + 1) % modes.length]
|
||||
} else {
|
||||
displayedModeRef.value = 'rgb'
|
||||
}
|
||||
}
|
||||
|
||||
@ -501,7 +481,7 @@ export default defineComponent({
|
||||
function renderPanel (): VNode {
|
||||
const { value: rgba } = rgbaRef
|
||||
const { value: displayedHue } = displayedHueRef
|
||||
const { internalActions } = props
|
||||
const { internalActions, modes } = props
|
||||
const { value: mergedTheme } = themeRef
|
||||
const { value: mergedClsPrefix } = mergedClsPrefixRef
|
||||
return (
|
||||
@ -540,6 +520,7 @@ export default defineComponent({
|
||||
clsPrefix={mergedClsPrefix}
|
||||
showAlpha={props.showAlpha}
|
||||
mode={displayedModeRef.value}
|
||||
modes={modes}
|
||||
onUpdateMode={handleUpdateDisplayedMode}
|
||||
value={mergedValueRef.value}
|
||||
valueArr={mergedValueArrRef.value}
|
||||
|
@ -120,7 +120,6 @@ export default c([
|
||||
flex-basis: 0;
|
||||
`),
|
||||
cE('mode', `
|
||||
cursor: pointer;
|
||||
width: 72px;
|
||||
text-align: center;
|
||||
`)
|
||||
|
@ -1,5 +1,27 @@
|
||||
import { warn } from '../../_utils'
|
||||
|
||||
export type ColorPickerMode = 'rgb' | 'hsl' | 'hsv' | 'hex'
|
||||
|
||||
export function deriveDefaultValue (
|
||||
modes: ColorPickerMode[],
|
||||
showAlpha: boolean
|
||||
): string {
|
||||
const mode = modes[0]
|
||||
switch (mode) {
|
||||
case 'hex':
|
||||
return showAlpha ? '#000000FF' : '#000000'
|
||||
case 'rgb':
|
||||
return showAlpha ? 'rgba(0, 0, 0, 1)' : 'rgb(0, 0, 0)'
|
||||
case 'hsl':
|
||||
return showAlpha ? 'hsla(0, 0%, 0%, 1)' : 'hsl(0, 0%, 0%)'
|
||||
case 'hsv':
|
||||
return showAlpha ? 'hsva(0, 0%, 0%, 1)' : 'hsv(0, 0%, 0%)'
|
||||
}
|
||||
if (__DEV__) warn('color-picker', 'props.modes is invalid.')
|
||||
// in case of invalid modes
|
||||
return '#000000'
|
||||
}
|
||||
|
||||
export function getModeFromValue (color: string | null): ColorPickerMode | null {
|
||||
if (color === null) return null
|
||||
if (/^ *#/.test(color)) return 'hex'
|
||||
|
75
src/color-picker/tests/ColorPicker.spec.tsx
Normal file
75
src/color-picker/tests/ColorPicker.spec.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import { nextTick } from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { NColorPicker } from '../index'
|
||||
import { ColorPickerMode } from '../src/utils'
|
||||
|
||||
describe('n-color-picker', () => {
|
||||
it('should work with import on demand', () => {
|
||||
mount(NColorPicker)
|
||||
})
|
||||
describe('props.modes', () => {
|
||||
it('multiple modes', async () => {
|
||||
const wrapper = mount(NColorPicker, {
|
||||
attachTo: document.body,
|
||||
props: {
|
||||
modes: ['hex', 'hsl']
|
||||
}
|
||||
})
|
||||
await wrapper.find('.n-color-picker-trigger').trigger('click')
|
||||
expect(document.querySelector('.n-color-picker-panel')).not.toEqual(null)
|
||||
const modeDom = document.querySelector('.n-color-picker-input__mode')
|
||||
expect(modeDom?.textContent).toEqual('HEXA')
|
||||
;(modeDom as HTMLElement).click()
|
||||
await nextTick()
|
||||
expect(modeDom?.textContent).toEqual('HSLA')
|
||||
;(modeDom as HTMLElement).click()
|
||||
await nextTick()
|
||||
expect(modeDom?.textContent).toEqual('HEXA')
|
||||
wrapper.unmount()
|
||||
})
|
||||
it('single mode', async () => {
|
||||
const wrapper = mount(NColorPicker, {
|
||||
attachTo: document.body,
|
||||
props: {
|
||||
modes: ['hsl']
|
||||
}
|
||||
})
|
||||
await wrapper.find('.n-color-picker-trigger').trigger('click')
|
||||
expect(document.querySelector('.n-color-picker-panel')).not.toEqual(null)
|
||||
const modeDom = document.querySelector('.n-color-picker-input__mode')
|
||||
expect(modeDom?.textContent).toEqual('HSLA')
|
||||
;(modeDom as HTMLElement).click()
|
||||
await nextTick()
|
||||
expect(modeDom?.textContent).toEqual('HSLA')
|
||||
wrapper.unmount()
|
||||
})
|
||||
it('has correct default value', () => {
|
||||
const input: Array<{ modes: ColorPickerMode[], value: string }> = [
|
||||
{
|
||||
modes: ['hsl'],
|
||||
value: 'hsla(0, 0%, 0%, 1)'
|
||||
},
|
||||
{
|
||||
modes: ['rgb'],
|
||||
value: 'rgb(0, 00, 0, 1)'
|
||||
},
|
||||
{
|
||||
modes: ['hex'],
|
||||
value: '#00000000'
|
||||
},
|
||||
{
|
||||
modes: ['hsv', 'hsl'],
|
||||
value: 'hsv(0, 0%, 0%, 1)'
|
||||
}
|
||||
]
|
||||
input.forEach(({ modes, value }) => {
|
||||
const wrapper = mount(NColorPicker, {
|
||||
props: {
|
||||
modes
|
||||
}
|
||||
})
|
||||
expect(wrapper.text().includes(value))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@ -6,7 +6,9 @@ import {
|
||||
PropType,
|
||||
provide,
|
||||
InjectionKey,
|
||||
renderSlot
|
||||
renderSlot,
|
||||
ComputedRef,
|
||||
markRaw
|
||||
} from 'vue'
|
||||
import { useMemo } from 'vooks'
|
||||
import { merge } from 'lodash-es'
|
||||
@ -18,17 +20,18 @@ import type {
|
||||
GlobalComponentConfig,
|
||||
GlobalIconConfig
|
||||
} from './interface'
|
||||
import type { ConfigProviderInjection } from './internal-interface'
|
||||
import type {
|
||||
ConfigProviderInjection,
|
||||
RtlProp,
|
||||
RtlEnabledState
|
||||
} from './internal-interface'
|
||||
import { NDateLocale, NLocale } from '../../locales'
|
||||
|
||||
export const configProviderInjectionKey: InjectionKey<ConfigProviderInjection> =
|
||||
Symbol('configProviderInjection')
|
||||
|
||||
export const configProviderProps = {
|
||||
abstract: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
abstract: Boolean,
|
||||
bordered: {
|
||||
type: Boolean as PropType<boolean | undefined>,
|
||||
default: undefined
|
||||
@ -37,6 +40,7 @@ export const configProviderProps = {
|
||||
locale: Object as PropType<NLocale | null>,
|
||||
dateLocale: Object as PropType<NDateLocale | null>,
|
||||
namespace: String,
|
||||
rtl: Array as PropType<RtlProp>,
|
||||
tag: {
|
||||
type: String,
|
||||
default: 'div'
|
||||
@ -160,7 +164,21 @@ export default defineComponent({
|
||||
const { clsPrefix } = props
|
||||
return NConfigProvider?.mergedClsPrefixRef.value ?? clsPrefix
|
||||
})
|
||||
const mergedRtlRef: ComputedRef<RtlEnabledState | undefined> = computed(
|
||||
() => {
|
||||
const { rtl } = props
|
||||
if (rtl === undefined) {
|
||||
return NConfigProvider?.mergedRtlRef.value
|
||||
}
|
||||
const rtlEnabledState: RtlEnabledState = {}
|
||||
for (const rtlInfo of rtl) {
|
||||
rtlEnabledState[rtlInfo.name] = markRaw(rtlInfo)
|
||||
}
|
||||
return rtlEnabledState
|
||||
}
|
||||
)
|
||||
provide(configProviderInjectionKey, {
|
||||
mergedRtlRef,
|
||||
mergedIconsRef,
|
||||
mergedComponentPropsRef,
|
||||
mergedBorderedRef,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { VNodeChild, Ref } from 'vue'
|
||||
import { CNode } from 'css-render'
|
||||
import type { AlertTheme } from '../../alert/styles'
|
||||
import type { AnchorTheme } from '../../anchor/styles'
|
||||
import type { AutoCompleteTheme } from '../../auto-complete/styles'
|
||||
@ -200,6 +201,16 @@ export interface GlobalIconConfig {
|
||||
zoomOut?: () => VNodeChild
|
||||
}
|
||||
|
||||
export interface RtlItem {
|
||||
name: keyof GlobalThemeWithoutCommon
|
||||
style: CNode
|
||||
}
|
||||
export type RtlProp = RtlItem[]
|
||||
|
||||
export type RtlEnabledState = Partial<
|
||||
Record<keyof GlobalThemeWithoutCommon, RtlItem>
|
||||
>
|
||||
|
||||
export interface ConfigProviderInjection {
|
||||
mergedClsPrefixRef: Ref<string | undefined>
|
||||
mergedBorderedRef: Ref<boolean | undefined>
|
||||
@ -211,6 +222,7 @@ export interface ConfigProviderInjection {
|
||||
mergedIconsRef: Ref<GlobalIconConfig | undefined>
|
||||
mergedThemeRef: Ref<GlobalTheme | undefined>
|
||||
mergedThemeOverridesRef: Ref<GlobalThemeOverrides | undefined>
|
||||
mergedRtlRef: Ref<RtlEnabledState | undefined>
|
||||
// deprecated
|
||||
/** @deprecated */
|
||||
mergedLegacyThemeRef: Ref<string | undefined>
|
||||
|
18
src/date-picker/demos/enUS/footerslot.demo.md
Normal file
18
src/date-picker/demos/enUS/footerslot.demo.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Extra Footer
|
||||
|
||||
```html
|
||||
<n-space vertical>
|
||||
<n-date-picker type="date">
|
||||
<template #footer> extra footer </template>
|
||||
</n-date-picker>
|
||||
<n-date-picker type="datetime">
|
||||
<template #footer> extra footer </template>
|
||||
</n-date-picker>
|
||||
<n-date-picker type="daterange">
|
||||
<template #footer> extra footer </template>
|
||||
</n-date-picker>
|
||||
<n-date-picker type="datetimerange">
|
||||
<template #footer> extra footer </template>
|
||||
</n-date-picker>
|
||||
</n-space>
|
||||
```
|
@ -16,6 +16,7 @@ actions
|
||||
events
|
||||
format
|
||||
ranges
|
||||
footerslot
|
||||
```
|
||||
|
||||
## Props
|
||||
@ -81,3 +82,9 @@ ranges
|
||||
| separator | `string` | `'to'` | |
|
||||
| start-placeholder | `string` | `'Start Date and Time'` | |
|
||||
| on-update:value | `(value: [number, number] \| null) => void` | `undefined` | |
|
||||
|
||||
## Slots
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| ------ | ---- | ------------- |
|
||||
| footer | `()` | Extra Footer. |
|
||||
|
18
src/date-picker/demos/zhCN/footerslot.demo.md
Normal file
18
src/date-picker/demos/zhCN/footerslot.demo.md
Normal file
@ -0,0 +1,18 @@
|
||||
# 额外内容
|
||||
|
||||
```html
|
||||
<n-space vertical>
|
||||
<n-date-picker type="date">
|
||||
<template #footer> extra footer </template>
|
||||
</n-date-picker>
|
||||
<n-date-picker type="datetime">
|
||||
<template #footer> extra footer </template>
|
||||
</n-date-picker>
|
||||
<n-date-picker type="daterange">
|
||||
<template #footer> extra footer </template>
|
||||
</n-date-picker>
|
||||
<n-date-picker type="datetimerange">
|
||||
<template #footer> extra footer </template>
|
||||
</n-date-picker>
|
||||
</n-space>
|
||||
```
|
@ -16,6 +16,7 @@ actions
|
||||
events
|
||||
format
|
||||
ranges
|
||||
footerslot
|
||||
```
|
||||
|
||||
## Props
|
||||
@ -81,3 +82,9 @@ ranges
|
||||
| separator | `string` | `'to'` | |
|
||||
| start-placeholder | `string` | `'开始日期时间'` | |
|
||||
| on-update:value | `(value: [number, number] \| null) => void` | `undefined` | |
|
||||
|
||||
## Slots
|
||||
|
||||
| 名称 | 参数 | 说明 |
|
||||
| ------ | ---- | -------------- |
|
||||
| footer | `()` | 添加额外的页脚 |
|
||||
|
@ -135,7 +135,7 @@ export type DatePickerProps = ExtractPublicPropTypes<typeof datePickerProps>
|
||||
export default defineComponent({
|
||||
name: 'DatePicker',
|
||||
props: datePickerProps,
|
||||
setup (props) {
|
||||
setup (props, { slots }) {
|
||||
const { localeRef, dateLocaleRef } = useLocale('DatePicker')
|
||||
const formItem = useFormItem(props)
|
||||
const {
|
||||
@ -464,7 +464,8 @@ export default defineComponent({
|
||||
isDateDisabledRef: toRef(props, 'isDateDisabled'),
|
||||
rangesRef: toRef(props, 'ranges'),
|
||||
...uniVaidation,
|
||||
...dualValidation
|
||||
...dualValidation,
|
||||
datePickerSlots: slots
|
||||
})
|
||||
return {
|
||||
mergedClsPrefix: mergedClsPrefixRef,
|
||||
@ -541,6 +542,7 @@ export default defineComponent({
|
||||
panelBoxShadow,
|
||||
panelBorderRadius,
|
||||
calendarTitleFontWeight,
|
||||
panelExtraFooterPadding,
|
||||
panelActionPadding,
|
||||
itemSize,
|
||||
itemCellWidth,
|
||||
@ -589,6 +591,7 @@ export default defineComponent({
|
||||
|
||||
// panel action
|
||||
'--panel-action-padding': panelActionPadding,
|
||||
'--panel-extra-footer-padding': panelExtraFooterPadding,
|
||||
'--panel-action-divider-color': panelActionDividerColor,
|
||||
|
||||
// panel item
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { InjectionKey, Ref } from 'vue'
|
||||
import { InjectionKey, Ref, Slots } from 'vue'
|
||||
import { NLocale, NDateLocale } from '../../locales'
|
||||
import {
|
||||
IsHourDisabled,
|
||||
@ -45,6 +45,7 @@ export type DatePickerInjection = {
|
||||
dateLocaleRef: Ref<NDateLocale>
|
||||
isDateDisabledRef: Ref<IsDateDisabled | undefined>
|
||||
rangesRef: Ref<Record<string, [number, number]> | undefined>
|
||||
datePickerSlots: Slots
|
||||
} & ReturnType<typeof uniCalendarValidation> &
|
||||
ReturnType<typeof dualCalendarValidation>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { h, defineComponent } from 'vue'
|
||||
import { h, defineComponent, renderSlot } from 'vue'
|
||||
import { NButton } from '../../../button'
|
||||
import {
|
||||
BackwardIcon,
|
||||
@ -101,6 +101,11 @@ export default defineComponent({
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{this.datePickerSlots.footer ? (
|
||||
<div class={`${mergedClsPrefix}-date-panel-footer`}>
|
||||
{renderSlot(this.datePickerSlots, 'footer')}
|
||||
</div>
|
||||
) : null}
|
||||
{this.actions?.length ? (
|
||||
<div class={`${mergedClsPrefix}-date-panel-actions`}>
|
||||
<div class={`${mergedClsPrefix}-date-panel-actions__prefix`}></div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { defineComponent, h, renderSlot } from 'vue'
|
||||
import { NButton, NxButton } from '../../../button'
|
||||
import {
|
||||
BackwardIcon,
|
||||
@ -189,6 +189,11 @@ export default defineComponent({
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{this.datePickerSlots.footer ? (
|
||||
<div class={`${mergedClsPrefix}-date-panel-footer`}>
|
||||
{renderSlot(this.datePickerSlots, 'footer')}
|
||||
</div>
|
||||
) : null}
|
||||
{this.actions?.length || ranges ? (
|
||||
<div class={`${mergedClsPrefix}-date-panel-actions`}>
|
||||
<div class={`${mergedClsPrefix}-date-panel-actions__prefix`}>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { h, defineComponent } from 'vue'
|
||||
import { h, defineComponent, renderSlot } from 'vue'
|
||||
import { NButton } from '../../../button'
|
||||
import { NTimePicker } from '../../../time-picker'
|
||||
import { NInput } from '../../../input'
|
||||
@ -132,6 +132,11 @@ export default defineComponent({
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{this.datePickerSlots.footer ? (
|
||||
<div class={`${mergedClsPrefix}-date-panel-footer`}>
|
||||
{renderSlot(this.datePickerSlots, 'footer')}
|
||||
</div>
|
||||
) : null}
|
||||
{this.actions?.length ? (
|
||||
<div class={`${mergedClsPrefix}-date-panel-actions`}>
|
||||
<div class={`${mergedClsPrefix}-date-panel-actions__prefix`}></div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { defineComponent, h, renderSlot } from 'vue'
|
||||
import { NButton, NxButton } from '../../../button'
|
||||
import { NInput } from '../../../input'
|
||||
import { NTimePicker } from '../../../time-picker'
|
||||
@ -247,6 +247,11 @@ export default defineComponent({
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{this.datePickerSlots.footer ? (
|
||||
<div class={`${mergedClsPrefix}-date-panel-footer`}>
|
||||
{renderSlot(this.datePickerSlots, 'footer')}
|
||||
</div>
|
||||
) : null}
|
||||
{this.actions?.length ? (
|
||||
<div class={`${mergedClsPrefix}-date-panel-actions`}>
|
||||
<div class={`${mergedClsPrefix}-date-panel-actions__prefix`}>
|
||||
|
@ -41,7 +41,8 @@ function useCalendar (
|
||||
isHourDisabledRef,
|
||||
isMinuteDisabledRef,
|
||||
isSecondDisabledRef,
|
||||
localeRef
|
||||
localeRef,
|
||||
datePickerSlots
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
} = inject(datePickerInjectionKey)!
|
||||
const validation = {
|
||||
@ -268,7 +269,8 @@ function useCalendar (
|
||||
handleTimePickerChange,
|
||||
clearSelectedDateTime,
|
||||
timePickerSize: panelCommon.timePickerSize,
|
||||
dateInputValue: dateInputValueRef
|
||||
dateInputValue: dateInputValueRef,
|
||||
datePickerSlots
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,8 @@ function useDualCalendar (
|
||||
isEndValueInvalidRef,
|
||||
isRangeInvalidRef,
|
||||
localeRef,
|
||||
rangesRef
|
||||
rangesRef,
|
||||
datePickerSlots
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
} = inject(datePickerInjectionKey)!
|
||||
const validation = {
|
||||
@ -572,7 +573,8 @@ function useDualCalendar (
|
||||
handleStartDateInput,
|
||||
handleStartDateInputBlur,
|
||||
handleEndDateInput,
|
||||
handleEndDateInputBlur
|
||||
handleEndDateInputBlur,
|
||||
datePickerSlots
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,12 +96,14 @@ export default c([
|
||||
cM('date', {
|
||||
gridTemplateAreas: `
|
||||
"left-calendar"
|
||||
"footer"
|
||||
"action"
|
||||
`
|
||||
}),
|
||||
cM('daterange', {
|
||||
gridTemplateAreas: `
|
||||
"left-calendar divider right-calendar"
|
||||
"footer footer footer"
|
||||
"action action action"
|
||||
`
|
||||
}),
|
||||
@ -109,6 +111,7 @@ export default c([
|
||||
gridTemplateAreas: `
|
||||
"header"
|
||||
"left-calendar"
|
||||
"footer"
|
||||
"action"
|
||||
`
|
||||
}),
|
||||
@ -116,15 +119,19 @@ export default c([
|
||||
gridTemplateAreas: `
|
||||
"header header header"
|
||||
"left-calendar divider right-calendar"
|
||||
"footer footer footer"
|
||||
"action action action"
|
||||
`
|
||||
}),
|
||||
cB('date-panel-header', {
|
||||
gridArea: 'header'
|
||||
cB('date-panel-footer', {
|
||||
gridArea: 'footer'
|
||||
}),
|
||||
cB('date-panel-actions', {
|
||||
gridArea: 'action'
|
||||
}),
|
||||
cB('date-panel-header', {
|
||||
gridArea: 'header'
|
||||
}),
|
||||
cB('date-panel-header', `
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
@ -329,6 +336,10 @@ export default c([
|
||||
width: 1px;
|
||||
background-color: var(--calendar-divider-color);
|
||||
`),
|
||||
cB('date-panel-footer', {
|
||||
borderTop: '1px solid var(--panel-action-divider-color)',
|
||||
padding: 'var(--panel-extra-footer-padding)'
|
||||
}),
|
||||
cB('date-panel-actions', `
|
||||
flex: 1;
|
||||
padding: var(--panel-action-padding);
|
||||
|
@ -2,6 +2,7 @@ export default {
|
||||
itemSize: '24px',
|
||||
itemCellWidth: '38px',
|
||||
itemCellHeight: '32px',
|
||||
panelExtraFooterPadding: '8px 12px',
|
||||
panelActionPadding: '8px 12px',
|
||||
calendarTitlePadding: '0',
|
||||
calendarTitleHeight: '28px',
|
||||
|
@ -19,7 +19,7 @@ export default defineComponent({
|
||||
const { clsPrefix } = this
|
||||
return (
|
||||
<div class={`${clsPrefix}-log-loader`}>
|
||||
<NBaseLoading clsPrefix={clsPrefix} strokeWidth={24} />
|
||||
<NBaseLoading clsPrefix={clsPrefix} strokeWidth={24} scale={0.85} />
|
||||
<span class={`${clsPrefix}-log-loader__content`}>
|
||||
{this.locale.loading}
|
||||
</span>
|
||||
|
@ -88,11 +88,11 @@ const menuProps = {
|
||||
},
|
||||
disabled: Boolean,
|
||||
inverted: Boolean,
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
'onUpdate:expandedKeys': [Function, Array] as PropType<
|
||||
MaybeArray<OnUpdateKeys>
|
||||
>,
|
||||
// eslint-disable-next-line vue/prop-name-casing
|
||||
onUpdateExpandedKeys: [Function, Array] as PropType<MaybeArray<OnUpdateKeys>>,
|
||||
onUpdateValue: [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
|
||||
'onUpdate:value': [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
|
||||
// deprecated
|
||||
onOpenNamesChange: {
|
||||
@ -233,10 +233,17 @@ export default defineComponent({
|
||||
toggleExpand
|
||||
})
|
||||
function doSelect (value: Key, item: MenuOption): void {
|
||||
const { 'onUpdate:value': onUpdateValue, onSelect } = props
|
||||
const {
|
||||
'onUpdate:value': _onUpdateValue,
|
||||
onUpdateValue,
|
||||
onSelect
|
||||
} = props
|
||||
if (onUpdateValue) {
|
||||
call(onUpdateValue as OnUpdateValueImpl, value, item)
|
||||
}
|
||||
if (_onUpdateValue) {
|
||||
call(_onUpdateValue as OnUpdateValueImpl, value, item)
|
||||
}
|
||||
if (onSelect) {
|
||||
call(onSelect as OnUpdateValueImpl, value, item)
|
||||
}
|
||||
@ -244,10 +251,14 @@ export default defineComponent({
|
||||
}
|
||||
function doUpdateExpandedKeys (value: Key[]): void {
|
||||
const {
|
||||
'onUpdate:expandedKeys': onUpdateExpandedKeys,
|
||||
'onUpdate:expandedKeys': _onUpdateExpandedKeys,
|
||||
onUpdateExpandedKeys,
|
||||
onExpandedNamesChange,
|
||||
onOpenNamesChange
|
||||
} = props
|
||||
if (_onUpdateExpandedKeys) {
|
||||
call(_onUpdateExpandedKeys as OnUpdateKeysImpl, value)
|
||||
}
|
||||
if (onUpdateExpandedKeys) {
|
||||
call(onUpdateExpandedKeys as OnUpdateKeysImpl, value)
|
||||
}
|
||||
|
@ -35,22 +35,19 @@ export type MenuGroupOption =
|
||||
|
||||
export type TmNode = TreeNode<MenuOption, MenuGroupOption>
|
||||
|
||||
export type OnUpdateValue = <T extends string & number & (string | number)>(
|
||||
value: T,
|
||||
export type OnUpdateValue = (
|
||||
value: string & number & (string | number),
|
||||
item: MenuOption
|
||||
) => void
|
||||
export type OnUpdateKeys = <
|
||||
T extends string[] & number[] & Array<string | number>
|
||||
>(
|
||||
keys: T
|
||||
) => void
|
||||
|
||||
export type OnUpdateValueImpl = <T extends string | number | (string | number)>(
|
||||
value: T,
|
||||
export type OnUpdateKeys = (
|
||||
keys: string[] & number[] & Array<string | number>
|
||||
) => void
|
||||
|
||||
export type OnUpdateValueImpl = (
|
||||
value: string | number | (string | number),
|
||||
item: MenuOption
|
||||
) => void
|
||||
export type OnUpdateKeysImpl = <
|
||||
T extends string[] | number[] | Array<string | number>
|
||||
>(
|
||||
keys: T
|
||||
export type OnUpdateKeysImpl = (
|
||||
keys: string[] | number[] | Array<string | number>
|
||||
) => void
|
||||
|
@ -5,4 +5,30 @@ 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 => {}
|
||||
const snCb = (v: string | number): void => {}
|
||||
const stringArrCb = (v: string[]): void => {}
|
||||
const numberArrCb = (v: number[]): void => {}
|
||||
const snArrCb = (v: Array<string | number>): void => {}
|
||||
mount(NMenu, {
|
||||
props: {
|
||||
onUpdateValue: stringCb,
|
||||
onUpdateExpandedKeys: stringArrCb
|
||||
}
|
||||
})
|
||||
mount(NMenu, {
|
||||
props: {
|
||||
onUpdateValue: numberCb,
|
||||
onUpdateExpandedKeys: numberArrCb
|
||||
}
|
||||
})
|
||||
mount(NMenu, {
|
||||
props: {
|
||||
onUpdateValue: snCb,
|
||||
onUpdateExpandedKeys: snArrCb
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -165,7 +165,11 @@ function createIconVNode (
|
||||
{{
|
||||
default: () =>
|
||||
type === 'loading' ? (
|
||||
<NBaseLoading clsPrefix={clsPrefix} scale={0.85} />
|
||||
<NBaseLoading
|
||||
clsPrefix={clsPrefix}
|
||||
strokeWidth={24}
|
||||
scale={0.85}
|
||||
/>
|
||||
) : (
|
||||
iconMap[type]
|
||||
)
|
||||
|
@ -72,32 +72,32 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { mergedClsPrefix } = this
|
||||
const { mergedClsPrefix, $slots } = this
|
||||
return (
|
||||
<div style={this.cssVars as CSSProperties}>
|
||||
<div class={`${mergedClsPrefix}-popconfirm__body`}>
|
||||
{this.showIcon ? (
|
||||
<div class={`${mergedClsPrefix}-popconfirm__icon`}>
|
||||
<slot name="icon">
|
||||
{renderSlot($slots, 'icon', undefined, () => [
|
||||
<NBaseIcon clsPrefix={mergedClsPrefix}>
|
||||
{{ default: () => <WarningIcon /> }}
|
||||
</NBaseIcon>
|
||||
</slot>
|
||||
])}
|
||||
</div>
|
||||
) : null}
|
||||
{renderSlot(this.$slots, 'default')}
|
||||
{renderSlot($slots, 'default')}
|
||||
</div>
|
||||
<div class={`${mergedClsPrefix}-popconfirm__action`}>
|
||||
{renderSlot(this.$slots, 'action', undefined, () => [
|
||||
{renderSlot($slots, 'action', undefined, () => [
|
||||
<NButton size="small" onClick={this.handleNegativeClick}>
|
||||
{this.localizedNegativeText}
|
||||
{{ default: () => this.localizedNegativeText }}
|
||||
</NButton>,
|
||||
<NButton
|
||||
size="small"
|
||||
type="primary"
|
||||
onClick={this.handlePositiveClick}
|
||||
>
|
||||
{this.localizedPositiveText}
|
||||
{{ default: () => this.localizedPositiveText }}
|
||||
</NButton>
|
||||
])}
|
||||
</div>
|
||||
|
@ -28,7 +28,6 @@ processing
|
||||
| indicator-text-color | `string` | `undefined` | |
|
||||
| percentage | `number \| Array<number>` | `0` | |
|
||||
| processing | `boolean` | `false` | |
|
||||
| processing | `boolean` | `false` | |
|
||||
| rail-color | `string \| string[]` | `undefined` | |
|
||||
| rail-style | `string \| CSS \| Array<string \| CSS>` | `undefined` | |
|
||||
| show-indicator | `boolean` | `true` | |
|
||||
|
@ -28,7 +28,6 @@ processing
|
||||
| indicator-text-color | `string` | `undefined` | |
|
||||
| percentage | `number \| Array<number>` | `0` | |
|
||||
| processing | `boolean` | `false` | |
|
||||
| processing | `boolean` | `false` | |
|
||||
| rail-color | `string \| string[]` | `undefined` | |
|
||||
| rail-style | `string \| CSS \| Array<string \| CSS>` | `undefined` | |
|
||||
| show-indicator | `boolean` | `true` | |
|
||||
|
@ -27,6 +27,7 @@ import type { MaybeArray, ExtractPublicPropTypes } from '../../_utils'
|
||||
import { sliderLight, SliderTheme } from '../styles'
|
||||
import style from './styles/index.cssr'
|
||||
import { OnUpdateValueImpl } from './interface'
|
||||
import { isTouchEvent } from './utils'
|
||||
|
||||
const sliderProps = {
|
||||
...(useTheme.props as ThemeProps<SliderTheme>),
|
||||
@ -295,14 +296,18 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleHandleMouseMove (e: MouseEvent, handleIndex: 0 | 1): void {
|
||||
function handleHandleMouseMove (
|
||||
e: MouseEvent | TouchEvent,
|
||||
handleIndex: 0 | 1
|
||||
): void {
|
||||
if (!handleRef1.value || !railRef.value) return
|
||||
const x = 'touches' in e ? e.touches[0].clientX : e.clientX
|
||||
const { width: handleWidth } = handleRef1.value.getBoundingClientRect()
|
||||
const { width: railWidth, left: railLeft } =
|
||||
railRef.value.getBoundingClientRect()
|
||||
const { min, max, range } = props
|
||||
const offsetRatio =
|
||||
(e.clientX - railLeft - handleWidth / 2) / (railWidth - handleWidth)
|
||||
(x - railLeft - handleWidth / 2) / (railWidth - handleWidth)
|
||||
const newValue = min + (max - min) * offsetRatio
|
||||
if (range) {
|
||||
if (handleIndex === 0) {
|
||||
@ -462,34 +467,44 @@ export default defineComponent({
|
||||
}
|
||||
return justifiedValue
|
||||
}
|
||||
function handleFirstHandleMouseDown (): void {
|
||||
function handleFirstHandleMouseDown (e: MouseEvent | TouchEvent): void {
|
||||
if (isTouchEvent(e)) e.preventDefault()
|
||||
if (props.range) {
|
||||
memoziedOtherValueRef.value = handleValue2Ref.value
|
||||
}
|
||||
doUpdateShow(true, false)
|
||||
handleClicked1Ref.value = true
|
||||
on('touchend', document, handleHandleMouseUp)
|
||||
on('mouseup', document, handleHandleMouseUp)
|
||||
on('touchmove', document, handleFirstHandleMouseMove)
|
||||
on('mousemove', document, handleFirstHandleMouseMove)
|
||||
}
|
||||
function handleSecondHandleMouseDown (): void {
|
||||
function handleSecondHandleMouseDown (e: MouseEvent | TouchEvent): void {
|
||||
if (isTouchEvent(e)) e.preventDefault()
|
||||
if (props.range) {
|
||||
memoziedOtherValueRef.value = handleValue1Ref.value
|
||||
}
|
||||
doUpdateShow(false, true)
|
||||
handleClicked2Ref.value = true
|
||||
on('touchend', document, handleHandleMouseUp)
|
||||
on('mouseup', document, handleHandleMouseUp)
|
||||
on('touchmove', document, handleSecondHandleMouseMove)
|
||||
on('mousemove', document, handleSecondHandleMouseMove)
|
||||
}
|
||||
function handleHandleMouseUp (e: MouseEvent): void {
|
||||
function handleHandleMouseUp (e: MouseEvent | TouchEvent): void {
|
||||
if (
|
||||
!handleRef1.value?.contains(e.target as Node) &&
|
||||
(props.range ? !handleRef2.value?.contains(e.target as Node) : true)
|
||||
isTouchEvent(e) ||
|
||||
(!handleRef1.value?.contains(e.target as Node) &&
|
||||
(props.range ? !handleRef2.value?.contains(e.target as Node) : true))
|
||||
) {
|
||||
doUpdateShow(false, false)
|
||||
}
|
||||
handleClicked2Ref.value = false
|
||||
handleClicked1Ref.value = false
|
||||
off('touchend', document, handleHandleMouseUp)
|
||||
off('mouseup', document, handleHandleMouseUp)
|
||||
off('touchmove', document, handleFirstHandleMouseMove)
|
||||
off('touchmove', document, handleSecondHandleMouseMove)
|
||||
off('mousemove', document, handleFirstHandleMouseMove)
|
||||
off('mousemove', document, handleSecondHandleMouseMove)
|
||||
}
|
||||
@ -540,10 +555,10 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleFirstHandleMouseMove (e: MouseEvent): void {
|
||||
function handleFirstHandleMouseMove (e: MouseEvent | TouchEvent): void {
|
||||
handleHandleMouseMove(e, 0)
|
||||
}
|
||||
function handleSecondHandleMouseMove (e: MouseEvent): void {
|
||||
function handleSecondHandleMouseMove (e: MouseEvent | TouchEvent): void {
|
||||
handleHandleMouseMove(e, 1)
|
||||
}
|
||||
function handleFirstHandleMouseEnter (): void {
|
||||
@ -652,8 +667,11 @@ export default defineComponent({
|
||||
})
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
off('touchmove', document, handleFirstHandleMouseMove)
|
||||
off('touchmove', document, handleSecondHandleMouseMove)
|
||||
off('mousemove', document, handleFirstHandleMouseMove)
|
||||
off('mousemove', document, handleSecondHandleMouseMove)
|
||||
off('touchend', document, handleHandleMouseUp)
|
||||
off('mouseup', document, handleHandleMouseUp)
|
||||
})
|
||||
return {
|
||||
@ -829,6 +847,7 @@ export default defineComponent({
|
||||
style={this.firstHandleStyle}
|
||||
onFocus={this.handleHandleFocus1}
|
||||
onBlur={this.handleHandleBlur1}
|
||||
onTouchstart={this.handleFirstHandleMouseDown}
|
||||
onMousedown={this.handleFirstHandleMouseDown}
|
||||
onMouseenter={this.handleFirstHandleMouseEnter}
|
||||
onMouseleave={this.handleFirstHandleMouseLeave}
|
||||
@ -883,6 +902,7 @@ export default defineComponent({
|
||||
style={this.secondHandleStyle}
|
||||
onFocus={this.handleHandleFocus2}
|
||||
onBlur={this.handleHandleBlur2}
|
||||
onTouchstart={this.handleSecondHandleMouseDown}
|
||||
onMousedown={this.handleSecondHandleMouseDown}
|
||||
onMouseenter={this.handleSecondHandleMouseEnter}
|
||||
onMouseleave={this.handleSecondHandleMouseLeave}
|
||||
|
3
src/slider/src/utils.ts
Normal file
3
src/slider/src/utils.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function isTouchEvent (e: MouseEvent | TouchEvent): boolean {
|
||||
return window.TouchEvent && e instanceof window.TouchEvent
|
||||
}
|
@ -13,10 +13,10 @@ wrap
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | |
|
||||
| show | `boolean` | `true` | If spin is active |
|
||||
| stroke-width | `number` | `undefined` | Relative width of spin's stroke, you can assume the outer radius of spin is 100 |
|
||||
| stroke | `string` | `undefined` | Color of spin |
|
||||
| size | `'small' \| 'medium' \| 'large' \| number` | `'medium'` | |
|
||||
| show | `boolean` | `true` | If spin is active. |
|
||||
| stroke-width | `number` | `undefined` | Relative width of spin's stroke, you can assume the outer radius of spin is 100. |
|
||||
| stroke | `string` | `undefined` | Color of the spin. |
|
||||
|
||||
## Slots
|
||||
|
||||
|
@ -13,7 +13,7 @@ wrap
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | |
|
||||
| size | `'small' \| 'medium' \| 'large' \| number` | `'medium'` | |
|
||||
| show | `boolean` | `true` | Spin 在填入内容的状态是否激活 |
|
||||
| stroke-width | `number` | `undefined` | Spin 边缘的相对宽度,假定 Spin 的外侧半径是 100 |
|
||||
| stroke | `string` | `undefined` | Spin 的颜色 |
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
CSSProperties
|
||||
} from 'vue'
|
||||
import { useCompitable } from 'vooks'
|
||||
import { pxfy } from 'seemly'
|
||||
import { NBaseLoading } from '../../_internal'
|
||||
import { useConfig, useTheme } from '../../_mixins'
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
@ -16,9 +17,9 @@ import type { SpinTheme } from '../styles'
|
||||
import style from './styles/index.cssr'
|
||||
|
||||
const STROKE_WIDTH = {
|
||||
small: 22,
|
||||
medium: 20,
|
||||
large: 18
|
||||
small: 20,
|
||||
medium: 18,
|
||||
large: 16
|
||||
}
|
||||
|
||||
const spinProps = {
|
||||
@ -28,7 +29,7 @@ const spinProps = {
|
||||
default: undefined
|
||||
},
|
||||
size: {
|
||||
type: [String, Number] as PropType<'small' | 'medium' | 'large'>,
|
||||
type: [String, Number] as PropType<'small' | 'medium' | 'large' | number>,
|
||||
default: 'medium'
|
||||
},
|
||||
show: {
|
||||
@ -71,14 +72,19 @@ export default defineComponent({
|
||||
const { strokeWidth } = props
|
||||
if (strokeWidth !== undefined) return strokeWidth
|
||||
const { size } = props
|
||||
return STROKE_WIDTH[size]
|
||||
return STROKE_WIDTH[typeof size === 'number' ? 'medium' : size]
|
||||
}),
|
||||
cssVars: computed(() => {
|
||||
const { size: spinSize } = props
|
||||
const {
|
||||
common: { cubicBezierEaseInOut },
|
||||
self: { opacitySpinning, color, [createKey('size', spinSize)]: size }
|
||||
self
|
||||
} = themeRef.value
|
||||
const { opacitySpinning, color } = self
|
||||
const size =
|
||||
typeof spinSize === 'number'
|
||||
? pxfy(spinSize)
|
||||
: self[createKey('size', spinSize)]
|
||||
return {
|
||||
'--bezier': cubicBezierEaseInOut,
|
||||
'--opacity-spinning': opacitySpinning,
|
||||
|
@ -6,8 +6,8 @@ export { avatarDark } from './avatar/styles'
|
||||
export { backTopDark } from './back-top/styles'
|
||||
export { badgeDark } from './badge/styles'
|
||||
export { breadcrumbDark } from './breadcrumb/styles'
|
||||
export { buttonDark } from './button/styles'
|
||||
export { cardDark } from './card/styles'
|
||||
export { buttonDark, buttonRtl as unstableButtonRtl } from './button/styles'
|
||||
export { cardDark, cardRtl as unstableCardRtl } from './card/styles'
|
||||
export { cascaderDark } from './cascader/styles'
|
||||
export { checkboxDark } from './checkbox/styles'
|
||||
export { codeDark } from './code/styles'
|
||||
|
@ -116,8 +116,9 @@ export default defineComponent({
|
||||
const barElRef = ref<HTMLElement | null>(null)
|
||||
const scrollWrapperElRef = ref<HTMLElement | null>(null)
|
||||
const addTabInstRef = ref<ComponentPublicInstance | null>(null)
|
||||
const xScrollInstRef =
|
||||
ref<(VXScrollInst & ComponentPublicInstance) | null>(null)
|
||||
const xScrollInstRef = ref<(VXScrollInst & ComponentPublicInstance) | null>(
|
||||
null
|
||||
)
|
||||
|
||||
const leftReachedRef = ref(true)
|
||||
const rightReachedRef = ref(true)
|
||||
@ -511,7 +512,6 @@ function filterMapTabPanes (
|
||||
if (vNode.key !== undefined) {
|
||||
vNode.key = name
|
||||
}
|
||||
console.log(vNode.props)
|
||||
if (useVShow) {
|
||||
children.push(withDirectives(vNode, [[vShow, show]]))
|
||||
} else if (show) {
|
||||
|
@ -1 +1 @@
|
||||
export default '2.11.4'
|
||||
export default '2.11.7'
|
||||
|
Loading…
Reference in New Issue
Block a user