mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-04-12 14:40:47 +08:00
Merge branch 'main' into docs
This commit is contained in:
commit
da4a6ec969
@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
extends: ['plugin:markdown/recommended', 'prettier'],
|
||||
extends: ['plugin:markdown/recommended-legacy', 'prettier'],
|
||||
overrides: [
|
||||
{
|
||||
files: '*.mjs',
|
||||
@ -39,7 +39,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
extends: ['standard-with-typescript', 'plugin:import/typescript'],
|
||||
extends: ['love', 'plugin:import/typescript'],
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
ecmaFeatures: {
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,6 +12,7 @@ yarn.lock
|
||||
.DS_Store
|
||||
.vscode
|
||||
.idea
|
||||
.pulsar
|
||||
*.swp
|
||||
*.tgz
|
||||
coverage
|
||||
|
@ -1,5 +1,50 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.38.2
|
||||
|
||||
`2024-05-03`
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fix `n-menu` Submenu's wai-aria role is not correct, closes [#5729](https://github.com/tusen-ai/naive-ui/issues/5729).
|
||||
- Fix `n-tabs` style bug with type is `segment`,closes [#5728](https://github.com/tusen-ai/naive-ui/issues/5728).
|
||||
- Fix the get\*String() methods for UTC/locale mismatch, closes [#5702](closes https://github.com/tusen-ai/naive-ui/issues/5702).
|
||||
- Fix `n-dialog` / `n-modal` calling `destroy` method may throw error.
|
||||
- Fix `useModal` setting `card` preset without corresponding props in `n-card` slots, closes [#5746](https://github.com/tusen-ai/naive-ui/issues/5746).
|
||||
- Fix `Submenu` component's wai-aria role setting error of `n-menu`,closes [#5729](https://github.com/tusen-ai/naive-ui/issues/5729).
|
||||
- Fix the `common` type error in the `theme-overrides` prop when modifying components' themes.
|
||||
- Fix `n-split` may emit value less than `0`.
|
||||
|
||||
### Features
|
||||
|
||||
- `n-watermark` support multi-lines in content.
|
||||
- Adds `n-infinite-scroll` component.
|
||||
- `n-watermark` adds `text-align` prop.
|
||||
- `n-qr-code` adds `type` prop, Customize rendering output by setting `type`, providing two options: `canvas` and `svg`.
|
||||
- `n-card` adds `action`, `content`, `cover`, `footer` and `header-extra` props.
|
||||
- `n-card`'s `title` prop supports render function.
|
||||
- `n-upload` expose the `index` arg in `on-remove` function, closes [#5747](https://github.com/tusen-ai/naive-ui/issues/5747).
|
||||
- `n-upload` exports `UploadOnDownload`, `UploadOnRemove`, `UploadOnFinish` and `UploadOnChange` types.
|
||||
- `n-dialog` adds `action-class`, `action-style`, `content-class`, `content-style`, `title-class` and `title-style` props.
|
||||
- `n-split` adds `pane1-class`, `pane1-style`, `pane2-class` and `pane2-style` props.
|
||||
- `n-mention` adds `filter` method, closes [#5721](https://github.com/tusen-ai/naive-ui/pull/5721).
|
||||
- `n-slider` adds wai-aria support.
|
||||
- `n-date-picker` adds `time-picker-format` prop.
|
||||
- `n-form-item` adds `feedback-class` and `feedback-style` props.
|
||||
- `n-split` supports using pixel unit string as `value`.
|
||||
- `n-scrollbar` adds `content-style` and `content-class` props, closes [#4497](https://github.com/tusen-ai/naive-ui/issues/4497).
|
||||
- `n-image` adds `render-toolbar` prop.
|
||||
- `n-cascader` adds `get-column-style` prop.
|
||||
- `n-cascader` adds `get-render-prefix` prop.
|
||||
- `n-cascader` adds `get-render-suffix` prop.
|
||||
- `n-image` optimizes download icon style.
|
||||
- `n-scrollbar` adds `height`, `width`, `radius`, `railInsetHorizontal`, `railInsetVertical` and `railColor` theme variables.
|
||||
|
||||
### i18n
|
||||
|
||||
- Add csCZ locale.
|
||||
- Add missing itIT locale translations
|
||||
|
||||
## 2.38.1
|
||||
|
||||
`2024-02-26`
|
||||
|
@ -1,5 +1,49 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.38.2
|
||||
|
||||
`2024-05-03`
|
||||
|
||||
### Fixes
|
||||
|
||||
- 修复 `n-menu` 中 Submenu 组件的 wai-aria role 设置错误,关闭 [#5729](https://github.com/tusen-ai/naive-ui/issues/5729)
|
||||
- 修复 `n-tabs` type 为 `segment` 时样式存在问题,关闭 [#5728](https://github.com/tusen-ai/naive-ui/issues/5728)
|
||||
- 修复 get\*String() 方法中 UTC/区域设置不匹配的问题,关闭 [#5702](https://github.com/tusen-ai/naive-ui/issues/5702)
|
||||
- 修复 `n-dialog` / `n-modal` 调用 `destroy` 方法时可能会报错
|
||||
- 修复 `useModal` 设置 `card` 预设时 `n-card` 插槽缺少相应属性,关闭 [#5746](https://github.com/tusen-ai/naive-ui/issues/5746)
|
||||
- 修复组件调整主题时 `theme-overrides` 属性中的 `common` 类型报错
|
||||
- 修复 `n-split` 可能产生小与 `0` 的值
|
||||
|
||||
### Features
|
||||
|
||||
- `n-watermark` 支持多行文本
|
||||
- 新增 `n-infinite-scroll` 组件
|
||||
- `n-watermark` 新增 `text-align` 属性
|
||||
- `n-qr-code` 新增 `type` 属性,设置 `type` 自定义渲染结果,提供 `canvas` 和 `svg` 两个选项
|
||||
- `n-card` 新增 `action`、`content`、`cover`、`footer`、`header-extra` 属性
|
||||
- `n-card` 的 `title` 属性支持渲染函数
|
||||
- `n-upload` 导出 `on-remove` 方法的 `index` 属性,关闭 [#5747](https://github.com/tusen-ai/naive-ui/issues/5747)
|
||||
- `n-upload` 导出 `UploadOnDownload`、`UploadOnRemove`、`UploadOnFinish` 和 `UploadOnChange` 类型
|
||||
- `n-dialog` 新增 `action-class`、`action-style`、`content-class`、`content-style`、`title-class` 和 `title-style` 属性
|
||||
- `n-split` 新增 `pane1-class`、`pane1-style`、`pane2-class` 和 `pane2-style` 属性
|
||||
- `n-mention` 新增 `filter` 方法,关闭 [#5721](https://github.com/tusen-ai/naive-ui/pull/5721)
|
||||
- `n-slider` 新增 wai-aria 支持
|
||||
- `n-date-picker` 新增 `time-picker-format` 属性
|
||||
- `n-form-item` 新增 `feedback-class` 和 `feedback-style` 属性
|
||||
- `n-split` 支持设置像素值大小
|
||||
- `n-scrollbar` 新增 `content-style` 和 `content-class` 属性,关闭 [#4497](https://github.com/tusen-ai/naive-ui/issues/4497)
|
||||
- `n-image` 新增 `render-toolbar` 属性
|
||||
- `n-cascader` 新增 `get-column-width` 属性
|
||||
- `n-cascader` 新增 `render-prefix` 属性
|
||||
- `n-cascader` 新增 `render-suffix` 属性
|
||||
- `n-image` 优化下载按钮图标
|
||||
- `n-scrollbar` 新增 `height`、`width`、`radius`、`railInsetHorizontal`、`railInsetVertical`、`railColor` 主题变量
|
||||
|
||||
### i18n
|
||||
|
||||
- 新增 csCZ locale
|
||||
- 增加缺少的 itIT locale
|
||||
|
||||
## 2.38.1
|
||||
|
||||
`2024-02-26`
|
||||
|
@ -552,6 +552,11 @@ export const enComponentRoutes = [
|
||||
path: 'split',
|
||||
component: () => import('../../src/split/demos/enUS/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'infinite-scroll',
|
||||
component: () =>
|
||||
import('../../src/infinite-scroll/demos/enUS/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'float-button',
|
||||
component: () =>
|
||||
@ -951,6 +956,11 @@ export const zhComponentRoutes = [
|
||||
path: 'split',
|
||||
component: () => import('../../src/split/demos/zhCN/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'infinite-scroll',
|
||||
component: () =>
|
||||
import('../../src/infinite-scroll/demos/zhCN/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'float-button',
|
||||
component: () =>
|
||||
|
@ -539,6 +539,13 @@ export function createComponentMenuOptions ({ lang, theme, mode }) {
|
||||
zh: '树',
|
||||
enSuffix: true,
|
||||
path: '/tree'
|
||||
},
|
||||
{
|
||||
en: 'Infinite Scroll',
|
||||
zh: '无限滚动',
|
||||
enSuffix: true,
|
||||
path: '/infinite-scroll',
|
||||
isNew: true
|
||||
}
|
||||
]
|
||||
}),
|
||||
|
16
package.json
16
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "naive-ui",
|
||||
"version": "2.38.1",
|
||||
"version": "2.38.2",
|
||||
"description": "A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.mjs",
|
||||
@ -88,15 +88,15 @@
|
||||
"@rollup/plugin-terser": "^0.4.3",
|
||||
"@types/estree": "^1.0.1",
|
||||
"@types/jest": "^29.5.4",
|
||||
"@typescript-eslint/eslint-plugin": "^6.6.0",
|
||||
"@typescript-eslint/parser": "^7.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^7.5.0",
|
||||
"@typescript-eslint/parser": "^7.5.0",
|
||||
"@vicons/fluent": "^0.12.0",
|
||||
"@vicons/ionicons4": "^0.12.0",
|
||||
"@vicons/ionicons5": "^0.12.0",
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"@vue/compiler-sfc": "^3.4.15",
|
||||
"@vue/eslint-config-standard": "^8.0.1",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"@vue/eslint-config-typescript": "^13.0.0",
|
||||
"@vue/server-renderer": "~3.4.15",
|
||||
"@vue/test-utils": "^2.4.1",
|
||||
"autoprefixer": "^10.4.15",
|
||||
@ -106,11 +106,11 @@
|
||||
"deepmerge": "^4.3.1",
|
||||
"esbuild": "0.20.1",
|
||||
"eslint": "^8.48.0",
|
||||
"eslint-config-love": "^44.0.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-config-standard-with-typescript": "^43.0.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-markdown": "^3.0.1",
|
||||
"eslint-plugin-markdown": "^4.0.1",
|
||||
"eslint-plugin-n": "^16.0.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
@ -133,12 +133,12 @@
|
||||
"rollup-plugin-esbuild": "^6.1.0",
|
||||
"superagent": "^8.1.2",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "5.3.3",
|
||||
"typescript": "5.4.2",
|
||||
"vfonts": "^0.0.3",
|
||||
"vite": "^5.0.4",
|
||||
"vue": "~3.4.15",
|
||||
"vue-router": "^4.2.4",
|
||||
"vue-tsc": "^1.8.27"
|
||||
"vue-tsc": "^2.0.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
|
@ -14,14 +14,14 @@ import type { PropType, CSSProperties, VNode, HTMLAttributes } from 'vue'
|
||||
import { on, off } from 'evtd'
|
||||
import { VResizeObserver } from 'vueuc'
|
||||
import { useIsIos } from 'vooks'
|
||||
import { getPreciseEventTarget } from 'seemly'
|
||||
import { depx, getPreciseEventTarget } from 'seemly'
|
||||
import { useConfig, useTheme, useThemeClass, useRtl } from '../../../_mixins'
|
||||
import type { ThemeProps } from '../../../_mixins'
|
||||
import type {
|
||||
ExtractInternalPropTypes,
|
||||
ExtractPublicPropTypes
|
||||
} from '../../../_utils'
|
||||
import { useReactivated, Wrapper } from '../../../_utils'
|
||||
import { rtlInset, useReactivated, Wrapper } from '../../../_utils'
|
||||
import { scrollbarLight } from '../styles'
|
||||
import type { ScrollbarTheme } from '../styles'
|
||||
import style from './styles/index.cssr'
|
||||
@ -75,10 +75,6 @@ export interface ScrollbarInst extends ScrollbarInstMethods {
|
||||
|
||||
const scrollbarProps = {
|
||||
...(useTheme.props as ThemeProps<ScrollbarTheme>),
|
||||
size: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 0
|
||||
@ -155,6 +151,15 @@ const Scrollbar = defineComponent({
|
||||
let memoMouseY: number = 0
|
||||
const isIos = useIsIos()
|
||||
|
||||
const themeRef = useTheme(
|
||||
'Scrollbar',
|
||||
'-scrollbar',
|
||||
style,
|
||||
scrollbarLight,
|
||||
props,
|
||||
mergedClsPrefixRef
|
||||
)
|
||||
|
||||
const yBarSizeRef = computed(() => {
|
||||
const { value: containerHeight } = containerHeightRef
|
||||
const { value: contentHeight } = contentHeightRef
|
||||
@ -168,7 +173,8 @@ const Scrollbar = defineComponent({
|
||||
} else {
|
||||
return Math.min(
|
||||
containerHeight,
|
||||
(yRailSize * containerHeight) / contentHeight + props.size * 1.5
|
||||
(yRailSize * containerHeight) / contentHeight +
|
||||
depx(themeRef.value.self.width) * 1.5
|
||||
)
|
||||
}
|
||||
})
|
||||
@ -186,7 +192,10 @@ const Scrollbar = defineComponent({
|
||||
) {
|
||||
return 0
|
||||
} else {
|
||||
return (xRailSize * containerWidth) / contentWidth + props.size * 1.5
|
||||
return (
|
||||
(xRailSize * containerWidth) / contentWidth +
|
||||
depx(themeRef.value.self.height) * 1.5
|
||||
)
|
||||
}
|
||||
})
|
||||
const xBarSizePxRef = computed(() => {
|
||||
@ -636,31 +645,32 @@ const Scrollbar = defineComponent({
|
||||
off('mousemove', window, handleYScrollMouseMove, true)
|
||||
off('mouseup', window, handleYScrollMouseUp, true)
|
||||
})
|
||||
const themeRef = useTheme(
|
||||
'Scrollbar',
|
||||
'-scrollbar',
|
||||
style,
|
||||
scrollbarLight,
|
||||
props,
|
||||
mergedClsPrefixRef
|
||||
)
|
||||
const cssVarsRef = computed(() => {
|
||||
const {
|
||||
common: {
|
||||
cubicBezierEaseInOut,
|
||||
scrollbarBorderRadius,
|
||||
scrollbarHeight,
|
||||
scrollbarWidth
|
||||
},
|
||||
self: { color, colorHover }
|
||||
common: { cubicBezierEaseInOut },
|
||||
self: {
|
||||
color,
|
||||
colorHover,
|
||||
height,
|
||||
width,
|
||||
borderRadius,
|
||||
railInsetHorizontal,
|
||||
railInsetVertical,
|
||||
railColor
|
||||
}
|
||||
} = themeRef.value
|
||||
return {
|
||||
'--n-scrollbar-bezier': cubicBezierEaseInOut,
|
||||
'--n-scrollbar-color': color,
|
||||
'--n-scrollbar-color-hover': colorHover,
|
||||
'--n-scrollbar-border-radius': scrollbarBorderRadius,
|
||||
'--n-scrollbar-width': scrollbarWidth,
|
||||
'--n-scrollbar-height': scrollbarHeight
|
||||
'--n-scrollbar-border-radius': borderRadius,
|
||||
'--n-scrollbar-width': width,
|
||||
'--n-scrollbar-height': height,
|
||||
'--n-scrollbar-rail-inset-horizontal': railInsetHorizontal,
|
||||
'--n-scrollbar-rail-inset-vertical': rtlEnabledRef?.value
|
||||
? rtlInset(railInsetVertical)
|
||||
: railInsetVertical,
|
||||
'--n-scrollbar-rail-color': railColor
|
||||
}
|
||||
})
|
||||
const themeClassHandle = inlineThemeDisabled
|
||||
|
@ -8,6 +8,9 @@ import { fadeInTransition } from '../../../../_styles/transitions/fade-in.cssr'
|
||||
// --n-scrollbar-width
|
||||
// --n-scrollbar-height
|
||||
// --n-scrollbar-border-radius
|
||||
// --n-scrollbar-rail-inset-horizontal
|
||||
// --n-scrollbar-rail-inset-vertical
|
||||
// --n-scrollbar-rail-color
|
||||
export default cB('scrollbar', `
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
@ -30,6 +33,7 @@ export default cB('scrollbar', `
|
||||
display: none;
|
||||
`),
|
||||
c('>', [
|
||||
// We can't set overflow hidden since it affects positioning.
|
||||
cB('scrollbar-content', `
|
||||
box-sizing: border-box;
|
||||
min-width: 100%;
|
||||
@ -42,12 +46,11 @@ export default cB('scrollbar', `
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
background: var(--n-scrollbar-rail-color);
|
||||
-webkit-user-select: none;
|
||||
`, [
|
||||
cM('horizontal', `
|
||||
left: 2px;
|
||||
right: 2px;
|
||||
bottom: 4px;
|
||||
inset: var(--n-scrollbar-rail-inset-horizontal);
|
||||
height: var(--n-scrollbar-height);
|
||||
`, [
|
||||
c('>', [
|
||||
@ -59,9 +62,7 @@ export default cB('scrollbar', `
|
||||
])
|
||||
]),
|
||||
cM('vertical', `
|
||||
right: 4px;
|
||||
top: 2px;
|
||||
bottom: 2px;
|
||||
inset: var(--n-scrollbar-rail-inset-vertical);
|
||||
width: var(--n-scrollbar-width);
|
||||
`, [
|
||||
c('>', [
|
||||
|
@ -13,11 +13,7 @@ export default cB('scrollbar', [
|
||||
right: unset;
|
||||
`)
|
||||
])
|
||||
]),
|
||||
cM('vertical', `
|
||||
left: 4px;
|
||||
right: unset;
|
||||
`)
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
|
5
src/_internal/scrollbar/styles/common.ts
Normal file
5
src/_internal/scrollbar/styles/common.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export const commonVars = {
|
||||
railInsetHorizontal: 'auto 2px 4px 2px',
|
||||
railInsetVertical: '2px 4px 2px auto',
|
||||
railColor: 'transparent'
|
||||
}
|
@ -1,10 +1,21 @@
|
||||
import { commonLight } from '../../../_styles/common'
|
||||
import type { ThemeCommonVars } from '../../../_styles/common'
|
||||
import type { Theme } from '../../../_mixins'
|
||||
import { commonVars } from './common'
|
||||
|
||||
export const self = (vars: ThemeCommonVars) => {
|
||||
const { scrollbarColor, scrollbarColorHover } = vars
|
||||
const {
|
||||
scrollbarColor,
|
||||
scrollbarColorHover,
|
||||
scrollbarHeight,
|
||||
scrollbarWidth,
|
||||
scrollbarBorderRadius
|
||||
} = vars
|
||||
return {
|
||||
...commonVars,
|
||||
height: scrollbarHeight,
|
||||
width: scrollbarWidth,
|
||||
borderRadius: scrollbarBorderRadius,
|
||||
color: scrollbarColor,
|
||||
colorHover: scrollbarColorHover
|
||||
}
|
||||
|
@ -35,33 +35,32 @@ export interface ThemePropsReactive<T> {
|
||||
builtinThemeOverrides?: ExtractThemeOverrides<T>
|
||||
}
|
||||
|
||||
export type ExtractThemeVars<T> = T extends Theme<unknown, infer U, unknown>
|
||||
? unknown extends U // self is undefined, ThemeVars is unknown
|
||||
? Record<string, unknown>
|
||||
: U
|
||||
: Record<string, unknown>
|
||||
export type ExtractThemeVars<T> =
|
||||
T extends Theme<unknown, infer U, unknown>
|
||||
? unknown extends U // self is undefined, ThemeVars is unknown
|
||||
? Record<string, unknown>
|
||||
: U
|
||||
: Record<string, unknown>
|
||||
|
||||
export type ExtractPeerOverrides<T> = T extends Theme<unknown, unknown, infer V>
|
||||
? {
|
||||
peers?: {
|
||||
[k in keyof V]?: ExtractThemeOverrides<V[k]>
|
||||
export type ExtractPeerOverrides<T> =
|
||||
T extends Theme<unknown, unknown, infer V>
|
||||
? {
|
||||
peers?: {
|
||||
[k in keyof V]?: ExtractThemeOverrides<V[k]>
|
||||
}
|
||||
}
|
||||
}
|
||||
: T
|
||||
: T
|
||||
|
||||
// V is peers theme
|
||||
export type ExtractMergedPeerOverrides<T> = T extends Theme<
|
||||
unknown,
|
||||
unknown,
|
||||
infer V
|
||||
>
|
||||
? {
|
||||
[k in keyof V]?: ExtractPeerOverrides<V[k]>
|
||||
}
|
||||
: T
|
||||
export type ExtractMergedPeerOverrides<T> =
|
||||
T extends Theme<unknown, unknown, infer V>
|
||||
? {
|
||||
[k in keyof V]?: ExtractPeerOverrides<V[k]>
|
||||
}
|
||||
: T
|
||||
|
||||
export type ExtractThemeOverrides<T> = Partial<ExtractThemeVars<T>> &
|
||||
ExtractPeerOverrides<T> & { common?: ThemeCommonVars }
|
||||
ExtractPeerOverrides<T> & { common?: Partial<ThemeCommonVars> }
|
||||
|
||||
export function createTheme<N extends string, T, R> (
|
||||
theme: Theme<N, T, R>
|
||||
@ -75,14 +74,15 @@ type UseThemeProps<T> = Readonly<{
|
||||
builtinThemeOverrides?: ExtractThemeOverrides<T>
|
||||
}>
|
||||
|
||||
export type MergedTheme<T> = T extends Theme<unknown, infer V, infer W>
|
||||
? {
|
||||
common: ThemeCommonVars
|
||||
self: V
|
||||
peers: W
|
||||
peerOverrides: ExtractMergedPeerOverrides<T>
|
||||
}
|
||||
: T
|
||||
export type MergedTheme<T> =
|
||||
T extends Theme<unknown, infer V, infer W>
|
||||
? {
|
||||
common: ThemeCommonVars
|
||||
self: V
|
||||
peers: W
|
||||
peerOverrides: ExtractMergedPeerOverrides<T>
|
||||
}
|
||||
: T
|
||||
|
||||
function useTheme<N, T, R> (
|
||||
resolveId: Exclude<keyof GlobalTheme, 'common' | 'name'>,
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
type VNodeChild
|
||||
} from 'vue'
|
||||
|
||||
function ensureValidVNode (
|
||||
export function ensureValidVNode (
|
||||
vnodes: VNodeArrayChildren
|
||||
): VNodeArrayChildren | null {
|
||||
return vnodes.some((child) => {
|
||||
|
@ -114,10 +114,7 @@ describe('n-auto-complete', () => {
|
||||
const wrapper = mount(NAutoComplete)
|
||||
await wrapper.setProps({
|
||||
getShow: (value: string | null) => {
|
||||
if (value && value.endsWith('@')) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return !!value?.endsWith('@')
|
||||
},
|
||||
options
|
||||
})
|
||||
|
@ -25,22 +25,27 @@ embedded.vue
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| action | `() => VNodeChild` | `undefined` | Operating area content, must be a render function. | NEXT_VERION |
|
||||
| bordered | `boolean` | `true` | Whether to show the card border. | |
|
||||
| closable | `boolean` | `false` | Is it allowed to close. | |
|
||||
| content | `string \| (() => VNodeChild)` | `undefined` | Card content, can be a render function. | NEXT_VERION |
|
||||
| content-class | `string` | `undefined` | The class of the card content area. | 2.36.0 |
|
||||
| content-style | `Object \| string` | `undefined` | The style of the card content area. | |
|
||||
| cover | `() => VNodeChild` | `undefined` | Cover content, must be a render function. | NEXT_VERION |
|
||||
| embedded | `boolean` | `false` | Use a darker background color to show the embedding effect (only for bright themes) | |
|
||||
| footer | `() => VNodeChild` | `undefined` | Footer content, must be a render function. | NEXT_VERION |
|
||||
| footer-class | `string` | `undefined` | The class of the bottom area of the card. | 2.36.0 |
|
||||
| footer-style | `Object \| string` | `undefined` | The style of the bottom area of the card. | |
|
||||
| header-class | `string` | `undefined` | The class of the card head area. | 2.36.0 |
|
||||
| header-style | `Object \| string` | `undefined` | The style of the card head area. | |
|
||||
| header-extra | `() => VNodeChild` | `undefined` | Header extra content, must be a render function. | NEXT_VERION |
|
||||
| header-extra-class | `string` | `undefined` | The class of the card head extra area. | 2.36.0 |
|
||||
| header-extra-style | `Object \| string` | `undefined` | The style of the card head extra area. | 2.25.0 |
|
||||
| hoverable | `boolean` | `false` | Whether to show shadow when hovering on the card. | |
|
||||
| segmented | `boolean \| { [part in 'content' \| 'footer' \| 'action']?: boolean \| 'soft' }` | `false` | Segment divider settings of the card. | |
|
||||
| size | `'small' \| 'medium' \| 'large' \| 'huge'` | `'medium'` | Card size. | |
|
||||
| tag | `string` | `'div'` | What tag need the card be rendered as. | 2.34.3 |
|
||||
| title | `string` | `undefined` | Card title. | |
|
||||
| title | `string \| (() => VNodeChild)` | `undefined` | Card title. | Render function since 2.38.2 |
|
||||
| on-close | `() => void` | `undefined` | Callback function triggered upon closing the card. | |
|
||||
|
||||
### Card Slots
|
||||
|
@ -27,22 +27,27 @@ embedded-debug.vue
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| action | `() => VNodeChild` | `undefined` | 操作区域内容。,需要是 render 函数 | NEXT_VERION |
|
||||
| bordered | `boolean` | `true` | 是否显示卡片边框 | |
|
||||
| closable | `boolean` | `false` | 是否允许关闭 | |
|
||||
| content | `string \| (() => VNodeChild)` | `undefined` | 卡片内容,,可以是 render 函数 | NEXT_VERION |
|
||||
| content-class | `string` | `undefined` | 卡片内容区域的类名 | 2.36.0 |
|
||||
| content-style | `Object \| string` | `undefined` | 卡片内容区域的样式 | |
|
||||
| cover | `() => VNodeChild` | `undefined` | 覆盖内容,需要是 render 函数 | NEXT_VERION |
|
||||
| embedded | `boolean` | `false` | 使用更深的背景色展现嵌入效果,只对亮色主题生效 | |
|
||||
| footer | `() => VNodeChild` | `undefined` | 底部内容 | NEXT_VERION |
|
||||
| footer-class | `string` | `undefined` | 卡片底部区域的类名 | 2.36.0 |
|
||||
| footer-style | `Object \| string` | `undefined` | 卡片底部区域的样式 | |
|
||||
| header-class | `string` | `undefined` | 卡片头部区域的类名 | 2.36.0 |
|
||||
| header-style | `Object \| string` | `undefined` | 卡片头部区域的样式 | |
|
||||
| header-extra | `() => VNodeChild` | `undefined` | 头部额外内容,需要是 render 函数 | NEXT_VERION |
|
||||
| header-extra-class | `string` | `undefined` | 卡片头部额外内容的类名 | 2.36.0 |
|
||||
| header-extra-style | `Object \| string` | `undefined` | 卡片头部额外内容的样式 | 2.25.0 |
|
||||
| hoverable | `boolean` | `false` | 卡片是否可悬浮 | |
|
||||
| segmented | `boolean \| { [part in 'content' \| 'footer' \| 'action']?: boolean \| 'soft' }` | `false` | 卡片的分段区域设置 | |
|
||||
| size | `'small' \| 'medium' \| 'large' \| 'huge'` | `'medium'` | 卡片的尺寸 | |
|
||||
| tag | `string` | `'div'` | 卡片组件要渲染为什么标签 | 2.34.3 |
|
||||
| title | `string` | `undefined` | 卡片的标题 | |
|
||||
| title | `string \| (() => VNodeChild)` | `undefined` | 卡片的标题,,可以是 render 函数 | 2.38.2 支持 render 函数 |
|
||||
| on-close | `() => void` | `undefined` | 点击卡片关闭图标时的回调 | |
|
||||
|
||||
### Card Slots
|
||||
|
@ -3,7 +3,8 @@ import {
|
||||
defineComponent,
|
||||
computed,
|
||||
type PropType,
|
||||
type CSSProperties
|
||||
type CSSProperties,
|
||||
type VNodeChild
|
||||
} from 'vue'
|
||||
import { getPadding } from 'seemly'
|
||||
import { useRtl } from '../../_mixins/use-rtl'
|
||||
@ -15,6 +16,7 @@ import { NBaseClose } from '../../_internal'
|
||||
import { cardLight } from '../styles'
|
||||
import type { CardTheme } from '../styles'
|
||||
import style from './styles/index.cssr'
|
||||
import { ensureValidVNode } from '../../_utils/vue/resolve-slot'
|
||||
|
||||
export interface CardSegmented {
|
||||
content?: boolean | 'soft'
|
||||
@ -23,7 +25,7 @@ export interface CardSegmented {
|
||||
}
|
||||
|
||||
export const cardBaseProps = {
|
||||
title: String,
|
||||
title: [String, Function] as PropType<string | (() => VNodeChild)>,
|
||||
contentClass: String,
|
||||
contentStyle: [Object, String] as PropType<CSSProperties | string>,
|
||||
headerClass: String,
|
||||
@ -52,7 +54,12 @@ export const cardBaseProps = {
|
||||
tag: {
|
||||
type: String as PropType<keyof HTMLElementTagNameMap>,
|
||||
default: 'div'
|
||||
}
|
||||
},
|
||||
cover: Function as PropType<() => VNodeChild>,
|
||||
content: [String, Function] as PropType<string | (() => VNodeChild)>,
|
||||
footer: Function as PropType<() => VNodeChild>,
|
||||
action: Function as PropType<() => VNodeChild>,
|
||||
headerExtra: Function as PropType<() => VNodeChild>
|
||||
} as const
|
||||
|
||||
export const cardBasePropKeys = keysOf(cardBaseProps)
|
||||
@ -216,31 +223,43 @@ export default defineComponent({
|
||||
style={this.cssVars as CSSProperties}
|
||||
role={this.role}
|
||||
>
|
||||
{resolveWrappedSlot(
|
||||
$slots.cover,
|
||||
(children) =>
|
||||
children && (
|
||||
{resolveWrappedSlot($slots.cover, (children) => {
|
||||
const mergedChildren = this.cover
|
||||
? ensureValidVNode([this.cover()])
|
||||
: children
|
||||
return (
|
||||
mergedChildren && (
|
||||
<div class={`${mergedClsPrefix}-card-cover`} role="none">
|
||||
{children}
|
||||
{mergedChildren}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
)
|
||||
})}
|
||||
{resolveWrappedSlot($slots.header, (children) => {
|
||||
return children || this.title || this.closable ? (
|
||||
const { title } = this
|
||||
const mergedChildren = title
|
||||
? ensureValidVNode(
|
||||
typeof title === 'function' ? [title()] : [title]
|
||||
)
|
||||
: children
|
||||
return mergedChildren || this.closable ? (
|
||||
<div
|
||||
class={[`${mergedClsPrefix}-card-header`, this.headerClass]}
|
||||
style={this.headerStyle}
|
||||
role="heading"
|
||||
>
|
||||
<div
|
||||
class={`${mergedClsPrefix}-card-header__main`}
|
||||
role="heading"
|
||||
>
|
||||
{children || this.title}
|
||||
{mergedChildren}
|
||||
</div>
|
||||
{resolveWrappedSlot(
|
||||
$slots['header-extra'],
|
||||
(children) =>
|
||||
children && (
|
||||
{resolveWrappedSlot($slots['header-extra'], (children) => {
|
||||
const mergedChildren = this.headerExtra
|
||||
? ensureValidVNode([this.headerExtra()])
|
||||
: children
|
||||
return (
|
||||
mergedChildren && (
|
||||
<div
|
||||
class={[
|
||||
`${mergedClsPrefix}-card-header__extra`,
|
||||
@ -248,56 +267,69 @@ export default defineComponent({
|
||||
]}
|
||||
style={this.headerExtraStyle}
|
||||
>
|
||||
{children}
|
||||
{mergedChildren}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
{this.closable ? (
|
||||
)
|
||||
})}
|
||||
{this.closable && (
|
||||
<NBaseClose
|
||||
clsPrefix={mergedClsPrefix}
|
||||
class={`${mergedClsPrefix}-card-header__close`}
|
||||
onClick={this.handleCloseClick}
|
||||
absolute
|
||||
/>
|
||||
) : null}
|
||||
)}
|
||||
</div>
|
||||
) : null
|
||||
})}
|
||||
{resolveWrappedSlot(
|
||||
$slots.default,
|
||||
(children) =>
|
||||
children && (
|
||||
{resolveWrappedSlot($slots.default, (children) => {
|
||||
const { content } = this
|
||||
const mergedChildren = content
|
||||
? ensureValidVNode(
|
||||
typeof content === 'function' ? [content()] : [content]
|
||||
)
|
||||
: children
|
||||
return (
|
||||
mergedChildren && (
|
||||
<div
|
||||
class={[`${mergedClsPrefix}-card__content`, this.contentClass]}
|
||||
style={this.contentStyle}
|
||||
role="none"
|
||||
>
|
||||
{children}
|
||||
{mergedChildren}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
{resolveWrappedSlot(
|
||||
$slots.footer,
|
||||
(children) =>
|
||||
children && [
|
||||
)
|
||||
})}
|
||||
{resolveWrappedSlot($slots.footer, (children) => {
|
||||
const mergedChildren = this.footer
|
||||
? ensureValidVNode([this.footer()])
|
||||
: children
|
||||
return (
|
||||
mergedChildren && (
|
||||
<div
|
||||
class={[`${mergedClsPrefix}-card__footer`, this.footerClass]}
|
||||
style={this.footerStyle}
|
||||
role="none"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
]
|
||||
)}
|
||||
{resolveWrappedSlot(
|
||||
$slots.action,
|
||||
(children) =>
|
||||
children && (
|
||||
<div class={`${mergedClsPrefix}-card__action`} role="none">
|
||||
{children}
|
||||
{mergedChildren}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
)
|
||||
})}
|
||||
{resolveWrappedSlot($slots.action, (children) => {
|
||||
const mergedChildren = this.action
|
||||
? ensureValidVNode([this.action()])
|
||||
: children
|
||||
return (
|
||||
mergedChildren && (
|
||||
<div class={`${mergedClsPrefix}-card__action`} role="none">
|
||||
{mergedChildren}
|
||||
</div>
|
||||
)
|
||||
)
|
||||
})}
|
||||
</Component>
|
||||
)
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ status.vue
|
||||
| filterable | `boolean` | `false` | Note: If `remote` is set, this won't have any effect. | |
|
||||
| filter | `(pattern: string, option: CascaderOption, path: CascaderOption[]) => boolean` | A string based filter algorithm. | Filter function of the cascader. | |
|
||||
| filter-menu-props | `HTMLAttributes` | `undefined` | The filter menu's dom props. | 2.27.0 |
|
||||
| get-column-style | `(detail: { level: number }) => string \| object` | `undefined` | Function that resolves column style. `level` starts from `0`. | 2.38.2 |
|
||||
| value-field | `string` | `'value'` | The value field in `CascaderOption`. | |
|
||||
| label-field | `string` | `'label'` | The label field in `CascaderOption`. | |
|
||||
| max-tag-count | `number \| 'responsive'` | `undefined` | Max tag count in multiple select mode. `responsive` will keep all the tags in single line. | |
|
||||
@ -48,7 +49,9 @@ status.vue
|
||||
| placeholder | `string` | `'Please Select'` | Placeholder text. | |
|
||||
| placement | `'top-start' \| 'top' \| 'top-end' \| 'right-start' \| 'right' \| 'right-end' \| 'bottom-start' \| 'bottom' \| 'bottom-end' \| 'left-start' \| 'left' \| 'left-end'` | `'bottom-start'` | Cascader placement. | 2.25.0 |
|
||||
| remote | `boolean` | `false` | Whether to obtain data remotely. | |
|
||||
| render-prefix | `(info: { option: CascaderOption, node: VNode \| null, checked: boolean }) => VNodeChild` | `undefined` | Render function of all the options' prefix. | 2.38.2 |
|
||||
| render-label | `(option: CascaderOption, checked: boolean) => VNodeChild` | `undefined` | Render function for cascader menu option label. | 2.24.0 |
|
||||
| render-suffix | `(info: { option: CascaderOption, node: VNode \| null, checked: boolean }) => VNodeChild` | `undefined` | Render function of all the options' suffix. | 2.38.2 |
|
||||
| separator | `string` | `' / '` | Selected option path value separator (used with `show-path`). | |
|
||||
| show | `boolean` | `undefined` | Whether to show the menu. | |
|
||||
| show-path | `boolean` | `true` | Whether to show the selected options as a path. | |
|
||||
|
@ -40,6 +40,7 @@ default-value-debug.vue
|
||||
| filterable | `boolean` | `false` | `remote` 被设定时不生效 | |
|
||||
| filter | `(pattern: string, option: CascaderOption, path: CascaderOption[]) => boolean` | 一个基于字符串的过滤算法 | 过滤选项的函数 | |
|
||||
| filter-menu-props | `HTMLAttributes` | `undefined` | 可过滤菜单的 DOM 属性 | 2.27.0 |
|
||||
| get-column-style | `(detail: { level: number }) => string \| object` | `undefined` | 获取列样式的函数,`level` 从 `0` 开始 | 2.38.2 |
|
||||
| value-field | `string` | `'value'` | 替代 `CascaderOption` 中的 value 字段名 | |
|
||||
| label-field | `string` | `'label'` | 替代 `CascaderOption` 中的 label 字段名 | |
|
||||
| max-tag-count | `number \| 'responsive'` | `undefined` | 多选标签的最大显示数量,`responsive` 会将所有标签保持在一行 | |
|
||||
@ -49,7 +50,9 @@ default-value-debug.vue
|
||||
| placeholder | `string` | `'请选择'` | 提示信息 | |
|
||||
| placement | `'top-start' \| 'top' \| 'top-end' \| 'right-start' \| 'right' \| 'right-end' \| 'bottom-start' \| 'bottom' \| 'bottom-end' \| 'left-start' \| 'left' \| 'left-end'` | `'bottom-start'` | 弹出位置 | 2.25.0 |
|
||||
| remote | `boolean` | `false` | 是否远程获取数据 | |
|
||||
| render-prefix | `(info: { option: CascaderOption, node: VNode \| null, checked: boolean }) => VNodeChild` | `undefined` | 节点前缀的渲染函数 | 2.38.2 |
|
||||
| render-label | `(option: CascaderOption, checked: boolean) => VNodeChild` | `undefined` | Cascader 菜单选项标签渲染函数 | 2.24.0 |
|
||||
| render-suffix | `(info: { option: CascaderOption, checked: boolean }) => VNodeChild` | `undefined` | 节点后缀的渲染函数 | 2.38.2 |
|
||||
| separator | `string` | `' / '` | 数据分隔符 | |
|
||||
| show | `boolean` | `undefined` | 是否打开菜单 | |
|
||||
| show-path | `boolean` | `true` | 是否在选择器中显示选项路径 | |
|
||||
|
@ -12,7 +12,8 @@ import {
|
||||
watchEffect,
|
||||
type VNodeChild,
|
||||
type HTMLAttributes,
|
||||
nextTick
|
||||
nextTick,
|
||||
type VNode
|
||||
} from 'vue'
|
||||
import {
|
||||
createTreeMate,
|
||||
@ -169,6 +170,23 @@ export const cascaderProps = {
|
||||
>,
|
||||
onBlur: Function as PropType<(e: FocusEvent) => void>,
|
||||
onFocus: Function as PropType<(e: FocusEvent) => void>,
|
||||
getColumnStyle: Function as PropType<
|
||||
(detail: { level: number }) => string | CSSProperties
|
||||
>,
|
||||
renderPrefix: Function as PropType<
|
||||
(props: {
|
||||
option: CascaderOption
|
||||
checked: boolean
|
||||
node: VNode | null
|
||||
}) => VNodeChild
|
||||
>,
|
||||
renderSuffix: Function as PropType<
|
||||
(props: {
|
||||
option: CascaderOption
|
||||
checked: boolean
|
||||
node: VNode | null
|
||||
}) => VNodeChild
|
||||
>,
|
||||
// deprecated
|
||||
onChange: [Function, Array] as PropType<MaybeArray<OnUpdateValue> | undefined>
|
||||
} as const
|
||||
@ -873,6 +891,9 @@ export default defineComponent({
|
||||
localeRef,
|
||||
labelFieldRef: toRef(props, 'labelField'),
|
||||
renderLabelRef: toRef(props, 'renderLabel'),
|
||||
getColumnStyleRef: toRef(props, 'getColumnStyle'),
|
||||
renderPrefixRef: toRef(props, 'renderPrefix'),
|
||||
renderSuffixRef: toRef(props, 'renderSuffix'),
|
||||
syncCascaderMenuPosition,
|
||||
syncSelectMenuPosition,
|
||||
updateKeyboardKey,
|
||||
|
@ -65,7 +65,8 @@ export default defineComponent({
|
||||
mergedClsPrefixRef,
|
||||
syncCascaderMenuPosition,
|
||||
handleCascaderMenuClickOutside,
|
||||
mergedThemeRef
|
||||
mergedThemeRef,
|
||||
getColumnStyleRef
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
} = inject(cascaderInjectionKey)!
|
||||
const submenuInstRefs: CascaderSubmenuInstance[] = []
|
||||
@ -114,6 +115,7 @@ export default defineComponent({
|
||||
submenuInstRefs,
|
||||
maskInstRef,
|
||||
mergedTheme: mergedThemeRef,
|
||||
getColumnStyle: getColumnStyleRef,
|
||||
handleFocusin,
|
||||
handleFocusout,
|
||||
handleClickOutside,
|
||||
@ -141,6 +143,7 @@ export default defineComponent({
|
||||
<div class={`${mergedClsPrefix}-cascader-submenu-wrapper`}>
|
||||
{this.menuModel.map((submenuOptions, index) => (
|
||||
<NCascaderSubmenu
|
||||
style={this.getColumnStyle?.({ level: index })}
|
||||
ref={
|
||||
((instance: CascaderSubmenuInstance) => {
|
||||
if (instance) {
|
||||
|
@ -4,7 +4,8 @@ import {
|
||||
inject,
|
||||
defineComponent,
|
||||
type PropType,
|
||||
Transition
|
||||
Transition,
|
||||
type VNode
|
||||
} from 'vue'
|
||||
import { useMemo } from 'vooks'
|
||||
import { NCheckbox } from '../../checkbox'
|
||||
@ -39,6 +40,8 @@ export default defineComponent({
|
||||
mergedThemeRef,
|
||||
labelFieldRef,
|
||||
showCheckboxRef,
|
||||
renderPrefixRef,
|
||||
renderSuffixRef,
|
||||
updateHoverKey,
|
||||
updateKeyboardKey,
|
||||
addLoadingKey,
|
||||
@ -171,88 +174,121 @@ export default defineComponent({
|
||||
handleCheckboxUpdateValue,
|
||||
mergedHandleMouseEnter: mergedHandleMouseEnterRef,
|
||||
mergedHandleMouseMove: mergedHandleMouseMoveRef,
|
||||
renderLabel: renderLabelRef
|
||||
renderLabel: renderLabelRef,
|
||||
renderPrefix: renderPrefixRef,
|
||||
renderSuffix: renderSuffixRef
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { mergedClsPrefix, renderLabel } = this
|
||||
const {
|
||||
mergedClsPrefix,
|
||||
showCheckbox,
|
||||
renderLabel,
|
||||
renderPrefix,
|
||||
renderSuffix
|
||||
} = this
|
||||
|
||||
let prefixNode: VNode | null = null
|
||||
if (showCheckbox || renderPrefix) {
|
||||
const originalNode = this.showCheckbox ? (
|
||||
<NCheckbox
|
||||
focusable={false}
|
||||
data-checkbox
|
||||
disabled={this.disabled}
|
||||
checked={this.checked}
|
||||
indeterminate={this.indeterminate}
|
||||
theme={this.mergedTheme.peers.Checkbox}
|
||||
themeOverrides={this.mergedTheme.peerOverrides.Checkbox}
|
||||
onUpdateChecked={this.handleCheckboxUpdateValue}
|
||||
/>
|
||||
) : null
|
||||
prefixNode = (
|
||||
<div class={`${mergedClsPrefix}-cascader-option__prefix`}>
|
||||
{renderPrefix
|
||||
? renderPrefix({
|
||||
option: this.tmNode.rawNode,
|
||||
checked: this.checked,
|
||||
node: originalNode
|
||||
})
|
||||
: originalNode}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
let suffixNode: VNode | null = null
|
||||
const originalSuffixChild = (
|
||||
<div class={`${mergedClsPrefix}-cascader-option-icon-placeholder`}>
|
||||
{!this.isLeaf ? (
|
||||
<NBaseLoading
|
||||
clsPrefix={mergedClsPrefix}
|
||||
scale={0.85}
|
||||
strokeWidth={24}
|
||||
show={this.isLoading}
|
||||
class={`${mergedClsPrefix}-cascader-option-icon`}
|
||||
>
|
||||
{{
|
||||
default: () => (
|
||||
<NBaseIcon
|
||||
clsPrefix={mergedClsPrefix}
|
||||
key="arrow"
|
||||
class={`${mergedClsPrefix}-cascader-option-icon ${mergedClsPrefix}-cascader-option-icon--arrow`}
|
||||
>
|
||||
{{
|
||||
default: () => <ChevronRightIcon />
|
||||
}}
|
||||
</NBaseIcon>
|
||||
)
|
||||
}}
|
||||
</NBaseLoading>
|
||||
) : this.checkStrategy === 'child' &&
|
||||
!(this.multiple && this.cascade) ? (
|
||||
<Transition name="fade-in-scale-up-transition">
|
||||
{{
|
||||
default: () =>
|
||||
this.checked ? (
|
||||
<NBaseIcon
|
||||
clsPrefix={mergedClsPrefix}
|
||||
class={`${mergedClsPrefix}-cascader-option-icon ${mergedClsPrefix}-cascader-option-icon--checkmark`}
|
||||
>
|
||||
{{ default: () => <CheckmarkIcon /> }}
|
||||
</NBaseIcon>
|
||||
) : null
|
||||
}}
|
||||
</Transition>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
suffixNode = (
|
||||
<div class={`${mergedClsPrefix}-cascader-option__suffix`}>
|
||||
{renderSuffix
|
||||
? renderSuffix({
|
||||
option: this.tmNode.rawNode,
|
||||
checked: this.checked,
|
||||
node: originalSuffixChild
|
||||
})
|
||||
: originalSuffixChild}
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div
|
||||
class={[
|
||||
`${mergedClsPrefix}-cascader-option`,
|
||||
{
|
||||
[`${mergedClsPrefix}-cascader-option--pending`]:
|
||||
this.keyboardPending || this.hoverPending,
|
||||
[`${mergedClsPrefix}-cascader-option--disabled`]: this.disabled,
|
||||
[`${mergedClsPrefix}-cascader-option--show-prefix`]:
|
||||
this.showCheckbox
|
||||
}
|
||||
this.keyboardPending ||
|
||||
(this.hoverPending &&
|
||||
`${mergedClsPrefix}-cascader-option--pending`),
|
||||
this.disabled && `${mergedClsPrefix}-cascader-option--disabled`,
|
||||
this.showCheckbox && `${mergedClsPrefix}-cascader-option--show-prefix`
|
||||
]}
|
||||
onMouseenter={this.mergedHandleMouseEnter}
|
||||
onMousemove={this.mergedHandleMouseMove}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
{this.showCheckbox ? (
|
||||
<div class={`${mergedClsPrefix}-cascader-option__prefix`}>
|
||||
<NCheckbox
|
||||
focusable={false}
|
||||
data-checkbox
|
||||
disabled={this.disabled}
|
||||
checked={this.checked}
|
||||
indeterminate={this.indeterminate}
|
||||
theme={this.mergedTheme.peers.Checkbox}
|
||||
themeOverrides={this.mergedTheme.peerOverrides.Checkbox}
|
||||
onUpdateChecked={this.handleCheckboxUpdateValue}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{prefixNode}
|
||||
<span class={`${mergedClsPrefix}-cascader-option__label`}>
|
||||
{renderLabel
|
||||
? renderLabel(this.tmNode.rawNode, this.checked)
|
||||
: this.label}
|
||||
</span>
|
||||
<div class={`${mergedClsPrefix}-cascader-option__suffix`}>
|
||||
<div class={`${mergedClsPrefix}-cascader-option-icon-placeholder`}>
|
||||
{!this.isLeaf ? (
|
||||
<NBaseLoading
|
||||
clsPrefix={mergedClsPrefix}
|
||||
scale={0.85}
|
||||
strokeWidth={24}
|
||||
show={this.isLoading}
|
||||
class={`${mergedClsPrefix}-cascader-option-icon`}
|
||||
>
|
||||
{{
|
||||
default: () => (
|
||||
<NBaseIcon
|
||||
clsPrefix={mergedClsPrefix}
|
||||
key="arrow"
|
||||
class={`${mergedClsPrefix}-cascader-option-icon ${mergedClsPrefix}-cascader-option-icon--arrow`}
|
||||
>
|
||||
{{
|
||||
default: () => <ChevronRightIcon />
|
||||
}}
|
||||
</NBaseIcon>
|
||||
)
|
||||
}}
|
||||
</NBaseLoading>
|
||||
) : this.checkStrategy === 'child' &&
|
||||
!(this.multiple && this.cascade) ? (
|
||||
<Transition name="fade-in-scale-up-transition">
|
||||
{{
|
||||
default: () =>
|
||||
this.checked ? (
|
||||
<NBaseIcon
|
||||
clsPrefix={mergedClsPrefix}
|
||||
class={`${mergedClsPrefix}-cascader-option-icon ${mergedClsPrefix}-cascader-option-icon--checkmark`}
|
||||
>
|
||||
{{ default: () => <CheckmarkIcon /> }}
|
||||
</NBaseIcon>
|
||||
) : null
|
||||
}}
|
||||
</Transition>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{suffixNode}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import type { CheckStrategy, TreeNode } from 'treemate'
|
||||
import type { MergedTheme } from '../../_mixins'
|
||||
import type { NLocale } from '../../locales'
|
||||
import type { CascaderTheme } from '../styles'
|
||||
import type { Ref, Slots, VNodeChild } from 'vue'
|
||||
import type { CSSProperties, Ref, Slots, VNode, VNodeChild } from 'vue'
|
||||
import { createInjectionKey } from '../../_utils'
|
||||
|
||||
export type ValueAtom = string | number
|
||||
@ -79,6 +79,25 @@ export interface CascaderInjection {
|
||||
optionHeightRef: Ref<string>
|
||||
labelFieldRef: Ref<string>
|
||||
showCheckboxRef: Ref<boolean>
|
||||
getColumnStyleRef: Ref<
|
||||
((detail: { level: number }) => string | CSSProperties) | undefined
|
||||
>
|
||||
renderPrefixRef: Ref<
|
||||
| ((info: {
|
||||
option: CascaderOption
|
||||
checked: boolean
|
||||
node: VNode | null
|
||||
}) => VNodeChild)
|
||||
| undefined
|
||||
>
|
||||
renderSuffixRef: Ref<
|
||||
| ((info: {
|
||||
option: CascaderOption
|
||||
checked: boolean
|
||||
node: VNode | null
|
||||
}) => VNodeChild)
|
||||
| undefined
|
||||
>
|
||||
syncCascaderMenuPosition: () => void
|
||||
syncSelectMenuPosition: () => void
|
||||
updateKeyboardKey: (value: Key | null) => void
|
||||
|
@ -37,17 +37,17 @@ export default c([
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
`),
|
||||
cB('scrollbar', {
|
||||
// if width not set, cascader select menu's inner scroll area's width is
|
||||
// not correct, which won't change after select menu width is set
|
||||
width: '100%'
|
||||
}),
|
||||
cB('base-menu-mask', {
|
||||
backgroundColor: 'var(--n-menu-mask-color)'
|
||||
}),
|
||||
cB('base-loading', {
|
||||
color: 'var(--n-loading-color)'
|
||||
}),
|
||||
// if width not set, cascader select menu's inner scroll area's width is
|
||||
// not correct, which won't change after select menu width is set
|
||||
cB('scrollbar', `
|
||||
width: 100%;
|
||||
`),
|
||||
cB('base-menu-mask', `
|
||||
background-color: var(--n-menu-mask-color);
|
||||
`),
|
||||
cB('base-loading', `
|
||||
color: var(--n-loading-color);
|
||||
`),
|
||||
cB('cascader-submenu-wrapper', `
|
||||
position: relative;
|
||||
display: flex;
|
||||
@ -61,9 +61,9 @@ export default c([
|
||||
cM('virtual', `
|
||||
width: var(--n-column-width);
|
||||
`),
|
||||
cB('scrollbar-content', {
|
||||
position: 'relative'
|
||||
}),
|
||||
cB('scrollbar-content', `
|
||||
position: relative;
|
||||
`),
|
||||
c('&:first-child', `
|
||||
border-top-left-radius: var(--n-menu-border-radius);
|
||||
border-bottom-left-radius: var(--n-menu-border-radius);
|
||||
@ -98,68 +98,68 @@ export default c([
|
||||
background-color .2s var(--n-bezier),
|
||||
color 0.2s var(--n-bezier);
|
||||
`, [
|
||||
cM('show-prefix', {
|
||||
paddingLeft: 0
|
||||
}),
|
||||
cM('show-prefix', `
|
||||
padding-left: 0;
|
||||
`),
|
||||
cE('label', `
|
||||
flex: 1 0 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
`),
|
||||
cE('prefix', {
|
||||
width: '32px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}),
|
||||
cE('suffix', {
|
||||
width: '32px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}),
|
||||
cB('cascader-option-icon-placeholder', {
|
||||
lineHeight: 0,
|
||||
position: 'relative',
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
fontSize: '16px'
|
||||
}, [
|
||||
cE('prefix', `
|
||||
min-width: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`),
|
||||
cE('suffix', `
|
||||
min-width: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`),
|
||||
cB('cascader-option-icon-placeholder', `
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
font-size: 16px;
|
||||
`, [
|
||||
cB('cascader-option-icon', [
|
||||
cM('checkmark', {
|
||||
color: 'var(--n-option-check-mark-color)'
|
||||
}, [
|
||||
cM('checkmark', `
|
||||
color: var(--n-option-check-mark-color);
|
||||
`, [
|
||||
fadeInScaleUpTransition({
|
||||
originalTransition: 'background-color .3s var(--n-bezier), box-shadow .3s var(--n-bezier)'
|
||||
})
|
||||
]),
|
||||
cM('arrow', {
|
||||
color: 'var(--n-option-arrow-color)'
|
||||
})
|
||||
cM('arrow', `
|
||||
color: var(--n-option-arrow-color);
|
||||
`)
|
||||
])
|
||||
]),
|
||||
cM('selected', {
|
||||
color: 'var(--n-option-text-color-active)'
|
||||
}),
|
||||
cM('active', {
|
||||
color: 'var(--n-option-text-color-active)',
|
||||
backgroundColor: 'var(--n-option-color-hover)'
|
||||
}),
|
||||
cM('pending', {
|
||||
backgroundColor: 'var(--n-option-color-hover)'
|
||||
}),
|
||||
c('&:hover', {
|
||||
backgroundColor: 'var(--n-option-color-hover)'
|
||||
}),
|
||||
cM('selected', `
|
||||
color: var(--n-option-text-color-active);
|
||||
`),
|
||||
cM('active', `
|
||||
color: var(--n-option-text-color-active);
|
||||
background-color: var(--n-option-color-hover);
|
||||
`),
|
||||
cM('pending', `
|
||||
background-color: var(--n-option-color-hover);
|
||||
`),
|
||||
c('&:hover', `
|
||||
background-color: var(--n-option-color-hover);
|
||||
`),
|
||||
cM('disabled', `
|
||||
color: var(--n-option-text-color-disabled);
|
||||
background-color: #0000;
|
||||
cursor: not-allowed;
|
||||
`, [
|
||||
cB('cascader-option-icon', [
|
||||
cM('arrow', {
|
||||
color: 'var(--n-option-text-color-disabled)'
|
||||
})
|
||||
cM('arrow', `
|
||||
color: var(--n-option-text-color-disabled);
|
||||
`)
|
||||
])
|
||||
])
|
||||
])
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { toRaw, h } from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { NCode } from '../index'
|
||||
import hljs from 'highlight.js/lib/core'
|
||||
@ -21,23 +22,19 @@ describe('n-code', () => {
|
||||
wrapper.unmount()
|
||||
})
|
||||
it('should work with `language` prop', () => {
|
||||
const wrapper = mount(NCode, {
|
||||
props: {
|
||||
code: 'console.log(a)',
|
||||
language: 'javascript',
|
||||
hljs
|
||||
}
|
||||
const wrapper = mount(() => {
|
||||
return (
|
||||
<NCode code="console.log(a)" language="javascript" hljs={toRaw(hljs)} />
|
||||
)
|
||||
})
|
||||
expect(wrapper.find('.hljs-variable').text()).toBe('console')
|
||||
wrapper.unmount()
|
||||
})
|
||||
it('should work with `hljs` prop', () => {
|
||||
const wrapper = mount(NCode, {
|
||||
props: {
|
||||
code: 'console.log(a)',
|
||||
language: 'javascript',
|
||||
hljs
|
||||
}
|
||||
const wrapper = mount(() => {
|
||||
return (
|
||||
<NCode code="console.log(a)" language="javascript" hljs={toRaw(hljs)} />
|
||||
)
|
||||
})
|
||||
expect(wrapper.find('.function_').text()).toBe('log')
|
||||
wrapper.unmount()
|
@ -51,6 +51,7 @@ export * from './legacy-transfer'
|
||||
export * from './list'
|
||||
export * from './loading-bar'
|
||||
export * from './log'
|
||||
export * from './infinite-scroll'
|
||||
export * from './menu'
|
||||
export * from './mention'
|
||||
export * from './message'
|
||||
|
@ -53,6 +53,7 @@ panel.vue
|
||||
| show | `boolean` | `undefined` | Whether to show panel. | 2.28.3 |
|
||||
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | Date picker size. | |
|
||||
| status | `'success' \| 'warning' \| 'error'` | `undefined` | Validation status. | 2.27.0 |
|
||||
| time-picker-format | `string \| undefined` | `undefined` | Format of the binding value in time picker inside date picker of type `'datetime'` and `'datetimerange'`. See [format](https://date-fns.org/v2.23.0/docs/format). | 2.38.2 |
|
||||
| to | `string \| HTMLElement \| false` | `body` | Container node of the panel. `false` will keep it not detached. | |
|
||||
| type | `'date' \| 'datetime' \| 'daterange' \| 'datetimerange' \| 'month' \| 'monthrange' \| 'year' \| 'yearrange' \| 'quarter' \| 'quarterrange' \| 'week'` | `'date'` | Date picker type. | `'quarter'` v2.22.0, `'monthrange'` 2.28.3 |
|
||||
| value | `number \| [number, number] \| null` | `undefined` | Value of the date picker when being manually set. | |
|
||||
|
@ -54,6 +54,7 @@ form-debug.vue
|
||||
| show | `boolean` | `undefined` | 是否展示面板 | 2.28.3 |
|
||||
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | 尺寸 | |
|
||||
| status | `'success' \| 'warning' \| 'error'` | `undefined` | 验证状态 | 2.27.0 |
|
||||
| time-picker-format | `string \| undefined` | `undefined` | 日期面板内时间的显示方式,详情见 [format](https://date-fns.org/v2.23.0/docs/format) | 2.38.2 |
|
||||
| to | `string \| HTMLElement \| false` | `body` | 面板的容器节点,`false` 会待在原地 | |
|
||||
| type | `'date' \| 'datetime' \| 'daterange' \| 'datetimerange' \| 'month' \| 'monthrange' \| 'year' \| 'yearrange' \| 'quarter' \| 'quarterrange' \| 'week'` | `'date'` | Date Picker 的类型 | `'quarter'` v2.22.0, `'monthrange'` 2.28.3 |
|
||||
| value | `number \| [number, number] \| null` | `undefined` | Date Picker 的值 | |
|
||||
|
@ -113,7 +113,7 @@ export const datePickerProps = {
|
||||
endPlaceholder: String,
|
||||
format: String,
|
||||
dateFormat: String,
|
||||
timeFormat: String,
|
||||
timerPickerFormat: String,
|
||||
actions: Array as PropType<Array<'clear' | 'confirm' | 'now'> | null>,
|
||||
shortcuts: Object as PropType<Shortcuts>,
|
||||
isDateDisabled: Function as PropType<IsDateDisabled>,
|
||||
@ -1022,7 +1022,8 @@ export default defineComponent({
|
||||
onNextMonth: this.onNextMonth,
|
||||
onPrevMonth: this.onPrevMonth,
|
||||
onNextYear: this.onNextYear,
|
||||
onPrevYear: this.onPrevYear
|
||||
onPrevYear: this.onPrevYear,
|
||||
timerPickerFormat: this.timerPickerFormat
|
||||
}
|
||||
const renderPanel = (): VNode => {
|
||||
const { type } = this
|
||||
|
@ -64,7 +64,7 @@ export default defineComponent({
|
||||
<NTimePicker
|
||||
size={this.timePickerSize}
|
||||
placeholder={this.locale.selectTime}
|
||||
format={this.timeFormat}
|
||||
format={this.timerPickerFormat}
|
||||
{...(Array.isArray(timePickerProps) ? undefined : timePickerProps)}
|
||||
showIcon={false}
|
||||
to={false}
|
||||
|
@ -67,7 +67,7 @@ export default defineComponent({
|
||||
/>
|
||||
<NTimePicker
|
||||
placeholder={this.locale.selectTime}
|
||||
format={this.timeFormat}
|
||||
format={this.timerPickerFormat}
|
||||
size={this.timePickerSize}
|
||||
{...(Array.isArray(timePickerProps)
|
||||
? timePickerProps[0]
|
||||
@ -98,7 +98,7 @@ export default defineComponent({
|
||||
/>
|
||||
<NTimePicker
|
||||
placeholder={this.locale.selectTime}
|
||||
format={this.timeFormat}
|
||||
format={this.timerPickerFormat}
|
||||
size={this.timePickerSize}
|
||||
{...(Array.isArray(timePickerProps)
|
||||
? timePickerProps[1]
|
||||
|
@ -22,7 +22,7 @@ const TIME_FORMAT = 'HH:mm:ss'
|
||||
const usePanelCommonProps = {
|
||||
active: Boolean,
|
||||
dateFormat: String,
|
||||
timeFormat: {
|
||||
timerPickerFormat: {
|
||||
type: String,
|
||||
value: TIME_FORMAT
|
||||
},
|
||||
|
@ -195,7 +195,7 @@ function getMonthString (
|
||||
monthFormat: string,
|
||||
locale: NDateLocale['locale']
|
||||
): string {
|
||||
const date = Date.UTC(2000, month, 1)
|
||||
const date = new Date(2000, month, 1).getTime()
|
||||
return format(date, monthFormat, { locale })
|
||||
}
|
||||
|
||||
@ -204,7 +204,7 @@ function getYearString (
|
||||
yearFormat: string,
|
||||
locale: NDateLocale['locale']
|
||||
): string {
|
||||
const date = Date.UTC(year, 1, 1)
|
||||
const date = new Date(year, 1, 1).getTime()
|
||||
return format(date, yearFormat, { locale })
|
||||
}
|
||||
|
||||
@ -213,7 +213,7 @@ function getQuarterString (
|
||||
quarterFormat: string,
|
||||
locale: NDateLocale['locale']
|
||||
): string {
|
||||
const date = Date.UTC(2000, quarter * 3 - 2, 1)
|
||||
const date = new Date(2000, quarter * 3 - 2, 1).getTime()
|
||||
return format(date, quarterFormat, { locale })
|
||||
}
|
||||
|
||||
|
@ -64,14 +64,18 @@ use-dialog-reactive-list.vue
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| action | `() => VNodeChild` | `undefined` | Content of the operation area, must be a `render` function. | |
|
||||
| action | `() => VNodeChild` | `undefined` | Content of the operation area, must be a render function. | |
|
||||
| actionClass | `string` | The class name of the action area. | 2.38.2 |
|
||||
| actionStyle | `Object \| string` | The style of the action area. | 2.38.2 |
|
||||
| autoFocus | `boolean` | `true` | Whether to focus the first focusable element inside modal. | 2.28.3 |
|
||||
| blockScroll | `boolean` | `true` | Whether to disabled body scrolling when it's active. | 2.28.3 |
|
||||
| bordered | `boolean` | `false` | Whether to show `border`. | |
|
||||
| class | `any` | `undefined` | Class name of the dialog. | 2.33.0 |
|
||||
| closable | `boolean` | `true` | Whether to show `close` icon. | |
|
||||
| closeOnEsc | `boolean` | `true` | Whether to close the dialog when the Esc key is pressed | 2.26.4 |
|
||||
| content | `string \| (() => VNodeChild)` | `undefined` | Content, can be a `render` function. | |
|
||||
| content | `string \| (() => VNodeChild)` | `undefined` | Content, can be a render function. | |
|
||||
| contentClass | `string` | The class name of the content. | 2.38.2 |
|
||||
| contentStyle | `Object \| string` | The style of the content. | 2.38.2 |
|
||||
| iconPlacement | `'left' \| 'top'` | `'left'` | Icon placement. | |
|
||||
| icon | `() => VNodeChild` | `undefined` | `Render` function of `icon`. | |
|
||||
| loading | `boolean` | `false` | Whether to display `loading` status. | |
|
||||
@ -82,7 +86,9 @@ use-dialog-reactive-list.vue
|
||||
| positiveText | `string` | `undefined` | Confirm button text. Corresponding button won't show if not set. | |
|
||||
| showIcon | `boolean` | `true` | Whether to show `icon`. | |
|
||||
| style | `string \| Object` | `undefined` | Style of the dialog. | |
|
||||
| title | `string \| (() => VNodeChild)` | `undefined` | Title, can be a `render` function. | |
|
||||
| title | `string \| (() => VNodeChild)` | `undefined` | Title, can be a render function. | |
|
||||
| titleClass | `string` | The class name of the content. | 2.38.2 |
|
||||
| titleStyle | `Object \| string` | The style of the content. | 2.38.2 |
|
||||
| transformOrigin | `'mouse' \| 'center'` | `'mouse'` | The transform origin of the dialog's display animation. | 2.34.0 |
|
||||
| type | `'error \| 'success' \| 'warning'` | `'warning'` | Dialog type. | |
|
||||
| onAfterEnter | `() => void` | `undefined` | Callback on enter animation ends. | 2.33.0 |
|
||||
@ -100,11 +106,15 @@ All the properties can be modified dynamically.
|
||||
|
||||
| Name | Type | Description | Version |
|
||||
| --- | --- | --- | --- |
|
||||
| actionClass | `string` | The class name of the action area. | 2.38.2 |
|
||||
| actionStyle | `Object \| string` | The style of the action area. | 2.38.2 |
|
||||
| bordered | `boolean` | Whether to show `border`. | |
|
||||
| class | `any` | Class name of the dialog. | 2.33.0 |
|
||||
| closable | `boolean` | Whether to show `close` icon. | |
|
||||
| closeOnEsc | `boolean` | Whether to close dialog on Esc is pressed. | 2.26.4 |
|
||||
| content | `string \| (() => VNodeChild)` | Content, can be a `render` function. | |
|
||||
| content | `string \| (() => VNodeChild)` | Content, can be a render function. | |
|
||||
| contentClass | `string` | The class name of the content. | 2.38.2 |
|
||||
| contentStyle | `Object \| string` | The style of the content. | 2.38.2 |
|
||||
| iconPlacement | `'left' \| 'top'` | Icon placement. | |
|
||||
| icon | `() => VNodeChild` | `Render` function of `icon`. | |
|
||||
| loading | `boolean` | Whether to display `loading` status. | |
|
||||
@ -115,7 +125,9 @@ All the properties can be modified dynamically.
|
||||
| positiveText | `string` | Corresponding button won't show if not set. | |
|
||||
| show-icon | `boolean` | Whether to show `icon`. | |
|
||||
| style | `string \| Object` | Style of the dialog. | |
|
||||
| title | `string \| (() => VNodeChild)` | Can be a `render` function. | |
|
||||
| title | `string \| (() => VNodeChild)` | Can be a render function. | |
|
||||
| titleClass | `string` | The class name of the content. | 2.38.2 |
|
||||
| titleStyle | `Object \| string` | The style of the content. | 2.38.2 |
|
||||
| transformOrigin | `'mouse' \| 'center'` | The transform origin of the dialog's display animation. | 2.34.0 |
|
||||
| type | `'error \| 'success' \| 'warning'` | Dialog type. | |
|
||||
| onAfterEnter | `() => void \| undefined` | Callback on enter animation ends. | 2.33.0 |
|
||||
@ -135,9 +147,13 @@ All the properties can be modified dynamically.
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| action-class | `string` | `undefined` | The class name of the action area. | 2.38.2 |
|
||||
| action-style | `Object \| string` | `undefined` | The style of the action area. | 2.38.2 |
|
||||
| bordered | `boolean` | `false` | Whether to show `border`. | |
|
||||
| closable | `boolean` | `true` | Whether to show `close` icon. | |
|
||||
| content | `string \| (() => VNodeChild)` | `undefined` | Can be a `render` function. | |
|
||||
| content | `string \| (() => VNodeChild)` | `undefined` | Can be a render function. | |
|
||||
| content-class | `string` | `undefined` | The class name of the content. | 2.38.2 |
|
||||
| content-style | `Object \| string` | `undefined` | The style of the content. | 2.38.2 |
|
||||
| icon-placement | `'left' \| 'top'` | `'left'` | Icon placement. | |
|
||||
| icon | `() => VNodeChild` | `undefined` | `Render` function of icon. | |
|
||||
| loading | `boolean` | `false` | Whether to display `loading` status. | |
|
||||
@ -146,7 +162,9 @@ All the properties can be modified dynamically.
|
||||
| positive-button-props | `ButtonProps` | `undefined` | Confirm button's DOM props | 2.27.0 |
|
||||
| positive-text | `string` | `undefined` | Corresponding button won't show if not set. | |
|
||||
| show-icon | `boolean` | `true` | Whether to display the `icon`. | |
|
||||
| title | `string \| (() => VNodeChild)` | `undefined` | Title, can be a `render` function. | |
|
||||
| title | `string \| (() => VNodeChild)` | `undefined` | Title, can be a render function. | |
|
||||
| title-class | `string` | `undefined` | The class name of the content. | 2.38.2 |
|
||||
| title-style | `Object \| string` | `undefined` | The style of the content. | 2.38.2 |
|
||||
| type | `'error \| 'success' \| 'warning' \| 'info'` | `'warning'` | Dialog type. | |
|
||||
| on-close | `() => void` | `undefined` | Calback on close button clicked. | |
|
||||
| on-negative-click | `(e: MouseEvent) => void` | `undefined` | Callback on positive button clicked. | |
|
||||
|
@ -66,16 +66,20 @@ rtl-debug.vue
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| action | `() => VNodeChild` | `undefined` | 操作区域的内容,需要是 `render` 函数 | |
|
||||
| action | `() => VNodeChild` | `undefined` | 操作区域的内容,需要是渲染函数 | |
|
||||
| actionClass | `string` | 操作区域的类名 | 2.38.2 |
|
||||
| actionStyle | `Object \| string` | 操作区域的样式 | 2.38.2 |
|
||||
| autoFocus | `boolean` | `true` | 是否自动聚焦 Modal 第一个可聚焦的元素 | 2.28.3 |
|
||||
| blockScroll | `boolean` | `true` | 是否在打开时禁用 body 滚动 | 2.28.3 |
|
||||
| bordered | `boolean` | `false` | 是否显示 `border` | |
|
||||
| class | `any` | `undefined` | 类名 | 2.33.0 |
|
||||
| closable | `boolean` | `true` | 是否显示 `close` 图标 | |
|
||||
| closeOnEsc | `boolean` | `true` | 是否在摁下 Esc 键的时候关闭对话框 | 2.26.4 |
|
||||
| content | `string \| (() => VNodeChild)` | `undefined` | 对话框内容,可以是 `render` 函数 | |
|
||||
| content | `string \| (() => VNodeChild)` | `undefined` | 对话框内容,可以是渲染函数 | |
|
||||
| contentClass | `string` | 内容的类名 | 2.38.2 |
|
||||
| contentStyle | `Object \| string` | 内容的样式 | 2.38.2 |
|
||||
| iconPlacement | `'left' \| 'top'` | `'left'` | 图标的位置 | |
|
||||
| icon | `() => VNodeChild` | `undefined` | 对话框 `icon`, 需要是 `render` 函数 | |
|
||||
| icon | `() => VNodeChild` | `undefined` | 对话框 `icon`, 需要是渲染函数 | |
|
||||
| loading | `boolean` | `false` | 是否显示 `loading` 状态 | |
|
||||
| maskClosable | `boolean` | `true` | 是否可以通过点击 `mask` 关闭对话框 | |
|
||||
| negativeButtonProps | `ButtonProps` | `undefined` | 取消按钮的属性 | 2.27.0 |
|
||||
@ -84,7 +88,9 @@ rtl-debug.vue
|
||||
| positiveText | `string` | `undefined` | 确认按钮的文字,不填对应的按钮不会出现 | |
|
||||
| showIcon | `boolean` | `true` | 是否显示 `icon` | |
|
||||
| style | `string \| Object` | `undefined` | 样式 | |
|
||||
| title | `string \| (() => VNodeChild)` | `undefined` | 标题,可以是 `render` 函数 | |
|
||||
| title | `string \| (() => VNodeChild)` | `undefined` | 标题,可以是渲染函数 | |
|
||||
| titleClass | `string` | 标题的类名 | 2.38.2 |
|
||||
| titleStyle | `Object \| string` | 标题的样式 | 2.38.2 |
|
||||
| transformOrigin | `'mouse' \| 'center'` | `'mouse'` | 对话框动画出现的位置 | 2.34.0 |
|
||||
| type | `'error \| 'success' \| 'warning'` | `'warning'` | 对话框类型 | |
|
||||
| onAfterEnter | `() => void` | `undefined` | 出现动画完成执行的回调 | 2.33.0 |
|
||||
@ -102,13 +108,17 @@ rtl-debug.vue
|
||||
|
||||
| 名称 | 类型 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- |
|
||||
| actionClass | `string` | 操作区域的类名 | 2.38.2 |
|
||||
| actionStyle | `Object \| string` | 操作区域的样式 | 2.38.2 |
|
||||
| bordered | `boolean` | 是否显示 `border` | |
|
||||
| class | `any` | 类名 | 2.33.0 |
|
||||
| closable | `boolean` | 是否显示 `close` 图标 | |
|
||||
| closeOnEsc | `boolean` | 是否在摁下 Esc 键的时候关闭对话框 | 2.26.4 |
|
||||
| content | `string \| (() => VNodeChild)` | 对话框内容,可以是 `render` 函数 | |
|
||||
| content | `string \| (() => VNodeChild)` | 对话框内容,可以是渲染函数 | |
|
||||
| contentClass | `string` | 内容的类名 | 2.38.2 |
|
||||
| contentStyle | `Object \| string` | 内容的样式 | 2.38.2 |
|
||||
| iconPlacement | `'left' \| 'top'` | 图标的位置 | |
|
||||
| icon | `() => VNodeChild` | 对话框 `icon`,需要是 `render` 函数 | |
|
||||
| icon | `() => VNodeChild` | 对话框 `icon`,需要是渲染函数 | |
|
||||
| loading | `boolean` | 是否显示 `loading` 状态 | |
|
||||
| maskClosable | `boolean` | 是否可以通过点击 `mask` 关闭对话框 | |
|
||||
| negativeButtonProps | `ButtonProps` | 取消按钮的属性 | 2.27.0 |
|
||||
@ -117,7 +127,9 @@ rtl-debug.vue
|
||||
| positiveText | `string` | 确认按钮的文字,不填对应的按钮不会出现 | |
|
||||
| showIcon | `boolean` | 是否显示 `icon` | |
|
||||
| style | `string \| Object` | 样式 | |
|
||||
| title | `string \| (() => VNodeChild)` | 可以是 `render` 函数 | |
|
||||
| title | `string \| (() => VNodeChild)` | 可以是渲染函数 | |
|
||||
| titleClass | `string` | 标题的类名 | 2.38.2 |
|
||||
| titleStyle | `Object \| string` | 标题的样式 | 2.38.2 |
|
||||
| transformOrigin | `'mouse' \| 'center'` | 对话框动画出现的位置 | 2.34.0 |
|
||||
| type | `'error \| 'success' \| 'warning'` | 对话框类型 | |
|
||||
| onAfterEnter | `() => void \| undefined` | 出现动画完成执行的回调 | 2.33.0 |
|
||||
@ -137,18 +149,24 @@ rtl-debug.vue
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| action-class | `string` | `undefined` | 操作区域的类名 | 2.38.2 |
|
||||
| action-style | `Object \| string` | `undefined` | 操作区域的样式 | 2.38.2 |
|
||||
| bordered | `boolean` | `false` | 是否显示 `border` | |
|
||||
| closable | `boolean` | `true` | 是否显示 `close` 图标 | |
|
||||
| content | `string \| (() => VNodeChild)` | `undefined` | 对话框内容,可以是 `render` 函数 | |
|
||||
| content | `string \| (() => VNodeChild)` | `undefined` | 对话框内容,可以是渲染函数 | |
|
||||
| content-class | `string` | `undefined` | 内容的类名 | 2.38.2 |
|
||||
| content-style | `Object \| string` | `undefined` | 内容的样式 | 2.38.2 |
|
||||
| icon-placement | `'left' \| 'top'` | `'left'` | 图标放置的位置 | |
|
||||
| icon | `() => VNodeChild` | `undefined` | 需要是 `render` 函数 | |
|
||||
| icon | `() => VNodeChild` | `undefined` | 需要是渲染函数 | |
|
||||
| loading | `boolean` | `false` | 是否显示 `loading` 状态 | |
|
||||
| negative-button-props | `ButtonProps` | `undefined` | 取消按钮的属性 | 2.27.0 |
|
||||
| negative-text | `string` | `undefined` | 取消按钮的文字,不填对应的按钮不会出现 | |
|
||||
| positive-button-props | `ButtonProps` | `undefined` | 确认按钮的属性 | 2.27.0 |
|
||||
| positive-text | `string` | `undefined` | 确认按钮的文字,不填对应的按钮不会出现 | |
|
||||
| show-icon | `boolean` | `true` | 是否显示 `icon` | |
|
||||
| title | `string \| (() => VNodeChild)` | `undefined` | 对话框标题,可以是 `render` 函数 | |
|
||||
| title | `string \| (() => VNodeChild)` | `undefined` | 对话框标题,可以是渲染函数 | |
|
||||
| title-class | `string` | `undefined` | 标题的类名 | 2.38.2 |
|
||||
| title-style | `Object \| string` | `undefined` | 标题的样式 | 2.38.2 |
|
||||
| type | `'error \| 'success' \| 'warning' \| 'info'` | `'warning'` | 对话框类型 | |
|
||||
| on-close | `() => void` | `undefined` | 点击关闭时执行的回调函数 | |
|
||||
| on-negative-click | `(e: MouseEvent) => void` | `undefined` | 执行 `negative` 时执行的回调函数 | |
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { h, defineComponent, computed, type CSSProperties } from 'vue'
|
||||
import { getMargin } from 'seemly'
|
||||
import {
|
||||
InfoIcon,
|
||||
SuccessIcon,
|
||||
@ -19,7 +20,6 @@ import { dialogLight } from '../styles'
|
||||
import type { DialogTheme } from '../styles'
|
||||
import { dialogProps } from './dialogProps'
|
||||
import style from './styles/index.cssr'
|
||||
import { getMargin } from 'seemly'
|
||||
|
||||
const iconRenderMap = {
|
||||
default: () => <InfoIcon />,
|
||||
@ -205,7 +205,10 @@ export const NDialog = defineComponent({
|
||||
|
||||
const actionNode = resolveWrappedSlot(this.$slots.action, (children) =>
|
||||
children || positiveText || negativeText || action ? (
|
||||
<div class={`${mergedClsPrefix}-dialog__action`}>
|
||||
<div
|
||||
class={[`${mergedClsPrefix}-dialog__action`, this.actionClass]}
|
||||
style={this.actionStyle}
|
||||
>
|
||||
{children ||
|
||||
(action
|
||||
? [render(action)]
|
||||
@ -278,15 +281,20 @@ export const NDialog = defineComponent({
|
||||
{showIcon && mergedIconPlacement === 'top' ? (
|
||||
<div class={`${mergedClsPrefix}-dialog-icon-container`}>{icon}</div>
|
||||
) : null}
|
||||
<div class={`${mergedClsPrefix}-dialog__title`}>
|
||||
<div
|
||||
class={[`${mergedClsPrefix}-dialog__title`, this.titleClass]}
|
||||
style={this.titleStyle}
|
||||
>
|
||||
{showIcon && mergedIconPlacement === 'left' ? icon : null}
|
||||
{resolveSlot(this.$slots.header, () => [render(title)])}
|
||||
</div>
|
||||
<div
|
||||
class={[
|
||||
`${mergedClsPrefix}-dialog__content`,
|
||||
actionNode ? '' : `${mergedClsPrefix}-dialog__content--last`
|
||||
actionNode ? '' : `${mergedClsPrefix}-dialog__content--last`,
|
||||
this.contentClass
|
||||
]}
|
||||
style={this.contentStyle}
|
||||
>
|
||||
{resolveSlot(this.$slots.default, () => [render(content)])}
|
||||
</div>
|
||||
|
@ -83,14 +83,14 @@ export const NDialogProvider = defineComponent({
|
||||
props: dialogProviderProps,
|
||||
setup () {
|
||||
const dialogListRef = ref<TypeSafeDialogReactive[]>([])
|
||||
const dialogInstRefs: Record<string, DialogInst> = {}
|
||||
const dialogInstRefs: Record<string, DialogInst | undefined> = {}
|
||||
function create (options: DialogOptions = {}): DialogReactive {
|
||||
const key = createId()
|
||||
const dialogReactive = reactive({
|
||||
...options,
|
||||
key,
|
||||
destroy: () => {
|
||||
dialogInstRefs[`n-dialog-${key}`].hide()
|
||||
dialogInstRefs[`n-dialog-${key}`]?.hide()
|
||||
}
|
||||
})
|
||||
dialogListRef.value.push(dialogReactive)
|
||||
@ -114,7 +114,7 @@ export const NDialogProvider = defineComponent({
|
||||
|
||||
function destroyAll (): void {
|
||||
Object.values(dialogInstRefs).forEach((dialogInstRef) => {
|
||||
dialogInstRef.hide()
|
||||
dialogInstRef?.hide()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { PropType, VNodeChild } from 'vue'
|
||||
import type { CSSProperties, PropType, VNodeChild } from 'vue'
|
||||
import type { ButtonProps } from '../../button'
|
||||
import type { ExtractPublicPropTypes } from '../../_utils'
|
||||
import { keysOf } from '../../_utils'
|
||||
@ -30,6 +30,12 @@ const dialogProps = {
|
||||
loading: Boolean,
|
||||
bordered: Boolean,
|
||||
iconPlacement: String as PropType<IconPlacement>,
|
||||
titleClass: [String, Array] as PropType<string | Array<string | undefined>>,
|
||||
titleStyle: [String, Object] as PropType<string | CSSProperties>,
|
||||
contentClass: [String, Array] as PropType<string | Array<string | undefined>>,
|
||||
contentStyle: [String, Object] as PropType<string | CSSProperties>,
|
||||
actionClass: [String, Array] as PropType<string | Array<string | undefined>>,
|
||||
actionStyle: [String, Object] as PropType<string | CSSProperties>,
|
||||
onPositiveClick: Function as PropType<(e: MouseEvent) => void>,
|
||||
onNegativeClick: Function as PropType<(e: MouseEvent) => void>,
|
||||
onClose: Function as PropType<() => void>
|
||||
|
35
src/form/demos/enUS/feedback-style.demo.vue
Normal file
35
src/form/demos/enUS/feedback-style.demo.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<markdown>
|
||||
# Custom feedback style
|
||||
|
||||
Using `feedback-style` and `feedback-class` to custom feedback.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-form :model="formValue">
|
||||
<n-form-item
|
||||
:rule="{
|
||||
required: true,
|
||||
message: 'Centered feedback',
|
||||
type: 'string',
|
||||
trigger: ['input', 'blur']
|
||||
}"
|
||||
label="Centered feedback"
|
||||
path="input"
|
||||
feedback-style="text-align: center;"
|
||||
>
|
||||
<n-input v-model:value="formValue.input" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue'
|
||||
|
||||
type FormValue = {
|
||||
input: string | null
|
||||
}
|
||||
|
||||
const formValue: FormValue = reactive({
|
||||
input: null
|
||||
})
|
||||
</script>
|
@ -25,6 +25,7 @@ show-label.vue
|
||||
partially-apply-rules.vue
|
||||
custom-messages.vue
|
||||
dynamic.vue
|
||||
feedback-style.vue
|
||||
```
|
||||
|
||||
## API
|
||||
@ -73,6 +74,8 @@ dynamic.vue
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| feedback | `string` | `undefined` | The feedback message of the form item. If set, it will replace any result of rule-based validation. | |
|
||||
| feedback-class | `string` | `undefined` | Feedback check vertical display positioning | 2.38.2 |
|
||||
| feedback-style | `string \| object` | `undefined` | Feedback check horizontal display positioning | 2.38.2 |
|
||||
| first | `boolean` | `false` | Whether to only show the first validation error message. | |
|
||||
| ignore-path-change | `boolean` | `false` | Usually, changing `path` will cause a re-render and naive-ui will clear the validation result. Setting `ignore-path-change` to `true` will disable that behavior. | |
|
||||
| label | `string` | `undefined` | Label. | |
|
||||
|
35
src/form/demos/zhCN/feedback-style.demo.vue
Normal file
35
src/form/demos/zhCN/feedback-style.demo.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<markdown>
|
||||
# 自定义反馈样式
|
||||
|
||||
使用 `feedback-style` 和 `feedback-class` 可以自定义反馈信息的样式。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-form :model="formValue">
|
||||
<n-form-item
|
||||
:rule="{
|
||||
required: true,
|
||||
message: '居中的 feedback',
|
||||
type: 'string',
|
||||
trigger: ['input', 'blur']
|
||||
}"
|
||||
label="Feedback 居中"
|
||||
path="input"
|
||||
feedback-style="text-align: center;"
|
||||
>
|
||||
<n-input v-model:value="formValue.input" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue'
|
||||
|
||||
type FormValue = {
|
||||
input: string | null
|
||||
}
|
||||
|
||||
const formValue: FormValue = reactive({
|
||||
input: null
|
||||
})
|
||||
</script>
|
@ -25,6 +25,7 @@ show-label.vue
|
||||
partially-apply-rules.vue
|
||||
custom-messages.vue
|
||||
dynamic.vue
|
||||
feedback-style.vue
|
||||
```
|
||||
|
||||
## API
|
||||
@ -67,6 +68,8 @@ dynamic.vue
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| feedback | `string` | `undefined` | 表项的反馈信息。不设为 `undefined` 时,会覆盖规则验证的结果 | |
|
||||
| feedback-class | `string` | `undefined` | 反馈校验竖向展示定位 | 2.38.2 |
|
||||
| feedback-style | `string \| object` | `undefined` | 反馈校验横向展示定位 | 2.38.2 |
|
||||
| first | `boolean` | `false` | 是否只展示首个出错信息 | |
|
||||
| ignore-path-change | `boolean` | `false` | 通常 `path` 的改变会导致数据来源的变化,所以 naive-ui 会清空验证信息。如果不期望这个行为,可以将其置为 `true` | |
|
||||
| label | `string` | `undefined` | 标签信息 | |
|
||||
|
@ -124,6 +124,7 @@ export default defineComponent({
|
||||
})
|
||||
}
|
||||
if (formInvalid) {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
||||
reject(errors.length ? errors : undefined)
|
||||
} else {
|
||||
resolve({
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
type ExtractPropTypes,
|
||||
ref,
|
||||
provide,
|
||||
type Slot,
|
||||
inject,
|
||||
watch,
|
||||
Transition,
|
||||
@ -78,6 +79,8 @@ export const formItemProps = {
|
||||
ignorePathChange: Boolean,
|
||||
validationStatus: String as PropType<'error' | 'warning' | 'success'>,
|
||||
feedback: String,
|
||||
feedbackClass: String,
|
||||
feedbackStyle: [String, Object] as PropType<string | CSSProperties>,
|
||||
showLabel: {
|
||||
type: Boolean as PropType<boolean | undefined>,
|
||||
default: undefined
|
||||
@ -255,6 +258,7 @@ export default defineComponent({
|
||||
if (validateCallback) {
|
||||
validateCallback(errors, { warnings })
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
||||
reject(errors)
|
||||
}
|
||||
})
|
||||
@ -604,64 +608,71 @@ export default defineComponent({
|
||||
{this.mergedShowFeedback ? (
|
||||
<div
|
||||
key={this.feedbackId}
|
||||
class={`${mergedClsPrefix}-form-item-feedback-wrapper`}
|
||||
style={this.feedbackStyle}
|
||||
class={[
|
||||
`${mergedClsPrefix}-form-item-feedback-wrapper`,
|
||||
this.feedbackClass
|
||||
]}
|
||||
>
|
||||
<Transition name="fade-down-transition" mode="out-in">
|
||||
{{
|
||||
default: () => {
|
||||
const { mergedValidationStatus } = this
|
||||
return resolveWrappedSlot($slots.feedback, (children) => {
|
||||
const { feedback } = this
|
||||
const feedbackNodes =
|
||||
children || feedback ? (
|
||||
<div
|
||||
key="__feedback__"
|
||||
class={`${mergedClsPrefix}-form-item-feedback__line`}
|
||||
>
|
||||
{children || feedback}
|
||||
</div>
|
||||
) : this.renderExplains.length ? (
|
||||
this.renderExplains?.map(({ key, render }) => (
|
||||
return resolveWrappedSlot(
|
||||
$slots.feedback as Slot | undefined,
|
||||
(children) => {
|
||||
const { feedback } = this
|
||||
const feedbackNodes =
|
||||
children || feedback ? (
|
||||
<div
|
||||
key={key}
|
||||
key="__feedback__"
|
||||
class={`${mergedClsPrefix}-form-item-feedback__line`}
|
||||
>
|
||||
{render()}
|
||||
{children || feedback}
|
||||
</div>
|
||||
))
|
||||
) : this.renderExplains.length ? (
|
||||
this.renderExplains?.map(({ key, render }) => (
|
||||
<div
|
||||
key={key}
|
||||
class={`${mergedClsPrefix}-form-item-feedback__line`}
|
||||
>
|
||||
{render()}
|
||||
</div>
|
||||
))
|
||||
) : null
|
||||
return feedbackNodes ? (
|
||||
mergedValidationStatus === 'warning' ? (
|
||||
<div
|
||||
key="controlled-warning"
|
||||
class={`${mergedClsPrefix}-form-item-feedback ${mergedClsPrefix}-form-item-feedback--warning`}
|
||||
>
|
||||
{feedbackNodes}
|
||||
</div>
|
||||
) : mergedValidationStatus === 'error' ? (
|
||||
<div
|
||||
key="controlled-error"
|
||||
class={`${mergedClsPrefix}-form-item-feedback ${mergedClsPrefix}-form-item-feedback--error`}
|
||||
>
|
||||
{feedbackNodes}
|
||||
</div>
|
||||
) : mergedValidationStatus === 'success' ? (
|
||||
<div
|
||||
key="controlled-success"
|
||||
class={`${mergedClsPrefix}-form-item-feedback ${mergedClsPrefix}-form-item-feedback--success`}
|
||||
>
|
||||
{feedbackNodes}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
key="controlled-default"
|
||||
class={`${mergedClsPrefix}-form-item-feedback`}
|
||||
>
|
||||
{feedbackNodes}
|
||||
</div>
|
||||
)
|
||||
) : null
|
||||
return feedbackNodes ? (
|
||||
mergedValidationStatus === 'warning' ? (
|
||||
<div
|
||||
key="controlled-warning"
|
||||
class={`${mergedClsPrefix}-form-item-feedback ${mergedClsPrefix}-form-item-feedback--warning`}
|
||||
>
|
||||
{feedbackNodes}
|
||||
</div>
|
||||
) : mergedValidationStatus === 'error' ? (
|
||||
<div
|
||||
key="controlled-error"
|
||||
class={`${mergedClsPrefix}-form-item-feedback ${mergedClsPrefix}-form-item-feedback--error`}
|
||||
>
|
||||
{feedbackNodes}
|
||||
</div>
|
||||
) : mergedValidationStatus === 'success' ? (
|
||||
<div
|
||||
key="controlled-success"
|
||||
class={`${mergedClsPrefix}-form-item-feedback ${mergedClsPrefix}-form-item-feedback--success`}
|
||||
>
|
||||
{feedbackNodes}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
key="controlled-default"
|
||||
class={`${mergedClsPrefix}-form-item-feedback`}
|
||||
>
|
||||
{feedbackNodes}
|
||||
</div>
|
||||
)
|
||||
) : null
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
}}
|
||||
</Transition>
|
||||
|
68
src/image/demos/enUS/custom-toolbar.demo.vue
Normal file
68
src/image/demos/enUS/custom-toolbar.demo.vue
Normal file
@ -0,0 +1,68 @@
|
||||
<markdown>
|
||||
# Custom Toolbar
|
||||
|
||||
You can customize the toolbar using `render-toolbar`.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-image
|
||||
width="100"
|
||||
src="https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg"
|
||||
:render-toolbar="renderToolbar"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, h } from 'vue'
|
||||
import { OpenOutline, ClipboardOutline } from '@vicons/ionicons5'
|
||||
import { useMessage, ImageRenderToolbarProps, NButton } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const message = useMessage()
|
||||
|
||||
const url = ref('https://picsum.photos/id/10/100/100')
|
||||
|
||||
const renderToolbar = ({ nodes }: ImageRenderToolbarProps) => {
|
||||
return [
|
||||
nodes.prev,
|
||||
nodes.next,
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
circle: true,
|
||||
type: 'primary',
|
||||
style: { marginLeft: '12px' },
|
||||
onClick: () => {
|
||||
window.open(url.value)
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: () => h(OpenOutline)
|
||||
}
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
circle: true,
|
||||
type: 'primary',
|
||||
style: { marginLeft: '12px' },
|
||||
onClick: async () => {
|
||||
await navigator.clipboard.writeText(url.value)
|
||||
message.success('Copied to clipboard')
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: () => h(ClipboardOutline)
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
return {
|
||||
url,
|
||||
renderToolbar
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
@ -9,6 +9,7 @@ basic.vue
|
||||
group.vue
|
||||
error.vue
|
||||
preview-disabled.vue
|
||||
custom-toolbar.vue
|
||||
custom.vue
|
||||
tooltip.vue
|
||||
lazy.vue
|
||||
@ -32,6 +33,7 @@ previewed-img-props.vue
|
||||
| preview-src | `string` | `undefined` | Source of preview image. | |
|
||||
| preview-disabled | `boolean` | `false` | Whether clicking image preview is disabled. | |
|
||||
| previewed-img-props | `HTMLAttributes` | `undefined` | DOM attributes of img element in preview mode. | 2.34.0 |
|
||||
| render-toolbar | `(props: { nodes: { prev: VNode, next: VNode, rotateCounterclockwise: VNode, rotateClockwise: VNode, resizeToOriginalSize: VNode, zoomOut: VNode, zoomIn: VNode, download: VNode, close: VNode } }) => VNodeChild` | `undefined` | Toolbar rendering function. | `2.38.2` |
|
||||
| show-toolbar | `boolean` | `true` | Whether to show the bottom toolbar when the image enlarge. | |
|
||||
| show-toolbar-tooltip | `boolean` | `false` | Whether to show toolbar buttons' tooltip. | 2.24.0 |
|
||||
| src | `string` | `undefined` | Image source. | |
|
||||
@ -43,10 +45,11 @@ previewed-img-props.vue
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| on-preview-prev | `() => void` | `undefined` | Click the callback from the previous slide | |
|
||||
| on-preview-next | `() => void` | `undefined` | Click the callback on the next slide |
|
||||
| render-toolbar | `(props: { nodes: { prev: VNode, next: VNode, rotateCounterclockwise: VNode, rotateClockwise: VNode, resizeToOriginalSize: VNode, zoomOut: VNode, zoomIn: VNode, download: VNode, close: VNode } }) => VNodeChild` | `undefined` | Toolbar rendering function. | `2.38.2` |
|
||||
| show-toolbar | `boolean` | `true` | Whether to show the bottom toolbar when the image enlarge. | |
|
||||
| show-toolbar-tooltip | `boolean` | `false` | Whether to show toolbar buttons' tooltip. | 2.24.0 |
|
||||
| on-preview-prev | `() => void` | `undefined` | Click the callback from the previous slide | |
|
||||
| on-preview-next | `() => void` | `undefined` | Click the callback on the next slide |
|
||||
|
||||
### Image Slots
|
||||
|
||||
|
67
src/image/demos/zhCN/custom-toolbar.demo.vue
Normal file
67
src/image/demos/zhCN/custom-toolbar.demo.vue
Normal file
@ -0,0 +1,67 @@
|
||||
<markdown>
|
||||
# 自定义工具栏
|
||||
|
||||
你可以使用 `render-toolbar` 来自定义工具栏。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-image
|
||||
width="100"
|
||||
src="https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg"
|
||||
:render-toolbar="renderToolbar"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, h } from 'vue'
|
||||
import { OpenOutline, ClipboardOutline } from '@vicons/ionicons5'
|
||||
import { useMessage, ImageRenderToolbarProps, NButton } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const message = useMessage()
|
||||
|
||||
const url = ref('https://picsum.photos/id/10/100/100')
|
||||
|
||||
const renderToolbar = ({ nodes }: ImageRenderToolbarProps) => {
|
||||
return [
|
||||
nodes.prev,
|
||||
nodes.next,
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
circle: true,
|
||||
type: 'primary',
|
||||
style: { marginLeft: '12px' },
|
||||
onClick: () => {
|
||||
window.open(url.value)
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: () => h(OpenOutline)
|
||||
}
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
circle: true,
|
||||
type: 'primary',
|
||||
style: { marginLeft: '12px' },
|
||||
onClick: async () => {
|
||||
await navigator.clipboard.writeText(url.value)
|
||||
message.success('已复制到剪贴板')
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: () => h(ClipboardOutline)
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
return {
|
||||
url,
|
||||
renderToolbar
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
@ -9,6 +9,7 @@ basic.vue
|
||||
group.vue
|
||||
error.vue
|
||||
preview-disabled.vue
|
||||
custom-toolbar.vue
|
||||
custom.vue
|
||||
tooltip.vue
|
||||
full-debug.vue
|
||||
@ -32,6 +33,7 @@ previewed-img-props.vue
|
||||
| preview-src | `string` | `undefined` | 预览图片的图片地址 | |
|
||||
| preview-disabled | `boolean` | `false` | 是否禁用单击图像预览 | |
|
||||
| previewed-img-props | `HTMLAttributes` | `undefined` | 预览图片时 img 元素的属性 | 2.34.0 |
|
||||
| render-toolbar | `(props: { nodes: { prev: VNode, next: VNode, rotateCounterclockwise: VNode, rotateClockwise: VNode, resizeToOriginalSize: VNode, zoomOut: VNode, zoomIn: VNode, download: VNode, close: VNode } }) => VNodeChild` | `undefined` | 工具栏的渲染函数 | `2.38.2` |
|
||||
| show-toolbar | `boolean` | `true` | 图片放大后是否展示底部工具栏 | |
|
||||
| show-toolbar-tooltip | `boolean` | `false` | 是否展示工具栏的提示 | 2.24.0 |
|
||||
| src | `string` | `undefined` | 图片来源 | |
|
||||
@ -43,10 +45,11 @@ previewed-img-props.vue
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| on-preview-prev | `() => void` | `undefined` | 点击上一张的回调 | |
|
||||
| on-preview-next | `() => void` | `undefined` | 点击下一张的回调 | |
|
||||
| render-toolbar | `(props: { nodes: { prev: VNode, next: VNode, rotateCounterclockwise: VNode, rotateClockwise: VNode, resizeToOriginalSize: VNode, zoomOut: VNode, zoomIn: VNode, download: VNode, close: VNode } }) => VNodeChild` | `undefined` | 工具栏的渲染函数 | `2.38.2` |
|
||||
| show-toolbar | `boolean` | `true` | 图片放大后是否展示底部工具栏 | |
|
||||
| show-toolbar-tooltip | `boolean` | `false` | 是否展示工具栏的提示 | 2.24.0 |
|
||||
| on-preview-prev | `() => void` | `undefined` | 点击上一张的回调 | |
|
||||
| on-preview-next | `() => void` | `undefined` | 点击下一张的回调 | |
|
||||
|
||||
### Image Slots
|
||||
|
||||
|
@ -2,3 +2,4 @@ export { default as NImage, imageProps } from './src/Image'
|
||||
export type { ImageProps } from './src/Image'
|
||||
export { default as NImageGroup, imageGroupProps } from './src/ImageGroup'
|
||||
export type { ImageGroupProps } from './src/ImageGroup'
|
||||
export type * from './src/public-types'
|
||||
|
@ -206,9 +206,11 @@ export default defineComponent({
|
||||
ref="previewInstRef"
|
||||
showToolbar={this.showToolbar}
|
||||
showToolbarTooltip={this.showToolbarTooltip}
|
||||
renderToolbar={this.renderToolbar}
|
||||
>
|
||||
{{
|
||||
default: () => imgNode
|
||||
default: () => imgNode,
|
||||
toolbar: () => this.$slots.toolbar?.()
|
||||
}}
|
||||
</NImagePreview>
|
||||
)}
|
||||
|
@ -4,7 +4,8 @@ import {
|
||||
ref,
|
||||
provide,
|
||||
getCurrentInstance,
|
||||
type Ref
|
||||
type Ref,
|
||||
toRef
|
||||
} from 'vue'
|
||||
import { createId } from 'seemly'
|
||||
import { createInjectionKey, type ExtractPublicPropTypes } from '../../_utils'
|
||||
@ -12,11 +13,13 @@ import { useConfig } from '../../_mixins'
|
||||
import NImagePreview from './ImagePreview'
|
||||
import type { ImagePreviewInst } from './ImagePreview'
|
||||
import { imagePreviewSharedProps } from './interface'
|
||||
import type { ImageRenderToolbar } from './public-types'
|
||||
|
||||
export const imageGroupInjectionKey = createInjectionKey<
|
||||
ImagePreviewInst & {
|
||||
groupId: string
|
||||
mergedClsPrefixRef: Ref<string>
|
||||
renderToolbarRef: Ref<ImageRenderToolbar | undefined>
|
||||
}
|
||||
>('n-image-group')
|
||||
|
||||
@ -67,7 +70,8 @@ export default defineComponent({
|
||||
toggleShow: () => {
|
||||
previewInstRef.value?.toggleShow()
|
||||
},
|
||||
groupId
|
||||
groupId,
|
||||
renderToolbarRef: toRef(props, 'renderToolbar')
|
||||
})
|
||||
const previewInstRef = ref<ImagePreviewInst | null>(null)
|
||||
return {
|
||||
@ -92,6 +96,7 @@ export default defineComponent({
|
||||
onNext={this.next}
|
||||
showToolbar={this.showToolbar}
|
||||
showToolbarTooltip={this.showToolbarTooltip}
|
||||
renderToolbar={this.renderToolbar}
|
||||
>
|
||||
{this.$slots}
|
||||
</NImagePreview>
|
||||
|
@ -27,14 +27,15 @@ import {
|
||||
RotateCounterclockwiseIcon,
|
||||
ZoomInIcon,
|
||||
ZoomOutIcon,
|
||||
ResizeSmallIcon
|
||||
ResizeSmallIcon,
|
||||
DownloadIcon
|
||||
} from '../../_internal/icons'
|
||||
import { useConfig, useLocale, useTheme, useThemeClass } from '../../_mixins'
|
||||
import { NBaseIcon } from '../../_internal'
|
||||
import { download } from '../../_utils'
|
||||
import { NTooltip } from '../../tooltip'
|
||||
import { imageLight } from '../styles'
|
||||
import { prevIcon, nextIcon, closeIcon, downloadIcon } from './icons'
|
||||
import { prevIcon, nextIcon, closeIcon } from './icons'
|
||||
import {
|
||||
imageContextKey,
|
||||
type MoveStrategy,
|
||||
@ -485,7 +486,75 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { clsPrefix } = this
|
||||
const { clsPrefix, renderToolbar, withTooltip } = this
|
||||
|
||||
const prevNode = withTooltip(
|
||||
<NBaseIcon clsPrefix={clsPrefix} onClick={this.handleSwitchPrev}>
|
||||
{{ default: () => prevIcon }}
|
||||
</NBaseIcon>,
|
||||
'tipPrevious'
|
||||
)
|
||||
const nextNode = withTooltip(
|
||||
<NBaseIcon clsPrefix={clsPrefix} onClick={this.handleSwitchNext}>
|
||||
{{ default: () => nextIcon }}
|
||||
</NBaseIcon>,
|
||||
'tipNext'
|
||||
)
|
||||
|
||||
const rotateCounterclockwiseNode = withTooltip(
|
||||
<NBaseIcon clsPrefix={clsPrefix} onClick={this.rotateCounterclockwise}>
|
||||
{{
|
||||
default: () => <RotateCounterclockwiseIcon />
|
||||
}}
|
||||
</NBaseIcon>,
|
||||
'tipCounterclockwise'
|
||||
)
|
||||
const rotateClockwiseNode = withTooltip(
|
||||
<NBaseIcon clsPrefix={clsPrefix} onClick={this.rotateClockwise}>
|
||||
{{
|
||||
default: () => <RotateClockwiseIcon />
|
||||
}}
|
||||
</NBaseIcon>,
|
||||
'tipClockwise'
|
||||
)
|
||||
const originalSizeNode = withTooltip(
|
||||
<NBaseIcon clsPrefix={clsPrefix} onClick={this.resizeToOrignalImageSize}>
|
||||
{{
|
||||
default: () => {
|
||||
return <ResizeSmallIcon />
|
||||
}
|
||||
}}
|
||||
</NBaseIcon>,
|
||||
'tipOriginalSize'
|
||||
)
|
||||
const zoomOutNode = withTooltip(
|
||||
<NBaseIcon clsPrefix={clsPrefix} onClick={this.zoomOut}>
|
||||
{{ default: () => <ZoomOutIcon /> }}
|
||||
</NBaseIcon>,
|
||||
'tipZoomOut'
|
||||
)
|
||||
|
||||
const downloadNode = withTooltip(
|
||||
<NBaseIcon clsPrefix={clsPrefix} onClick={this.handleDownloadClick}>
|
||||
{{ default: () => <DownloadIcon /> }}
|
||||
</NBaseIcon>,
|
||||
'tipDownload'
|
||||
)
|
||||
|
||||
const closeNode = withTooltip(
|
||||
<NBaseIcon clsPrefix={clsPrefix} onClick={this.toggleShow}>
|
||||
{{ default: () => closeIcon }}
|
||||
</NBaseIcon>,
|
||||
'tipClose'
|
||||
)
|
||||
|
||||
const zoomInNode = withTooltip(
|
||||
<NBaseIcon clsPrefix={clsPrefix} onClick={this.zoomIn}>
|
||||
{{ default: () => <ZoomInIcon /> }}
|
||||
</NBaseIcon>,
|
||||
'tipZoomIn'
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.$slots.default?.()}
|
||||
@ -521,103 +590,39 @@ export default defineComponent({
|
||||
{{
|
||||
default: () => {
|
||||
if (!this.show) return null
|
||||
const { withTooltip } = this
|
||||
return (
|
||||
<div class={`${clsPrefix}-image-preview-toolbar`}>
|
||||
{this.onPrev ? (
|
||||
{renderToolbar ? (
|
||||
renderToolbar({
|
||||
nodes: {
|
||||
prev: prevNode,
|
||||
next: nextNode,
|
||||
rotateCounterclockwise:
|
||||
rotateCounterclockwiseNode,
|
||||
rotateClockwise: rotateClockwiseNode,
|
||||
resizeToOriginalSize: originalSizeNode,
|
||||
zoomOut: zoomOutNode,
|
||||
zoomIn: zoomInNode,
|
||||
download: downloadNode,
|
||||
close: closeNode
|
||||
}
|
||||
})
|
||||
) : (
|
||||
<>
|
||||
{withTooltip(
|
||||
<NBaseIcon
|
||||
clsPrefix={clsPrefix}
|
||||
onClick={this.handleSwitchPrev}
|
||||
>
|
||||
{{ default: () => prevIcon }}
|
||||
</NBaseIcon>,
|
||||
'tipPrevious'
|
||||
)}
|
||||
{withTooltip(
|
||||
<NBaseIcon
|
||||
clsPrefix={clsPrefix}
|
||||
onClick={this.handleSwitchNext}
|
||||
>
|
||||
{{ default: () => nextIcon }}
|
||||
</NBaseIcon>,
|
||||
'tipNext'
|
||||
)}
|
||||
{this.onPrev ? (
|
||||
<>
|
||||
{prevNode}
|
||||
{nextNode}
|
||||
</>
|
||||
) : null}
|
||||
{rotateCounterclockwiseNode}
|
||||
{rotateClockwiseNode}
|
||||
{originalSizeNode}
|
||||
{zoomOutNode}
|
||||
{zoomInNode}
|
||||
{downloadNode}
|
||||
{closeNode}
|
||||
</>
|
||||
) : null}
|
||||
{withTooltip(
|
||||
<NBaseIcon
|
||||
clsPrefix={clsPrefix}
|
||||
onClick={this.rotateCounterclockwise}
|
||||
>
|
||||
{{
|
||||
default: () => (
|
||||
<RotateCounterclockwiseIcon />
|
||||
)
|
||||
}}
|
||||
</NBaseIcon>,
|
||||
'tipCounterclockwise'
|
||||
)}
|
||||
{withTooltip(
|
||||
<NBaseIcon
|
||||
clsPrefix={clsPrefix}
|
||||
onClick={this.rotateClockwise}
|
||||
>
|
||||
{{
|
||||
default: () => <RotateClockwiseIcon />
|
||||
}}
|
||||
</NBaseIcon>,
|
||||
'tipClockwise'
|
||||
)}
|
||||
{withTooltip(
|
||||
<NBaseIcon
|
||||
clsPrefix={clsPrefix}
|
||||
onClick={this.resizeToOrignalImageSize}
|
||||
>
|
||||
{{
|
||||
default: () => {
|
||||
return <ResizeSmallIcon />
|
||||
}
|
||||
}}
|
||||
</NBaseIcon>,
|
||||
'tipOriginalSize'
|
||||
)}
|
||||
{withTooltip(
|
||||
<NBaseIcon
|
||||
clsPrefix={clsPrefix}
|
||||
onClick={this.zoomOut}
|
||||
>
|
||||
{{ default: () => <ZoomOutIcon /> }}
|
||||
</NBaseIcon>,
|
||||
'tipZoomOut'
|
||||
)}
|
||||
{withTooltip(
|
||||
<NBaseIcon
|
||||
clsPrefix={clsPrefix}
|
||||
onClick={this.zoomIn}
|
||||
>
|
||||
{{ default: () => <ZoomInIcon /> }}
|
||||
</NBaseIcon>,
|
||||
'tipZoomIn'
|
||||
)}
|
||||
{withTooltip(
|
||||
<NBaseIcon
|
||||
clsPrefix={clsPrefix}
|
||||
onClick={this.handleDownloadClick}
|
||||
>
|
||||
{{ default: () => downloadIcon }}
|
||||
</NBaseIcon>,
|
||||
'tipDownload'
|
||||
)}
|
||||
{withTooltip(
|
||||
<NBaseIcon
|
||||
clsPrefix={clsPrefix}
|
||||
onClick={this.toggleShow}
|
||||
>
|
||||
{{ default: () => closeIcon }}
|
||||
</NBaseIcon>,
|
||||
'tipClose'
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
@ -26,17 +26,3 @@ export const closeIcon = (
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const downloadIcon = (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 1024 1024"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
@ -3,6 +3,7 @@ import type { ThemeProps } from '../../_mixins'
|
||||
import { useTheme } from '../../_mixins'
|
||||
import { createInjectionKey } from '../../_utils'
|
||||
import type { ImageTheme } from '../styles'
|
||||
import type { ImageRenderToolbar } from './public-types'
|
||||
|
||||
export interface MoveStrategy {
|
||||
moveVerticalDirection: 'verticalTop' | 'verticalBottom'
|
||||
@ -16,7 +17,8 @@ export const imagePreviewSharedProps = {
|
||||
onPreviewPrev: Function as PropType<() => void>,
|
||||
onPreviewNext: Function as PropType<() => void>,
|
||||
showToolbar: { type: Boolean, default: true },
|
||||
showToolbarTooltip: Boolean
|
||||
showToolbarTooltip: Boolean,
|
||||
renderToolbar: Function as PropType<ImageRenderToolbar>
|
||||
}
|
||||
|
||||
export interface ImageContext {
|
||||
|
19
src/image/src/public-types.ts
Normal file
19
src/image/src/public-types.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import type { VNode, VNodeChild } from 'vue'
|
||||
|
||||
export interface ImageRenderToolbarProps {
|
||||
nodes: {
|
||||
prev: VNode
|
||||
next: VNode
|
||||
rotateCounterclockwise: VNode
|
||||
rotateClockwise: VNode
|
||||
resizeToOriginalSize: VNode
|
||||
zoomOut: VNode
|
||||
zoomIn: VNode
|
||||
download: VNode
|
||||
close: VNode
|
||||
}
|
||||
}
|
||||
|
||||
export type ImageRenderToolbar = (props: ImageRenderToolbarProps) => VNodeChild
|
||||
export type ImageGroupRenderToolbarProps = ImageRenderToolbarProps
|
||||
export type ImageGroupRenderToolbar = ImageRenderToolbar
|
43
src/infinite-scroll/demos/enUS/basic.demo.vue
Normal file
43
src/infinite-scroll/demos/enUS/basic.demo.vue
Normal file
@ -0,0 +1,43 @@
|
||||
<markdown>
|
||||
# Basic
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-infinite-scroll style="height: 240px" :distance="10" @load="handleLoad">
|
||||
<div v-for="i in count" :key="i" class="item">
|
||||
{{ i }}
|
||||
</div>
|
||||
</n-infinite-scroll>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const count = ref(6)
|
||||
const handleLoad = () => {
|
||||
count.value += 1
|
||||
}
|
||||
return {
|
||||
count,
|
||||
handleLoad
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 46px;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
background-color: #e7f5ee;
|
||||
}
|
||||
|
||||
.item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
102
src/infinite-scroll/demos/enUS/chat.demo.vue
Normal file
102
src/infinite-scroll/demos/enUS/chat.demo.vue
Normal file
@ -0,0 +1,102 @@
|
||||
<markdown>
|
||||
# A bit complex example
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-infinite-scroll style="height: 240px" :distance="10" @load="handleLoad">
|
||||
<div
|
||||
v-for="(item, index) in items"
|
||||
:key="item.key"
|
||||
class="message"
|
||||
:class="{ reverse: index % 5 === 0 }"
|
||||
>
|
||||
<img class="avatar" :src="item.avatar" alt="">
|
||||
<span> {{ item.message }} {{ index % 5 === 0 ? '?' : '' }}</span>
|
||||
</div>
|
||||
<div v-if="loading" class="text">
|
||||
Loading...
|
||||
</div>
|
||||
<div v-if="noMore" class="text">
|
||||
No More 🤪
|
||||
</div>
|
||||
</n-infinite-scroll>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const loading = ref(false)
|
||||
const noMore = computed(() => items.value.length > 16)
|
||||
|
||||
const avatars = [
|
||||
'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg',
|
||||
'https://avatars.githubusercontent.com/u/20943608?s=60&v=4',
|
||||
'https://avatars.githubusercontent.com/u/46394163?s=60&v=4',
|
||||
'https://avatars.githubusercontent.com/u/39197136?s=60&v=4',
|
||||
'https://avatars.githubusercontent.com/u/19239641?s=60&v=4'
|
||||
]
|
||||
|
||||
const messages = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
|
||||
|
||||
const mock = (i: number) => ({
|
||||
key: `${i}`,
|
||||
value: i,
|
||||
avatar: avatars[i % avatars.length],
|
||||
message: messages[Math.floor(Math.random() * messages.length)]
|
||||
})
|
||||
|
||||
const items = ref(Array.from({ length: 10 }, (_, i) => mock(i)))
|
||||
|
||||
const handleLoad = async () => {
|
||||
if (loading.value || noMore.value) return
|
||||
loading.value = true
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
items.value.push(
|
||||
...[mock(items.value.length), mock(items.value.length + 1)]
|
||||
)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
items,
|
||||
noMore,
|
||||
loading,
|
||||
handleLoad
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.message:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.reverse {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.text {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.reverse .avatar {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
22
src/infinite-scroll/demos/enUS/index.demo-entry.md
Normal file
22
src/infinite-scroll/demos/enUS/index.demo-entry.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Infinite Scroll
|
||||
|
||||
Scroll, scroll, scroll, scroll...
|
||||
|
||||
Available since `2.38.2`.
|
||||
|
||||
## Demos
|
||||
|
||||
```demo
|
||||
basic.vue
|
||||
chat.vue
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Infinite Scroll Props
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| distance | `number` | `0` | Distance threshold that triggers loading. | 2.38.2 |
|
||||
| scrollbar-props | `Object` | `undefined` | Attribute reference [Scrollbar props](scrollbar#Scrollbar-Props). | 2.38.2 |
|
||||
| on-load | `() => Promise<void> \| void` | `undefined` | The callback function when scrolling to the bottom. | 2.38.2 |
|
43
src/infinite-scroll/demos/zhCN/basic.demo.vue
Normal file
43
src/infinite-scroll/demos/zhCN/basic.demo.vue
Normal file
@ -0,0 +1,43 @@
|
||||
<markdown>
|
||||
# 基础
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-infinite-scroll style="height: 240px" :distance="10" @load="handleLoad">
|
||||
<div v-for="i in count" :key="i" class="item">
|
||||
{{ i }}
|
||||
</div>
|
||||
</n-infinite-scroll>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const count = ref(6)
|
||||
const handleLoad = () => {
|
||||
count.value += 1
|
||||
}
|
||||
return {
|
||||
count,
|
||||
handleLoad
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 46px;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
background-color: #e7f5ee;
|
||||
}
|
||||
|
||||
.item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
102
src/infinite-scroll/demos/zhCN/chat.demo.vue
Normal file
102
src/infinite-scroll/demos/zhCN/chat.demo.vue
Normal file
@ -0,0 +1,102 @@
|
||||
<markdown>
|
||||
# 稍微复杂的例子
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-infinite-scroll style="height: 240px" :distance="10" @load="handleLoad">
|
||||
<div
|
||||
v-for="(item, index) in items"
|
||||
:key="item.key"
|
||||
class="message"
|
||||
:class="{ reverse: index % 5 === 0 }"
|
||||
>
|
||||
<img class="avatar" :src="item.avatar" alt="">
|
||||
<span> {{ item.message }} {{ index % 5 === 0 ? '?' : '' }}</span>
|
||||
</div>
|
||||
<div v-if="loading" class="text">
|
||||
加载中...
|
||||
</div>
|
||||
<div v-if="noMore" class="text">
|
||||
没有更多了 🤪
|
||||
</div>
|
||||
</n-infinite-scroll>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const loading = ref(false)
|
||||
const noMore = computed(() => items.value.length > 16)
|
||||
|
||||
const avatars = [
|
||||
'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg',
|
||||
'https://avatars.githubusercontent.com/u/20943608?s=60&v=4',
|
||||
'https://avatars.githubusercontent.com/u/46394163?s=60&v=4',
|
||||
'https://avatars.githubusercontent.com/u/39197136?s=60&v=4',
|
||||
'https://avatars.githubusercontent.com/u/19239641?s=60&v=4'
|
||||
]
|
||||
|
||||
const messages = ['星期一', '星期二', '星期三', '星期四', '星期五']
|
||||
|
||||
const mock = (i: number) => ({
|
||||
key: `${i}`,
|
||||
value: i,
|
||||
avatar: avatars[i % avatars.length],
|
||||
message: messages[Math.floor(Math.random() * messages.length)]
|
||||
})
|
||||
|
||||
const items = ref(Array.from({ length: 10 }, (_, i) => mock(i)))
|
||||
|
||||
const handleLoad = async () => {
|
||||
if (loading.value || noMore.value) return
|
||||
loading.value = true
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
items.value.push(
|
||||
...[mock(items.value.length), mock(items.value.length + 1)]
|
||||
)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
items,
|
||||
noMore,
|
||||
loading,
|
||||
handleLoad
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.message:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.reverse {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.text {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.reverse .avatar {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
22
src/infinite-scroll/demos/zhCN/index.demo-entry.md
Normal file
22
src/infinite-scroll/demos/zhCN/index.demo-entry.md
Normal file
@ -0,0 +1,22 @@
|
||||
# 无限滚动
|
||||
|
||||
滚雪球,滚啊滚,内容越来越多,停不下来。
|
||||
|
||||
`2.38.2` 版本开始提供该组件。
|
||||
|
||||
## 演示
|
||||
|
||||
```demo
|
||||
basic.vue
|
||||
chat.vue
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Infinite Scroll Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| distance | `number` | `0` | 触发加载的距离阈值 | 2.38.2 |
|
||||
| scrollbar-props | `Object` | `undefined` | 属性参考 [Scrollbar props](scrollbar#Scrollbar-Props) | 2.38.2 |
|
||||
| on-load | `() => Promise<void> \| void` | `undefined` | 滚动到底部时的回调函数 | 2.38.2 |
|
5
src/infinite-scroll/index.ts
Normal file
5
src/infinite-scroll/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export {
|
||||
default as NInfiniteScroll,
|
||||
infiniteScrollProps
|
||||
} from './src/InfiniteScroll'
|
||||
export type { InfiniteScrollProps } from './src/InfiniteScroll'
|
86
src/infinite-scroll/src/InfiniteScroll.tsx
Normal file
86
src/infinite-scroll/src/InfiniteScroll.tsx
Normal file
@ -0,0 +1,86 @@
|
||||
import { h, defineComponent, type PropType, ref } from 'vue'
|
||||
import type { ExtractPublicPropTypes } from '../../_utils'
|
||||
import { resolveSlot } from '../../_utils'
|
||||
import { type ScrollbarProps } from '../../scrollbar/src/Scrollbar'
|
||||
import { NxScrollbar, type ScrollbarInst } from '../../_internal'
|
||||
|
||||
export const infiniteScrollProps = {
|
||||
distance: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
onLoad: Function as PropType<() => Promise<void> | void>,
|
||||
scrollbarProps: Object as PropType<ScrollbarProps>
|
||||
} as const
|
||||
|
||||
export type InfiniteScrollProps = ExtractPublicPropTypes<
|
||||
typeof infiniteScrollProps
|
||||
>
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InfiniteScroll',
|
||||
props: infiniteScrollProps,
|
||||
setup (props) {
|
||||
const scrollbarInstRef = ref<ScrollbarInst | null>(null)
|
||||
|
||||
let loading = false
|
||||
|
||||
const handleCheckBottom = async (): Promise<void> => {
|
||||
const { value: scrollbarInst } = scrollbarInstRef
|
||||
if (scrollbarInst) {
|
||||
const { containerRef, containerScrollTop } = scrollbarInst
|
||||
const scrollHeight = containerRef?.scrollHeight
|
||||
const clientHeight = containerRef?.clientHeight
|
||||
if (
|
||||
containerRef &&
|
||||
scrollHeight !== undefined &&
|
||||
clientHeight !== undefined
|
||||
) {
|
||||
if (
|
||||
containerScrollTop + clientHeight >=
|
||||
scrollHeight - props.distance
|
||||
) {
|
||||
loading = true
|
||||
try {
|
||||
await props.onLoad?.()
|
||||
} catch {}
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleScroll = (): void => {
|
||||
if (loading) return
|
||||
void handleCheckBottom()
|
||||
}
|
||||
|
||||
const handleWheel = (e: WheelEvent): void => {
|
||||
if (e.deltaY <= 0) return
|
||||
if (loading) return
|
||||
void handleCheckBottom()
|
||||
}
|
||||
|
||||
return {
|
||||
scrollbarInstRef,
|
||||
handleScroll,
|
||||
handleWheel
|
||||
}
|
||||
},
|
||||
render () {
|
||||
return (
|
||||
<NxScrollbar
|
||||
{...this.scrollbarProps}
|
||||
ref="scrollbarInstRef"
|
||||
onWheel={this.handleWheel}
|
||||
onScroll={this.handleScroll}
|
||||
>
|
||||
{{
|
||||
default: () => {
|
||||
return resolveSlot(this.$slots.default, () => [])
|
||||
}
|
||||
}}
|
||||
</NxScrollbar>
|
||||
)
|
||||
}
|
||||
})
|
@ -2161,6 +2161,150 @@ exports[`locale works 15`] = `
|
||||
`;
|
||||
|
||||
exports[`locale works 16`] = `
|
||||
"<div class="n-config-provider">
|
||||
<div>
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);">
|
||||
<div class="n-input-wrapper">
|
||||
<!---->
|
||||
<div class="n-input__input"><input type="text" class="n-input__input-el" placeholder="Zadejte" size="20">
|
||||
<div class="n-input__placeholder"><span>Zadejte</span></div>
|
||||
<!---->
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="n-input__border"></div>
|
||||
<div class="n-input__state-border"></div>
|
||||
<!---->
|
||||
</div>
|
||||
<div>
|
||||
<div class="n-date-picker" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-icon-color-override: rgba(194, 194, 194, 1); --n-icon-color-disabled-override: rgba(209, 209, 209, 1);">
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);" tabindex="0">
|
||||
<div class="n-input-wrapper">
|
||||
<!---->
|
||||
<div class="n-input__input"><input type="text" class="n-input__input-el" tabindex="-1" placeholder="Vyberte čas" size="20">
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
<div class="n-input__suffix">
|
||||
<!---->
|
||||
<!---->
|
||||
<!----><i class="n-base-icon n-date-picker-icon"><svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" stroke-width="1" fill-rule="evenodd">
|
||||
<g fill-rule="nonzero">
|
||||
<path d="M21.75,3 C23.5449254,3 25,4.45507456 25,6.25 L25,21.75 C25,23.5449254 23.5449254,25 21.75,25 L6.25,25 C4.45507456,25 3,23.5449254 3,21.75 L3,6.25 C3,4.45507456 4.45507456,3 6.25,3 L21.75,3 Z M23.5,9.503 L4.5,9.503 L4.5,21.75 C4.5,22.7164983 5.28350169,23.5 6.25,23.5 L21.75,23.5 C22.7164983,23.5 23.5,22.7164983 23.5,21.75 L23.5,9.503 Z M21.75,4.5 L6.25,4.5 C5.28350169,4.5 4.5,5.28350169 4.5,6.25 L4.5,8.003 L23.5,8.003 L23.5,6.25 C23.5,5.28350169 22.7164983,4.5 21.75,4.5 Z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg></i>
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="n-input__border"></div>
|
||||
<div class="n-input__state-border"></div>
|
||||
<!---->
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
<div class="n-date-picker" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-icon-color-override: rgba(194, 194, 194, 1); --n-icon-color-disabled-override: rgba(209, 209, 209, 1);">
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);" tabindex="0">
|
||||
<div class="n-input-wrapper">
|
||||
<!---->
|
||||
<div class="n-input__input"><input type="text" class="n-input__input-el" tabindex="-1" placeholder="Vyberte datum a čas" size="20">
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
<div class="n-input__suffix">
|
||||
<!---->
|
||||
<!---->
|
||||
<!----><i class="n-base-icon n-date-picker-icon"><svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" stroke-width="1" fill-rule="evenodd">
|
||||
<g fill-rule="nonzero">
|
||||
<path d="M21.75,3 C23.5449254,3 25,4.45507456 25,6.25 L25,21.75 C25,23.5449254 23.5449254,25 21.75,25 L6.25,25 C4.45507456,25 3,23.5449254 3,21.75 L3,6.25 C3,4.45507456 4.45507456,3 6.25,3 L21.75,3 Z M23.5,9.503 L4.5,9.503 L4.5,21.75 C4.5,22.7164983 5.28350169,23.5 6.25,23.5 L21.75,23.5 C22.7164983,23.5 23.5,22.7164983 23.5,21.75 L23.5,9.503 Z M21.75,4.5 L6.25,4.5 C5.28350169,4.5 4.5,5.28350169 4.5,6.25 L4.5,8.003 L23.5,8.003 L23.5,6.25 C23.5,5.28350169 22.7164983,4.5 21.75,4.5 Z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg></i>
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="n-input__border"></div>
|
||||
<div class="n-input__state-border"></div>
|
||||
<!---->
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
<div class="n-date-picker" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-icon-color-override: rgba(194, 194, 194, 1); --n-icon-color-disabled-override: rgba(209, 209, 209, 1);">
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);" tabindex="0">
|
||||
<div class="n-input-wrapper">
|
||||
<!---->
|
||||
<div class="n-input__input"><input type="text" class="n-input__input-el" tabindex="-1" placeholder="Vyberte rok" size="20">
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
<div class="n-input__suffix">
|
||||
<!---->
|
||||
<!---->
|
||||
<!----><i class="n-base-icon n-date-picker-icon"><svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" stroke-width="1" fill-rule="evenodd">
|
||||
<g fill-rule="nonzero">
|
||||
<path d="M21.75,3 C23.5449254,3 25,4.45507456 25,6.25 L25,21.75 C25,23.5449254 23.5449254,25 21.75,25 L6.25,25 C4.45507456,25 3,23.5449254 3,21.75 L3,6.25 C3,4.45507456 4.45507456,3 6.25,3 L21.75,3 Z M23.5,9.503 L4.5,9.503 L4.5,21.75 C4.5,22.7164983 5.28350169,23.5 6.25,23.5 L21.75,23.5 C22.7164983,23.5 23.5,22.7164983 23.5,21.75 L23.5,9.503 Z M21.75,4.5 L6.25,4.5 C5.28350169,4.5 4.5,5.28350169 4.5,6.25 L4.5,8.003 L23.5,8.003 L23.5,6.25 C23.5,5.28350169 22.7164983,4.5 21.75,4.5 Z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg></i>
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="n-input__border"></div>
|
||||
<div class="n-input__state-border"></div>
|
||||
<!---->
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
<div class="n-date-picker" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-icon-color-override: rgba(194, 194, 194, 1); --n-icon-color-disabled-override: rgba(209, 209, 209, 1);">
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);" tabindex="0">
|
||||
<div class="n-input-wrapper">
|
||||
<!---->
|
||||
<div class="n-input__input"><input type="text" class="n-input__input-el" tabindex="-1" placeholder="Vyberte měsíc" size="20">
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
<div class="n-input__suffix">
|
||||
<!---->
|
||||
<!---->
|
||||
<!----><i class="n-base-icon n-date-picker-icon"><svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" stroke-width="1" fill-rule="evenodd">
|
||||
<g fill-rule="nonzero">
|
||||
<path d="M21.75,3 C23.5449254,3 25,4.45507456 25,6.25 L25,21.75 C25,23.5449254 23.5449254,25 21.75,25 L6.25,25 C4.45507456,25 3,23.5449254 3,21.75 L3,6.25 C3,4.45507456 4.45507456,3 6.25,3 L21.75,3 Z M23.5,9.503 L4.5,9.503 L4.5,21.75 C4.5,22.7164983 5.28350169,23.5 6.25,23.5 L21.75,23.5 C22.7164983,23.5 23.5,22.7164983 23.5,21.75 L23.5,9.503 Z M21.75,4.5 L6.25,4.5 C5.28350169,4.5 4.5,5.28350169 4.5,6.25 L4.5,8.003 L23.5,8.003 L23.5,6.25 C23.5,5.28350169 22.7164983,4.5 21.75,4.5 Z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg></i>
|
||||
<!---->
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
<!---->
|
||||
<div class="n-input__border"></div>
|
||||
<div class="n-input__state-border"></div>
|
||||
<!---->
|
||||
</div>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`locale works 17`] = `
|
||||
"<div class="n-config-provider">
|
||||
<div>
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);">
|
||||
@ -2304,7 +2448,7 @@ exports[`locale works 16`] = `
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`locale works 17`] = `
|
||||
exports[`locale works 18`] = `
|
||||
"<div class="n-config-provider">
|
||||
<div>
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);">
|
||||
@ -2448,7 +2592,7 @@ exports[`locale works 17`] = `
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`locale works 18`] = `
|
||||
exports[`locale works 19`] = `
|
||||
"<div class="n-config-provider">
|
||||
<div>
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);">
|
||||
@ -2592,7 +2736,7 @@ exports[`locale works 18`] = `
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`locale works 19`] = `
|
||||
exports[`locale works 20`] = `
|
||||
"<div class="n-config-provider">
|
||||
<div>
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);">
|
||||
@ -2736,7 +2880,7 @@ exports[`locale works 19`] = `
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`locale works 20`] = `
|
||||
exports[`locale works 21`] = `
|
||||
"<div class="n-config-provider">
|
||||
<div>
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);">
|
||||
@ -2880,7 +3024,7 @@ exports[`locale works 20`] = `
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`locale works 21`] = `
|
||||
exports[`locale works 22`] = `
|
||||
"<div class="n-config-provider">
|
||||
<div>
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);">
|
||||
@ -3024,7 +3168,7 @@ exports[`locale works 21`] = `
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`locale works 22`] = `
|
||||
exports[`locale works 23`] = `
|
||||
"<div class="n-config-provider">
|
||||
<div>
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);">
|
||||
@ -3168,7 +3312,7 @@ exports[`locale works 22`] = `
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`locale works 23`] = `
|
||||
exports[`locale works 24`] = `
|
||||
"<div class="n-config-provider">
|
||||
<div>
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);">
|
||||
@ -3312,7 +3456,7 @@ exports[`locale works 23`] = `
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`locale works 24`] = `
|
||||
exports[`locale works 25`] = `
|
||||
"<div class="n-config-provider">
|
||||
<div>
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);">
|
||||
@ -3456,7 +3600,7 @@ exports[`locale works 24`] = `
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`locale works 25`] = `
|
||||
exports[`locale works 26`] = `
|
||||
"<div class="n-config-provider">
|
||||
<div>
|
||||
<div class="n-input n-input--resizable n-input--stateful" style="--n-bezier: cubic-bezier(.4, 0, .2, 1); --n-count-text-color: rgb(118, 124, 130); --n-count-text-color-disabled: rgba(194, 194, 194, 1); --n-color: rgba(255, 255, 255, 1); --n-font-size: 14px; --n-border-radius: 3px; --n-height: 34px; --n-padding-left: 12px; --n-padding-right: 12px; --n-text-color: rgb(51, 54, 57); --n-caret-color: #18a058; --n-text-decoration-color: rgb(51, 54, 57); --n-border: 1px solid rgb(224, 224, 230); --n-border-disabled: 1px solid rgb(224, 224, 230); --n-border-hover: 1px solid #36ad6a; --n-border-focus: 1px solid #36ad6a; --n-placeholder-color: rgba(194, 194, 194, 1); --n-placeholder-color-disabled: rgba(209, 209, 209, 1); --n-icon-size: 16px; --n-line-height-textarea: 1.6; --n-color-disabled: rgb(250, 250, 252); --n-color-focus: rgba(255, 255, 255, 1); --n-text-color-disabled: rgba(194, 194, 194, 1); --n-box-shadow-focus: 0 0 0 2px rgba(24, 160, 88, 0.2); --n-loading-color: #18a058; --n-caret-color-warning: #f0a020; --n-color-focus-warning: rgba(255, 255, 255, 1); --n-box-shadow-focus-warning: 0 0 0 2px rgba(240, 160, 32, 0.2); --n-border-warning: 1px solid #f0a020; --n-border-focus-warning: 1px solid #fcb040; --n-border-hover-warning: 1px solid #fcb040; --n-loading-color-warning: #f0a020; --n-caret-color-error: #d03050; --n-color-focus-error: rgba(255, 255, 255, 1); --n-box-shadow-focus-error: 0 0 0 2px rgba(208, 48, 80, 0.2); --n-border-error: 1px solid #d03050; --n-border-focus-error: 1px solid #de576d; --n-border-hover-error: 1px solid #de576d; --n-loading-color-error: #d03050; --n-clear-color: rgba(194, 194, 194, 1); --n-clear-size: 16px; --n-clear-color-hover: rgba(146, 146, 146, 1); --n-clear-color-pressed: rgba(175, 175, 175, 1); --n-icon-color: rgba(194, 194, 194, 1); --n-icon-color-hover: rgba(146, 146, 146, 1); --n-icon-color-pressed: rgba(175, 175, 175, 1); --n-icon-color-disabled: rgba(209, 209, 209, 1); --n-suffix-text-color: rgb(51, 54, 57);">
|
||||
|
128
src/locales/common/csCZ.ts
Normal file
128
src/locales/common/csCZ.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import type { NLocale } from './enUS'
|
||||
|
||||
const csCZ: NLocale = {
|
||||
name: 'cs-CZ',
|
||||
global: {
|
||||
undo: 'Zpět',
|
||||
redo: 'Obnovit',
|
||||
confirm: 'Potvrdit',
|
||||
clear: 'Vyčistit'
|
||||
},
|
||||
Popconfirm: {
|
||||
positiveText: 'Potvrdit',
|
||||
negativeText: 'Zrušit'
|
||||
},
|
||||
Cascader: {
|
||||
placeholder: 'Prosím vyberte',
|
||||
loading: 'Načítání',
|
||||
loadingRequiredMessage: (label: string): string =>
|
||||
`Prosím načtěte před kontrolou všechny potomky pro ${label}.`
|
||||
},
|
||||
Time: {
|
||||
dateFormat: 'd-M-yyyy',
|
||||
dateTimeFormat: 'd-M-yyyy HH:mm:ss'
|
||||
},
|
||||
DatePicker: {
|
||||
yearFormat: 'yyyy',
|
||||
monthFormat: 'MMM',
|
||||
dayFormat: 'EEEE',
|
||||
yearTypeFormat: 'yyyy',
|
||||
monthTypeFormat: 'MMM-yyyy',
|
||||
dateFormat: 'd-M-yyyy',
|
||||
dateTimeFormat: 'd-M-yyyy HH:mm:ss',
|
||||
quarterFormat: 'qqq-yyyy',
|
||||
weekFormat: 'yyyy-w',
|
||||
clear: 'Vyčistit',
|
||||
now: 'Teď',
|
||||
confirm: 'Potvrdit',
|
||||
selectTime: 'Vybrat čas',
|
||||
selectDate: 'Vybrat datum',
|
||||
datePlaceholder: 'Vyberte čas',
|
||||
datetimePlaceholder: 'Vyberte datum a čas',
|
||||
monthPlaceholder: 'Vyberte měsíc',
|
||||
yearPlaceholder: 'Vyberte rok',
|
||||
quarterPlaceholder: 'Vyberte čtvrtletí',
|
||||
weekPlaceholder: 'Vyberte týden',
|
||||
startDatePlaceholder: 'Datum začátku',
|
||||
endDatePlaceholder: 'Datum ukončení',
|
||||
startDatetimePlaceholder: 'Datum a čas začátku',
|
||||
endDatetimePlaceholder: 'Datum a čas ukončení ',
|
||||
startMonthPlaceholder: 'Začátek měsíce',
|
||||
endMonthPlaceholder: 'Konec měsíce',
|
||||
monthBeforeYear: true,
|
||||
firstDayOfWeek: 6 as 0 | 1 | 2 | 3 | 4 | 5 | 6,
|
||||
today: 'Dnes'
|
||||
},
|
||||
DataTable: {
|
||||
checkTableAll: 'Vybrat vše v tabulce',
|
||||
uncheckTableAll: 'Zrušit výběr všeho v tabulce ',
|
||||
confirm: 'Potvrdit',
|
||||
clear: 'Vyčistit'
|
||||
},
|
||||
LegacyTransfer: {
|
||||
sourceTitle: 'Zdroj',
|
||||
targetTitle: 'Cíl'
|
||||
},
|
||||
Transfer: {
|
||||
selectAll: 'Vybrat vše',
|
||||
unselectAll: 'Odznačit vše',
|
||||
clearAll: 'Vyčistit',
|
||||
total: (num: number): string => `Celkem ${num} položek`,
|
||||
selected: (num: number): string => `${num} položek vybráno`
|
||||
},
|
||||
Empty: {
|
||||
description: 'Žádná data'
|
||||
},
|
||||
Select: {
|
||||
placeholder: 'Prosím vyberte'
|
||||
},
|
||||
TimePicker: {
|
||||
placeholder: 'Vybrat čas',
|
||||
positiveText: 'OK',
|
||||
negativeText: 'Zrušit',
|
||||
now: 'Teď',
|
||||
clear: 'Vyčistit'
|
||||
},
|
||||
Pagination: {
|
||||
goto: 'Jít na',
|
||||
selectionSuffix: 'Strana'
|
||||
},
|
||||
DynamicTags: {
|
||||
add: 'Přidat'
|
||||
},
|
||||
Log: {
|
||||
loading: 'Načítání'
|
||||
},
|
||||
Input: {
|
||||
placeholder: 'Zadejte'
|
||||
},
|
||||
InputNumber: {
|
||||
placeholder: 'Zadejte'
|
||||
},
|
||||
DynamicInput: {
|
||||
create: 'Vytvořit'
|
||||
},
|
||||
ThemeEditor: {
|
||||
title: 'Editor témat',
|
||||
clearAllVars: 'Vymazat všechny proměnné',
|
||||
clearSearch: 'Vymazat vyhledávání',
|
||||
filterCompName: 'Filtrovat název komponenty',
|
||||
filterVarName: 'Filztrovat název proměnné',
|
||||
import: 'Importovat',
|
||||
export: 'Exportovat',
|
||||
restore: 'Obnovit původní nastavení'
|
||||
},
|
||||
Image: {
|
||||
tipPrevious: 'Předchozí obrázek (←)',
|
||||
tipNext: 'Další obrázek (→)',
|
||||
tipCounterclockwise: 'Proti směru hodinových ručiček',
|
||||
tipClockwise: 'Ve směru hodinových ručiček',
|
||||
tipZoomOut: 'Oddálit',
|
||||
tipZoomIn: 'Přiblížit',
|
||||
tipDownload: 'Stáhnout',
|
||||
tipClose: 'Zavřít (Esc)',
|
||||
tipOriginalSize: 'Přiblížit na původní velikost'
|
||||
}
|
||||
}
|
||||
|
||||
export default csCZ
|
@ -47,9 +47,8 @@ const itIT: NLocale = {
|
||||
endDatePlaceholder: 'Data fine',
|
||||
startDatetimePlaceholder: 'Data e ora di inizio',
|
||||
endDatetimePlaceholder: 'Data e ora di fine',
|
||||
// FIXME: translation needed
|
||||
startMonthPlaceholder: 'Start Month',
|
||||
endMonthPlaceholder: 'End Month',
|
||||
startMonthPlaceholder: 'Mese di inizio',
|
||||
endMonthPlaceholder: 'Mese di fine',
|
||||
monthBeforeYear: true,
|
||||
firstDayOfWeek: 0 as 0 | 1 | 2 | 3 | 4 | 5 | 6,
|
||||
today: 'Oggi'
|
||||
@ -64,13 +63,18 @@ const itIT: NLocale = {
|
||||
sourceTitle: 'Fonte',
|
||||
targetTitle: 'Destinazione'
|
||||
},
|
||||
// TODO: translation
|
||||
Transfer: {
|
||||
selectAll: 'Select all',
|
||||
unselectAll: 'Unselect all',
|
||||
clearAll: 'Clear',
|
||||
total: (num: number): string => `Total ${num} items`,
|
||||
selected: (num: number): string => `${num} items selected`
|
||||
selectAll: 'Seleziona tutto',
|
||||
unselectAll: 'Deseleziona tutto',
|
||||
clearAll: 'Pulisci',
|
||||
total: (num: number): string => {
|
||||
if (num !== 1) return `${num} elementi in totale`
|
||||
return '1 elemento in totale'
|
||||
},
|
||||
selected: (num: number): string => {
|
||||
if (num !== 1) return `${num} elementi selezionati`
|
||||
return '1 elemento selezionato'
|
||||
}
|
||||
},
|
||||
Empty: {
|
||||
description: 'Nessun Dato'
|
||||
@ -123,8 +127,7 @@ const itIT: NLocale = {
|
||||
tipZoomIn: 'Riduci',
|
||||
tipDownload: 'Download',
|
||||
tipClose: 'Chiudi (Esc)',
|
||||
// TODO: translation
|
||||
tipOriginalSize: 'Zoom to original size'
|
||||
tipOriginalSize: 'Torna alla dimensione originale'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,14 +42,13 @@ const skSK: NLocale = {
|
||||
monthPlaceholder: 'Vyberte mesiac',
|
||||
yearPlaceholder: 'Vyberte rok',
|
||||
quarterPlaceholder: 'Vyberte štvrťrok',
|
||||
weekPlaceholder: 'Select Week',
|
||||
weekPlaceholder: 'Vyberte týždeň',
|
||||
startDatePlaceholder: 'Dátum začiatku',
|
||||
endDatePlaceholder: 'Dátum ukončenia',
|
||||
startDatetimePlaceholder: 'Dátum a čas začiatku',
|
||||
endDatetimePlaceholder: 'Dátum a čas ukončenia ',
|
||||
// FIXME: translation needed
|
||||
startMonthPlaceholder: 'Start Month',
|
||||
endMonthPlaceholder: 'End Month',
|
||||
startMonthPlaceholder: 'Začiatok mesiaca',
|
||||
endMonthPlaceholder: 'Koniec mesiaca',
|
||||
monthBeforeYear: true,
|
||||
firstDayOfWeek: 6 as 0 | 1 | 2 | 3 | 4 | 5 | 6,
|
||||
today: 'Dnes'
|
||||
@ -64,13 +63,12 @@ const skSK: NLocale = {
|
||||
sourceTitle: 'Zdroj',
|
||||
targetTitle: 'Cieľ'
|
||||
},
|
||||
// TODO: translation
|
||||
Transfer: {
|
||||
selectAll: 'Select all',
|
||||
unselectAll: 'Unselect all',
|
||||
clearAll: 'Clear',
|
||||
total: (num: number): string => `Total ${num} items`,
|
||||
selected: (num: number): string => `${num} items selected`
|
||||
selectAll: 'Vybrať všetko',
|
||||
unselectAll: 'odznačiť všetko',
|
||||
clearAll: 'Vyčistiť',
|
||||
total: (num: number): string => `Celkom ${num} položiek`,
|
||||
selected: (num: number): string => `Vybratých ${num} položiek`
|
||||
},
|
||||
Empty: {
|
||||
description: 'Žiadne dáta'
|
||||
@ -86,7 +84,7 @@ const skSK: NLocale = {
|
||||
clear: 'Vyčistiť'
|
||||
},
|
||||
Pagination: {
|
||||
goto: 'Ísť',
|
||||
goto: 'Ísť na',
|
||||
selectionSuffix: 'Strana'
|
||||
},
|
||||
DynamicTags: {
|
||||
@ -108,13 +106,12 @@ const skSK: NLocale = {
|
||||
title: 'Editor tém',
|
||||
clearAllVars: 'Vymazať všetky premenné',
|
||||
clearSearch: 'Vymazať vyhľadávanie',
|
||||
filterCompName: 'Názov komponentu filtra',
|
||||
filterVarName: 'Názov premennej filtra',
|
||||
filterCompName: 'Filtrovať názov komponentu',
|
||||
filterVarName: 'Filtrovať názov premennej',
|
||||
import: 'Importovať',
|
||||
export: 'Exportovať',
|
||||
restore: 'Obnoviť pôvodné nastavenia'
|
||||
},
|
||||
// TODO: translation
|
||||
Image: {
|
||||
tipPrevious: 'Predchádzajúci obrázok (←)',
|
||||
tipNext: 'Ďalší obrázok (→)',
|
||||
@ -124,8 +121,7 @@ const skSK: NLocale = {
|
||||
tipZoomIn: 'Priblížiť',
|
||||
tipDownload: 'Sťahovať',
|
||||
tipClose: 'Zavrieť (Esc)',
|
||||
// TODO: translation
|
||||
tipOriginalSize: 'Zoom to original size'
|
||||
tipOriginalSize: 'Priblížiť na pôvodnú veľkosť'
|
||||
}
|
||||
}
|
||||
|
||||
|
9
src/locales/date/csCZ.ts
Normal file
9
src/locales/date/csCZ.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import cs from 'date-fns/esm/locale/cs'
|
||||
import { type NDateLocale } from './enUS'
|
||||
|
||||
const dateCsCZ: NDateLocale = {
|
||||
name: 'cs-CZ',
|
||||
locale: cs
|
||||
}
|
||||
|
||||
export default dateCsCZ
|
@ -16,6 +16,7 @@ import {
|
||||
esAR,
|
||||
itIT,
|
||||
skSK,
|
||||
csCZ,
|
||||
enGB,
|
||||
plPL,
|
||||
ptBR,
|
||||
@ -41,6 +42,7 @@ import {
|
||||
dateEsAR,
|
||||
dateItIT,
|
||||
dateSkSK,
|
||||
dateCsCZ,
|
||||
dateEnGB,
|
||||
datePlPL,
|
||||
datePtBR,
|
||||
@ -274,6 +276,14 @@ describe('locale', () => {
|
||||
}
|
||||
}).html()
|
||||
).toMatchSnapshot()
|
||||
expect(
|
||||
mount(Wrapper, {
|
||||
props: {
|
||||
dateLocale: dateCsCZ,
|
||||
locale: csCZ
|
||||
}
|
||||
}).html()
|
||||
).toMatchSnapshot()
|
||||
expect(
|
||||
mount(Wrapper, {
|
||||
props: {
|
||||
|
@ -13,6 +13,7 @@ export { default as frFR } from './common/frFR'
|
||||
export { default as esAR } from './common/esAR'
|
||||
export { default as itIT } from './common/itIT'
|
||||
export { default as skSK } from './common/skSK'
|
||||
export { default as csCZ } from './common/csCZ'
|
||||
export { default as enGB } from './common/enGB'
|
||||
export { default as plPL } from './common/plPL'
|
||||
export { default as ptBR } from './common/ptBR'
|
||||
@ -39,6 +40,7 @@ export { default as dateFrFR } from './date/frFR'
|
||||
export { default as dateEsAR } from './date/esAR'
|
||||
export { default as dateItIT } from './date/itIT'
|
||||
export { default as dateSkSK } from './date/skSK'
|
||||
export { default as dateCsCZ } from './date/csCZ'
|
||||
export { default as dateEnGB } from './date/enGB'
|
||||
export { default as datePlPL } from './date/plPL'
|
||||
export { default as datePtBR } from './date/ptBR'
|
||||
|
@ -30,8 +30,8 @@ Mention requires `v2.2.0` and above.
|
||||
| separator | `string` | `' '` | Character to split mentions. The string length must be exactly 1. | |
|
||||
| bordered | `boolean` | `true` | Whether to display the border of the input element. | |
|
||||
| disabled | `boolean` | `false` | Whether to disable the input element. | |
|
||||
| value | `string \| null` | `undefined` | Manually set input value. | |
|
||||
| default-value | `string` | `''` | Default value when the value is not manually set. | |
|
||||
| filter | `(pattern: string, option: MentionOption) => boolean` | Default filter method | Method to filter options corresponding to `pattern`. | 2.38.2 |
|
||||
| loading | `boolean` | `false` | Whether the selection panel of mentions is in a loading state. | |
|
||||
| prefix | `string \| string[]` | `'@'` | Prefix character(s) to trigger mentions. The string length(s) must be exactly 1. | |
|
||||
| placeholder | `string` | `''` | Placeholder. | |
|
||||
@ -40,6 +40,7 @@ Mention requires `v2.2.0` and above.
|
||||
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | Input size. | |
|
||||
| status | `'success' \| 'warning' \| 'error'` | `undefined` | Validation status. | 2.27.0 |
|
||||
| to | `string \| HTMLElement \| false` | `body` | Container node of the menu. `false` will keep it not detached. | |
|
||||
| value | `string \| null` | `undefined` | Manually set input value. | |
|
||||
| on-update:show | `(show: boolean) => void` | `undefined` | Callback when the selection panel of mentions is shown or hidden. | 2.34.0 |
|
||||
| on-update:value | `(value: string) => void` | `undefined` | Triggered when the input box value is updated. | |
|
||||
| on-select | `(option: MentionOption, prefix: string) => void` | `undefined` | Triggered when the input box is selected. | |
|
||||
|
@ -30,8 +30,8 @@ Mention 在 `v2.2.0` 及以后可用。
|
||||
| separator | `string` | `' '` | 切分提及使用的字符,长度必须为 1 | |
|
||||
| bordered | `boolean` | `true` | 是否显示输入框边框 | |
|
||||
| disabled | `boolean` | `false` | 是否设置输入框为禁用状态 | |
|
||||
| value | `string \| null` | `undefined` | 输入框的值 | |
|
||||
| default-value | `string` | `''` | 输入框的默认值 | |
|
||||
| filter | `(pattern: string, option: MentionOption) => boolean` | 内置的过滤函数 | 根据 `pattern` 决定显示那些选项的过滤函数 | 2.38.2 |
|
||||
| loading | `boolean` | `false` | 选择面板是否显示加载状态 | |
|
||||
| prefix | `string \| string[]` | `'@'` | 触发提及的前缀,长度必须为 1 | |
|
||||
| placeholder | `string` | `''` | 输入框的占位符 | |
|
||||
@ -40,6 +40,7 @@ Mention 在 `v2.2.0` 及以后可用。
|
||||
| size | `'small' \| 'medium' \| 'large'` | `'medium'` | 输入框的大小 | |
|
||||
| status | `'success' \| 'warning' \| 'error'` | `undefined` | 验证状态 | 2.27.0 |
|
||||
| to | `string \| HTMLElement \| false` | `body` | 菜单的容器节点,`false` 会待在原地 | |
|
||||
| value | `string \| null` | `undefined` | 输入框的值 | |
|
||||
| on-update:show | `(show: boolean) => void` | `undefined` | 选择面板显示状态发生变化时触发 | 2.34.0 |
|
||||
| on-update:value | `(value: string) => void` | `undefined` | 输入框值发生更新时触发 | |
|
||||
| on-select | `(option: MentionOption, prefix: string) => void` | `undefined` | 输入框的选中时触发 | |
|
||||
|
@ -51,6 +51,21 @@ export const mentionProps = {
|
||||
type: Array as PropType<MentionOption[]>,
|
||||
default: []
|
||||
},
|
||||
filter: {
|
||||
type: Function as PropType<
|
||||
(pattern: string, option: MentionOption) => boolean
|
||||
>,
|
||||
default: (pattern: string, option: MentionOption) => {
|
||||
if (!pattern) return true
|
||||
if (typeof option.label === 'string') {
|
||||
return option.label.startsWith(pattern)
|
||||
}
|
||||
if (typeof option.value === 'string') {
|
||||
return option.value.startsWith(pattern)
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<'text' | 'textarea'>,
|
||||
default: 'text'
|
||||
@ -147,16 +162,7 @@ export default defineComponent({
|
||||
let cachedPartialPatternEnd: number | null = null
|
||||
const filteredOptionsRef = computed(() => {
|
||||
const { value: pattern } = partialPatternRef
|
||||
return props.options.filter((option) => {
|
||||
if (!pattern) return true
|
||||
if (typeof option.label === 'string') {
|
||||
return option.label.startsWith(pattern)
|
||||
}
|
||||
if (typeof option.value === 'string') {
|
||||
return option.value.startsWith(pattern)
|
||||
}
|
||||
return false
|
||||
})
|
||||
return props.options.filter((option) => props.filter(pattern, option))
|
||||
})
|
||||
const treeMateRef = computed(() => {
|
||||
return createTreeMate<
|
||||
|
@ -213,7 +213,7 @@ export const NSubmenu = defineComponent({
|
||||
default: () => (
|
||||
<div
|
||||
class={`${mergedClsPrefix}-submenu`}
|
||||
role="menuitem"
|
||||
role="menu"
|
||||
aria-expanded={!this.collapsed}
|
||||
id={this.domId}
|
||||
>
|
||||
@ -226,7 +226,7 @@ export const NSubmenu = defineComponent({
|
||||
) : (
|
||||
<div
|
||||
class={`${mergedClsPrefix}-submenu`}
|
||||
role="menuitem"
|
||||
role="menu"
|
||||
aria-expanded={!this.collapsed}
|
||||
id={this.domId}
|
||||
>
|
||||
|
@ -7,34 +7,55 @@ You can use `useModal.create` to create a modal. (Please make sure this API is c
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-button @click="handleClick">
|
||||
Start me up
|
||||
</n-button>
|
||||
<n-flex>
|
||||
<n-button @click="showDialogPreset">
|
||||
Start me up Dialog
|
||||
</n-button>
|
||||
<n-button @click="showCardPreset">
|
||||
Start me up Card
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { useModal, useMessage } from 'naive-ui'
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { useModal, useMessage, NButton } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const modal = useModal()
|
||||
const message = useMessage()
|
||||
|
||||
const handleClick = () => {
|
||||
const modalInst = modal.create({
|
||||
title: 'Modal',
|
||||
content: 'Content',
|
||||
preset: 'dialog'
|
||||
const showDialogPreset = () => {
|
||||
const m = modal.create({
|
||||
title: 'Dialog perset',
|
||||
preset: 'dialog',
|
||||
content: 'Content'
|
||||
})
|
||||
message.info('Shut down in three seconds')
|
||||
setTimeout(() => {
|
||||
modalInst.destroy()
|
||||
m.destroy()
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
const showCardPreset = () => {
|
||||
const m = modal.create({
|
||||
title: 'Card preset',
|
||||
preset: 'card',
|
||||
style: {
|
||||
width: '400px'
|
||||
},
|
||||
content: 'Content',
|
||||
footer: () =>
|
||||
h(
|
||||
NButton,
|
||||
{ type: 'primary', onClick: () => m.destroy() },
|
||||
() => 'Close'
|
||||
)
|
||||
})
|
||||
}
|
||||
return {
|
||||
handleClick
|
||||
showDialogPreset,
|
||||
showCardPreset
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -7,34 +7,55 @@
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-button @click="handleClick">
|
||||
来吧
|
||||
</n-button>
|
||||
<n-flex>
|
||||
<n-button @click="showDialogPreset">
|
||||
来吧 Dialog
|
||||
</n-button>
|
||||
<n-button @click="showCardPreset">
|
||||
来吧 Card
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { useModal, useMessage } from 'naive-ui'
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { useModal, useMessage, NButton } from 'naive-ui'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const modal = useModal()
|
||||
const message = useMessage()
|
||||
|
||||
const handleClick = () => {
|
||||
const modalInst = modal.create({
|
||||
title: '模态框',
|
||||
content: '内容',
|
||||
preset: 'dialog'
|
||||
const showDialogPreset = () => {
|
||||
const m = modal.create({
|
||||
title: 'Dialog 预设',
|
||||
preset: 'dialog',
|
||||
content: '内容'
|
||||
})
|
||||
message.info('三秒钟后关闭')
|
||||
setTimeout(() => {
|
||||
modalInst.destroy()
|
||||
m.destroy()
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
const showCardPreset = () => {
|
||||
const m = modal.create({
|
||||
title: 'Card 预设',
|
||||
preset: 'card',
|
||||
style: {
|
||||
width: '400px'
|
||||
},
|
||||
content: '内容',
|
||||
footer: () =>
|
||||
h(
|
||||
NButton,
|
||||
{ type: 'primary', onClick: () => m.destroy() },
|
||||
() => '关闭'
|
||||
)
|
||||
})
|
||||
}
|
||||
return {
|
||||
handleClick
|
||||
showDialogPreset,
|
||||
showCardPreset
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -76,14 +76,14 @@ export const NModalProvider = defineComponent({
|
||||
const clickedPositionRef = useClickPosition()
|
||||
|
||||
const modalListRef = ref<TypeSafeModalReactive[]>([])
|
||||
const modalInstRefs: Record<string, ModalInst> = {}
|
||||
const modalInstRefs: Record<string, ModalInst | undefined> = {}
|
||||
function create (options: ModalOptions = {}): ModalReactive {
|
||||
const key = createId()
|
||||
const modalReactive = reactive({
|
||||
...options,
|
||||
key,
|
||||
destroy: () => {
|
||||
modalInstRefs[`n-modal-${key}`].hide()
|
||||
modalInstRefs[`n-modal-${key}`]?.hide()
|
||||
}
|
||||
})
|
||||
modalListRef.value.push(modalReactive)
|
||||
@ -100,7 +100,7 @@ export const NModalProvider = defineComponent({
|
||||
|
||||
function destroyAll (): void {
|
||||
Object.values(modalInstRefs).forEach((modalInstRef) => {
|
||||
modalInstRef.hide()
|
||||
modalInstRef?.hide()
|
||||
})
|
||||
}
|
||||
|
||||
@ -131,8 +131,7 @@ export const NModalProvider = defineComponent({
|
||||
this.modalList.map((modal) =>
|
||||
h(
|
||||
NModalEnvironment,
|
||||
omit(modal, ['destroy', 'style'], {
|
||||
internalStyle: modal.style,
|
||||
omit(modal, ['destroy'], {
|
||||
to: modal.to ?? this.to,
|
||||
ref: ((inst: ModalInst | null) => {
|
||||
if (inst === null) {
|
||||
|
@ -13,6 +13,7 @@ size.vue
|
||||
color.vue
|
||||
error-correction.vue
|
||||
download.vue
|
||||
type.vue
|
||||
```
|
||||
|
||||
## API
|
||||
@ -31,6 +32,7 @@ download.vue
|
||||
| padding | `number \| string` | `12` | Padding size of the QR Code. | 2.36.0 |
|
||||
| value | `string` | `''` | Text information. | 2.36.0 |
|
||||
| size | `number` | `100` | Size of the qrcode. | 2.36.0 |
|
||||
| type | `'canvas'` \| `'svg'` | `'canvas'` | Customize Render Type. | 2.38.2 |
|
||||
|
||||
### About QR code error correction level
|
||||
|
||||
|
26
src/qr-code/demos/enUS/type.demo.vue
Normal file
26
src/qr-code/demos/enUS/type.demo.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<markdown>
|
||||
# Customize Render Type
|
||||
|
||||
Customize rendering output by setting `type`, providing two options: `canvas` and `svg`.
|
||||
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space>
|
||||
<n-qr-code :value="text" type="canvas" />
|
||||
<n-qr-code :value="text" type="svg" />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const text = ref('The rain dampened the sky')
|
||||
return {
|
||||
text
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
@ -20,6 +20,7 @@
|
||||
<n-qr-code
|
||||
value="https://www.naiveui.com/"
|
||||
icon-src="https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg"
|
||||
type="svg"
|
||||
:icon-size="32"
|
||||
error-correction-level="H"
|
||||
/>
|
||||
|
@ -13,6 +13,7 @@ size.vue
|
||||
color.vue
|
||||
error-correction.vue
|
||||
download.vue
|
||||
type.vue
|
||||
```
|
||||
|
||||
## API
|
||||
@ -31,6 +32,7 @@ download.vue
|
||||
| padding | `number \| string` | `12` | 二维码填充大小 | 2.36.0 |
|
||||
| value | `string` | `''` | 文本信息 | 2.36.0 |
|
||||
| size | `number` | `100` | 二维码大小 | 2.36.0 |
|
||||
| type | `'canvas'` \| `'svg'` | `'canvas'` | 自定义二维码渲染类型 | 2.38.2 |
|
||||
|
||||
### 关于二维码纠错级别
|
||||
|
||||
|
26
src/qr-code/demos/zhCN/type.demo.vue
Normal file
26
src/qr-code/demos/zhCN/type.demo.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<markdown>
|
||||
# 自定义渲染类型
|
||||
|
||||
通过设置 `type` 自定义渲染结果,提供 `canvas` 和 `svg` 两个选项。
|
||||
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-space>
|
||||
<n-qr-code :value="text" type="canvas" />
|
||||
<n-qr-code :value="text" type="svg" />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const text = ref('雨淋湿了天空')
|
||||
return {
|
||||
text
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
@ -14,6 +14,8 @@ import style from './styles/index.cssr'
|
||||
import { type QrCodeTheme, qrcodeLight } from '../styles'
|
||||
import qrcodegen from './qrcodegen'
|
||||
|
||||
type Modules = ReturnType<qrcodegen.QrCode['getModules']>
|
||||
|
||||
const ERROR_CORRECTION_LEVEL: Record<string, qrcodegen.QrCode.Ecc> = {
|
||||
L: qrcodegen.QrCode.Ecc.LOW,
|
||||
M: qrcodegen.QrCode.Ecc.MEDIUM,
|
||||
@ -56,6 +58,10 @@ export const qrCodeProps = {
|
||||
errorCorrectionLevel: {
|
||||
type: String,
|
||||
default: 'M'
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'canvas'
|
||||
}
|
||||
} as const
|
||||
|
||||
@ -104,6 +110,7 @@ export default defineComponent({
|
||||
const imageLoadedTrigger = ref(0)
|
||||
let loadedIcon: HTMLImageElement | null = null
|
||||
watchEffect(() => {
|
||||
if (props.type === 'svg') return
|
||||
void imageLoadedTrigger.value
|
||||
drawCanvas(
|
||||
qr.value,
|
||||
@ -120,7 +127,9 @@ export default defineComponent({
|
||||
: null
|
||||
)
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.type === 'svg') return
|
||||
const { iconSrc } = props
|
||||
if (iconSrc) {
|
||||
let aborted = false
|
||||
@ -197,11 +206,112 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
function generatePath (modules: Modules, margin: number = 0): string {
|
||||
const ops: string[] = []
|
||||
modules.forEach(function (row, y) {
|
||||
let start: number | null = null
|
||||
row.forEach(function (cell, x) {
|
||||
if (!cell && start !== null) {
|
||||
// M0 0h7v1H0z injects the space with the move and drops the comma,
|
||||
// saving a char per operation
|
||||
ops.push(
|
||||
`M${start + margin} ${y + margin}h${x - start}v1H${start + margin}z`
|
||||
)
|
||||
start = null
|
||||
return
|
||||
}
|
||||
|
||||
// end of row, clean up or skip
|
||||
if (x === row.length - 1) {
|
||||
if (!cell) {
|
||||
// We would have closed the op above already so this can only mean
|
||||
// 2+ light modules in a row.
|
||||
return
|
||||
}
|
||||
if (start === null) {
|
||||
// Just a single dark module.
|
||||
ops.push(`M${x + margin},${y + margin} h1v1H${x + margin}z`)
|
||||
} else {
|
||||
// Otherwise finish the current line.
|
||||
ops.push(
|
||||
`M${start + margin},${y + margin} h${x + 1 - start}v1H${
|
||||
start + margin
|
||||
}z`
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (cell && start === null) {
|
||||
start = x
|
||||
}
|
||||
})
|
||||
})
|
||||
return ops.join('')
|
||||
}
|
||||
|
||||
function svgInfo (
|
||||
qr: qrcodegen.QrCode,
|
||||
size: number,
|
||||
iconConfig: {
|
||||
iconSrc: string
|
||||
iconBorderRadius: number
|
||||
iconSize: number
|
||||
iconBackgroundColor: string
|
||||
} | null
|
||||
): {
|
||||
innerHtml: string
|
||||
numCells: number
|
||||
} {
|
||||
const cells = qr.getModules()
|
||||
const numCells = cells.length
|
||||
const cellsToDraw = cells
|
||||
let svgInnerHtml = ''
|
||||
const path1Html = `<path fill="transparent" d="M0,0 h${numCells}v${numCells}H0z" shape-rendering="crispEdges"></path>`
|
||||
const path2Html = `<path fill="${props.color}" d="${generatePath(cellsToDraw, 0)}" shape-rendering="crispEdges"></path>`
|
||||
let iconHtml = ''
|
||||
if (iconConfig) {
|
||||
const { iconSrc, iconSize } = iconConfig
|
||||
|
||||
const DEFAULT_IMG_SCALE = 0.1
|
||||
const defaultSize = Math.floor(size * DEFAULT_IMG_SCALE)
|
||||
const scale = numCells / size
|
||||
const h = (iconSize || defaultSize) * scale
|
||||
const w = (iconSize || defaultSize) * scale
|
||||
const x = cells.length / 2 - w / 2
|
||||
const y = cells.length / 2 - h / 2
|
||||
iconHtml += `<image href="${iconSrc}" width="${w}" height="${h}" x="${x}" y="${y}" preserveAspectRatio="none"></image>`
|
||||
}
|
||||
svgInnerHtml += path1Html
|
||||
svgInnerHtml += path2Html
|
||||
svgInnerHtml += iconHtml
|
||||
return {
|
||||
innerHtml: svgInnerHtml,
|
||||
numCells
|
||||
}
|
||||
}
|
||||
|
||||
const svgInfoRef = computed(() =>
|
||||
svgInfo(
|
||||
qr.value,
|
||||
props.size,
|
||||
props.iconSrc
|
||||
? {
|
||||
iconSrc: props.iconSrc,
|
||||
iconBorderRadius: props.iconBorderRadius,
|
||||
iconSize: props.iconSize,
|
||||
iconBackgroundColor: props.iconBackgroundColor
|
||||
}
|
||||
: null
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
canvasRef,
|
||||
mergedClsPrefix: mergedClsPrefixRef,
|
||||
cssVars: inlineThemeDisabled ? undefined : cssVarsRef,
|
||||
themeClass: themeClassHandle?.themeClass
|
||||
themeClass: themeClassHandle?.themeClass,
|
||||
svgInfo: svgInfoRef
|
||||
}
|
||||
},
|
||||
render () {
|
||||
@ -211,8 +321,10 @@ export default defineComponent({
|
||||
padding,
|
||||
cssVars,
|
||||
themeClass,
|
||||
size
|
||||
size,
|
||||
type
|
||||
} = this
|
||||
|
||||
return (
|
||||
<div
|
||||
class={[`${mergedClsPrefix}-qr-code`, themeClass]}
|
||||
@ -224,13 +336,23 @@ export default defineComponent({
|
||||
...cssVars
|
||||
}}
|
||||
>
|
||||
<canvas
|
||||
ref="canvasRef"
|
||||
style={{
|
||||
width: `${size}px`,
|
||||
height: `${size}px`
|
||||
}}
|
||||
/>
|
||||
{type === 'canvas' ? (
|
||||
<canvas
|
||||
ref="canvasRef"
|
||||
style={{
|
||||
width: `${size}px`,
|
||||
height: `${size}px`
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<svg
|
||||
height={size}
|
||||
width={size}
|
||||
viewBox={`0 0 ${this.svgInfo.numCells} ${this.svgInfo.numCells}`}
|
||||
role="img"
|
||||
innerHTML={this.svgInfo.innerHtml}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -265,6 +265,11 @@ namespace qrcodegen {
|
||||
)
|
||||
}
|
||||
|
||||
// Modified to expose modules for easy access
|
||||
public getModules (): boolean[][] {
|
||||
return this.modules
|
||||
}
|
||||
|
||||
/* -- Private helper methods for constructor: Drawing function modules -- */
|
||||
|
||||
// Reads this object's version field, and draws and marks all function modules.
|
||||
|
37
src/scrollbar/demos/enUS/custom.demo.vue
Normal file
37
src/scrollbar/demos/enUS/custom.demo.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<markdown>
|
||||
# Custom scrollbar style
|
||||
|
||||
You can use `theme-overrides` to control the style of the scrollbar.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-config-provider
|
||||
:theme-overrides="{
|
||||
Scrollbar: {
|
||||
width: '8px',
|
||||
railInsetHorizontal: '4px 4px 4px auto',
|
||||
borderRadius: 0
|
||||
}
|
||||
}"
|
||||
>
|
||||
<n-scrollbar style="max-height: 120px">
|
||||
我们在田野上面找猪<br>
|
||||
想象中已找到了三只<br>
|
||||
小鸟在白云上面追逐<br>
|
||||
它们在树底下跳舞<br>
|
||||
啦啦啦啦啦啦啦啦咧<br>
|
||||
啦啦啦啦咧<br>
|
||||
我们在想象中度过了许多年<br>
|
||||
想象中我们是如此的疯狂<br>
|
||||
我们在城市里面找猪<br>
|
||||
想象中已找到了几百万只<br>
|
||||
小鸟在公园里面唱歌<br>
|
||||
它们独自在想象里跳舞<br>
|
||||
啦啦啦啦啦啦啦啦咧<br>
|
||||
啦啦啦啦咧<br>
|
||||
我们在想象中度过了许多年<br>
|
||||
许多年之后我们又开始想象<br>
|
||||
啦啦啦啦啦啦啦啦咧
|
||||
</n-scrollbar>
|
||||
</n-config-provider>
|
||||
</template>
|
@ -8,6 +8,8 @@ It looks better but I'm sure it's not as reliable as native scrollbar.
|
||||
basic.vue
|
||||
x.vue
|
||||
trigger.vue
|
||||
no-sync.vue
|
||||
custom.vue
|
||||
```
|
||||
|
||||
## API
|
||||
@ -16,10 +18,12 @@ trigger.vue
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| content-class | `string` | `undefined` | Class name of content div. | 2.38.2 |
|
||||
| content-style | `string \| object` | `undefined` | Style of content div. | 2.38.2 |
|
||||
| size | `number` | `undefined` | Size of scrollbar. | 2.34.4 |
|
||||
| trigger | `'hover' \| 'none'` | `'hover'` | Trigger of show scrollbar. `'none'` means always show it. | 2.29.1 |
|
||||
| x-scrollable | `boolean` | `false` | Whether it can scroll horizontally. | |
|
||||
| on-scroll | `(e: Event) => void` | `undefined` | Callback on scroll | |
|
||||
| size | `number` | `undefined` | Size of scrollbar. | 2.34.4 |
|
||||
|
||||
### Scrollbar Slots
|
||||
|
||||
|
27
src/scrollbar/demos/enUS/no-sync.demo.vue
Normal file
27
src/scrollbar/demos/enUS/no-sync.demo.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<markdown>
|
||||
# Mouse dragging cannot scroll to the bottom
|
||||
|
||||
When the last element has a `margin-bottom`, dragging the scrollbar with the mouse cannot scroll to the bottom. This is caused by the native scrolling behavior of the browser. `n-scrollbar` cannot automatically handle this issue. You can solve this problem by setting `content-style` to `overflow: hidden;`.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-scrollbar style="max-height: 120px">
|
||||
Without content-style
|
||||
<p style="margin-bottom: 90px">
|
||||
margin-bottom: 90px
|
||||
</p>
|
||||
<p style="margin-bottom: 90px">
|
||||
margin-bottom: 90px
|
||||
</p>
|
||||
</n-scrollbar>
|
||||
<n-divider />
|
||||
<n-scrollbar style="max-height: 120px" content-style="overflow: hidden;">
|
||||
content-style="overflow: hidden;"
|
||||
<p style="margin-bottom: 90px">
|
||||
margin-bottom: 90px
|
||||
</p>
|
||||
<p style="margin-bottom: 90px">
|
||||
margin-bottom: 90px
|
||||
</p>
|
||||
</n-scrollbar>
|
||||
</template>
|
37
src/scrollbar/demos/zhCN/custom.demo.vue
Normal file
37
src/scrollbar/demos/zhCN/custom.demo.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<markdown>
|
||||
# 自定义样式
|
||||
|
||||
你可以通过 `theme-overrides` 去控制滚动条的样式。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-config-provider
|
||||
:theme-overrides="{
|
||||
Scrollbar: {
|
||||
width: '8px',
|
||||
railInsetHorizontal: '4px 4px 4px auto',
|
||||
borderRadius: 0
|
||||
}
|
||||
}"
|
||||
>
|
||||
<n-scrollbar style="max-height: 120px">
|
||||
我们在田野上面找猪<br>
|
||||
想象中已找到了三只<br>
|
||||
小鸟在白云上面追逐<br>
|
||||
它们在树底下跳舞<br>
|
||||
啦啦啦啦啦啦啦啦咧<br>
|
||||
啦啦啦啦咧<br>
|
||||
我们在想象中度过了许多年<br>
|
||||
想象中我们是如此的疯狂<br>
|
||||
我们在城市里面找猪<br>
|
||||
想象中已找到了几百万只<br>
|
||||
小鸟在公园里面唱歌<br>
|
||||
它们独自在想象里跳舞<br>
|
||||
啦啦啦啦啦啦啦啦咧<br>
|
||||
啦啦啦啦咧<br>
|
||||
我们在想象中度过了许多年<br>
|
||||
许多年之后我们又开始想象<br>
|
||||
啦啦啦啦啦啦啦啦咧
|
||||
</n-scrollbar>
|
||||
</n-config-provider>
|
||||
</template>
|
@ -8,7 +8,9 @@
|
||||
basic.vue
|
||||
x.vue
|
||||
trigger.vue
|
||||
no-sync.vue
|
||||
rtl-debug.vue
|
||||
custom.vue
|
||||
```
|
||||
|
||||
## API
|
||||
@ -17,10 +19,12 @@ rtl-debug.vue
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| content-class | `string` | `undefined` | 内容 div 的类名 | 2.38.2 |
|
||||
| content-style | `string \| object` | `undefined` | 内容 div 的 style | 2.38.2 |
|
||||
| size | `number` | `undefined` | 滚动条的大小 | 2.34.4 |
|
||||
| trigger | `'hover' \| 'none'` | `'hover'` | 显示滚动条的时机,`'none'` 表示一直显示 | 2.29.1 |
|
||||
| x-scrollable | `boolean` | `false` | 是否可以横向滚动 | |
|
||||
| on-scroll | `(e: Event) => void` | `undefined` | 滚动的回调 | |
|
||||
| size | `number` | `undefined` | 滚动条的大小 | 2.34.4 |
|
||||
|
||||
### Scrollbar Slots
|
||||
|
||||
|
27
src/scrollbar/demos/zhCN/no-sync.demo.vue
Normal file
27
src/scrollbar/demos/zhCN/no-sync.demo.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<markdown>
|
||||
# 鼠标拖动滚不到最下面
|
||||
|
||||
在最后一个元素含有 `margin-bottom` 的情况下,通过鼠标拖动滚动条,滚动不到最下面。这是浏览器的原生滚动行为导致的,`n-scrollbar` 无法自动的处理这种问题,你可以通过设定 `content-style` 为 `'overflow: hidden;'` 来解决这个问题。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-scrollbar style="max-height: 120px">
|
||||
不设定 content-style
|
||||
<p style="margin-bottom: 90px">
|
||||
margin-bottom: 90px
|
||||
</p>
|
||||
<p style="margin-bottom: 90px">
|
||||
margin-bottom: 90px
|
||||
</p>
|
||||
</n-scrollbar>
|
||||
<n-divider />
|
||||
<n-scrollbar style="max-height: 120px" content-style="overflow: hidden;">
|
||||
content-style="overflow: hidden;"
|
||||
<p style="margin-bottom: 90px">
|
||||
margin-bottom: 90px
|
||||
</p>
|
||||
<p style="margin-bottom: 90px">
|
||||
margin-bottom: 90px
|
||||
</p>
|
||||
</n-scrollbar>
|
||||
</template>
|
@ -24,6 +24,8 @@ export const scrollbarProps = {
|
||||
trigger: String as PropType<'none' | 'hover'>,
|
||||
xScrollable: Boolean,
|
||||
onScroll: Function as PropType<(e: Event) => void>,
|
||||
contentClass: String,
|
||||
contentStyle: [Object, String] as PropType<string | Record<string, any>>,
|
||||
size: Number
|
||||
} as const
|
||||
|
||||
|
@ -723,6 +723,14 @@ export default defineComponent({
|
||||
ref={this.setHandleRefs(index)}
|
||||
class={`${mergedClsPrefix}-slider-handle-wrapper`}
|
||||
tabindex={this.mergedDisabled ? -1 : 0}
|
||||
role="slider"
|
||||
aria-valuenow={value}
|
||||
aria-valuemin={this.min}
|
||||
aria-valuemax={this.max}
|
||||
aria-orientation={
|
||||
this.vertical ? 'vertical' : 'horizontal'
|
||||
}
|
||||
aria-disabled={this.disabled}
|
||||
style={this.getHandleStyle(value, index)}
|
||||
onFocus={() => {
|
||||
this.handleHandleFocus(index)
|
||||
|
@ -211,4 +211,28 @@ describe('n-slider', () => {
|
||||
expect((handle.element as HTMLElement).style.left).toEqual('30%')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('should have the aria role of "slider"', () => {
|
||||
const wrapper = mount(NSlider)
|
||||
expect(wrapper.find('.n-slider-handle-wrapper').attributes('role')).toBe(
|
||||
'slider'
|
||||
)
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('should be some aria properties for "slider"', () => {
|
||||
const wrapper = mount(NSlider, {
|
||||
props: {
|
||||
defaultValue: 50,
|
||||
disabled: true
|
||||
}
|
||||
})
|
||||
const handle = wrapper.find('.n-slider-handle-wrapper')
|
||||
expect(handle.attributes('aria-valuenow')).toBe('50')
|
||||
expect(handle.attributes('aria-valuemin')).toBe('0')
|
||||
expect(handle.attributes('aria-valuemax')).toBe('100')
|
||||
expect(handle.attributes('aria-orientation')).toBe('horizontal')
|
||||
expect(handle.attributes('aria-disabled')).toBe('true')
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
|
@ -4,7 +4,13 @@
|
||||
|
||||
<template>
|
||||
<n-flex vertical>
|
||||
<n-input-number v-model:value="split" :step="0.1" clearable />
|
||||
<n-input-number
|
||||
v-model:value="split"
|
||||
:step="0.1"
|
||||
clearable
|
||||
:max="1"
|
||||
:min="0"
|
||||
/>
|
||||
<NSplit v-model:size="split" style="height: 200px">
|
||||
<template #1>
|
||||
<div style="width: 100%; background-color: black" />
|
||||
|
@ -21,23 +21,27 @@ controlled.vue
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| default-size | `number` | `0.5` | Default split size, 0~1 is a percentage. | 2.36.0 |
|
||||
| size | `number` | `undefined` | Split is the controlled split size, with 0~1 representing the percentage. | 2.38.0 |
|
||||
| disabled | `boolean` | `false` | Whether to disable the split. | 2.36.0 |
|
||||
| default-size | `number` | `0.5` | Default split size, when it's `number` it should in 0 ~ 1, when it's `string` it should be formatted in `${number}px`. | 2.36.0, `string` 2.38.2 |
|
||||
| direction | `'horizontal' \| 'vertical'` | `'horizontal'` | The direction of the split. | 2.36.0 |
|
||||
| min | `number` | `0` | The minimum threshold for splitting, 0~1 is a percentage. | 2.36.0 |
|
||||
| max | `number` | `1` | The maximum split threshold, 0~1 is a percentage. | 2.36.0 |
|
||||
| disabled | `boolean` | `false` | Whether to disable the split. | 2.36.0 |
|
||||
| max | `string \| number` | `1` | The maximum split threshold, when it's `number` it should in 0 ~ 1, when it's `string` it should be formatted in `${number}px`. | 2.36.0, `string` 2.38.2 |
|
||||
| min | `string \| number` | `0` | The minimum threshold for splitting, when it's `number` it should in 0 ~ 1, when it's `string` it should be formatted in `${number}px`. | 2.36.0, `string` 2.38.2 |
|
||||
| pane1-class | `string` | `undefined` | The class name of the first pane. | 2.38.2 |
|
||||
| pane1-style | `Object \| string` | `undefined` | The Style of the first pane | 2.38.2 |
|
||||
| pane2-class | `string` | `undefined` | The class name of the second pane. | 2.38.2 |
|
||||
| pane2-style | `Object \| string` | `undefined` | The Style of the second pane | 2.38.2 |
|
||||
| resize-trigger-size | `number` | `3` | Size of the resize trigger. | 2.36.0 |
|
||||
| size | `string \| number` | `undefined` | Split is the controlled split size, when it's `number` it should in 0 ~ 1, when it's `string` it should be formatted in `${number}px`. | 2.38.0, `string` 2.38.2 |
|
||||
| watch-props | `Array<'defaultSize'>` | `undefined` | Default prop names that needed to be watched. Components will be updated after the prop is changed. Note: the `watch-props` itself is not reactive. | 2.38.0 |
|
||||
| on-drag-start | `(e: Event) => void` | `undefined` | Callback function when drag start. | 2.36.0 |
|
||||
| on-drag-move | `(e: Event) => void` | `undefined` | Callback function when dragging. | 2.36.0 |
|
||||
| on-drag-end | `(e: Event) => void` | `undefined` | Callback function when drag end. | 2.36.0 |
|
||||
| on-update:size | `(value: number) => void` | `undefined` | Callback fired on size changes. | 2.38.0 |
|
||||
| on-update:size | `(value: string \| number) => void` | `undefined` | Callback fired on `size` changes. If `props.value` or `props.defaultValue` is `string`, `value` is `string`. | 2.38.0, `string` 2.38.2 |
|
||||
|
||||
### Split Slots
|
||||
|
||||
| Name | Parameters | Description | Version |
|
||||
| -------------- | ---------- | ------------------------- | ------- |
|
||||
| 1 | `()` | The first panel content. | 2.36.0 |
|
||||
| 2 | `()` | The Second panel content. | 2.36.0 |
|
||||
| resize-trigger | `()` | Split bar content. | 2.36.0 |
|
||||
| Name | Parameters | Description | Version |
|
||||
| -------------- | ---------- | ------------------------ | ------- |
|
||||
| 1 | `()` | The first pane content. | 2.36.0 |
|
||||
| 2 | `()` | The Second pane content. | 2.36.0 |
|
||||
| resize-trigger | `()` | Split bar content. | 2.36.0 |
|
||||
|
@ -4,7 +4,13 @@
|
||||
|
||||
<template>
|
||||
<n-flex vertical>
|
||||
<n-input-number v-model:value="split" :step="0.1" clearable />
|
||||
<n-input-number
|
||||
v-model:value="split"
|
||||
:step="0.1"
|
||||
clearable
|
||||
:max="1"
|
||||
:min="0"
|
||||
/>
|
||||
<NSplit v-model:size="split" style="height: 200px">
|
||||
<template #1>
|
||||
<div style="width: 100%; background-color: black" />
|
||||
|
@ -13,6 +13,7 @@ nest.vue
|
||||
event.vue
|
||||
slot.vue
|
||||
controlled.vue
|
||||
pixel-value.vue
|
||||
```
|
||||
|
||||
## API
|
||||
@ -21,18 +22,22 @@ controlled.vue
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| default-size | `number` | `0.5` | Split 的默认分割大小,0~1 代表百分比 | 2.36.0 |
|
||||
| size | `number` | `undefined` | Split 的受控分割大小,0~1 代表百分比 | 2.38.0 |
|
||||
| disabled | `boolean` | `false` | 是否禁用 | 2.36.0 |
|
||||
| default-size | `string \| number` | `0.5` | Split 的默认分割大小,为 `number` 类型时应该为 0 ~ 1 之间的值,为 `string` 类型时应该为 `${number}px` 格式 | 2.36.0, `string` 2.38.2 |
|
||||
| direction | `'horizontal' \| 'vertical'` | `'horizontal'` | Split 的分割方向 | 2.36.0 |
|
||||
| min | `number` | `0` | Split 的分割最小阈值,0~1 代表百分比 | 2.36.0 |
|
||||
| max | `number` | `1` | Split 的分割最大阈值,0~1 代表百分比 | 2.36.0 |
|
||||
| disabled | `boolean` | `false` | 是否禁用 | 2.36.0 |
|
||||
| max | `string \| number` | `1` | Split 的分割最大阈值,为 `number` 类型时应该为 0 ~ 1 之间的值,为 `string` 类型时应该为 `${number}px` 格式 | 2.36.0, `string` 2.38.2 |
|
||||
| min | `string \| number` | `0` | Split 的分割最小阈值,为 `number` 类型时应该为 0 ~ 1 之间的值,为 `string` 类型时应该为 `${number}px` 格式 | 2.36.0, `string` 2.38.2 |
|
||||
| pane1-class | `string` | `undefined` | 第一个面板的类名 | 2.38.2 |
|
||||
| pane1-style | `Object \| string` | `undefined` | 第一个面板的样式 | 2.38.2 |
|
||||
| pane2-class | `string` | `undefined` | 第二个面板的类名 | 2.38.2 |
|
||||
| pane2-style | `Object \| string` | `undefined` | 第二个面板的样式 | 2.38.2 |
|
||||
| resize-trigger-size | `number` | `3` | Split 的分隔条大小 | 2.36.0 |
|
||||
| size | `string \| number` | `undefined` | Split 的受控分割大小,为 `number` 类型时应该为 0 ~ 1 之间的值,为 `string` 类型时应该为 `${number}px` 格式 | 2.38.0, `string` 2.38.2 |
|
||||
| watch-props | `Array<'defaultSize'>` | `undefined` | 需要检测变更的默认属性,检测后组件状态会更新。注意:`watch-props` 本身不是响应式的 | 2.38.0 |
|
||||
| on-drag-start | `(e: Event) => void` | `undefined` | 开始拖拽的回调函数 | 2.36.0 |
|
||||
| on-drag-move | `(e: Event) => void` | `undefined` | 拖拽中的回调函数 | 2.36.0 |
|
||||
| on-drag-end | `(e: Event) => void` | `undefined` | 结束拖拽的回调函数 | 2.36.0 |
|
||||
| on-update:size | `(value: number) => void` | `undefined` | 组件 size 属性变化时触发的回调 | 2.38.0 |
|
||||
| on-update:size | `(value: string \| number) => void` | `undefined` | 组件 `size` 属性变化时触发的回调,如果 `props.value` 或 `props.defaultValue` 是 `string`, 则 `value` 为 `string` | 2.38.0 |
|
||||
|
||||
### Split Slots
|
||||
|
||||
|
25
src/split/demos/zhCN/pixel-value.demo.vue
Normal file
25
src/split/demos/zhCN/pixel-value.demo.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<markdown>
|
||||
# 使用像素值控制尺寸
|
||||
|
||||
自 `2.38.2` 开始,`min`、`max`、`size` 和 `default-size` 属性可以接受像素值。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-split
|
||||
direction="horizontal"
|
||||
style="height: 200px"
|
||||
max="300px"
|
||||
min="100px"
|
||||
default-size="200px"
|
||||
>
|
||||
<template #1>
|
||||
Pane 1:<br>
|
||||
min 100px<br>
|
||||
default 200px<br>
|
||||
max 300px
|
||||
</template>
|
||||
<template #2>
|
||||
Pane 2
|
||||
</template>
|
||||
</n-split>
|
||||
</template>
|
@ -16,6 +16,7 @@ import { type ThemeProps, useTheme, useThemeClass } from '../../_mixins'
|
||||
import style from './styles/index.cssr'
|
||||
import { type SplitTheme, splitLight } from '../styles'
|
||||
import { type SplitOnUpdateSize } from './types'
|
||||
import { depx } from 'seemly'
|
||||
|
||||
export const splitProps = {
|
||||
...(useTheme.props as ThemeProps<SplitTheme>),
|
||||
@ -29,7 +30,7 @@ export const splitProps = {
|
||||
},
|
||||
disabled: Boolean,
|
||||
defaultSize: {
|
||||
type: Number,
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: 0.5
|
||||
},
|
||||
'onUpdate:size': [Function, Array] as PropType<
|
||||
@ -38,15 +39,19 @@ export const splitProps = {
|
||||
onUpdateSize: [Function, Array] as PropType<
|
||||
SplitOnUpdateSize | SplitOnUpdateSize[]
|
||||
>,
|
||||
size: Number,
|
||||
size: [String, Number] as PropType<string | number>,
|
||||
min: {
|
||||
type: Number,
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: 0
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
type: [String, Number] as PropType<string | number>,
|
||||
default: 1
|
||||
},
|
||||
pane1Class: String,
|
||||
pane1Style: [Object, String] as PropType<CSSProperties | string>,
|
||||
pane2Class: String,
|
||||
pane2Style: [Object, String] as PropType<CSSProperties | string>,
|
||||
onDragStart: Function as PropType<(e: Event) => void>,
|
||||
onDragMove: Function as PropType<(e: Event) => void>,
|
||||
onDragEnd: Function as PropType<(e: Event) => void>,
|
||||
@ -88,17 +93,27 @@ export default defineComponent({
|
||||
watchEffect(() => (uncontrolledSizeRef.value = props.defaultSize))
|
||||
}
|
||||
// use to update controlled or uncontrolled values
|
||||
const doUpdateSize = (size: number): void => {
|
||||
const doUpdateSize = (size: number | string): void => {
|
||||
const _onUpdateSize = props['onUpdate:size']
|
||||
if (props.onUpdateSize) call(props.onUpdateSize, size)
|
||||
if (_onUpdateSize) call(_onUpdateSize, size)
|
||||
if (props.onUpdateSize) call(props.onUpdateSize, size as string & number)
|
||||
if (_onUpdateSize) call(_onUpdateSize, size as string & number)
|
||||
uncontrolledSizeRef.value = size
|
||||
}
|
||||
const mergedSizeRef = useMergedState(controlledSizeRef, uncontrolledSizeRef)
|
||||
|
||||
const firstPaneStyle = computed(() => {
|
||||
const size = mergedSizeRef.value * 100
|
||||
return {
|
||||
flex: `0 0 calc(${size}% - ${(props.resizeTriggerSize * size) / 100}px)`
|
||||
const sizeValue = mergedSizeRef.value
|
||||
if (typeof sizeValue === 'string') {
|
||||
return {
|
||||
flex: `0 0 ${sizeValue}`
|
||||
}
|
||||
} else if (typeof sizeValue === 'number') {
|
||||
const size = sizeValue * 100
|
||||
return {
|
||||
flex: `0 0 calc(${size}% - ${
|
||||
(props.resizeTriggerSize * size) / 100
|
||||
}px)`
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -154,32 +169,50 @@ export default defineComponent({
|
||||
offset = elRect.top - e.clientY
|
||||
}
|
||||
}
|
||||
|
||||
updateSize(e)
|
||||
}
|
||||
|
||||
const updateSize = (event: MouseEvent): void => {
|
||||
const parentRect =
|
||||
const containerRect =
|
||||
resizeTriggerElRef.value?.parentElement?.getBoundingClientRect()
|
||||
if (!parentRect) return
|
||||
const newSize =
|
||||
props.direction === 'horizontal'
|
||||
? (event.clientX - parentRect.left - offset) /
|
||||
(parentRect.width - props.resizeTriggerSize)
|
||||
: (event.clientY - parentRect.top + offset) /
|
||||
(parentRect.height - props.resizeTriggerSize)
|
||||
let nextSize = newSize
|
||||
if (props.min) {
|
||||
nextSize = Math.max(newSize, props.min)
|
||||
if (!containerRect) return
|
||||
|
||||
const { direction } = props
|
||||
|
||||
const containerUsableWidth = containerRect.width - props.resizeTriggerSize
|
||||
const containerUsableHeight =
|
||||
containerRect.height - props.resizeTriggerSize
|
||||
const containerUsableSize =
|
||||
direction === 'horizontal'
|
||||
? containerUsableWidth
|
||||
: containerUsableHeight
|
||||
|
||||
const newPxSize =
|
||||
direction === 'horizontal'
|
||||
? event.clientX - containerRect.left - offset
|
||||
: event.clientY - containerRect.top + offset
|
||||
|
||||
const { min, max } = props
|
||||
|
||||
const pxMin =
|
||||
typeof min === 'string' ? depx(min) : min * containerUsableSize
|
||||
const pxMax =
|
||||
typeof max === 'string' ? depx(max) : max * containerUsableSize
|
||||
|
||||
let nextPxSize = newPxSize
|
||||
nextPxSize = Math.max(nextPxSize, pxMin)
|
||||
nextPxSize = Math.min(nextPxSize, pxMax, containerUsableSize)
|
||||
// in pixel mode
|
||||
if (typeof mergedSizeRef.value === 'string') {
|
||||
doUpdateSize(`${nextPxSize}px`)
|
||||
} else {
|
||||
// in percentage mode
|
||||
doUpdateSize(nextPxSize / containerUsableSize)
|
||||
}
|
||||
if (props.max) {
|
||||
nextSize = Math.min(nextSize, props.max)
|
||||
}
|
||||
doUpdateSize(nextSize)
|
||||
}
|
||||
|
||||
const themeClassHandle = inlineThemeDisabled
|
||||
? useThemeClass('float-button', undefined, cssVarsRef, props)
|
||||
? useThemeClass('split', undefined, cssVarsRef, props)
|
||||
: undefined
|
||||
|
||||
return {
|
||||
@ -207,8 +240,8 @@ export default defineComponent({
|
||||
style={this.cssVars as CSSProperties}
|
||||
>
|
||||
<div
|
||||
class={`${this.mergedClsPrefix}-split-pane-1`}
|
||||
style={this.firstPaneStyle}
|
||||
class={[`${this.mergedClsPrefix}-split-pane-1`, this.pane1Class]}
|
||||
style={[this.firstPaneStyle, this.pane1Style]}
|
||||
>
|
||||
{this.$slots[1]?.()}
|
||||
</div>
|
||||
@ -231,7 +264,10 @@ export default defineComponent({
|
||||
])}
|
||||
</div>
|
||||
)}
|
||||
<div class={`${this.mergedClsPrefix}-split-pane-2`}>
|
||||
<div
|
||||
class={[`${this.mergedClsPrefix}-split-pane-2`, this.pane2Class]}
|
||||
style={this.pane2Style}
|
||||
>
|
||||
{this.$slots[2]?.()}
|
||||
</div>
|
||||
</div>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user