Merge branch 'dev' into next

This commit is contained in:
chenjiahan 2021-11-15 10:59:04 +08:00
commit 1bd2ab00ac
81 changed files with 565 additions and 753 deletions

View File

@ -1,4 +1,7 @@
{ {
"root": true, "root": true,
"extends": ["@vant"] "extends": ["@vant"],
"rules": {
"prefer-object-spread": "off"
}
} }

View File

@ -1,7 +1,7 @@
name: 我要反馈 Vant 的 Bug name: 我要反馈 Vant 的 Bug
description: 通过标准模板进行 Bug 反馈。 description: 通过标准模板进行 Bug 反馈。
title: "[Bug Report] 在此填写标题" title: "[Bug Report] 在此填写标题"
labels: ["🐞 bug"] labels: ["bug: need confirm"]
body: body:
- type: markdown - type: markdown
attributes: attributes:

View File

@ -1,7 +1,7 @@
name: 我要反馈 Vant Cli 的 Bug name: 我要反馈 Vant Cli 的 Bug
description: 通过标准模板进行 Bug 反馈。 description: 通过标准模板进行 Bug 反馈。
title: "[Bug Report] 在此填写标题" title: "[Bug Report] 在此填写标题"
labels: ["🐞 bug", "cli"] labels: ["bug: need confirm", "cli"]
body: body:
- type: markdown - type: markdown
attributes: attributes:

View File

@ -1,7 +1,7 @@
name: 我想要一个 Vant 的新功能 name: 我想要一个 Vant 的新功能
description: 通过标准模板描述一下你的功能需求。 description: 通过标准模板描述一下你的功能需求。
title: "[Feature Request] 在此填写标题" title: "[Feature Request] 在此填写标题"
labels: ["💡 feature"] labels: ["feature: need confirm"]
body: body:
- type: markdown - type: markdown
attributes: attributes:

View File

@ -1,7 +1,7 @@
name: 我想要一个 Vant Cli 的新功能 name: 我想要一个 Vant Cli 的新功能
description: 通过标准模板描述一下你的功能需求。 description: 通过标准模板描述一下你的功能需求。
title: "[Feature Request] 在此填写标题" title: "[Feature Request] 在此填写标题"
labels: ["💡 feature", "cli"] labels: ["feature: need confirm", "cli"]
body: body:
- type: markdown - type: markdown
attributes: attributes:

View File

@ -1,7 +1,7 @@
name: Bug Report name: Bug Report
description: Use issue template to report a bug. description: Use issue template to report a bug.
title: "[Bug Report] Title" title: "[Bug Report] Title"
labels: ["🐞 bug"] labels: ["bug: need confirm"]
body: body:
- type: input - type: input
id: reproduce id: reproduce

View File

@ -1,7 +1,7 @@
name: Feature Request name: Feature Request
description: Use issue template to request a new feature. description: Use issue template to request a new feature.
title: "[Feature Request] Title" title: "[Feature Request] Title"
labels: ["💡 feature"] labels: ["feature: need confirm"]
body: body:
- type: textarea - type: textarea
id: description id: description

View File

@ -41,12 +41,24 @@
## Install ## Install
```bash Using `npm` to install:
# Install Vant 2 for Vue 2 project
npm i vant -S
# Install Vant 3 for Vue 3 project ```bash
npm i vant@next -S # install Vant 2 for Vue 2 project
npm i vant@2
# install Vant 3 for Vue 3 project
npm i vant@3
```
Using `yarn` or `pnpm`:
```bash
# with yarn
yarn add vant@3
# with pnpm
pnpm add vant@3
``` ```
## Quickstart ## Quickstart

View File

@ -45,12 +45,24 @@ Vant 是**有赞前端团队**开源的移动端组件库,于 2017 年开源
## 安装 ## 安装
```bash 在现有项目中使用 Vant 时,可以通过 `npm` 进行安装:
# Vue 2 项目,安装 Vant 2
npm i vant -S
# Vue 3 项目,安装 Vant 3 ```bash
npm i vant@next -S # Vue 2 项目,安装 Vant 2
npm i vant@2
# Vue 3 项目,安装 Vant 3
npm i vant@3
```
当然,你也可以通过 `yarn``pnpm` 进行安装:
```bash
# 通过 yarn 安装
yarn add vant@3
# 通过 pnpm 安装
pnpm add vant@3
``` ```
## 快速上手 ## 快速上手

View File

@ -5,6 +5,7 @@
"dev": "pnpm dev --dir ./packages/vant", "dev": "pnpm dev --dir ./packages/vant",
"lint": "pnpm lint --dir ./packages/vant", "lint": "pnpm lint --dir ./packages/vant",
"test": "pnpm test --dir ./packages/vant", "test": "pnpm test --dir ./packages/vant",
"test:watch": "pnpm test:watch --dir ./packages/vant",
"build": "pnpm build --dir ./packages/vant", "build": "pnpm build --dir ./packages/vant",
"build:site": "pnpm build:site --dir ./packages/vant" "build:site": "pnpm build:site --dir ./packages/vant"
}, },
@ -18,10 +19,12 @@
"devDependencies": { "devDependencies": {
"@vant/cli": "workspace:*", "@vant/cli": "workspace:*",
"@vant/eslint-config": "workspace:*", "@vant/eslint-config": "workspace:*",
"@vant/stylelint-config": "workspace:*",
"eslint": "^8.2.0", "eslint": "^8.2.0",
"husky": "^7.0.4", "husky": "^7.0.4",
"lint-staged": "^11.2.6", "lint-staged": "^11.2.6",
"prettier": "^2.4.1", "prettier": "^2.4.1",
"rimraf": "^3.0.2",
"stylelint": "^13.13.1" "stylelint": "^13.13.1"
} }
} }

View File

@ -3,9 +3,12 @@
### 安装 ### 安装
```bash ```bash
# 通过 npm 安装 # 通过 npm
npm i <%= name %> -S npm i <%= name %>
# 通过 yarn 安装 # 通过 yarn
yarn add <%= name %> yarn add <%= name %>
# 通过 pnpm
pnpm add <%= name %>
``` ```

View File

@ -3,9 +3,12 @@
### 安装 ### 安装
```bash ```bash
# 通过 npm 安装 # 通过 npm
npm i <%= name %> -S npm i <%= name %>
# 通过 yarn 安装 # 通过 yarn
yarn add <%= name %> yarn add <%= name %>
# 通过 pnpm
pnpm add <%= name %>
``` ```

View File

@ -4,16 +4,15 @@
## 安装 ## 安装
#### NPM
```shell ```shell
npm i @vant/area-data -D # 通过 npm
``` npm i @vant/area-data
#### YARN # 通过 yarn
yarn add @vant/area-data
```shell # 通过 pnpm
yarn add @vant/area-data --dev pnpm add @vant/area-data
``` ```
## 使用 ## 使用

View File

@ -20,11 +20,14 @@ yarn create vant-cli-app
### 手动安装 ### 手动安装
```shell ```shell
# 通过 npm 安装 # 通过 npm
npm i @vant/cli -D npm i @vant/cli -D
# 通过 yarn 安装 # 通过 yarn
yarn add @vant/cli --dev yarn add @vant/cli -D
# 通过 pnpm
pnpm add @vant/cli -D
``` ```
安装完成后,请将以下配置添加到 package.json 文件中 安装完成后,请将以下配置添加到 package.json 文件中

View File

@ -36,6 +36,8 @@
yarn add sass yarn add sass
``` ```
- 为了避免 Phantom dependency不再默认依赖 `@vue/test-utils`,使用时需要手动安装
### Features ### Features
- 新增 site.htmlMeta 配置项 - 新增 site.htmlMeta 配置项

View File

@ -55,7 +55,6 @@
"@vitejs/plugin-vue": "^1.9.4", "@vitejs/plugin-vue": "^1.9.4",
"@vitejs/plugin-vue-jsx": "^1.2.0", "@vitejs/plugin-vue-jsx": "^1.2.0",
"@vue/babel-plugin-jsx": "^1.1.1", "@vue/babel-plugin-jsx": "^1.1.1",
"@vue/test-utils": "2.0.0-rc.14",
"autoprefixer": "^10.4.0", "autoprefixer": "^10.4.0",
"babel-jest": "^27.3.1", "babel-jest": "^27.3.1",
"chalk": "^4.1.2", "chalk": "^4.1.2",

View File

@ -2,16 +2,15 @@
## Install ## Install
#### NPM
```shell ```shell
# with npm
npm i @vant/eslint-config -D npm i @vant/eslint-config -D
```
#### YARN # with yarn
yarn add @vant/eslint-config -D
```shell # with pnpm
yarn add @vant/eslint-config --dev pnpm add @vant/eslint-config -D
``` ```
## Usage ## Usage

View File

@ -2,16 +2,15 @@
## Install ## Install
#### NPM
```shell ```shell
npm i @vant/icons -S # with npm
``` npm i @vant/icons
#### YARN # with yarn
```shell
yarn add @vant/icons yarn add @vant/icons
# with pnpm
pnpm add @vant/icons
``` ```
## Document ## Document

View File

@ -1 +0,0 @@
lib

View File

@ -1,27 +0,0 @@
# @vant/lazyload
This is a fork of [vue-lazyload](https://github.com/hilongjw/vue-lazyload) with Vue 3 support.
## Install
```shell
yarn add @vant/lazyload
```
## Usage
```js
import { createApp } from 'vue';
import { LazyLoad } from '@vant/lazyload';
const app = createApp();
app.use(LazyLoad);
```
## API
see: https://github.com/hilongjw/vue-lazyload
## TODO
support TypeScript.

View File

@ -1,3 +0,0 @@
module.exports = {
presets: [['@vant/cli/preset.cjs', { loose: true }]],
};

View File

@ -1,42 +0,0 @@
{
"name": "@vant/lazyload",
"version": "1.4.0",
"description": "This is a fork of vue-lazyload",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "types/index.d.ts",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"files": [
"dist",
"types"
],
"scripts": {
"dev": "rollup --config rollup.config.js --watch",
"build": "rollup --config rollup.config.js",
"release": "pnpm build && release-it",
"prepare": "pnpm build"
},
"license": "MIT",
"repository": "https://github.com/youzan/vant/tree/dev/packages/vant-lazyload",
"dependencies": {
"@vant/use": "^1.3.2"
},
"devDependencies": {
"vue": "3.x",
"@vue/runtime-core": "3.x",
"@vant/cli": "workspace:*",
"@rollup/plugin-babel": "^5.2.1",
"@rollup/plugin-node-resolve": "^10.0.0",
"release-it": "^14.2.2",
"rollup": "^2.33.3"
},
"release-it": {
"git": {
"tag": false,
"commitMessage": "release: @vant/lazyload ${version}"
}
}
}

View File

@ -1,22 +0,0 @@
import path from 'path';
import babel from '@rollup/plugin-babel';
import nodeResolve from '@rollup/plugin-node-resolve';
export default {
input: path.join(__dirname, 'src', 'index.js'),
output: [
{
dir: 'dist/cjs',
format: 'cjs',
},
{
dir: 'dist/esm',
format: 'esm',
},
],
external: ['vue', '@vant/use'],
plugins: [
babel({ babelHelpers: 'bundled', extensions: ['.js', '.ts'] }),
nodeResolve(),
],
};

View File

@ -4,27 +4,26 @@ Simple and fast vue markdown loader, transform markdown to vue component.
## Install ## Install
### NPM
```shell ```shell
npm i @vant/markdown-loader -S # with npm
``` npm i @vant/markdown-loader -D
### YARN # with yarn
yarn add @vant/markdown-loader -D
```shell # with pnpm
yarn add @vant/markdown-loader pnpm add @vant/markdown-loader -D
``` ```
## Options ## Options
- `enableMetaData`: Default `false`. Whether to use [front-matter](https://github.com/jxson/front-matter) to extract markdown meta data - `enableMetaData`: Default `false`. Whether to use [front-matter](https://github.com/jxson/front-matter) to extract markdown meta data
- `linkOpen`: Default `true`. Whether to add target="_blank" to all links - `linkOpen`: Default `true`. Whether to add target="\_blank" to all links
- `wrapper(html, fm)`: Format the returned content using a custom function - `wrapper(html, fm)`: Format the returned content using a custom function
- `html`: The result of [markdown-it](https://github.com/markdown-it/markdown-it)'s render - `html`: The result of [markdown-it](https://github.com/markdown-it/markdown-it)'s render
- `fm`: See [fm(string)](https://github.com/jxson/front-matter#fmstring). If `enableMetaData` option is `false`, the value is `undefined`. - `fm`: See [fm(string)](https://github.com/jxson/front-matter#fmstring). If `enableMetaData` option is `false`, the value is `undefined`.
- `attributes` - `attributes`
- `body` - `body`
- `frontmatter` - `frontmatter`

View File

@ -4,16 +4,15 @@
## Install ## Install
#### NPM
```shell ```shell
# with npm
npm i @vant/markdown-vetur -D npm i @vant/markdown-vetur -D
```
#### YARN # with yarn
yarn add @vant/markdown-vetur -D
```shell # with pnpm
yarn add @vant/markdown-vetur --dev pnpm add @vant/markdown-vetur -D
``` ```
## API ## API

View File

@ -5,7 +5,14 @@
## Install ## Install
```shell ```shell
# with npm
npm i @vant/popperjs
# with yarn
yarn add @vant/popperjs yarn add @vant/popperjs
# with pnpm
pnpm add @vant/popperjs
``` ```
## Usage ## Usage

View File

@ -1,3 +0,0 @@
module.exports = {
presets: [['@vant/cli/preset.cjs', { loose: true }]],
};

View File

@ -1,10 +1,11 @@
{ {
"name": "@vant/popperjs", "name": "@vant/popperjs",
"version": "1.1.0", "version": "1.1.0",
"description": "Precompiled popperjs core", "description": "Pre-compiled popperjs core",
"main": "dist/cjs/index.js", "main": "dist/cjs/index.js",
"module": "dist/esm/index.js", "module": "dist/esm/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
"sideEffects": false,
"files": [ "files": [
"dist" "dist"
], ],
@ -13,8 +14,11 @@
"registry": "https://registry.npmjs.org/" "registry": "https://registry.npmjs.org/"
}, },
"scripts": { "scripts": {
"clean": "rimraf ./dist",
"dev": "rollup --config rollup.config.js --watch", "dev": "rollup --config rollup.config.js --watch",
"build": "rollup --config rollup.config.js && tsc -p ./tsconfig.json --emitDeclarationOnly", "build:types": "tsc -p ./tsconfig.json --emitDeclarationOnly",
"build:bundle": "rollup --config rollup.config.js",
"build": "pnpm clean && pnpm build:bundle && pnpm build:types",
"release": "pnpm build && release-it", "release": "pnpm build && release-it",
"prepare": "pnpm build" "prepare": "pnpm build"
}, },
@ -24,11 +28,11 @@
"@popperjs/core": "^2.9.2" "@popperjs/core": "^2.9.2"
}, },
"devDependencies": { "devDependencies": {
"@vant/cli": "workspace:*", "typescript": "4.x",
"@rollup/plugin-babel": "^5.2.1", "rollup": "^2.33.3",
"@rollup/plugin-node-resolve": "^10.0.0", "rollup-plugin-esbuild": "^4.6.0",
"release-it": "^14.2.2", "@rollup/plugin-node-resolve": "^13.0.0",
"rollup": "^2.33.3" "release-it": "^14.2.2"
}, },
"release-it": { "release-it": {
"git": { "git": {

View File

@ -1,5 +1,5 @@
import path from 'path'; import path from 'path';
import babel from '@rollup/plugin-babel'; import esbuild from 'rollup-plugin-esbuild';
import nodeResolve from '@rollup/plugin-node-resolve'; import nodeResolve from '@rollup/plugin-node-resolve';
export default { export default {
@ -14,8 +14,5 @@ export default {
format: 'esm', format: 'esm',
}, },
], ],
plugins: [ plugins: [esbuild(), nodeResolve()],
babel({ babelHelpers: 'bundled', extensions: ['.js', '.ts'] }),
nodeResolve(),
],
}; };

View File

@ -2,13 +2,11 @@
"compilerOptions": { "compilerOptions": {
"target": "ES2015", "target": "ES2015",
"outDir": "./dist", "outDir": "./dist",
"module": "ES2015", "module": "ESNext",
"strict": true, "strict": true,
"declaration": true, "declaration": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "moduleResolution": "Node",
"moduleResolution": "node",
"lib": ["esnext", "dom"]
}, },
"include": ["src/**/*"] "include": ["src/**/*"]
} }

View File

@ -2,16 +2,15 @@
## Install ## Install
#### NPM
```shell ```shell
# with npm
npm i @vant/stylelint-config -D npm i @vant/stylelint-config -D
```
#### YARN # with yarn
yarn add @vant/stylelint-config -D
```shell # with pnpm
yarn add @vant/stylelint-config --dev pnpm add @vant/stylelint-config -D
``` ```
## Usage ## Usage

View File

@ -4,16 +4,15 @@
## Install ## Install
#### NPM
```shell ```shell
npm i @vant/touch-emulator -S # with npm
``` npm i @vant/touch-emulator
#### YARN # with yarn
```shell
yarn add @vant/touch-emulator yarn add @vant/touch-emulator
# with pnpm
pnpm add @vant/touch-emulator
``` ```
## 使用指南 ## 使用指南

View File

@ -1,17 +1,16 @@
# Vant Use # Vant Use
Vant Use 是从 Vant 实际应用场景中沉淀的 Vue 组合式 API 库。 Built-in composition APIs of Vant.
## 安装 ## Install
如果项目中已经安装了 Vant 3则无须手动安装 Vant Use。 ```shell
# with npm
npm i @vant/use
如果项目中未使用 Vant可以通过 `npm``yarn` 手动安装 Vant Use。 # with yarn
```bash
# 通过 npm 安装
npm i @vant/use -S
# 通过 yarn 安装
yarn add @vant/use yarn add @vant/use
# with pnpm
pnpm add @vant/use
``` ```

View File

@ -1,3 +0,0 @@
module.exports = {
presets: [['@vant/cli/preset.cjs', { loose: true }]],
};

View File

@ -1,144 +1,149 @@
# 更新日志 # Changelog
### v1.3.3
- Allow to call useWindowSize outside setup
- Improve usePageVisibility event bindings performance
### v1.3.2 ### v1.3.2
- 移除 passive event 的 polyfill -Remove passive event polyfill
### v1.3.1 ### v1.3.1
- 移除 requestAnimationFrame 的 polyfill - Remove requestAnimationFrame polyfill
### v1.3.0 ### v1.3.0
- 新增 `useCustomFieldValue` 方法 - Added `useCustomFieldValue` method
### v1.2.2 ### v1.2.2
`2021-07-22` `2021-07-22`
- `useEventListener`: 修复 `invalid watch source` 问题 - `useEventListener`: fix `invalid watch source` issue
### v1.2.1 ### v1.2.1
`2021-07-21` `2021-07-21`
- `useEventListener` 现在会监听 target 的变化,并重新监听事件 - `useEventListener` will now watch the target changes and re-listen the events
### v1.2.0 ### v1.2.0
`2021-07-12` `2021-07-12`
- 调整 `useParent``useChildren` 的类型定义 - Adjust type definition of `useParent` and `useChildren`
### v1.1.2 ### v1.1.2
`2021-04-22` `2021-04-22`
- 修复 `useScrollParent` 在 SSR 下报错的问题 - Fix the issue of `useScrollParent` reporting errors under SSR
### v1.1.1 ### v1.1.1
`2021-04-16` `2021-04-16`
- 移除 `@babel/runtime` 依赖 - Remove `@babel/runtime` dependency
### v1.1.0 ### v1.1.0
`2021-04-06` `2021-04-06`
- 构建结果由 ES5 调整为 ES6 - Compile to ES6 instead of ES5
### v1.0.5 ### v1.0.5
`2021-02-13` `2021-02-13`
- 优化 `useRect` 的返回值类型,始终返回 `DOMRect` - Optimize the return value type of `useRect`, always return `DOMRect`
### v1.0.4 ### v1.0.4
`2021-02-12` `2021-02-12`
- `useChildren` 支持通过泛型定义 Children 的类型 - `useChildren` supports defining the type of Children through generics
### v1.0.3 ### v1.0.3
`2021-02-10` `2021-02-10`
- 当 parent 不存在时,`useParent` 现在返回的 index 为 -1 而不是 undefined - When parent does not exist, `useParent` now returns index -1 instead of undefined
### v1.0.2 ### v1.0.2
`2021-01-01` `2021-01-01`
- 修复 useToggle 类型定义错误的问题 - Fix the problem of incorrect useToggle type definition
### v1.0.1 ### v1.0.1
`2020-12-27` `2020-12-27`
- 导出个别内部方法供 Vant 使用 - Export individual internal methods for use by Vant
### v1.0.0 ### v1.0.0
`2020-12-15` `2020-12-15`
- 优化构建产物体积 - Optimize the volume of the build product
### v0.1.0 ### v0.1.0
`2020-11-11` `2020-11-11`
- 新增 `onMountedOrActivated` 方法 - Added `onMountedOrActivated` method
### v0.0.8 ### v0.0.8
`2020-10-09` `2020-10-09`
- 改进类型定义 - Improve type definition
### v0.0.7 ### v0.0.7
`2020-10-06` `2020-10-06`
- 修复 `useCountDown` 未被导出的问题 - Fix the problem that `useCountDown` is not exported
### v0.0.6 ### v0.0.6
`2020-10-06` `2020-10-06`
- 导出所有类型定义 - Export all type definitions
### v0.0.5 ### v0.0.5
`2020-10-06` `2020-10-06`
- 新增 `useCountDown` 方法 - Added `useCountDown` method
### v0.0.4 ### v0.0.4
`2020-10-05` `2020-10-05`
- 新增 `useRect` 方法 - Added `useRect` method
### v0.0.3 ### v0.0.3
`2020-09-27` `2020-09-27`
- 新增 `useParent` 方法 - Added `useParent` method
- 新增 `useChildren` 方法 - Added `useChildren` method
### v0.0.2 ### v0.0.2
`2020-09-15` `2020-09-15`
- 新增 `useWindowSize` 方法 - Added `useWindowSize` method
### v0.0.1 ### v0.0.1
`2020-09-15` `2020-09-15`
- 新增 `useClickAway` 方法 - Added `useClickAway` method
- 新增 `useEventListener` 方法 - Added `useEventListener` method
- 新增 `usePageVisibility` 方法 - Added `usePageVisibility` method
- 新增 `useScrollParent` 方法 - Added `useScrollParent` method
- 新增 `useToggle` 方法 - Added `useToggle` method

View File

@ -1,19 +1,20 @@
{ {
"name": "@vant/use", "name": "@vant/use",
"version": "1.3.2", "version": "1.3.3",
"description": "Vant Composition API", "description": "Vant Composition API",
"main": "dist/cjs/index.js", "main": "dist/cjs/index.js",
"module": "dist/esm/index.js", "module": "dist/esm/index.js",
"typings": "dist/types/index.d.ts", "typings": "dist/index.d.ts",
"sideEffects": false, "sideEffects": false,
"files": [ "files": [
"dist" "dist"
], ],
"scripts": { "scripts": {
"clean": "rm -rf ./dist", "clean": "rimraf ./dist",
"build:lib": "node ./scripts/build.js", "dev": "rollup --config rollup.config.js --watch",
"build:types": "tsc -p ./tsconfig.json --emitDeclarationOnly", "build:types": "tsc -p ./tsconfig.json --emitDeclarationOnly",
"build": "pnpm build:lib && pnpm build:types", "build:bundle": "rollup --config rollup.config.js",
"build": "pnpm clean && pnpm build:bundle && pnpm build:types",
"release": "pnpm build && release-it", "release": "pnpm build && release-it",
"prepare": "pnpm build" "prepare": "pnpm build"
}, },
@ -24,12 +25,10 @@
"license": "MIT", "license": "MIT",
"repository": "https://github.com/youzan/vant/tree/dev/packages/vant-use", "repository": "https://github.com/youzan/vant/tree/dev/packages/vant-use",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.9",
"@vant/cli": "workspace:*",
"fast-glob": "^3.2.7",
"fs-extra": "^10.0.0",
"release-it": "^14.0.2", "release-it": "^14.0.2",
"typescript": "4.x", "typescript": "4.x",
"rollup": "^2.33.3",
"rollup-plugin-esbuild": "^4.6.0",
"vue": "^3.2.20" "vue": "^3.2.20"
}, },
"release-it": { "release-it": {

View File

@ -0,0 +1,18 @@
import path from 'path';
import esbuild from 'rollup-plugin-esbuild';
export default {
input: path.join(__dirname, 'src', 'index.ts'),
output: [
{
dir: 'dist/cjs',
format: 'cjs',
},
{
dir: 'dist/esm',
format: 'esm',
},
],
external: ['vue'],
plugins: [esbuild()],
};

View File

@ -1,40 +0,0 @@
const glob = require('fast-glob');
const { join } = require('path');
const { transformAsync } = require('@babel/core');
const { readFileSync, outputFileSync } = require('fs-extra');
const srcDir = join(__dirname, '..', 'src');
const distDir = join(__dirname, '..', 'dist');
const srcFiles = glob.sync(join(srcDir, '**', '*.ts'), {
ignore: ['**/node_modules', '**/*.spec.ts'],
});
const compile = (filePath, distDir) =>
new Promise((resolve, reject) => {
const code = readFileSync(filePath, 'utf-8');
const distPath = filePath.replace(srcDir, distDir).replace('.ts', '.js');
transformAsync(code, { filename: filePath })
.then((result) => {
if (result) {
outputFileSync(distPath, result.code);
resolve();
}
})
.catch(reject);
});
async function build() {
// esm output
await Promise.all(
srcFiles.map((srcFile) => compile(srcFile, join(distDir, 'esm')))
);
// cjs output
process.env.BABEL_MODULE = 'commonjs';
await Promise.all(
srcFiles.map((srcFile) => compile(srcFile, join(distDir, 'cjs')))
);
}
build();

View File

@ -1,5 +1,4 @@
import { Ref, unref } from 'vue'; import { Ref, unref } from 'vue';
import { inBrowser } from '../utils';
import { useEventListener } from '../useEventListener'; import { useEventListener } from '../useEventListener';
export type UseClickAwayOptions = { export type UseClickAwayOptions = {
@ -11,10 +10,6 @@ export function useClickAway(
listener: EventListener, listener: EventListener,
options: UseClickAwayOptions = {} options: UseClickAwayOptions = {}
) { ) {
if (!inBrowser) {
return;
}
const { eventName = 'click' } = options; const { eventName = 'click' } = options;
const onClick = (event: Event) => { const onClick = (event: Event) => {

View File

@ -1,18 +1,21 @@
import { ref } from 'vue'; import { ref, Ref } from 'vue';
import { inBrowser } from '../utils'; import { inBrowser } from '../utils';
import { useEventListener } from '../useEventListener';
let visibility: Ref<VisibilityState>;
export function usePageVisibility() { export function usePageVisibility() {
const visibility = ref<VisibilityState>('visible'); if (!visibility) {
visibility = ref<VisibilityState>('visible');
const setVisibility = () => {
if (inBrowser) { if (inBrowser) {
visibility.value = document.hidden ? 'hidden' : 'visible'; const update = () => {
} visibility.value = document.hidden ? 'hidden' : 'visible';
}; };
setVisibility(); update();
useEventListener('visibilitychange', setVisibility); window.addEventListener('visibilitychange', update);
}
}
return visibility; return visibility;
} }

View File

@ -1,19 +1,16 @@
import { Ref, unref } from 'vue'; import { Ref, unref } from 'vue';
function isWindow(val: unknown): val is Window { const isWindow = (val: unknown): val is Window => val === window;
return val === window;
}
function makeDOMRect(width: number, height: number) { const makeDOMRect = (width: number, height: number) =>
return { ({
top: 0, top: 0,
left: 0, left: 0,
right: width, right: width,
bottom: height, bottom: height,
width, width,
height, height,
} as DOMRect; } as DOMRect);
}
export const useRect = ( export const useRect = (
elementOrRef: Element | Window | Ref<Element | Window | undefined> elementOrRef: Element | Window | Ref<Element | Window | undefined>
@ -26,7 +23,7 @@ export const useRect = (
return makeDOMRect(width, height); return makeDOMRect(width, height);
} }
if (element && element.getBoundingClientRect) { if (element?.getBoundingClientRect) {
return element.getBoundingClientRect(); return element.getBoundingClientRect();
} }

View File

@ -1,6 +1,5 @@
import { ref, Ref } from 'vue'; import { ref, Ref } from 'vue';
import { inBrowser } from '../utils'; import { inBrowser } from '../utils';
import { useEventListener } from '../useEventListener';
let width: Ref<number>; let width: Ref<number>;
let height: Ref<number>; let height: Ref<number>;
@ -10,16 +9,16 @@ export function useWindowSize() {
width = ref(0); width = ref(0);
height = ref(0); height = ref(0);
const update = () => { if (inBrowser) {
if (inBrowser) { const update = () => {
width.value = window.innerWidth; width.value = window.innerWidth;
height.value = window.innerHeight; height.value = window.innerHeight;
} };
};
update(); update();
useEventListener('resize', update); window.addEventListener('resize', update, { passive: true });
useEventListener('orientationchange', update); window.addEventListener('orientationchange', update, { passive: true });
}
} }
return { width, height }; return { width, height };

View File

@ -1,4 +1,4 @@
import { raf, cancelRaf } from '../utils'; import { raf, cancelRaf } from '../src/utils';
test('raf', async () => { test('raf', async () => {
const spy = jest.fn(); const spy = jest.fn();

View File

@ -1,13 +1,12 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2015", "target": "ES2015",
"outDir": "./dist/types", "outDir": "./dist",
"module": "ES2015", "module": "ESNext",
"strict": true, "strict": true,
"declaration": true, "declaration": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "moduleResolution": "Node",
"moduleResolution": "Node"
}, },
"include": ["src/**/*"] "include": ["src/**/*"]
} }

View File

@ -4,12 +4,24 @@
### npm ### npm
```bash Using `npm` to install:
# Install Vant 2 for Vue 2 project
npm i vant -S
# Install Vant 3 for Vue 3 project ```bash
npm i vant@next -S # install Vant 2 for Vue 2 project
npm i vant@2
# install Vant 3 for Vue 3 project
npm i vant@3
```
Using `yarn` or `pnpm`:
```bash
# with yarn
yarn add vant@3
# with pnpm
pnpm add vant@3
``` ```
### CDN ### CDN

View File

@ -8,14 +8,24 @@
### 通过 npm 安装 ### 通过 npm 安装
在现有项目中使用 Vant 时,可以通过 `npm` `yarn` 进行安装: 在现有项目中使用 Vant 时,可以通过 `npm` 进行安装:
```bash ```bash
# Vue 2 项目,安装 Vant 2 # Vue 2 项目,安装 Vant 2
npm i vant -S npm i vant@2
# Vue 3 项目,安装 Vant 3 # Vue 3 项目,安装 Vant 3
npm i vant@next -S npm i vant@3
```
当然,你也可以通过 `yarn``pnpm` 进行安装:
```bash
# 通过 yarn 安装
yarn add vant@3
# 通过 pnpm 安装
pnpm add vant@3
``` ```
### 通过 CDN 安装 ### 通过 CDN 安装

View File

@ -2,6 +2,7 @@ module.exports = {
testPathIgnorePatterns: ['/node_modules/'], testPathIgnorePatterns: ['/node_modules/'],
collectCoverageFrom: [ collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx,vue}', 'src/**/*.{js,jsx,ts,tsx,vue}',
'!src/lazyload/vue-lazyload/**',
'!**/demo/**', '!**/demo/**',
'!**/test/**', '!**/test/**',
'!**/lang/**', '!**/lang/**',

View File

@ -45,18 +45,18 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vant/icons": "^1.7.1", "@vant/icons": "^1.7.1",
"@vant/lazyload": "^1.4.0",
"@vant/popperjs": "^1.1.0", "@vant/popperjs": "^1.1.0",
"@vant/use": "^1.3.2" "@vant/use": "^1.3.3"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^3.0.0" "vue": "^3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@vant/area-data": "^1.1.3", "@vant/area-data": "^1.1.3",
"@vant/cli": "^4.0.0-beta.5", "@vant/cli": "workspace:*",
"@vue/compiler-sfc": "^3.2.20", "@vue/compiler-sfc": "^3.2.20",
"@vue/runtime-core": "^3.2.20", "@vue/runtime-core": "^3.2.20",
"@vue/test-utils": "^2.0.0-rc.16",
"typescript": "4.x", "typescript": "4.x",
"vue": "^3.2.20", "vue": "^3.2.20",
"vue-router": "^4.0.12" "vue-router": "^4.0.12"

View File

@ -27,7 +27,8 @@ import { useExpose } from '../composables/use-expose';
// Components // Components
import { Area, AreaList, AreaColumnOption, AreaInstance } from '../area'; import { Area, AreaList, AreaColumnOption, AreaInstance } from '../area';
import { Cell } from '../cell'; import { Cell } from '../cell';
import { Field } from '../field'; import { Form } from '../form';
import { Field, FieldRule } from '../field';
import { Popup } from '../popup'; import { Popup } from '../popup';
import { Toast } from '../toast'; import { Toast } from '../toast';
import { Button } from '../button'; import { Button } from '../button';
@ -111,25 +112,16 @@ export default defineComponent({
setup(props, { emit, slots }) { setup(props, { emit, slots }) {
const areaRef = ref<AreaInstance>(); const areaRef = ref<AreaInstance>();
const state = reactive({ const data = reactive({} as AddressEditInfo);
data: {} as AddressEditInfo, const showAreaPopup = ref(false);
showAreaPopup: false, const detailFocused = ref(false);
detailFocused: false,
errorInfo: {
tel: '',
name: '',
areaCode: '',
postalCode: '',
addressDetail: '',
} as Record<string, string>,
});
const areaListLoaded = computed( const areaListLoaded = computed(
() => isObject(props.areaList) && Object.keys(props.areaList).length () => isObject(props.areaList) && Object.keys(props.areaList).length
); );
const areaText = computed(() => { const areaText = computed(() => {
const { country, province, city, county, areaCode } = state.data; const { country, province, city, county, areaCode } = data;
if (areaCode) { if (areaCode) {
const arr = [country, province, city, county]; const arr = [country, province, city, county];
if (province && province === city) { if (province && province === city) {
@ -142,7 +134,7 @@ export default defineComponent({
// hide bottom field when use search && detail get focused // hide bottom field when use search && detail get focused
const hideBottomFields = computed( const hideBottomFields = computed(
() => props.searchResult?.length && state.detailFocused () => props.searchResult?.length && detailFocused.value
); );
const assignAreaValues = () => { const assignAreaValues = () => {
@ -150,70 +142,52 @@ export default defineComponent({
const detail: Record<string, string> = areaRef.value.getArea(); const detail: Record<string, string> = areaRef.value.getArea();
detail.areaCode = detail.code; detail.areaCode = detail.code;
delete detail.code; delete detail.code;
extend(state.data, detail); extend(data, detail);
} }
}; };
const onFocus = (key: string) => { const onFocus = (key: string) => {
state.errorInfo[key] = ''; detailFocused.value = key === 'addressDetail';
state.detailFocused = key === 'addressDetail';
emit('focus', key); emit('focus', key);
}; };
const getErrorMessage = (key: string) => { const rules = computed<Record<string, FieldRule[]>>(() => {
const value = String((state.data as any)[key] || '').trim(); const { validator, telValidator, postalValidator } = props;
if (props.validator) { const makeRule = (name: string, emptyMessage: string): FieldRule => ({
const message = props.validator(key, value); validator: (value) => {
if (message) { if (validator) {
return message; const message = validator(name, value);
} if (message) {
} return message;
}
switch (key) { }
case 'name': if (!value) {
return value ? '' : t('nameEmpty'); return emptyMessage;
case 'tel': }
return props.telValidator(value) ? '' : t('telInvalid'); return true;
case 'areaCode': },
return value ? '' : t('areaEmpty');
case 'addressDetail':
return value ? '' : t('addressEmpty');
case 'postalCode':
return value && !props.postalValidator(value) ? t('postalEmpty') : '';
}
};
const onSave = () => {
const items = ['name', 'tel'];
if (props.showArea) {
items.push('areaCode');
}
if (props.showDetail) {
items.push('addressDetail');
}
if (props.showPostal) {
items.push('postalCode');
}
const isValid = items.every((item) => {
const msg = getErrorMessage(item);
if (msg) {
state.errorInfo[item] = msg;
}
return !msg;
}); });
if (isValid && !props.isSaving) { return {
emit('save', state.data); name: [makeRule('name', t('nameEmpty'))],
} tel: [
}; makeRule('tel', t('telInvalid')),
{ validator: telValidator, message: t('telInvalid') },
],
areaCode: [makeRule('areaCode', t('areaEmpty'))],
addressDetail: [makeRule('addressDetail', t('addressEmpty'))],
postalCode: [
makeRule('addressDetail', t('postalEmpty')),
{ validator: postalValidator, message: t('postalEmpty') },
],
};
});
const onSave = () => emit('save', data);
const onChangeDetail = (val: string) => { const onChangeDetail = (val: string) => {
state.data.addressDetail = val; data.addressDetail = val;
emit('change-detail', val); emit('change-detail', val);
}; };
@ -222,22 +196,21 @@ export default defineComponent({
if (values.some((value) => !value.code)) { if (values.some((value) => !value.code)) {
Toast(t('areaEmpty')); Toast(t('areaEmpty'));
return; } else {
showAreaPopup.value = false;
assignAreaValues();
emit('change-area', values);
} }
state.showAreaPopup = false;
assignAreaValues();
emit('change-area', values);
}; };
const onDelete = () => emit('delete', state.data); const onDelete = () => emit('delete', data);
// get values of area component // get values of area component
const getArea = () => (areaRef.value ? areaRef.value.getValues() : []); const getArea = () => areaRef.value?.getValues() || [];
// set area code to area component // set area code to area component
const setAreaCode = (code?: string) => { const setAreaCode = (code?: string) => {
state.data.areaCode = code || ''; data.areaCode = code || '';
if (code) { if (code) {
nextTick(assignAreaValues); nextTick(assignAreaValues);
@ -247,12 +220,12 @@ export default defineComponent({
const onDetailBlur = () => { const onDetailBlur = () => {
// await for click search event // await for click search event
setTimeout(() => { setTimeout(() => {
state.detailFocused = false; detailFocused.value = false;
}); });
}; };
const setAddressDetail = (value: string) => { const setAddressDetail = (value: string) => {
state.data.addressDetail = value; data.addressDetail = value;
}; };
const renderSetDefaultCell = () => { const renderSetDefaultCell = () => {
@ -260,7 +233,7 @@ export default defineComponent({
const slots = { const slots = {
'right-icon': () => ( 'right-icon': () => (
<Switch <Switch
v-model={state.data.isDefault} v-model={data.isDefault}
size="24" size="24"
onChange={(event) => emit('change-default', event)} onChange={(event) => emit('change-default', event)}
/> />
@ -277,8 +250,6 @@ export default defineComponent({
/> />
); );
} }
return null;
}; };
useExpose({ useExpose({
@ -289,13 +260,13 @@ export default defineComponent({
watch( watch(
() => props.areaList, () => props.areaList,
() => setAreaCode(state.data.areaCode) () => setAreaCode(data.areaCode)
); );
watch( watch(
() => props.addressInfo, () => props.addressInfo,
(value) => { (value) => {
state.data = extend({}, DEFAULT_DATA, value); extend(data, DEFAULT_DATA, value);
setAreaCode(value.areaCode); setAreaCode(value.areaCode);
}, },
{ {
@ -305,18 +276,17 @@ export default defineComponent({
); );
return () => { return () => {
const { data, errorInfo } = state;
const { disableArea } = props; const { disableArea } = props;
return ( return (
<div class={bem()}> <Form class={bem()} onSubmit={onSave}>
<div class={bem('fields')}> <div class={bem('fields')}>
<Field <Field
v-model={data.name} v-model={data.name}
clearable clearable
label={t('name')} label={t('name')}
rules={rules.value.name}
placeholder={t('name')} placeholder={t('name')}
errorMessage={errorInfo.name}
onFocus={() => onFocus('name')} onFocus={() => onFocus('name')}
/> />
<Field <Field
@ -324,9 +294,9 @@ export default defineComponent({
clearable clearable
type="tel" type="tel"
label={t('tel')} label={t('tel')}
rules={rules.value.tel}
maxlength={props.telMaxlength} maxlength={props.telMaxlength}
placeholder={t('tel')} placeholder={t('tel')}
errorMessage={errorInfo.tel}
onFocus={() => onFocus('tel')} onFocus={() => onFocus('tel')}
/> />
<Field <Field
@ -335,22 +305,22 @@ export default defineComponent({
label={t('area')} label={t('area')}
is-link={!disableArea} is-link={!disableArea}
modelValue={areaText.value} modelValue={areaText.value}
rules={rules.value.areaCode}
placeholder={props.areaPlaceholder || t('area')} placeholder={props.areaPlaceholder || t('area')}
errorMessage={errorInfo.areaCode}
onFocus={() => onFocus('areaCode')} onFocus={() => onFocus('areaCode')}
onClick={() => { onClick={() => {
emit('click-area'); emit('click-area');
state.showAreaPopup = !disableArea; showAreaPopup.value = !disableArea;
}} }}
/> />
<AddressEditDetail <AddressEditDetail
show={props.showDetail} show={props.showDetail}
rows={props.detailRows}
rules={rules.value.addressDetail}
value={data.addressDetail} value={data.addressDetail}
focused={state.detailFocused} focused={detailFocused.value}
detailRows={props.detailRows} maxlength={props.detailMaxlength}
errorMessage={errorInfo.addressDetail}
searchResult={props.searchResult} searchResult={props.searchResult}
detailMaxlength={props.detailMaxlength}
showSearchResult={props.showSearchResult} showSearchResult={props.showSearchResult}
onBlur={onDetailBlur} onBlur={onDetailBlur}
onFocus={() => onFocus('addressDetail')} onFocus={() => onFocus('addressDetail')}
@ -362,10 +332,10 @@ export default defineComponent({
v-show={!hideBottomFields.value} v-show={!hideBottomFields.value}
v-model={data.postalCode} v-model={data.postalCode}
type="tel" type="tel"
rules={rules.value.postalCode}
label={t('postal')} label={t('postal')}
maxlength="6" maxlength="6"
placeholder={t('postal')} placeholder={t('postal')}
errorMessage={errorInfo.postalCode}
onFocus={() => onFocus('postalCode')} onFocus={() => onFocus('postalCode')}
/> />
)} )}
@ -380,6 +350,7 @@ export default defineComponent({
text={props.saveButtonText || t('save')} text={props.saveButtonText || t('save')}
class={bem('button')} class={bem('button')}
loading={props.isSaving} loading={props.isSaving}
nativeType="submit"
onClick={onSave} onClick={onSave}
/> />
{props.showDelete && ( {props.showDelete && (
@ -394,7 +365,7 @@ export default defineComponent({
)} )}
</div> </div>
<Popup <Popup
v-model:show={state.showAreaPopup} v-model:show={showAreaPopup.value}
round round
teleport="body" teleport="body"
position="bottom" position="bottom"
@ -408,11 +379,11 @@ export default defineComponent({
columnsPlaceholder={props.areaColumnsPlaceholder} columnsPlaceholder={props.areaColumnsPlaceholder}
onConfirm={onAreaConfirm} onConfirm={onAreaConfirm}
onCancel={() => { onCancel={() => {
state.showAreaPopup = false; showAreaPopup.value = false;
}} }}
/> />
</Popup> </Popup>
</div> </Form>
); );
}; };
}, },

View File

@ -9,7 +9,7 @@ import { Field } from '../field';
// Types // Types
import type { AddressEditSearchItem } from './types'; import type { AddressEditSearchItem } from './types';
import type { FieldInstance } from '../field/types'; import type { FieldRule, FieldInstance } from '../field/types';
const [name, bem, t] = createNamespace('address-edit-detail'); const [name, bem, t] = createNamespace('address-edit-detail');
@ -18,12 +18,12 @@ export default defineComponent({
props: { props: {
show: Boolean, show: Boolean,
rows: numericProp,
value: String, value: String,
rules: Array as PropType<FieldRule[]>,
focused: Boolean, focused: Boolean,
detailRows: numericProp, maxlength: numericProp,
searchResult: Array as PropType<AddressEditSearchItem[]>, searchResult: Array as PropType<AddressEditSearchItem[]>,
errorMessage: String,
detailMaxlength: numericProp,
showSearchResult: Boolean, showSearchResult: Boolean,
}, },
@ -86,14 +86,14 @@ export default defineComponent({
clearable clearable
ref={field} ref={field}
class={bem()} class={bem()}
rows={props.detailRows} rows={props.rows}
type="textarea" type="textarea"
rules={props.rules}
label={t('label')} label={t('label')}
border={!showSearchResult()} border={!showSearchResult()}
maxlength={props.detailMaxlength} maxlength={props.maxlength}
modelValue={props.value} modelValue={props.value}
placeholder={t('placeholder')} placeholder={t('placeholder')}
errorMessage={props.errorMessage}
onBlur={onBlur} onBlur={onBlur}
onFocus={onFocus} onFocus={onFocus}
onUpdate:modelValue={onInput} onUpdate:modelValue={onInput}

View File

@ -2,7 +2,7 @@
exports[`should render demo and match snapshot 1`] = ` exports[`should render demo and match snapshot 1`] = `
<div> <div>
<div class="van-address-edit"> <form class="van-form van-address-edit">
<div class="van-address-edit__fields"> <div class="van-address-edit__fields">
<div class="van-cell van-field"> <div class="van-cell van-field">
<div class="van-cell__title van-field__label"> <div class="van-cell__title van-field__label">
@ -104,7 +104,7 @@ exports[`should render demo and match snapshot 1`] = `
</div> </div>
</div> </div>
<div class="van-address-edit__buttons"> <div class="van-address-edit__buttons">
<button type="button" <button type="submit"
class="van-button van-button--danger van-button--normal van-button--block van-button--round van-address-edit__button" class="van-button van-button--danger van-button--normal van-button--block van-button--round van-address-edit__button"
> >
<div class="van-button__content"> <div class="van-button__content">
@ -123,6 +123,6 @@ exports[`should render demo and match snapshot 1`] = `
</div> </div>
</button> </button>
</div> </div>
</div> </form>
</div> </div>
`; `;

View File

@ -7,7 +7,7 @@ exports[`should allow to custom validator with validator prop 1`] = `
`; `;
exports[`should render AddressEdit correctly 1`] = ` exports[`should render AddressEdit correctly 1`] = `
<div class="van-address-edit"> <form class="van-form van-address-edit">
<div class="van-address-edit__fields"> <div class="van-address-edit__fields">
<div class="van-cell van-field"> <div class="van-cell van-field">
<div class="van-cell__title van-field__label"> <div class="van-cell__title van-field__label">
@ -78,7 +78,7 @@ exports[`should render AddressEdit correctly 1`] = `
</div> </div>
</div> </div>
<div class="van-address-edit__buttons"> <div class="van-address-edit__buttons">
<button type="button" <button type="submit"
class="van-button van-button--danger van-button--normal van-button--block van-button--round van-address-edit__button" class="van-button van-button--danger van-button--normal van-button--block van-button--round van-address-edit__button"
> >
<div class="van-button__content"> <div class="van-button__content">
@ -88,11 +88,11 @@ exports[`should render AddressEdit correctly 1`] = `
</div> </div>
</button> </button>
</div> </div>
</div> </form>
`; `;
exports[`should render AddressEdit with props correctly 1`] = ` exports[`should render AddressEdit with props correctly 1`] = `
<div class="van-address-edit"> <form class="van-form van-address-edit">
<div class="van-address-edit__fields"> <div class="van-address-edit__fields">
<div class="van-cell van-field"> <div class="van-cell van-field">
<div class="van-cell__title van-field__label"> <div class="van-cell__title van-field__label">
@ -193,7 +193,7 @@ exports[`should render AddressEdit with props correctly 1`] = `
</div> </div>
</div> </div>
<div class="van-address-edit__buttons"> <div class="van-address-edit__buttons">
<button type="button" <button type="submit"
class="van-button van-button--danger van-button--normal van-button--block van-button--round van-address-edit__button" class="van-button van-button--danger van-button--normal van-button--block van-button--round van-address-edit__button"
> >
<div class="van-button__content"> <div class="van-button__content">
@ -203,7 +203,7 @@ exports[`should render AddressEdit with props correctly 1`] = `
</div> </div>
</button> </button>
</div> </div>
</div> </form>
`; `;
exports[`should valid address detail and render error message correctly 1`] = ` exports[`should valid address detail and render error message correctly 1`] = `
@ -229,26 +229,6 @@ exports[`should valid address detail and render error message correctly 1`] = `
</div> </div>
`; `;
exports[`should valid address detail and render error message correctly 2`] = `
<div class="van-cell van-field van-address-edit-detail">
<div class="van-cell__title van-field__label">
<label>
Address
</label>
</div>
<div class="van-cell__value van-field__value">
<div class="van-field__body">
<textarea rows="1"
class="van-field__control"
placeholder="Address"
style="height: auto;"
>
</textarea>
</div>
</div>
</div>
`;
exports[`should valid area code and render error message correctly 1`] = ` exports[`should valid area code and render error message correctly 1`] = `
<div class="van-cell van-cell--clickable van-field" <div class="van-cell van-cell--clickable van-field"
role="button" role="button"
@ -276,30 +256,6 @@ exports[`should valid area code and render error message correctly 1`] = `
</div> </div>
`; `;
exports[`should valid area code and render error message correctly 2`] = `
<div class="van-cell van-cell--clickable van-field"
role="button"
tabindex="0"
>
<div class="van-cell__title van-field__label">
<label>
Area
</label>
</div>
<div class="van-cell__value van-field__value">
<div class="van-field__body">
<input type="text"
class="van-field__control"
readonly
placeholder="Area"
>
</div>
</div>
<i class="van-badge__wrapper van-icon van-icon-arrow van-cell__right-icon">
</i>
</div>
`;
exports[`should valid name and render error message correctly 1`] = ` exports[`should valid name and render error message correctly 1`] = `
<div class="van-cell van-field"> <div class="van-cell van-field">
<div class="van-cell__title van-field__label"> <div class="van-cell__title van-field__label">
@ -321,24 +277,6 @@ exports[`should valid name and render error message correctly 1`] = `
</div> </div>
`; `;
exports[`should valid name and render error message correctly 2`] = `
<div class="van-cell van-field">
<div class="van-cell__title van-field__label">
<label>
Name
</label>
</div>
<div class="van-cell__value van-field__value">
<div class="van-field__body">
<input type="text"
class="van-field__control"
placeholder="Name"
>
</div>
</div>
</div>
`;
exports[`should valid postal code and render error message correctly 1`] = ` exports[`should valid postal code and render error message correctly 1`] = `
<div class="van-cell van-field"> <div class="van-cell van-field">
<div class="van-cell__title van-field__label"> <div class="van-cell__title van-field__label">
@ -360,24 +298,6 @@ exports[`should valid postal code and render error message correctly 1`] = `
</div> </div>
`; `;
exports[`should valid postal code and render error message correctly 2`] = `
<div class="van-cell van-field">
<div class="van-cell__title van-field__label">
<label>
Postal
</label>
</div>
<div class="van-cell__value van-field__value">
<div class="van-field__body">
<input type="tel"
class="van-field__control"
placeholder="Postal"
>
</div>
</div>
</div>
`;
exports[`should valid tel and render error message correctly 1`] = ` exports[`should valid tel and render error message correctly 1`] = `
<div class="van-cell van-field"> <div class="van-cell van-field">
<div class="van-cell__title van-field__label"> <div class="van-cell__title van-field__label">
@ -398,21 +318,3 @@ exports[`should valid tel and render error message correctly 1`] = `
</div> </div>
</div> </div>
`; `;
exports[`should valid tel and render error message correctly 2`] = `
<div class="van-cell van-field">
<div class="van-cell__title van-field__label">
<label>
Phone
</label>
</div>
<div class="van-cell__value van-field__value">
<div class="van-field__body">
<input type="tel"
class="van-field__control"
placeholder="Phone"
>
</div>
</div>
</div>
`;

View File

@ -1,6 +1,7 @@
import { AddressEdit } from '..'; import { AddressEdit } from '..';
import { areaList } from '../../area/demo/area-simple'; import { areaList } from '../../area/demo/area-simple';
import { mount, later, trigger } from '../../../test'; import { mount, later, trigger } from '../../../test';
import { submitForm } from '../../form/test/shared';
const defaultAddressInfo = { const defaultAddressInfo = {
name: '测试', name: '测试',
@ -27,12 +28,10 @@ const createComponent = (addressInfo = {}) => {
}, },
}); });
const button = wrapper.find('.van-button');
const fields = wrapper.findAll('.van-field'); const fields = wrapper.findAll('.van-field');
return { return {
vm: wrapper.vm, vm: wrapper.vm,
fields, fields,
button,
wrapper, wrapper,
}; };
}; };
@ -55,13 +54,6 @@ test('should render AddressEdit with props correctly', () => {
expect(wrapper.html()).toMatchSnapshot(); expect(wrapper.html()).toMatchSnapshot();
}); });
// test('set-default', () => {
// const { wrapper } = createComponent();
// wrapper.find('.van-switch').trigger('click');
// expect(wrapper.html()).toMatchSnapshot();
// });
test('should allow to custom validator with validator prop', async () => { test('should allow to custom validator with validator prop', async () => {
const wrapper = mount(AddressEdit, { const wrapper = mount(AddressEdit, {
props: { props: {
@ -70,63 +62,53 @@ test('should allow to custom validator with validator prop', async () => {
}, },
}); });
const button = wrapper.find('.van-button'); await submitForm(wrapper);
await button.trigger('click');
expect(wrapper.find('.van-field__error-message').html()).toMatchSnapshot(); expect(wrapper.find('.van-field__error-message').html()).toMatchSnapshot();
}); });
test('should valid name and render error message correctly', async () => { test('should valid name and render error message correctly', async () => {
const { fields, button } = createComponent({ const { fields, wrapper } = createComponent({
name: '', name: '',
}); });
await button.trigger('click'); await submitForm(wrapper);
expect(fields[0].html()).toMatchSnapshot();
await fields[0].find('input').trigger('focus');
expect(fields[0].html()).toMatchSnapshot(); expect(fields[0].html()).toMatchSnapshot();
}); });
test('should valid tel and render error message correctly', async () => { test('should valid tel and render error message correctly', async () => {
const { fields, button } = createComponent({ const { fields, wrapper } = createComponent({
tel: '', tel: '',
}); });
await button.trigger('click'); await submitForm(wrapper);
expect(fields[1].html()).toMatchSnapshot();
await fields[1].find('input').trigger('focus');
expect(fields[1].html()).toMatchSnapshot(); expect(fields[1].html()).toMatchSnapshot();
}); });
test('should valid area code and render error message correctly', async () => { test('should valid area code and render error message correctly', async () => {
const { fields, button } = createComponent({ const { fields, wrapper } = createComponent({
areaCode: '', areaCode: '',
}); });
await button.trigger('click'); await submitForm(wrapper);
expect(fields[2].html()).toMatchSnapshot();
await fields[2].find('input').trigger('focus');
expect(fields[2].html()).toMatchSnapshot(); expect(fields[2].html()).toMatchSnapshot();
}); });
test('should valid address detail and render error message correctly', async () => { test('should valid address detail and render error message correctly', async () => {
const { fields, button } = createComponent({ const { fields, wrapper } = createComponent({
addressDetail: '', addressDetail: '',
}); });
await button.trigger('click'); await submitForm(wrapper);
expect(fields[3].html()).toMatchSnapshot(); await later();
await fields[3].find('textarea').trigger('focus');
expect(fields[3].html()).toMatchSnapshot(); expect(fields[3].html()).toMatchSnapshot();
}); });
test('should valid postal code and render error message correctly', async () => { test('should valid postal code and render error message correctly', async () => {
const { fields, button } = createComponent({ const { fields, wrapper } = createComponent({
postalCode: '123', postalCode: '123',
}); });
await button.trigger('click'); await submitForm(wrapper);
expect(fields[4].html()).toMatchSnapshot();
await fields[4].find('input').trigger('focus');
expect(fields[4].html()).toMatchSnapshot(); expect(fields[4].html()).toMatchSnapshot();
}); });

View File

@ -55,7 +55,14 @@ export default {
Vant officially provides a default area data, which can be imported through [@vant/area-data](https://github.com/youzan/vant/tree/dev/packages/vant-area-data): Vant officially provides a default area data, which can be imported through [@vant/area-data](https://github.com/youzan/vant/tree/dev/packages/vant-area-data):
```bash ```bash
# with npm
npm i @vant/area-data
# with yarn
yarn add @vant/area-data yarn add @vant/area-data
# with pnpm
pnpm add @vant/area-data
``` ```
```ts ```ts

View File

@ -57,7 +57,14 @@ const areaList = {
Vant 官方提供了一份默认的省市区数据,可以通过 [@vant/area-data](https://github.com/youzan/vant/tree/dev/packages/vant-area-data) 引入: Vant 官方提供了一份默认的省市区数据,可以通过 [@vant/area-data](https://github.com/youzan/vant/tree/dev/packages/vant-area-data) 引入:
```bash ```bash
# 通过 npm
npm i @vant/area-data
# 通过 yarn
yarn add @vant/area-data yarn add @vant/area-data
# 通过 pnpm
pnpm add @vant/area-data
``` ```
```ts ```ts

View File

@ -7,13 +7,9 @@ export type CheckboxLabelPosition = CheckerLabelPosition;
export type CheckboxExpose = { export type CheckboxExpose = {
toggle: (newValue?: boolean) => void; toggle: (newValue?: boolean) => void;
/** /** @private */
* @private
*/
props: CheckboxProps; props: CheckboxProps;
/** /** @private */
* @private
*/
checked: ComputedRef<boolean>; checked: ComputedRef<boolean>;
}; };

View File

@ -17,10 +17,12 @@ export const routeProps = {
export type RouteProps = ExtractPropTypes<typeof routeProps>; export type RouteProps = ExtractPropTypes<typeof routeProps>;
export function route(vm: ComponentPublicInstance<RouteProps>) { export function route({
const router = vm.$router; to,
const { to, url, replace } = vm; url,
replace,
$router: router,
}: ComponentPublicInstance<RouteProps>) {
if (to && router) { if (to && router) {
router[replace ? 'replace' : 'push'](to); router[replace ? 'replace' : 'push'](to);
} else if (url) { } else if (url) {

View File

@ -14,17 +14,13 @@ export type DropdownItemExpose = {
immediate?: boolean; immediate?: boolean;
} }
) => void; ) => void;
/** /** @private */
* @private
*/
state: { state: {
showPopup: boolean; showPopup: boolean;
transition: boolean; transition: boolean;
showWrapper: boolean; showWrapper: boolean;
}; };
/** /** @private */
* @private
*/
renderTitle: () => string | VNode[]; renderTitle: () => string | VNode[];
}; };

View File

@ -310,13 +310,16 @@ export default defineComponent({
nextTick(adjustTextareaSize); nextTick(adjustTextareaSize);
// readonly not work in legacy mobile safari // readonly not work in legacy mobile safari
const readonly = getProp('readonly'); if (getProp('readonly')) {
if (readonly) {
blur(); blur();
} }
}; };
const onBlur = (event: Event) => { const onBlur = (event: Event) => {
if (getProp('readonly')) {
return;
}
state.focused = false; state.focused = false;
updateValue(getModelValue(), 'onBlur'); updateValue(getModelValue(), 'onBlur');
emit('blur', event); emit('blur', event);

View File

@ -318,8 +318,11 @@ import type {
FieldProps, FieldProps,
FieldInstance, FieldInstance,
FieldTextAlign, FieldTextAlign,
FieldRuleMessage,
FieldClearTrigger, FieldClearTrigger,
FieldFormatTrigger, FieldFormatTrigger,
FieldRuleValidator,
FiledRuleFormatter,
FieldValidateError, FieldValidateError,
FieldAutosizeConfig, FieldAutosizeConfig,
FieldValidateTrigger, FieldValidateTrigger,

View File

@ -337,8 +337,11 @@ import type {
FieldProps, FieldProps,
FieldInstance, FieldInstance,
FieldTextAlign, FieldTextAlign,
FieldRuleMessage,
FieldClearTrigger, FieldClearTrigger,
FieldFormatTrigger, FieldFormatTrigger,
FieldRuleValidator,
FiledRuleFormatter,
FieldValidateError, FieldValidateError,
FieldAutosizeConfig, FieldAutosizeConfig,
FieldValidateTrigger, FieldValidateTrigger,

View File

@ -9,8 +9,11 @@ export type {
FieldRule, FieldRule,
FieldInstance, FieldInstance,
FieldTextAlign, FieldTextAlign,
FieldRuleMessage,
FieldClearTrigger, FieldClearTrigger,
FieldFormatTrigger, FieldFormatTrigger,
FieldRuleValidator,
FiledRuleFormatter,
FieldValidateError, FieldValidateError,
FieldAutosizeConfig, FieldAutosizeConfig,
FieldValidateTrigger, FieldValidateTrigger,

View File

@ -1,3 +1,4 @@
/* eslint-disable no-use-before-define */
import type { ComputedRef, ComponentPublicInstance } from 'vue'; import type { ComputedRef, ComponentPublicInstance } from 'vue';
import type { FieldProps } from './Field'; import type { FieldProps } from './Field';
@ -28,16 +29,24 @@ export type FieldValidateError = {
message: string; message: string;
}; };
export type FieldRuleMessage =
| string
| ((value: any, rule: FieldRule) => string);
export type FieldRuleValidator = (
value: any,
rule: FieldRule
) => boolean | string | Promise<boolean | string>;
export type FiledRuleFormatter = (value: any, rule: FieldRule) => string;
export type FieldRule = { export type FieldRule = {
pattern?: RegExp; pattern?: RegExp;
trigger?: FieldValidateTrigger; trigger?: FieldValidateTrigger;
message?: string | ((value: any, rule: FieldRule) => string); message?: FieldRuleMessage;
required?: boolean; required?: boolean;
validator?: ( validator?: FieldRuleValidator;
value: any, formatter?: FiledRuleFormatter;
rule: FieldRule
) => boolean | string | Promise<boolean | string>;
formatter?: (value: any, rule: FieldRule) => string;
}; };
// Shared props of Field and Form // Shared props of Field and Form
@ -57,9 +66,7 @@ export type FieldExpose = {
rules?: FieldRule[] | undefined rules?: FieldRule[] | undefined
) => Promise<void | FieldValidateError>; ) => Promise<void | FieldValidateError>;
resetValidation: () => void; resetValidation: () => void;
/** /** @private */
* @private
*/
formValue: ComputedRef<unknown>; formValue: ComputedRef<unknown>;
}; };

View File

@ -63,15 +63,10 @@ export default defineComponent({
const { $Lazyload } = getCurrentInstance()!.proxy!; const { $Lazyload } = getCurrentInstance()!.proxy!;
const style = computed(() => { const style = computed(() => {
const style: CSSProperties = {}; const style: CSSProperties = {
width: addUnit(props.width),
if (isDef(props.width)) { height: addUnit(props.height),
style.width = addUnit(props.width); };
}
if (isDef(props.height)) {
style.height = addUnit(props.height);
}
if (isDef(props.radius)) { if (isDef(props.radius)) {
style.overflow = 'hidden'; style.overflow = 'hidden';

View File

@ -1,4 +1,4 @@
import { Lazyload } from '@vant/lazyload'; import { Lazyload } from './vue-lazyload';
export default Lazyload; export default Lazyload;
export { Lazyload }; export { Lazyload };

View File

@ -1,4 +1,4 @@
import { App } from 'vue'; import type { App } from 'vue';
declare type ListenEvent = declare type ListenEvent =
| 'scroll' | 'scroll'

View File

@ -1,3 +1,7 @@
/**
* This is a fork of [vue-lazyload](https://github.com/hilongjw/vue-lazyload) with Vue 3 support.
*/
import Lazy from './lazy'; import Lazy from './lazy';
import LazyComponent from './lazy-component'; import LazyComponent from './lazy-component';
import LazyContainer from './lazy-container'; import LazyContainer from './lazy-container';

View File

@ -1,5 +1,5 @@
import { h } from 'vue'; import { h } from 'vue';
import { inBrowser } from '@vant/use'; import { inBrowser, useRect } from '@vant/use';
export default (lazy) => ({ export default (lazy) => ({
props: { props: {
@ -24,7 +24,6 @@ export default (lazy) => ({
state: { state: {
loaded: false, loaded: false,
}, },
rect: {},
show: false, show: false,
}; };
}, },
@ -40,18 +39,14 @@ export default (lazy) => ({
}, },
methods: { methods: {
getRect() {
this.rect = this.$el.getBoundingClientRect();
},
checkInView() { checkInView() {
this.getRect(); const rect = useRect(this.$el);
return ( return (
inBrowser && inBrowser &&
this.rect.top < window.innerHeight * lazy.options.preLoad && rect.top < window.innerHeight * lazy.options.preLoad &&
this.rect.bottom > 0 && rect.bottom > 0 &&
this.rect.left < window.innerWidth * lazy.options.preLoad && rect.left < window.innerWidth * lazy.options.preLoad &&
this.rect.right > 0 rect.right > 0
); );
}, },

View File

@ -1,5 +1,6 @@
import { inBrowser } from '@vant/use'; import { useRect } from '@vant/use';
import { loadImageAsync, noop } from './util'; import { loadImageAsync } from './util';
import { noop } from '../../utils';
export default (lazyManager) => ({ export default (lazyManager) => ({
props: { props: {
@ -34,7 +35,6 @@ export default (lazyManager) => ({
error: false, error: false,
attempt: 0, attempt: 0,
}, },
rect: {},
renderSrc: '', renderSrc: '',
}; };
}, },
@ -66,17 +66,13 @@ export default (lazyManager) => ({
this.options.loading = loading; this.options.loading = loading;
this.renderSrc = this.options.loading; this.renderSrc = this.options.loading;
}, },
getRect() {
this.rect = this.$el.getBoundingClientRect();
},
checkInView() { checkInView() {
this.getRect(); const rect = useRect(this.$el);
return ( return (
inBrowser && rect.top < window.innerHeight * lazyManager.options.preLoad &&
this.rect.top < window.innerHeight * lazyManager.options.preLoad && rect.bottom > 0 &&
this.rect.bottom > 0 && rect.left < window.innerWidth * lazyManager.options.preLoad &&
this.rect.left < window.innerWidth * lazyManager.options.preLoad && rect.right > 0
this.rect.right > 0
); );
}, },
load(onFinish = noop) { load(onFinish = noop) {

View File

@ -8,11 +8,11 @@ import {
supportWebp, supportWebp,
getDPR, getDPR,
getBestSelectionFromSrcset, getBestSelectionFromSrcset,
isObject,
hasIntersectionObserver, hasIntersectionObserver,
modeType, modeType,
ImageCache, ImageCache,
} from './util'; } from './util';
import { isObject } from '../../utils';
import ReactiveListener from './listener'; import ReactiveListener from './listener';
const DEFAULT_URL = const DEFAULT_URL =
@ -64,7 +64,6 @@ export default function () {
attempt: attempt || 3, attempt: attempt || 3,
scale: scale || getDPR(scale), scale: scale || getDPR(scale),
ListenEvents: listenEvents || DEFAULT_EVENTS, ListenEvents: listenEvents || DEFAULT_EVENTS,
hasbind: false,
supportWebp: supportWebp(), supportWebp: supportWebp(),
filter: filter || {}, filter: filter || {},
adapter: adapter || {}, adapter: adapter || {},

View File

@ -1,4 +1,6 @@
import { loadImageAsync, noop } from './util'; import { useRect } from '@vant/use';
import { loadImageAsync } from './util';
import { noop } from '../../utils';
export default class ReactiveListener { export default class ReactiveListener {
constructor({ constructor({
@ -26,8 +28,6 @@ export default class ReactiveListener {
this.options = options; this.options = options;
this.rect = null;
this.$parent = $parent; this.$parent = $parent;
this.elRenderer = elRenderer; this.elRenderer = elRenderer;
this._imageCache = imageCache; this._imageCache = imageCache;
@ -87,25 +87,17 @@ export default class ReactiveListener {
} }
} }
/*
* get el node rect
* @return
*/
getRect() {
this.rect = this.el.getBoundingClientRect();
}
/* /*
* check el is in view * check el is in view
* @return {Boolean} el is in view * @return {Boolean} el is in view
*/ */
checkInView() { checkInView() {
this.getRect(); const rect = useRect(this.el);
return ( return (
this.rect.top < window.innerHeight * this.options.preLoad && rect.top < window.innerHeight * this.options.preLoad &&
this.rect.bottom > this.options.preLoadTop && rect.bottom > this.options.preLoadTop &&
this.rect.left < window.innerWidth * this.options.preLoad && rect.left < window.innerWidth * this.options.preLoad &&
this.rect.right > 0 rect.right > 0
); );
} }

View File

@ -158,12 +158,6 @@ export const loadImageAsync = (item, resolve, reject) => {
image.onerror = (e) => reject(e); image.onerror = (e) => reject(e);
}; };
export function isObject(obj) {
return obj !== null && typeof obj === 'object';
}
export function noop() {}
export class ImageCache { export class ImageCache {
constructor({ max }) { constructor({ max }) {
this.options = { this.options = {

View File

@ -19,9 +19,7 @@ export type SwipeExpose = {
next: () => void; next: () => void;
resize: () => void; resize: () => void;
swipeTo: (index: number, options?: SwipeToOptions) => void; swipeTo: (index: number, options?: SwipeToOptions) => void;
/** /** @private */
* @private
*/
state: SwipeState; state: SwipeState;
}; };

View File

@ -31,6 +31,8 @@
left: 0; left: 0;
width: var(--van-switch-node-size); width: var(--van-switch-node-size);
height: var(--van-switch-node-size); height: var(--van-switch-node-size);
// https://github.com/youzan/vant/issues/9839
font-size: inherit;
background: var(--van-switch-node-background); background: var(--van-switch-node-background);
border-radius: 100%; border-radius: 100%;
box-shadow: var(--van-switch-node-shadow); box-shadow: var(--van-switch-node-shadow);

View File

@ -63,13 +63,14 @@ export default defineComponent({
return pathMatched || nameMatched; return pathMatched || nameMatched;
} }
return (props.name || index.value) === modelValue; return (props.name ?? index.value) === modelValue;
}); });
const onClick = (event: MouseEvent) => { const onClick = (event: MouseEvent) => {
parent.setActive(props.name ?? index.value); if (!active.value) {
parent.setActive(props.name ?? index.value, route);
}
emit('click', event); emit('click', event);
route();
}; };
const renderIcon = () => { const renderIcon = () => {

View File

@ -44,7 +44,7 @@ export type TabbarProps = ExtractPropTypes<typeof tabbarProps>;
export type TabbarProvide = { export type TabbarProvide = {
props: TabbarProps; props: TabbarProps;
setActive: (active: number | string) => void; setActive: (active: number | string, afterChange: () => void) => void;
}; };
export const TABBAR_KEY: InjectionKey<TabbarProvide> = Symbol(name); export const TABBAR_KEY: InjectionKey<TabbarProvide> = Symbol(name);
@ -83,16 +83,15 @@ export default defineComponent({
); );
}; };
const setActive = (active: number | string) => { const setActive = (active: number | string, afterChange: () => void) => {
if (active !== props.modelValue) { callInterceptor(props.beforeChange, {
callInterceptor(props.beforeChange, { args: [active],
args: [active], done() {
done() { emit('update:modelValue', active);
emit('update:modelValue', active); emit('change', active);
emit('change', active); afterChange();
}, },
}); });
}
}; };
linkChildren({ props, setActive }); linkChildren({ props, setActive });

View File

@ -69,6 +69,34 @@ test('should match active tab by route path in route mode', async () => {
expect(items[3].classes()).not.toContain(activeClass); expect(items[3].classes()).not.toContain(activeClass);
}); });
test('should allow to use before-change prop in route mode', async () => {
const wrapper = mount(
{
render: () => (
<Tabbar route beforeChange={() => false}>
<TabbarItem replace to="/">
Tab
</TabbarItem>
<TabbarItem replace to="/search">
Tab
</TabbarItem>
</Tabbar>
),
},
{
global: {
mocks: getMockRouter(),
},
}
);
const items = wrapper.findAll('.van-tabbar-item');
expect(items[0].classes()).toContain(activeClass);
await items[1].trigger('click');
expect(items[1].classes()).not.toContain(activeClass);
});
test('should match active tab by route name in route mode', async () => { test('should match active tab by route name in route mode', async () => {
const wrapper = mount( const wrapper = mount(
{ {

View File

@ -1,13 +1,13 @@
import { CSSProperties } from 'vue'; import { CSSProperties } from 'vue';
import { useWindowSize } from '@vant/use';
import { inBrowser } from './basic'; import { inBrowser } from './basic';
import { isDef, isNumeric } from './validate'; import { isDef, isNumeric } from './validate';
export function addUnit(value?: string | number): string | undefined { export function addUnit(value?: string | number): string | undefined {
if (!isDef(value)) { if (isDef(value)) {
return undefined; return isNumeric(value) ? `${value}px` : String(value);
} }
return undefined;
return isNumeric(value) ? `${value}px` : String(value);
} }
export function getSizeStyle( export function getSizeStyle(
@ -51,13 +51,15 @@ function convertRem(value: string) {
} }
function convertVw(value: string) { function convertVw(value: string) {
const { width } = useWindowSize();
value = value.replace(/vw/g, ''); value = value.replace(/vw/g, '');
return (+value * window.innerWidth) / 100; return (+value * width.value) / 100;
} }
function convertVh(value: string) { function convertVh(value: string) {
const { height } = useWindowSize();
value = value.replace(/vh/g, ''); value = value.replace(/vh/g, '');
return (+value * window.innerHeight) / 100; return (+value * height.value) / 100;
} }
export function unitToPx(value: string | number): number { export function unitToPx(value: string | number): number {

View File

@ -6,5 +6,10 @@
"emitDeclarationOnly": true "emitDeclarationOnly": true
}, },
"include": ["es/**/*", "lib/**/*"], "include": ["es/**/*", "lib/**/*"],
"exclude": ["node_modules", "**/test/**/*", "**/demo/**/*"] "exclude": [
"node_modules",
"**/test/**/*",
"**/demo/**/*",
"**/vue-lazyload/*"
]
} }

117
pnpm-lock.yaml generated
View File

@ -6,18 +6,22 @@ importers:
specifiers: specifiers:
'@vant/cli': workspace:* '@vant/cli': workspace:*
'@vant/eslint-config': workspace:* '@vant/eslint-config': workspace:*
'@vant/stylelint-config': workspace:*
eslint: ^8.2.0 eslint: ^8.2.0
husky: ^7.0.4 husky: ^7.0.4
lint-staged: ^11.2.6 lint-staged: ^11.2.6
prettier: ^2.4.1 prettier: ^2.4.1
rimraf: ^3.0.2
stylelint: ^13.13.1 stylelint: ^13.13.1
devDependencies: devDependencies:
'@vant/cli': link:packages/vant-cli '@vant/cli': link:packages/vant-cli
'@vant/eslint-config': link:packages/vant-eslint-config '@vant/eslint-config': link:packages/vant-eslint-config
'@vant/stylelint-config': link:packages/vant-stylelint-config
eslint: 8.2.0 eslint: 8.2.0
husky: 7.0.4 husky: 7.0.4
lint-staged: 11.2.6 lint-staged: 11.2.6
prettier: 2.4.1 prettier: 2.4.1
rimraf: 3.0.2
stylelint: 13.13.1 stylelint: 13.13.1
packages/create-vant-cli-app: packages/create-vant-cli-app:
@ -46,19 +50,18 @@ importers:
packages/vant: packages/vant:
specifiers: specifiers:
'@vant/area-data': ^1.1.3 '@vant/area-data': ^1.1.3
'@vant/cli': ^4.0.0-beta.5 '@vant/cli': workspace:*
'@vant/icons': ^1.7.1 '@vant/icons': ^1.7.1
'@vant/lazyload': ^1.4.0
'@vant/popperjs': ^1.1.0 '@vant/popperjs': ^1.1.0
'@vant/use': ^1.3.2 '@vant/use': ^1.3.3
'@vue/compiler-sfc': ^3.2.20 '@vue/compiler-sfc': ^3.2.20
'@vue/runtime-core': ^3.2.20 '@vue/runtime-core': ^3.2.20
'@vue/test-utils': ^2.0.0-rc.16
typescript: 4.x typescript: 4.x
vue: ^3.2.20 vue: ^3.2.20
vue-router: ^4.0.12 vue-router: ^4.0.12
dependencies: dependencies:
'@vant/icons': link:../vant-icons '@vant/icons': link:../vant-icons
'@vant/lazyload': link:../vant-lazyload
'@vant/popperjs': link:../vant-popperjs '@vant/popperjs': link:../vant-popperjs
'@vant/use': link:../vant-use '@vant/use': link:../vant-use
devDependencies: devDependencies:
@ -66,6 +69,7 @@ importers:
'@vant/cli': link:../vant-cli '@vant/cli': link:../vant-cli
'@vue/compiler-sfc': 3.2.21 '@vue/compiler-sfc': 3.2.21
'@vue/runtime-core': 3.2.21 '@vue/runtime-core': 3.2.21
'@vue/test-utils': 2.0.0-rc.16_vue@3.2.21
typescript: 4.4.4 typescript: 4.4.4
vue: 3.2.21 vue: 3.2.21
vue-router: 4.0.12_vue@3.2.21 vue-router: 4.0.12_vue@3.2.21
@ -93,7 +97,6 @@ importers:
'@vitejs/plugin-vue-jsx': ^1.2.0 '@vitejs/plugin-vue-jsx': ^1.2.0
'@vue/babel-plugin-jsx': ^1.1.1 '@vue/babel-plugin-jsx': ^1.1.1
'@vue/compiler-sfc': ^3.2.20 '@vue/compiler-sfc': ^3.2.20
'@vue/test-utils': 2.0.0-rc.14
autoprefixer: ^10.4.0 autoprefixer: ^10.4.0
babel-jest: ^27.3.1 babel-jest: ^27.3.1
chalk: ^4.1.2 chalk: ^4.1.2
@ -146,7 +149,6 @@ importers:
'@vitejs/plugin-vue': 1.9.4_vite@2.6.13 '@vitejs/plugin-vue': 1.9.4_vite@2.6.13
'@vitejs/plugin-vue-jsx': 1.2.0 '@vitejs/plugin-vue-jsx': 1.2.0
'@vue/babel-plugin-jsx': 1.1.1_@babel+core@7.16.0 '@vue/babel-plugin-jsx': 1.1.1_@babel+core@7.16.0
'@vue/test-utils': 2.0.0-rc.14_vue@3.2.21
autoprefixer: 10.4.0_postcss@8.3.11 autoprefixer: 10.4.0_postcss@8.3.11
babel-jest: 27.3.1_@babel+core@7.16.0 babel-jest: 27.3.1_@babel+core@7.16.0
chalk: 4.1.2 chalk: 4.1.2
@ -219,27 +221,6 @@ importers:
devDependencies: devDependencies:
release-it: 14.11.6 release-it: 14.11.6
packages/vant-lazyload:
specifiers:
'@rollup/plugin-babel': ^5.2.1
'@rollup/plugin-node-resolve': ^10.0.0
'@vant/cli': workspace:*
'@vant/use': ^1.3.2
'@vue/runtime-core': 3.x
release-it: ^14.2.2
rollup: ^2.33.3
vue: 3.x
dependencies:
'@vant/use': link:../vant-use
devDependencies:
'@rollup/plugin-babel': 5.3.0_rollup@2.59.0
'@rollup/plugin-node-resolve': 10.0.0_rollup@2.59.0
'@vant/cli': link:../vant-cli
'@vue/runtime-core': 3.2.21
release-it: 14.11.6
rollup: 2.59.0
vue: 3.2.21
packages/vant-markdown-loader: packages/vant-markdown-loader:
specifiers: specifiers:
front-matter: ^4.0.2 front-matter: ^4.0.2
@ -270,19 +251,19 @@ importers:
packages/vant-popperjs: packages/vant-popperjs:
specifiers: specifiers:
'@popperjs/core': ^2.9.2 '@popperjs/core': ^2.9.2
'@rollup/plugin-babel': ^5.2.1 '@rollup/plugin-node-resolve': ^13.0.0
'@rollup/plugin-node-resolve': ^10.0.0
'@vant/cli': workspace:*
release-it: ^14.2.2 release-it: ^14.2.2
rollup: ^2.33.3 rollup: ^2.33.3
rollup-plugin-esbuild: ^4.6.0
typescript: 4.x
dependencies: dependencies:
'@popperjs/core': 2.10.2 '@popperjs/core': 2.10.2
devDependencies: devDependencies:
'@rollup/plugin-babel': 5.3.0_rollup@2.59.0 '@rollup/plugin-node-resolve': 13.0.6_rollup@2.59.0
'@rollup/plugin-node-resolve': 10.0.0_rollup@2.59.0
'@vant/cli': link:../vant-cli
release-it: 14.11.6 release-it: 14.11.6
rollup: 2.59.0 rollup: 2.59.0
rollup-plugin-esbuild: 4.6.0_rollup@2.59.0
typescript: 4.4.4
packages/vant-stylelint-config: packages/vant-stylelint-config:
specifiers: specifiers:
@ -300,19 +281,15 @@ importers:
packages/vant-use: packages/vant-use:
specifiers: specifiers:
'@babel/core': ^7.12.9
'@vant/cli': workspace:*
fast-glob: ^3.2.7
fs-extra: ^10.0.0
release-it: ^14.0.2 release-it: ^14.0.2
rollup: ^2.33.3
rollup-plugin-esbuild: ^4.6.0
typescript: 4.x typescript: 4.x
vue: ^3.2.20 vue: ^3.2.20
devDependencies: devDependencies:
'@babel/core': 7.16.0
'@vant/cli': link:../vant-cli
fast-glob: 3.2.7
fs-extra: 10.0.0
release-it: 14.11.6 release-it: 14.11.6
rollup: 2.59.0
rollup-plugin-esbuild: 4.6.0_rollup@2.59.0
typescript: 4.4.4 typescript: 4.4.4
vue: 3.2.21 vue: 3.2.21
@ -1972,27 +1949,11 @@ packages:
resolution: {integrity: sha1-B5jAM1Hw3qGlpMq93yalWny+5ZA=, tarball: '@popperjs/core/download/@popperjs/core-2.10.2.tgz'} resolution: {integrity: sha1-B5jAM1Hw3qGlpMq93yalWny+5ZA=, tarball: '@popperjs/core/download/@popperjs/core-2.10.2.tgz'}
dev: false dev: false
/@rollup/plugin-babel/5.3.0_rollup@2.59.0: /@rollup/plugin-node-resolve/13.0.6_rollup@2.59.0:
resolution: {integrity: sha1-nLHFFG3daklorZbyCcUMYvkvmHk=, tarball: '@rollup/plugin-babel/download/@rollup/plugin-babel-5.3.0.tgz'} resolution: {integrity: sha1-KWKQcLt2dWe+gVf1dc+o8rjp73c=, tarball: '@rollup/plugin-node-resolve/download/@rollup/plugin-node-resolve-13.0.6.tgz'}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
peerDependencies: peerDependencies:
'@babel/core': ^7.0.0 rollup: ^2.42.0
'@types/babel__core': ^7.1.9
rollup: ^1.20.0||^2.0.0
peerDependenciesMeta:
'@types/babel__core':
optional: true
dependencies:
'@babel/helper-module-imports': 7.16.0
'@rollup/pluginutils': 3.1.0_rollup@2.59.0
rollup: 2.59.0
dev: true
/@rollup/plugin-node-resolve/10.0.0_rollup@2.59.0:
resolution: {integrity: sha1-RAZKK5jfdTDmas+JQf8mL8m06tg=, tarball: '@rollup/plugin-node-resolve/download/@rollup/plugin-node-resolve-10.0.0.tgz'}
engines: {node: '>= 10.0.0'}
peerDependencies:
rollup: ^1.20.0||^2.0.0
dependencies: dependencies:
'@rollup/pluginutils': 3.1.0_rollup@2.59.0 '@rollup/pluginutils': 3.1.0_rollup@2.59.0
'@types/resolve': 1.17.1 '@types/resolve': 1.17.1
@ -2021,7 +1982,6 @@ packages:
dependencies: dependencies:
estree-walker: 2.0.2 estree-walker: 2.0.2
picomatch: 2.3.0 picomatch: 2.3.0
dev: false
/@sindresorhus/is/0.14.0: /@sindresorhus/is/0.14.0:
resolution: {integrity: sha1-n7OjzzEyMoFR81PeRjLgHlIQK+o=, tarball: '@sindresorhus/is/download/@sindresorhus/is-0.14.0.tgz'} resolution: {integrity: sha1-n7OjzzEyMoFR81PeRjLgHlIQK+o=, tarball: '@sindresorhus/is/download/@sindresorhus/is-0.14.0.tgz'}
@ -2509,13 +2469,13 @@ packages:
resolution: {integrity: sha1-TNgMDmLPZaetqyRJ6GtvDLM6Ews=, tarball: '@vue/shared/download/@vue/shared-3.2.21.tgz'} resolution: {integrity: sha1-TNgMDmLPZaetqyRJ6GtvDLM6Ews=, tarball: '@vue/shared/download/@vue/shared-3.2.21.tgz'}
dev: true dev: true
/@vue/test-utils/2.0.0-rc.14_vue@3.2.21: /@vue/test-utils/2.0.0-rc.16_vue@3.2.21:
resolution: {integrity: sha1-naG+ew42X/X5RWd9oXv2yKeoOr0=, tarball: '@vue/test-utils/download/@vue/test-utils-2.0.0-rc.14.tgz'} resolution: {integrity: sha1-WTgPAocPhWrAAqKcAmgdPz/Lr+s=, tarball: '@vue/test-utils/download/@vue/test-utils-2.0.0-rc.16.tgz'}
peerDependencies: peerDependencies:
vue: ^3.0.1 vue: ^3.0.1
dependencies: dependencies:
vue: 3.2.21 vue: 3.2.21
dev: false dev: true
/JSONStream/1.3.5: /JSONStream/1.3.5:
resolution: {integrity: sha1-MgjB8I06TZkmGrZPkjArwV4RHKA=, tarball: JSONStream/download/JSONStream-1.3.5.tgz} resolution: {integrity: sha1-MgjB8I06TZkmGrZPkjArwV4RHKA=, tarball: JSONStream/download/JSONStream-1.3.5.tgz}
@ -3594,7 +3554,7 @@ packages:
resolution: {integrity: sha1-pvLc5hL63S7x9Rm3NVHxfoUZmDE=, tarball: deep-is/download/deep-is-0.1.4.tgz} resolution: {integrity: sha1-pvLc5hL63S7x9Rm3NVHxfoUZmDE=, tarball: deep-is/download/deep-is-0.1.4.tgz}
/deepmerge/4.2.2: /deepmerge/4.2.2:
resolution: {integrity: sha1-RNLqNnm49NT/ujPwPYZfwee/SVU=, tarball: deepmerge/download/deepmerge-4.2.2.tgz?cache=0&sync_timestamp=1632822781299&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fdeepmerge%2Fdownload%2Fdeepmerge-4.2.2.tgz} resolution: {integrity: sha1-RNLqNnm49NT/ujPwPYZfwee/SVU=, tarball: deepmerge/download/deepmerge-4.2.2.tgz}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
/defaults/1.0.3: /defaults/1.0.3:
@ -4455,6 +4415,7 @@ packages:
graceful-fs: 4.2.8 graceful-fs: 4.2.8
jsonfile: 6.1.0 jsonfile: 6.1.0
universalify: 2.0.0 universalify: 2.0.0
dev: false
/fs-extra/8.1.0: /fs-extra/8.1.0:
resolution: {integrity: sha1-SdQ8RaiM2Wd2aMt74bRu/bjS4cA=, tarball: fs-extra/download/fs-extra-8.1.0.tgz?cache=0&sync_timestamp=1632822706452&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Ffs-extra%2Fdownload%2Ffs-extra-8.1.0.tgz} resolution: {integrity: sha1-SdQ8RaiM2Wd2aMt74bRu/bjS4cA=, tarball: fs-extra/download/fs-extra-8.1.0.tgz?cache=0&sync_timestamp=1632822706452&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Ffs-extra%2Fdownload%2Ffs-extra-8.1.0.tgz}
@ -5812,6 +5773,11 @@ packages:
- utf-8-validate - utf-8-validate
dev: false dev: false
/joycon/3.0.1:
resolution: {integrity: sha1-kHTJsIzPN6Zyb/dKGEhfhe/K3a8=, tarball: joycon/download/joycon-3.0.1.tgz}
engines: {node: '>=10'}
dev: true
/js-tokens/4.0.0: /js-tokens/4.0.0:
resolution: {integrity: sha1-GSA/tZmR35jjoocFDUZHzerzJJk=, tarball: js-tokens/download/js-tokens-4.0.0.tgz} resolution: {integrity: sha1-GSA/tZmR35jjoocFDUZHzerzJJk=, tarball: js-tokens/download/js-tokens-4.0.0.tgz}
@ -5921,6 +5887,10 @@ packages:
dependencies: dependencies:
minimist: 1.2.5 minimist: 1.2.5
/jsonc-parser/3.0.0:
resolution: {integrity: sha1-q914VwHH5+rKip7IzwcMpRp0WiI=, tarball: jsonc-parser/download/jsonc-parser-3.0.0.tgz}
dev: true
/jsonfile/4.0.0: /jsonfile/4.0.0:
resolution: {integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=, tarball: jsonfile/download/jsonfile-4.0.0.tgz} resolution: {integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=, tarball: jsonfile/download/jsonfile-4.0.0.tgz}
optionalDependencies: optionalDependencies:
@ -5933,6 +5903,7 @@ packages:
universalify: 2.0.0 universalify: 2.0.0
optionalDependencies: optionalDependencies:
graceful-fs: 4.2.8 graceful-fs: 4.2.8
dev: false
/jsonparse/1.3.1: /jsonparse/1.3.1:
resolution: {integrity: sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=, tarball: jsonparse/download/jsonparse-1.3.1.tgz} resolution: {integrity: sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=, tarball: jsonparse/download/jsonparse-1.3.1.tgz}
@ -7333,11 +7304,24 @@ packages:
engines: {iojs: '>=1.0.0', node: '>=0.10.0'} engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
/rimraf/3.0.2: /rimraf/3.0.2:
resolution: {integrity: sha1-8aVAK6YiCtUswSgrrBrjqkn9Bho=, tarball: rimraf/download/rimraf-3.0.2.tgz?cache=0&sync_timestamp=1632822764504&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Frimraf%2Fdownload%2Frimraf-3.0.2.tgz} resolution: {integrity: sha1-8aVAK6YiCtUswSgrrBrjqkn9Bho=, tarball: rimraf/download/rimraf-3.0.2.tgz}
hasBin: true hasBin: true
dependencies: dependencies:
glob: 7.2.0 glob: 7.2.0
/rollup-plugin-esbuild/4.6.0_rollup@2.59.0:
resolution: {integrity: sha1-KTsMjS5kt1ORjzAjwfuH3ErN7zs=, tarball: rollup-plugin-esbuild/download/rollup-plugin-esbuild-4.6.0.tgz}
engines: {node: '>=12'}
peerDependencies:
esbuild: '>=0.10.1'
rollup: ^1.20.0 || ^2.0.0
dependencies:
'@rollup/pluginutils': 4.1.1
joycon: 3.0.1
jsonc-parser: 3.0.0
rollup: 2.59.0
dev: true
/rollup/2.59.0: /rollup/2.59.0:
resolution: {integrity: sha1-EIxhsPoKN+vI0fFk8oFiIFbw21k=, tarball: rollup/download/rollup-2.59.0.tgz} resolution: {integrity: sha1-EIxhsPoKN+vI0fFk8oFiIFbw21k=, tarball: rollup/download/rollup-2.59.0.tgz}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
@ -8138,6 +8122,7 @@ packages:
/universalify/2.0.0: /universalify/2.0.0:
resolution: {integrity: sha1-daSYTv7cSwiXXFrrc/Uw0C3yVxc=, tarball: universalify/download/universalify-2.0.0.tgz} resolution: {integrity: sha1-daSYTv7cSwiXXFrrc/Uw0C3yVxc=, tarball: universalify/download/universalify-2.0.0.tgz}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
dev: false
/update-notifier/5.1.0: /update-notifier/5.1.0:
resolution: {integrity: sha1-SrDXx/NqIx3XMWz3cpMT8CFNmtk=, tarball: update-notifier/download/update-notifier-5.1.0.tgz} resolution: {integrity: sha1-SrDXx/NqIx3XMWz3cpMT8CFNmtk=, tarball: update-notifier/download/update-notifier-5.1.0.tgz}