├── .eslintignore ├── src ├── icon │ ├── local.less │ ├── index.less │ └── test │ │ └── __snapshots__ │ │ └── index.spec.js.snap ├── lazyload │ └── index.js ├── style │ ├── clearfix.less │ ├── mixins │ │ ├── clearfix.less │ │ ├── ellipsis.less │ │ └── hairline.less │ ├── base.less │ ├── ellipsis.less │ ├── normalize.less │ └── hairline.less ├── grid │ ├── index.less │ ├── test │ │ └── demo.spec.js │ └── index.js ├── area │ ├── test │ │ └── demo.spec.js │ └── demo │ │ └── area-simple.js ├── card │ └── test │ │ └── demo.spec.js ├── cell │ ├── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ │ └── index.spec.js.snap │ └── shared.ts ├── col │ ├── test │ │ └── demo.spec.js │ └── index.less ├── empty │ ├── test │ │ ├── demo.spec.js │ │ └── index.spec.js │ └── index.less ├── field │ ├── test │ │ └── demo.spec.js │ └── demo │ │ ├── Disabled.vue │ │ ├── BasicUsage.vue │ │ ├── Autosize.vue │ │ ├── InputAlign.vue │ │ ├── ShowWordLimit.vue │ │ ├── ShowIcon.vue │ │ ├── InsertButton.vue │ │ ├── ErrorInfo.vue │ │ ├── index.vue │ │ └── FormatValue.vue ├── form │ ├── test │ │ ├── demo.spec.js │ │ ├── shared.js │ │ └── __snapshots__ │ │ │ └── events.spec.js.snap │ └── demo │ │ ├── index.vue │ │ ├── FieldTypeCalendar.vue │ │ └── FieldTypeDatetimePicker.vue ├── image │ ├── test │ │ └── demo.spec.js │ └── index.less ├── list │ ├── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ │ └── index.spec.js.snap │ └── index.less ├── panel │ ├── test │ │ └── demo.spec.js │ ├── index.less │ └── demo │ │ └── index.vue ├── popup │ └── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ └── index.spec.js.snap ├── radio │ ├── test │ │ └── demo.spec.js │ └── index.js ├── rate │ ├── test │ │ └── demo.spec.js │ └── index.less ├── sku │ ├── test │ │ └── demo.spec.js │ ├── constants.js │ ├── components │ │ └── SkuHeaderItem.tsx │ ├── utils │ │ └── time-helper.js │ └── index.js ├── steps │ ├── test │ │ └── demo.spec.js │ ├── index.less │ └── index.js ├── tab │ ├── test │ │ └── demo.spec.js │ └── index.less ├── tag │ └── test │ │ ├── demo.spec.js │ │ └── index.spec.js ├── toast │ ├── test │ │ └── demo.spec.js │ └── lock-click.ts ├── button │ └── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ └── index.spec.js.snap ├── calendar │ └── test │ │ ├── demo.spec.js │ │ ├── utils.js │ │ └── utils.spec.js ├── checkbox │ └── test │ │ └── demo.spec.js ├── circle │ ├── test │ │ └── demo.spec.js │ └── index.less ├── collapse │ ├── test │ │ └── demo.spec.js │ └── index.js ├── dialog │ └── test │ │ └── demo.spec.js ├── divider │ └── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ └── demo.spec.js.snap ├── index-bar │ ├── test │ │ └── demo.spec.js │ └── index.less ├── loading │ └── test │ │ ├── demo.spec.js │ │ ├── index.spec.js │ │ └── __snapshots__ │ │ └── index.spec.js.snap ├── nav-bar │ ├── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ │ ├── index.spec.js.snap │ │ │ └── demo.spec.js.snap │ └── demo │ │ └── index.vue ├── notice-bar │ └── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ └── index.spec.js.snap ├── notify │ ├── test │ │ └── demo.spec.js │ └── index.less ├── overlay │ ├── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ │ ├── index.spec.js.snap │ │ │ └── demo.spec.js.snap │ └── index.less ├── pagination │ └── test │ │ └── demo.spec.js ├── picker │ ├── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ │ └── cascade.spec.js.snap │ └── shared.ts ├── progress │ ├── test │ │ ├── demo.spec.js │ │ ├── __snapshots__ │ │ │ └── index.spec.js.snap │ │ └── index.spec.js │ └── index.less ├── search │ └── test │ │ └── demo.spec.js ├── sidebar │ ├── test │ │ └── demo.spec.js │ ├── index.less │ └── index.js ├── skeleton │ ├── test │ │ └── demo.spec.js │ └── index.less ├── slider │ └── test │ │ └── demo.spec.js ├── stepper │ └── test │ │ └── demo.spec.js ├── sticky │ ├── test │ │ └── demo.spec.js │ └── index.less ├── submit-bar │ └── test │ │ └── demo.spec.js ├── swipe-cell │ ├── test │ │ └── demo.spec.js │ └── index.less ├── switch │ ├── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ │ └── index.spec.js.snap │ └── shared.ts ├── tabbar │ ├── test │ │ └── demo.spec.js │ └── index.less ├── uploader │ └── test │ │ ├── demo.spec.js │ │ └── utils.spec.js ├── action-sheet │ └── test │ │ └── demo.spec.js ├── address-edit │ ├── test │ │ └── demo.spec.js │ └── index.less ├── address-list │ └── test │ │ └── demo.spec.js ├── contact-card │ ├── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ │ └── index.spec.js.snap │ └── index.less ├── coupon-list │ └── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ └── demo.spec.js.snap ├── datetime-picker │ ├── test │ │ ├── demo.spec.js │ │ └── datetime-picker.spec.js │ ├── utils.ts │ └── index.js ├── dropdown-menu │ └── test │ │ └── demo.spec.js ├── goods-action │ ├── test │ │ └── demo.spec.js │ ├── index.less │ └── index.js ├── number-keyboard │ ├── test │ │ └── demo.spec.js │ └── DeleteIcon.tsx ├── password-input │ └── test │ │ ├── demo.spec.js │ │ └── index.spec.js ├── pull-refresh │ ├── test │ │ └── demo.spec.js │ └── index.less ├── share-sheet │ └── test │ │ ├── demo.spec.js │ │ └── __snapshots__ │ │ └── index.spec.js.snap ├── switch-cell │ ├── test │ │ ├── demo.spec.js │ │ ├── __snapshots__ │ │ │ └── index.spec.js.snap │ │ └── index.spec.js │ ├── index.less │ └── demo │ │ └── index.vue ├── tree-select │ ├── test │ │ └── demo.spec.js │ ├── demo │ │ ├── data-zh.ts │ │ └── data-en.ts │ └── index.less ├── coupon-cell │ └── index.less ├── radio-group │ ├── index.less │ └── index.js ├── checkbox-group │ └── index.less ├── swipe-item │ └── index.less ├── utils │ ├── dom │ │ ├── node.ts │ │ ├── reset-scroll.ts │ │ ├── style.ts │ │ └── raf.ts │ ├── validate │ │ ├── mobile.ts │ │ ├── date.ts │ │ ├── number.ts │ │ ├── system.ts │ │ └── email.ts │ ├── deep-clone.ts │ ├── format │ │ ├── string.ts │ │ └── number.ts │ ├── create │ │ ├── index.ts │ │ ├── i18n.ts │ │ └── bem.ts │ ├── constant.ts │ ├── deep-assign.ts │ ├── vnodes.ts │ ├── types.ts │ ├── index.ts │ └── test │ │ └── bem.spec.js ├── image-preview │ └── shared.js ├── count-down │ ├── index.less │ └── test │ │ └── __snapshots__ │ │ └── index.spec.js.snap ├── info │ ├── test │ │ ├── __snapshots__ │ │ │ └── index.spec.js.snap │ │ └── index.spec.js │ ├── index.less │ └── index.tsx ├── coupon │ └── shared.ts ├── cell-group │ └── index.less ├── mixins │ ├── popup │ │ ├── type.ts │ │ └── context.ts │ ├── slots.js │ ├── field.js │ ├── bind-event.js │ ├── click-outside.js │ ├── close-on-popstate.js │ └── portal.js ├── swipe │ └── test │ │ └── demo.spec.js ├── index-anchor │ └── index.less ├── contact-edit │ └── index.less ├── row │ └── index.less ├── goods-action-icon │ └── index.less ├── dropdown-item │ └── index.less ├── tabbar-item │ └── index.less ├── goods-action-button │ └── index.less ├── locale │ ├── index.ts │ └── README.zh-CN.md └── contact-list │ └── index.less ├── packages ├── vant-cli │ ├── src │ │ ├── config │ │ │ ├── jest.style-mock.ts │ │ │ ├── jest.file-mock.ts │ │ │ ├── jest.setup.ts │ │ │ ├── postcss.config.ts │ │ │ └── webpack.site.prd.ts │ │ ├── compiler │ │ │ ├── compile-sass.ts │ │ │ ├── vant-cli-release-plugin.ts │ │ │ ├── compile-package.ts │ │ │ ├── compile-css.ts │ │ │ ├── gen-vetur-config.ts │ │ │ ├── compile-js.ts │ │ │ ├── compile-less.ts │ │ │ └── compile-style.ts │ │ ├── commands │ │ │ ├── dev.ts │ │ │ ├── build-site.ts │ │ │ ├── clean.ts │ │ │ ├── release.ts │ │ │ ├── jest.ts │ │ │ └── commit-lint.ts │ │ ├── common │ │ │ ├── types.ts │ │ │ ├── logger.ts │ │ │ └── manager.ts │ │ └── module.d.ts │ ├── preset.js │ ├── template │ │ ├── changelog-header.hbs │ │ ├── changelog-main.hbs │ │ └── changelog-commit.hbs │ ├── tsconfig.json │ └── site │ │ ├── desktop │ │ ├── utils.js │ │ ├── main.js │ │ └── components │ │ │ └── Container.vue │ │ ├── mobile │ │ ├── components │ │ │ ├── ArrowRight.vue │ │ │ ├── DemoSection.vue │ │ │ └── DemoBlock.vue │ │ ├── App.vue │ │ └── main.js │ │ └── common │ │ ├── locales.js │ │ ├── iframe-router.js │ │ ├── index.js │ │ └── style │ │ ├── base.less │ │ └── var.less ├── vant-icons │ ├── .gitignore │ ├── assets │ │ └── icons.sketch │ ├── .svgo.yml │ ├── build │ │ ├── build-encode.js │ │ ├── export.js │ │ └── template.tpl │ ├── README.md │ └── package.json ├── vant-waterfall │ ├── babel.config.js │ ├── src │ │ ├── index.js │ │ ├── event.js │ │ └── scroll.js │ └── package.json ├── create-vant-cli-app │ ├── generators │ │ └── templates │ │ │ ├── eslintignore.tpl │ │ │ ├── babel.config.js │ │ │ ├── docs │ │ │ ├── home.md │ │ │ └── quickstart.md │ │ │ ├── src │ │ │ └── demo-button │ │ │ │ ├── test │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── index.spec.js.snap │ │ │ │ └── index.spec.js │ │ │ │ ├── demo │ │ │ │ └── index.vue │ │ │ │ ├── index.vue │ │ │ │ └── README.md │ │ │ ├── gitignore.tpl │ │ │ └── vant.config.js │ ├── src │ │ ├── constant.ts │ │ └── index.ts │ └── tsconfig.json ├── vant-markdown-loader │ ├── src │ │ ├── highlight.js │ │ ├── card-wrapper.js │ │ └── link-open.js │ ├── package.json │ └── README.md ├── vant-eslint-config │ ├── README.md │ └── package.json ├── vant-markdown-vetur │ ├── tsconfig.json │ ├── src │ │ ├── web-types.ts │ │ ├── utils.ts │ │ └── vetur.ts │ ├── README.md │ └── package.json ├── vant-touch-emulator │ ├── package.json │ ├── changelog.md │ └── README.md └── vant-stylelint-config │ ├── README.md │ ├── index.js │ └── package.json ├── babel.config.js ├── docs ├── assets │ ├── logo.sketch │ ├── design.sketch │ └── nav-logo.sketch ├── site │ └── release.sh └── markdown │ └── design.zh-CN.md ├── types ├── mixins │ └── popup.d.ts ├── list.d.ts ├── calendar.d.ts ├── area.d.ts ├── checkbox.d.ts ├── field.d.ts ├── dropdown-item.d.ts ├── address-edit.d.ts ├── checkbox-group.d.ts ├── component.d.ts ├── lazyload.d.ts ├── tabs.d.ts ├── uploader.d.ts ├── locale.d.ts ├── swipe-cell.d.ts ├── datetime-picker.d.ts ├── count-down.d.ts ├── jsx.d.ts ├── swipe.d.ts ├── form.d.ts ├── picker.d.ts └── notify.d.ts ├── .gitpod.yml ├── .gitignore ├── webpack.config.js ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── release-tag.yml │ ├── sync.yml │ └── test.yml ├── .ls-lint.yml ├── tsconfig.json ├── test ├── index.ts └── demo.ts └── LICENSE /.eslintignore: -------------------------------------------------------------------------------- 1 | es 2 | lib 3 | dist 4 | node_modules 5 | -------------------------------------------------------------------------------- /src/icon/local.less: -------------------------------------------------------------------------------- 1 | @import '~@vant/icons/src/encode.less'; 2 | -------------------------------------------------------------------------------- /packages/vant-cli/src/config/jest.style-mock.ts: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vant/cli/preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/vant-cli/src/config/jest.file-mock.ts: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /packages/vant-icons/.gitignore: -------------------------------------------------------------------------------- 1 | assets/svg 2 | src/*.ttf 3 | src/*.woff 4 | src/*.woff2 5 | -------------------------------------------------------------------------------- /docs/assets/logo.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sijinglei/vant/dev/docs/assets/logo.sketch -------------------------------------------------------------------------------- /src/lazyload/index.js: -------------------------------------------------------------------------------- 1 | import Lazyload from 'vue-lazyload'; 2 | 3 | export default Lazyload; 4 | -------------------------------------------------------------------------------- /docs/assets/design.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sijinglei/vant/dev/docs/assets/design.sketch -------------------------------------------------------------------------------- /docs/assets/nav-logo.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sijinglei/vant/dev/docs/assets/nav-logo.sketch -------------------------------------------------------------------------------- /packages/vant-waterfall/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@babel/env'], 3 | }; 4 | -------------------------------------------------------------------------------- /types/mixins/popup.d.ts: -------------------------------------------------------------------------------- 1 | export class VanPopupMixin { 2 | open(): void; 3 | close(): void; 4 | } 5 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/generators/templates/eslintignore.tpl: -------------------------------------------------------------------------------- 1 | es 2 | lib 3 | dist 4 | node_modules 5 | -------------------------------------------------------------------------------- /src/style/clearfix.less: -------------------------------------------------------------------------------- 1 | @import './mixins/clearfix'; 2 | 3 | .van-clearfix { 4 | .clearfix(); 5 | } 6 | -------------------------------------------------------------------------------- /src/grid/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-grid { 4 | display: flex; 5 | flex-wrap: wrap; 6 | } 7 | -------------------------------------------------------------------------------- /packages/vant-icons/assets/icons.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sijinglei/vant/dev/packages/vant-icons/assets/icons.sketch -------------------------------------------------------------------------------- /packages/create-vant-cli-app/generators/templates/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vant/cli/preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/vant-icons/.svgo.yml: -------------------------------------------------------------------------------- 1 | 2 | plugins: 3 | - removeAttrs: 4 | attrs: 5 | - 'fill' 6 | - 'stroke' 7 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: npm run bootstrap 3 | command: npm run dev 4 | ports: 5 | - port: 8080 6 | onOpen: open-preview 7 | -------------------------------------------------------------------------------- /packages/vant-cli/preset.js: -------------------------------------------------------------------------------- 1 | const babelConfig = require('./lib/config/babel.config'); 2 | 3 | module.exports = api => babelConfig(api); 4 | -------------------------------------------------------------------------------- /src/area/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/card/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/cell/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/col/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/empty/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/field/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/form/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/grid/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/image/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/list/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/panel/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/popup/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/radio/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/rate/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/sku/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/steps/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/tab/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/tag/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/toast/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /types/list.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class List extends VanComponent { 4 | check(): void; 5 | } 6 | -------------------------------------------------------------------------------- /src/button/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/calendar/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/checkbox/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/circle/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/collapse/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/dialog/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/divider/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/index-bar/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/loading/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/nav-bar/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/notice-bar/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/notify/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/overlay/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/pagination/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/picker/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/progress/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/search/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/sidebar/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/skeleton/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/slider/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/stepper/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/sticky/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/style/mixins/clearfix.less: -------------------------------------------------------------------------------- 1 | .clearfix() { 2 | &::after { 3 | display: table; 4 | clear: both; 5 | content: ''; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/submit-bar/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/swipe-cell/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/switch/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/tabbar/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/uploader/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /types/calendar.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class Calendar extends VanComponent { 4 | reset(): void; 5 | } 6 | -------------------------------------------------------------------------------- /packages/vant-cli/template/changelog-header.hbs: -------------------------------------------------------------------------------- 1 | ### [v{{version}}]({{~@root.repoUrl}}/compare/{{previousTag}}...{{currentTag}}) 2 | 3 | `{{date}}` 4 | -------------------------------------------------------------------------------- /src/action-sheet/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/address-edit/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/address-list/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/contact-card/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/coupon-list/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/datetime-picker/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/dropdown-menu/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/goods-action/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/number-keyboard/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/password-input/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/pull-refresh/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/share-sheet/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/switch-cell/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /src/tree-select/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | 4 | snapshotDemo(Demo); 5 | -------------------------------------------------------------------------------- /types/area.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class Area extends VanComponent { 4 | reset(code?: string): void; 5 | } 6 | -------------------------------------------------------------------------------- /docs/site/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | vant-cli build-site 3 | 4 | superman-cdn /vant ./site/*.js 5 | 6 | rm -rf site/*.js 7 | 8 | gh-pages -d site --add 9 | -------------------------------------------------------------------------------- /src/coupon-cell/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-coupon-cell { 4 | &--selected { 5 | color: @coupon-cell-selected-text-color; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/radio-group/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-radio-group { 4 | &--horizontal { 5 | display: flex; 6 | flex-wrap: wrap; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /types/checkbox.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class Checkbox extends VanComponent { 4 | toggle(checked?: boolean): void; 5 | } 6 | -------------------------------------------------------------------------------- /types/field.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class Field extends VanComponent { 4 | focus(): void; 5 | 6 | blur(): void; 7 | } 8 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/generators/templates/docs/home.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 3 | ### 关于 4 | 5 | 这是一段组件库的介绍 6 | 7 | ### 特性 8 | 9 | - 特性一 10 | - 特性二 11 | - 特性三 12 | -------------------------------------------------------------------------------- /src/checkbox-group/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-checkbox-group { 4 | &--horizontal { 5 | display: flex; 6 | flex-wrap: wrap; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/swipe-item/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-swipe-item { 4 | position: relative; 5 | flex-shrink: 0; 6 | width: 100%; 7 | height: 100%; 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/dom/node.ts: -------------------------------------------------------------------------------- 1 | export function removeNode(el: Node) { 2 | const parent = el.parentNode; 3 | 4 | if (parent) { 5 | parent.removeChild(el); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /types/dropdown-item.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class DropdownItem extends VanComponent { 4 | toggle(show?: boolean): void; 5 | } 6 | -------------------------------------------------------------------------------- /src/sidebar/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-sidebar { 4 | width: @sidebar-width; 5 | overflow-y: auto; 6 | -webkit-overflow-scrolling: touch; 7 | } 8 | -------------------------------------------------------------------------------- /types/address-edit.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class AddressEdit extends VanComponent { 4 | setAddressDetail(val: string): void; 5 | } 6 | -------------------------------------------------------------------------------- /types/checkbox-group.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class CheckboxGroup extends VanComponent { 4 | toggleAll(checked?: boolean): void; 5 | } 6 | -------------------------------------------------------------------------------- /types/component.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | export class VanComponent extends Vue { 4 | static name: string; 5 | 6 | static install(vue: typeof Vue): void; 7 | } 8 | -------------------------------------------------------------------------------- /types/lazyload.d.ts: -------------------------------------------------------------------------------- 1 | import { PluginFunction } from 'vue'; 2 | 3 | export interface Lazyload { 4 | install: PluginFunction; 5 | } 6 | 7 | export const Lazyload: Lazyload; 8 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/src/constant.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | 3 | export const CWD = process.cwd(); 4 | export const GENERATOR_DIR = join(__dirname, '../generators'); 5 | -------------------------------------------------------------------------------- /src/image-preview/shared.js: -------------------------------------------------------------------------------- 1 | import { createNamespace } from '../utils'; 2 | 3 | const [createComponent, bem] = createNamespace('image-preview'); 4 | 5 | export { createComponent, bem }; 6 | -------------------------------------------------------------------------------- /types/tabs.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class Tabs extends VanComponent { 4 | resize(): void; 5 | 6 | scrollTo(name: string | number): void; 7 | } 8 | -------------------------------------------------------------------------------- /types/uploader.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class Uploader extends VanComponent { 4 | closeImagePreview(): void; 5 | 6 | chooseFile(): void; 7 | } 8 | -------------------------------------------------------------------------------- /packages/vant-cli/src/config/jest.setup.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import 'jest-canvas-mock'; 3 | // @ts-ignore 4 | import Package from '../../dist/package-entry'; 5 | 6 | Vue.use(Package); 7 | -------------------------------------------------------------------------------- /types/locale.d.ts: -------------------------------------------------------------------------------- 1 | export interface Locale { 2 | install(): void; 3 | use(lang: string, messages: Object): void; 4 | add(messages: Object): void; 5 | } 6 | 7 | export const Locale: Locale; 8 | -------------------------------------------------------------------------------- /types/swipe-cell.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class SwipeCell extends VanComponent { 4 | open(position: 'left' | 'right'): void; 5 | 6 | close(): void; 7 | } 8 | -------------------------------------------------------------------------------- /src/count-down/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-count-down { 4 | color: @count-down-text-color; 5 | font-size: @count-down-font-size; 6 | line-height: @count-down-line-height; 7 | } 8 | -------------------------------------------------------------------------------- /types/datetime-picker.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | import { Picker } from './picker'; 3 | 4 | export class DatetimePicker extends VanComponent { 5 | getPicker(): Picker; 6 | } 7 | -------------------------------------------------------------------------------- /types/count-down.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class CountDown extends VanComponent { 4 | start(): void; 5 | 6 | pause(): void; 7 | 8 | reset(): void; 9 | } 10 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/generators/templates/docs/quickstart.md: -------------------------------------------------------------------------------- 1 | # 快速上手 2 | 3 | ### 安装 4 | 5 | ```bash 6 | # 通过 npm 安装 7 | npm i <%= name %> -S 8 | 9 | # 通过 yarn 安装 10 | yarn add <%= name %> 11 | ``` 12 | -------------------------------------------------------------------------------- /src/icon/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | @import '~@vant/icons/src/index.less'; 3 | 4 | .van-icon { 5 | &__image { 6 | width: 1em; 7 | height: 1em; 8 | object-fit: contain; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/vant-cli/src/compiler/compile-sass.ts: -------------------------------------------------------------------------------- 1 | import { renderSync } from 'sass'; 2 | 3 | export async function compileSass(filePath: string) { 4 | const { css } = renderSync({ file: filePath }); 5 | return css; 6 | } 7 | -------------------------------------------------------------------------------- /src/sticky/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-sticky { 4 | &--fixed { 5 | position: fixed; 6 | top: 0; 7 | right: 0; 8 | left: 0; 9 | z-index: @sticky-z-index; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/style/base.less: -------------------------------------------------------------------------------- 1 | /** 2 | * Entry of basic styles 3 | */ 4 | 5 | @import './var'; 6 | @import './normalize'; 7 | @import './ellipsis'; 8 | @import './clearfix'; 9 | @import './hairline'; 10 | @import './animation'; 11 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/generators/templates/src/demo-button/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`render demo button 1`] = ``; 4 | -------------------------------------------------------------------------------- /src/utils/validate/mobile.ts: -------------------------------------------------------------------------------- 1 | export function isMobile(value: string): boolean { 2 | value = value.replace(/[^-|\d]/g, ''); 3 | return ( 4 | /^((\+86)|(86))?(1)\d{10}$/.test(value) || /^0[0-9-]{10,13}$/.test(value) 5 | ); 6 | } 7 | -------------------------------------------------------------------------------- /packages/vant-cli/src/commands/dev.ts: -------------------------------------------------------------------------------- 1 | import { setNodeEnv } from '../common'; 2 | import { compileSite } from '../compiler/compile-site'; 3 | 4 | export async function dev() { 5 | setNodeEnv('development'); 6 | await compileSite(); 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log* 2 | .cache 3 | .DS_Store 4 | .idea 5 | .vscode 6 | 7 | # npm 8 | node_modules 9 | package-lock.json 10 | 11 | # dist file 12 | es 13 | lib 14 | dist 15 | /site 16 | changelog.generated.md 17 | test/coverage 18 | vetur 19 | -------------------------------------------------------------------------------- /src/sku/constants.js: -------------------------------------------------------------------------------- 1 | export const LIMIT_TYPE = { 2 | QUOTA_LIMIT: 0, 3 | STOCK_LIMIT: 1, 4 | }; 5 | 6 | export const UNSELECTED_SKU_VALUE_ID = ''; 7 | 8 | export default { 9 | LIMIT_TYPE, 10 | UNSELECTED_SKU_VALUE_ID, 11 | }; 12 | -------------------------------------------------------------------------------- /src/utils/validate/date.ts: -------------------------------------------------------------------------------- 1 | import { isNaN } from './number'; 2 | 3 | export function isDate(val: Date): val is Date { 4 | return ( 5 | Object.prototype.toString.call(val) === '[object Date]' && 6 | !isNaN(val.getTime()) 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /packages/vant-cli/src/common/types.ts: -------------------------------------------------------------------------------- 1 | import type Webpack from 'webpack'; 2 | import type WebpackDevServer from 'webpack-dev-server'; 3 | 4 | export type WebpackConfig = Webpack.Configuration & { 5 | devServer?: WebpackDevServer.Configuration; 6 | }; 7 | -------------------------------------------------------------------------------- /packages/vant-cli/template/changelog-main.hbs: -------------------------------------------------------------------------------- 1 | {{> header}} 2 | {{#each commitGroups}} 3 | 4 | {{#if title}} 5 | **{{title}}** 6 | 7 | {{/if}} 8 | {{#each commits}} 9 | {{> commit root=@root}} 10 | {{/each}} 11 | {{/each}} 12 | {{> footer}} 13 | -------------------------------------------------------------------------------- /src/style/ellipsis.less: -------------------------------------------------------------------------------- 1 | @import './mixins/ellipsis'; 2 | 3 | .van-ellipsis { 4 | .ellipsis(); 5 | } 6 | 7 | .van-multi-ellipsis--l2 { 8 | .multi-ellipsis(2); 9 | } 10 | 11 | .van-multi-ellipsis--l3 { 12 | .multi-ellipsis(3); 13 | } 14 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | if (process.env.BUILD_TARGET === 'package') { 3 | return {}; 4 | } 5 | 6 | return { 7 | entry: { 8 | 'site-mobile': ['./docs/site/mobile'], 9 | }, 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /src/overlay/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-overlay { 4 | position: fixed; 5 | top: 0; 6 | left: 0; 7 | z-index: @overlay-z-index; 8 | width: 100%; 9 | height: 100%; 10 | background-color: @overlay-background-color; 11 | } 12 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/generators/templates/gitignore.tpl: -------------------------------------------------------------------------------- 1 | *.log* 2 | .cache 3 | .DS_Store 4 | .idea 5 | .vscode 6 | 7 | # npm 8 | node_modules 9 | package-lock.json 10 | 11 | # dist file 12 | es 13 | lib 14 | site 15 | 16 | # test 17 | test/coverage 18 | -------------------------------------------------------------------------------- /packages/vant-waterfall/src/index.js: -------------------------------------------------------------------------------- 1 | import Waterfall from './directive'; 2 | 3 | Waterfall.install = function(Vue) { 4 | Vue.directive('WaterfallLower', Waterfall('lower')); 5 | Vue.directive('WaterfallUpper', Waterfall('upper')); 6 | }; 7 | 8 | export default Waterfall; 9 | -------------------------------------------------------------------------------- /src/panel/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-panel { 4 | background: @panel-background-color; 5 | 6 | &__header-value { 7 | color: @panel-header-value-color; 8 | } 9 | 10 | &__footer { 11 | padding: @panel-footer-padding; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /packages/vant-markdown-loader/src/highlight.js: -------------------------------------------------------------------------------- 1 | const hljs = require('highlight.js'); 2 | 3 | module.exports = function highlight(str, lang) { 4 | if (lang && hljs.getLanguage(lang)) { 5 | return hljs.highlight(lang, str, true).value; 6 | } 7 | 8 | return ''; 9 | }; 10 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/generators/templates/src/demo-button/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils'; 2 | import DemoButton from '..'; 3 | 4 | test('render demo button', () => { 5 | const wrapper = mount(DemoButton); 6 | expect(wrapper).toMatchSnapshot(); 7 | }); 8 | -------------------------------------------------------------------------------- /types/jsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue'; 2 | 3 | declare global { 4 | namespace JSX { 5 | interface Element extends VNode {} 6 | interface ElementClass extends Vue {} 7 | interface IntrinsicElements { 8 | [elem: string]: any; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /types/swipe.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export type SwipeToOptions = { 4 | immediate?: boolean; 5 | }; 6 | 7 | export class Swipe extends VanComponent { 8 | swipeTo(index: number, options?: SwipeToOptions): void; 9 | 10 | resize(): void; 11 | } 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 你好,请使用下面的链接创建 issue 以帮助我们更快的排查问题,不规范的 issue 会被关闭,感谢配合。 2 | 3 | https://youzan.github.io/vant-issue-generater 4 | 5 | --- 6 | 7 | Please use the link below to create a new issue, the non-standard issue will be closed. 8 | 9 | https://youzan.github.io/vant-issue-generater 10 | -------------------------------------------------------------------------------- /src/picker/test/__snapshots__/cascade.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`disabled in cascade 1`] = ` 4 |
  • 5 |
    A2
    6 |
  • 7 | `; 8 | -------------------------------------------------------------------------------- /packages/vant-cli/src/common/logger.ts: -------------------------------------------------------------------------------- 1 | import ora from 'ora'; 2 | import chalk from 'chalk'; 3 | import consola from 'consola'; 4 | import { ROOT } from '../common/constant'; 5 | 6 | export function slimPath(path: string) { 7 | return chalk.yellow(path.replace(ROOT, '')); 8 | } 9 | 10 | export { ora, consola }; 11 | -------------------------------------------------------------------------------- /src/info/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`should not render when info is empty string 1`] = ``; 4 | 5 | exports[`should not render when info is empty undefined 1`] = ``; 6 | 7 | exports[`should render when info is zero 1`] = `
    0
    `; 8 | -------------------------------------------------------------------------------- /src/tab/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-tab { 4 | &__pane { 5 | &, 6 | &-wrapper { 7 | flex-shrink: 0; 8 | box-sizing: border-box; 9 | width: 100%; 10 | } 11 | 12 | &-wrapper--inactive { 13 | height: 0; 14 | overflow: visible; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.ls-lint.yml: -------------------------------------------------------------------------------- 1 | ls: 2 | src/**: 3 | .js: kebab-case | PascalCase 4 | .ts: kebab-case | PascalCase 5 | .tsx: kebab-case | PascalCase 6 | .vue: kebab-case | PascalCase 7 | .less: kebab-case 8 | .spec.js: kebab-case 9 | 10 | types: 11 | .d.ts: kebab-case 12 | 13 | ignore: 14 | - src/locale/lang 15 | -------------------------------------------------------------------------------- /types/form.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class Form extends VanComponent { 4 | submit(): void; 5 | 6 | validate(name?: string): Promise; 7 | 8 | resetValidation(name?: string): void; 9 | 10 | scrollToField(name: string, options?: boolean | ScrollIntoViewOptions): void; 11 | } 12 | -------------------------------------------------------------------------------- /packages/vant-eslint-config/README.md: -------------------------------------------------------------------------------- 1 | # Eslint Config of Vant 2 | 3 | ## Install 4 | 5 | #### NPM 6 | 7 | ```shell 8 | npm i @vant/eslint-config -D 9 | ``` 10 | 11 | #### YARN 12 | 13 | ```shell 14 | yarn add @vant/eslint-config --dev 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```js 20 | { 21 | "extends": ["@vant"] 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /src/utils/deep-clone.ts: -------------------------------------------------------------------------------- 1 | import { deepAssign } from './deep-assign'; 2 | 3 | export function deepClone(obj: object): object { 4 | if (Array.isArray(obj)) { 5 | return obj.map((item) => deepClone(item)); 6 | } 7 | 8 | if (typeof obj === 'object') { 9 | return deepAssign({}, obj); 10 | } 11 | 12 | return obj; 13 | } 14 | -------------------------------------------------------------------------------- /src/coupon/shared.ts: -------------------------------------------------------------------------------- 1 | export type Coupon = { 2 | id: string; 3 | name: string; 4 | endAt: number; 5 | value: number; 6 | startAt: number; 7 | reason?: string; 8 | discount?: number; 9 | unitDesc?: string; 10 | condition?: string; 11 | valueDesc?: string; 12 | description: string; 13 | denominations?: number; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "outDir": "./lib", 5 | "module": "commonjs", 6 | "strict": true, 7 | "declaration": true, 8 | "skipLibCheck": true, 9 | "esModuleInterop": true, 10 | "lib": ["esnext"] 11 | }, 12 | "include": ["src/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/vant-markdown-vetur/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "outDir": "./lib", 5 | "module": "commonjs", 6 | "strict": true, 7 | "declaration": true, 8 | "skipLibCheck": true, 9 | "esModuleInterop": true, 10 | "lib": ["esnext"] 11 | }, 12 | "include": ["src/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/validate/number.ts: -------------------------------------------------------------------------------- 1 | export function isNumeric(val: string): boolean { 2 | return /^\d+(\.\d+)?$/.test(val); 3 | } 4 | 5 | export function isNaN(val: number): val is typeof NaN { 6 | if (Number.isNaN) { 7 | return Number.isNaN(val); 8 | } 9 | 10 | // eslint-disable-next-line no-self-compare 11 | return val !== val; 12 | } 13 | -------------------------------------------------------------------------------- /packages/vant-cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "outDir": "./lib", 5 | "module": "commonjs", 6 | "strict": true, 7 | "declaration": true, 8 | "skipLibCheck": true, 9 | "esModuleInterop": true, 10 | "lib": ["esnext", "dom"] 11 | }, 12 | "include": ["src/**/*", "site"] 13 | } 14 | -------------------------------------------------------------------------------- /src/cell-group/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-cell-group { 4 | background-color: @cell-group-background-color; 5 | 6 | &__title { 7 | padding: @cell-group-title-padding; 8 | color: @cell-group-title-color; 9 | font-size: @cell-group-title-font-size; 10 | line-height: @cell-group-title-line-height; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/vant-touch-emulator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vant/touch-emulator", 3 | "version": "1.2.0", 4 | "description": "Vant touch emulator", 5 | "main": "index.js", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "license": "MIT", 10 | "repository": "https://github.com/youzan/vant/tree/dev/packages/vant-touch-emulator" 11 | } 12 | -------------------------------------------------------------------------------- /packages/vant-stylelint-config/README.md: -------------------------------------------------------------------------------- 1 | # Stylelint Config of Vant 2 | 3 | ## Install 4 | 5 | #### NPM 6 | 7 | ```shell 8 | npm i @vant/stylelint-config -D 9 | ``` 10 | 11 | #### YARN 12 | 13 | ```shell 14 | yarn add @vant/stylelint-config --dev 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```js 20 | { 21 | "extends": ["@vant/stylelint-config"] 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /packages/vant-touch-emulator/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ### [v1.2.0] 4 | `2019-11-22` 5 | 6 | - fix incorrect touchmove behaviour in Firefox [\#5118](https://github.com/youzan/vant/pull/5118) 7 | 8 | ### [v1.1.0] 9 | `2019-06-03` 10 | 11 | - skip emulator when browser support touch event 12 | 13 | ### [v1.0.0] 14 | `2019-05-28` 15 | 16 | - initial release 17 | -------------------------------------------------------------------------------- /src/style/mixins/ellipsis.less: -------------------------------------------------------------------------------- 1 | .multi-ellipsis(@lines) { 2 | display: -webkit-box; 3 | overflow: hidden; 4 | text-overflow: ellipsis; 5 | -webkit-line-clamp: @lines; 6 | 7 | /* autoprefixer: ignore next */ 8 | -webkit-box-orient: vertical; 9 | } 10 | 11 | .ellipsis() { 12 | overflow: hidden; 13 | white-space: nowrap; 14 | text-overflow: ellipsis; 15 | } 16 | -------------------------------------------------------------------------------- /src/list/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-list { 4 | &__loading, 5 | &__finished-text, 6 | &__error-text { 7 | color: @list-text-color; 8 | font-size: @list-text-font-size; 9 | line-height: @list-text-line-height; 10 | text-align: center; 11 | } 12 | 13 | &__placeholder { 14 | height: 0; 15 | pointer-events: none; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/switch-cell/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-switch-cell { 4 | padding-top: @switch-cell-padding-top; 5 | padding-bottom: @switch-cell-padding-bottom; 6 | 7 | &--large { 8 | padding-top: @switch-cell-large-padding-top; 9 | padding-bottom: @switch-cell-large-padding-bottom; 10 | } 11 | 12 | .van-switch { 13 | float: right; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/vant-cli/src/commands/build-site.ts: -------------------------------------------------------------------------------- 1 | import { emptyDir } from 'fs-extra'; 2 | import { setNodeEnv } from '../common'; 3 | import { compileSite } from '../compiler/compile-site'; 4 | import { SITE_DIST_DIR } from '../common/constant'; 5 | 6 | export async function buildSite() { 7 | setNodeEnv('production'); 8 | await emptyDir(SITE_DIST_DIR); 9 | await compileSite(true); 10 | } 11 | -------------------------------------------------------------------------------- /src/mixins/popup/type.ts: -------------------------------------------------------------------------------- 1 | export type GetContainer = () => Element; 2 | 3 | export type PopupMixinProps = { 4 | value: boolean; 5 | zIndex: string | number; 6 | overlay?: boolean; 7 | lockScroll: boolean; 8 | lazyRender: boolean; 9 | overlayClass?: any; 10 | overlayStyle?: object | object[]; 11 | getContainer?: string | GetContainer; 12 | closeOnClickOverlay?: boolean; 13 | }; 14 | -------------------------------------------------------------------------------- /src/toast/lock-click.ts: -------------------------------------------------------------------------------- 1 | let lockCount = 0; 2 | 3 | export function lockClick(lock: boolean) { 4 | if (lock) { 5 | if (!lockCount) { 6 | document.body.classList.add('van-toast--unclickable'); 7 | } 8 | 9 | lockCount++; 10 | } else { 11 | lockCount--; 12 | 13 | if (!lockCount) { 14 | document.body.classList.remove('van-toast--unclickable'); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/format/string.ts: -------------------------------------------------------------------------------- 1 | const camelizeRE = /-(\w)/g; 2 | 3 | export function camelize(str: string): string { 4 | return str.replace(camelizeRE, (_, c) => c.toUpperCase()); 5 | } 6 | 7 | export function padZero(num: number | string, targetLength = 2): string { 8 | let str = num + ''; 9 | 10 | while (str.length < targetLength) { 11 | str = '0' + str; 12 | } 13 | 14 | return str; 15 | } 16 | -------------------------------------------------------------------------------- /packages/vant-cli/template/changelog-commit.hbs: -------------------------------------------------------------------------------- 1 | -{{#if scope}} {{scope}}: 2 | {{~/if}} {{#if subject}} 3 | {{~subject}} 4 | {{~else}} 5 | {{~header}} 6 | {{~/if}} 7 | {{#if references~}} 8 | {{~#each references}} [{{~this.repository}}#{{this.issue}}]({{~@root.repoUrl}}/{{~@root.issue}}/{{this.issue}}){{/each}} 9 | {{~else}} [{{shortHash}}]({{~@root.repoUrl}}/{{~@root.commit}}/{{hash}}) 10 | {{~/if}} 11 | 12 | -------------------------------------------------------------------------------- /src/col/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-col { 4 | float: left; 5 | box-sizing: border-box; 6 | min-height: 1px; 7 | } 8 | 9 | .generate-col(24); 10 | .generate-col(@n, @i: 1) when (@i =< @n) { 11 | .van-col--@{i} { 12 | width: @i * 100% / 24; 13 | } 14 | 15 | .van-col--offset-@{i} { 16 | margin-left: @i * 100% / 24; 17 | } 18 | 19 | .generate-col(@n, (@i + 1)); 20 | } 21 | -------------------------------------------------------------------------------- /src/mixins/popup/context.ts: -------------------------------------------------------------------------------- 1 | import { OverlayConfig } from './overlay'; 2 | 3 | export type StackItem = { 4 | vm: any; 5 | overlay: any; 6 | config: OverlayConfig; 7 | }; 8 | 9 | export const context = { 10 | zIndex: 2000, 11 | lockCount: 0, 12 | stack: [] as StackItem[], 13 | find(vm: any): StackItem | undefined { 14 | return this.stack.filter((item) => item.vm === vm)[0]; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /src/progress/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`calc width 1`] = `
    `; 4 | 5 | exports[`calc width 2`] = `
    test
    `; 6 | -------------------------------------------------------------------------------- /packages/vant-cli/src/commands/clean.ts: -------------------------------------------------------------------------------- 1 | import { remove } from 'fs-extra'; 2 | import { 3 | ES_DIR, 4 | LIB_DIR, 5 | DIST_DIR, 6 | VETUR_DIR, 7 | SITE_DIST_DIR, 8 | } from '../common/constant'; 9 | 10 | export async function clean() { 11 | await Promise.all([ 12 | remove(ES_DIR), 13 | remove(LIB_DIR), 14 | remove(DIST_DIR), 15 | remove(VETUR_DIR), 16 | remove(SITE_DIST_DIR), 17 | ]); 18 | } 19 | -------------------------------------------------------------------------------- /packages/vant-stylelint-config/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'stylelint-config-standard', 4 | 'stylelint-config-rational-order', 5 | 'stylelint-config-prettier', 6 | ], 7 | rules: { 8 | 'no-descending-specificity': null, 9 | 'selector-pseudo-element-no-unknown': [ 10 | true, 11 | { 12 | ignorePseudoElements: ['v-deep'], 13 | }, 14 | ], 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /src/utils/validate/system.ts: -------------------------------------------------------------------------------- 1 | import { isServer } from '..'; 2 | 3 | export function isAndroid(): boolean { 4 | /* istanbul ignore next */ 5 | return isServer ? false : /android/.test(navigator.userAgent.toLowerCase()); 6 | } 7 | 8 | export function isIOS(): boolean { 9 | /* istanbul ignore next */ 10 | return isServer 11 | ? false 12 | : /ios|iphone|ipad|ipod/.test(navigator.userAgent.toLowerCase()); 13 | } 14 | -------------------------------------------------------------------------------- /src/swipe/test/demo.spec.js: -------------------------------------------------------------------------------- 1 | import Demo from '../demo'; 2 | import { snapshotDemo } from '../../../test/demo'; 3 | import { mockGetBoundingClientRect } from '../../../test'; 4 | 5 | let restore; 6 | 7 | snapshotDemo(Demo, { 8 | beforeTest: () => { 9 | restore = mockGetBoundingClientRect({ 10 | width: 100, 11 | height: 100, 12 | }); 13 | }, 14 | afterTest: () => { 15 | restore(); 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/generators/templates/src/demo-button/demo/index.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /packages/vant-cli/site/desktop/utils.js: -------------------------------------------------------------------------------- 1 | export function scrollToAnchor(selector) { 2 | let count = 0; 3 | 4 | const timer = setInterval(() => { 5 | const el = document.querySelector(selector); 6 | if (el) { 7 | el.scrollIntoView(); 8 | clearInterval(timer); 9 | } else { 10 | count++; 11 | 12 | if (count > 10) { 13 | clearInterval(timer); 14 | } 15 | } 16 | }, 100); 17 | } 18 | -------------------------------------------------------------------------------- /src/area/demo/area-simple.js: -------------------------------------------------------------------------------- 1 | export default { 2 | province_list: { 3 | 110000: '北京市', 4 | 120000: '天津市', 5 | }, 6 | city_list: { 7 | 110100: '北京市', 8 | 110200: '县', 9 | 120100: '天津市', 10 | 120200: '县', 11 | }, 12 | county_list: { 13 | 110101: '东城区', 14 | 110102: '西城区', 15 | 110228: '密云县', 16 | 110229: '延庆县', 17 | 120101: '和平区', 18 | 120102: '河东区', 19 | 120225: '蓟县', 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /src/steps/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-steps { 4 | overflow: hidden; 5 | background-color: @steps-background-color; 6 | 7 | &--horizontal { 8 | padding: 10px 10px 0; 9 | 10 | .van-steps__items { 11 | position: relative; 12 | display: flex; 13 | margin: 0 0 10px; 14 | padding-bottom: 22px; 15 | } 16 | } 17 | 18 | &--vertical { 19 | padding: 0 0 0 @padding-xl; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/mixins/slots.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use scopedSlots in Vue 2.6+ 3 | * downgrade to slots in lower version 4 | */ 5 | export const SlotsMixin = { 6 | methods: { 7 | slots(name = 'default', props) { 8 | const { $slots, $scopedSlots } = this; 9 | const scopedSlot = $scopedSlots[name]; 10 | 11 | if (scopedSlot) { 12 | return scopedSlot(props); 13 | } 14 | 15 | return $slots[name]; 16 | }, 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/password-input/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import PasswordInput from '..'; 2 | import { mount } from '../../../test'; 3 | 4 | test('focus event', () => { 5 | const focus = jest.fn(); 6 | const wrapper = mount(PasswordInput, { 7 | context: { 8 | on: { 9 | focus, 10 | }, 11 | }, 12 | }); 13 | 14 | wrapper.find('.van-password-input__security').trigger('touchstart'); 15 | expect(focus).toHaveBeenCalledTimes(1); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/vant-markdown-loader/src/card-wrapper.js: -------------------------------------------------------------------------------- 1 | module.exports = function cardWrapper(html) { 2 | const group = html 3 | .replace(/

    { 9 | if (fragment.indexOf('${fragment}`; 11 | } 12 | 13 | return fragment; 14 | }) 15 | .join(''); 16 | }; 17 | -------------------------------------------------------------------------------- /packages/vant-cli/src/compiler/vant-cli-release-plugin.ts: -------------------------------------------------------------------------------- 1 | import releaseIt from 'release-it'; 2 | import { build } from '../commands/build'; 3 | import { changelog } from '../commands/changelog'; 4 | 5 | class VantCliReleasePlugin extends releaseIt.Plugin { 6 | async beforeRelease() { 7 | // log an empty line 8 | console.log(''); 9 | 10 | await build(); 11 | await changelog(); 12 | } 13 | } 14 | 15 | module.exports = VantCliReleasePlugin; 16 | -------------------------------------------------------------------------------- /src/form/demo/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /src/utils/create/index.ts: -------------------------------------------------------------------------------- 1 | import { createBEM, BEM } from './bem'; 2 | import { createComponent } from './component'; 3 | import { createI18N, Translate } from './i18n'; 4 | 5 | type CreateNamespaceReturn = [ 6 | ReturnType, 7 | BEM, 8 | Translate 9 | ]; 10 | 11 | export function createNamespace(name: string): CreateNamespaceReturn { 12 | name = 'van-' + name; 13 | return [createComponent(name), createBEM(name), createI18N(name)]; 14 | } 15 | -------------------------------------------------------------------------------- /packages/vant-cli/site/desktop/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import App from './App'; 3 | import { router } from './router'; 4 | import { scrollToAnchor } from './utils'; 5 | 6 | if (process.env.NODE_ENV !== 'production') { 7 | Vue.config.productionTip = false; 8 | } 9 | 10 | new Vue({ 11 | el: '#app', 12 | mounted() { 13 | if (this.$route.hash) { 14 | scrollToAnchor(this.$route.hash); 15 | } 16 | }, 17 | render: h => h(App), 18 | router, 19 | }); 20 | -------------------------------------------------------------------------------- /packages/vant-cli/src/module.d.ts: -------------------------------------------------------------------------------- 1 | // some modules with missing type definitions 2 | declare module 'less'; 3 | declare module 'sass'; 4 | declare module 'execa'; 5 | declare module 'hash-sum'; 6 | declare module 'clean-css'; 7 | declare module 'webpackbar'; 8 | declare module 'release-it'; 9 | declare module 'html-webpack-plugin'; 10 | declare module 'conventional-changelog'; 11 | declare module '@vant/markdown-vetur'; 12 | declare module '@nuxt/friendly-errors-webpack-plugin'; 13 | -------------------------------------------------------------------------------- /src/switch/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`inactive-color prop 1`] = ` 4 |
    5 |
    6 |
    7 | `; 8 | 9 | exports[`size prop 1`] = ` 10 |
    11 |
    12 |
    13 | `; 14 | -------------------------------------------------------------------------------- /src/utils/dom/reset-scroll.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Hack for iOS12 page scroll 3 | * https://developers.weixin.qq.com/community/develop/doc/00044ae90742f8c82fb78fcae56800 4 | */ 5 | 6 | import { isIOS as checkIsIOS } from '../validate/system'; 7 | import { getRootScrollTop, setRootScrollTop } from './scroll'; 8 | 9 | const isIOS = checkIsIOS(); 10 | 11 | /* istanbul ignore next */ 12 | export function resetScroll() { 13 | if (isIOS) { 14 | setRootScrollTop(getRootScrollTop()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/goods-action/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-goods-action { 4 | position: fixed; 5 | right: 0; 6 | bottom: 0; 7 | left: 0; 8 | display: flex; 9 | align-items: center; 10 | box-sizing: content-box; 11 | height: @goods-action-height; 12 | padding-bottom: constant(safe-area-inset-bottom); 13 | padding-bottom: env(safe-area-inset-bottom); 14 | background-color: @goods-action-background-color; 15 | 16 | &--unfit { 17 | padding-bottom: 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/list/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`error slot 1`] = ` 4 |
    5 |
    Custom Error
    6 |
    7 |
    8 | `; 9 | 10 | exports[`finished slot 1`] = ` 11 |
    12 |
    Custom Finished
    13 |
    14 |
    15 | `; 16 | -------------------------------------------------------------------------------- /packages/vant-cli/src/compiler/compile-package.ts: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import { getPackageConfig } from '../config/webpack.package'; 3 | 4 | export async function compilePackage(isMinify: boolean) { 5 | return new Promise((resolve, reject) => { 6 | const config = getPackageConfig(isMinify); 7 | 8 | webpack(config, (err, stats) => { 9 | if (err || stats.hasErrors()) { 10 | reject(); 11 | } else { 12 | resolve(); 13 | } 14 | }); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/dom/style.ts: -------------------------------------------------------------------------------- 1 | export function isHidden(el: HTMLElement) { 2 | const style = window.getComputedStyle(el); 3 | const hidden = style.display === 'none'; 4 | 5 | // offsetParent returns null in the following situations: 6 | // 1. The element or its parent element has the display property set to none. 7 | // 2. The element has the position property set to fixed 8 | const parentHidden = el.offsetParent === null && style.position !== 'fixed'; 9 | 10 | return hidden || parentHidden; 11 | } 12 | -------------------------------------------------------------------------------- /packages/vant-cli/src/commands/release.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-template-curly-in-string */ 2 | import releaseIt from 'release-it'; 3 | import { join } from 'path'; 4 | 5 | const PLUGIN_PATH = join(__dirname, '../compiler/vant-cli-release-plugin.js'); 6 | 7 | export async function release() { 8 | await releaseIt({ 9 | plugins: { 10 | [PLUGIN_PATH]: {}, 11 | }, 12 | git: { 13 | tagName: 'v${version}', 14 | commitMessage: 'chore: release ${version}', 15 | }, 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "jsx": "preserve", 5 | "target": "esnext", 6 | "module": "esnext", 7 | "strict": true, 8 | "allowJs": true, 9 | "noEmit": true, 10 | "noImplicitThis": true, 11 | "esModuleInterop": true, 12 | "moduleResolution": "node" 13 | }, 14 | "include": [ 15 | "types/**/*", 16 | "src/**/*", 17 | ], 18 | "exclude": [ 19 | "**/*.spec.ts", 20 | "**/*.spec.js", 21 | "node_modules" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /src/tabbar/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-tabbar { 4 | z-index: @tabbar-z-index; 5 | display: flex; 6 | box-sizing: content-box; 7 | width: 100%; 8 | height: @tabbar-height; 9 | padding-bottom: constant(safe-area-inset-bottom); 10 | padding-bottom: env(safe-area-inset-bottom); 11 | background-color: @tabbar-background-color; 12 | 13 | &--fixed { 14 | position: fixed; 15 | bottom: 0; 16 | left: 0; 17 | } 18 | 19 | &--unfit { 20 | padding-bottom: 0; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { mount, TransitionStub } from '@vue/test-utils'; 3 | 4 | // prevent vue warning log 5 | Vue.config.silent = true; 6 | 7 | // stub transition 8 | Vue.component('transition', TransitionStub as any); 9 | 10 | // promisify setTimeout 11 | export function later(delay: number = 0): Promise { 12 | return new Promise((resolve) => { 13 | setTimeout(resolve, delay); 14 | }); 15 | } 16 | 17 | export * from './dom'; 18 | export * from './event'; 19 | export { mount }; 20 | -------------------------------------------------------------------------------- /src/coupon-list/test/__snapshots__/demo.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders demo correctly 1`] = ` 4 |
    5 |
    6 |
    7 |
    优惠券
    8 |
    2张可用
    9 | 10 |
    11 | 12 |
    13 |
    14 | `; 15 | -------------------------------------------------------------------------------- /src/mixins/field.js: -------------------------------------------------------------------------------- 1 | export const FieldMixin = { 2 | inject: { 3 | vanField: { 4 | default: null, 5 | }, 6 | }, 7 | 8 | watch: { 9 | value() { 10 | const field = this.vanField; 11 | 12 | if (field) { 13 | field.resetValidation(); 14 | field.validateWithTrigger('onChange'); 15 | } 16 | }, 17 | }, 18 | 19 | created() { 20 | const field = this.vanField; 21 | 22 | if (field && !field.children) { 23 | field.children = this; 24 | } 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/vant-cli/site/mobile/components/ArrowRight.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /src/count-down/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`complete format prop 1`] = `
    01-05-59-59-999
    `; 4 | 5 | exports[`disable auto-start prop 1`] = `
    100
    `; 6 | 7 | exports[`incomplate format prop 1`] = `
    29-59-59-999
    `; 8 | 9 | exports[`milliseconds format S 1`] = `
    01-5
    `; 10 | 11 | exports[`milliseconds format SS 1`] = `
    01-50
    `; 12 | -------------------------------------------------------------------------------- /src/calendar/test/utils.js: -------------------------------------------------------------------------------- 1 | export const now = new Date(); 2 | export const minDate = new Date(2010, 0, 10); 3 | export const maxDate = new Date(2010, 0, 20); 4 | 5 | export function formatDate(date) { 6 | if (date) { 7 | return `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`; 8 | } 9 | 10 | return ''; 11 | } 12 | 13 | export function formatRange([start, end]) { 14 | return `${formatDate(start)}-${formatDate(end)}`; 15 | } 16 | 17 | export function formatMultiple(dates) { 18 | return dates.map(formatDate).join(','); 19 | } 20 | -------------------------------------------------------------------------------- /src/style/normalize.less: -------------------------------------------------------------------------------- 1 | @import './var'; 2 | 3 | html { 4 | -webkit-tap-highlight-color: transparent; 5 | } 6 | 7 | body { 8 | margin: 0; 9 | font-family: @base-font-family; 10 | } 11 | 12 | a { 13 | text-decoration: none; 14 | } 15 | 16 | input, 17 | button, 18 | textarea { 19 | color: inherit; 20 | font: inherit; 21 | } 22 | 23 | a, 24 | input, 25 | button, 26 | textarea, 27 | [class*='van-'] { 28 | &:focus { 29 | outline: none; 30 | } 31 | } 32 | 33 | ol, 34 | ul { 35 | margin: 0; 36 | padding: 0; 37 | list-style: none; 38 | } 39 | -------------------------------------------------------------------------------- /packages/vant-cli/src/compiler/compile-css.ts: -------------------------------------------------------------------------------- 1 | import postcss from 'postcss'; 2 | import postcssrc from 'postcss-load-config'; 3 | import CleanCss from 'clean-css'; 4 | import { POSTCSS_CONFIG_FILE } from '../common/constant'; 5 | 6 | const cleanCss = new CleanCss(); 7 | 8 | export async function compileCss(source: string | Buffer) { 9 | const config = await postcssrc({}, POSTCSS_CONFIG_FILE); 10 | const { css } = await postcss(config.plugins as any).process(source, { 11 | from: undefined, 12 | }); 13 | 14 | return cleanCss.minify(css).styles; 15 | } 16 | -------------------------------------------------------------------------------- /src/loading/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '../../../test'; 2 | import Loading from '..'; 3 | 4 | test('size prop', () => { 5 | const wrapper = mount(Loading, { 6 | propsData: { 7 | size: 20, 8 | }, 9 | }); 10 | 11 | expect(wrapper).toMatchSnapshot(); 12 | }); 13 | 14 | test('text-size prop', () => { 15 | const wrapper = mount(Loading, { 16 | propsData: { 17 | textSize: 20, 18 | }, 19 | scopedSlots: { 20 | default: () => 'Text', 21 | }, 22 | }); 23 | 24 | expect(wrapper).toMatchSnapshot(); 25 | }); 26 | -------------------------------------------------------------------------------- /src/utils/constant.ts: -------------------------------------------------------------------------------- 1 | // color 2 | export const RED = '#ee0a24'; 3 | export const BLUE = '#1989fa'; 4 | export const GREEN = '#07c160'; 5 | export const WHITE = '#fff'; 6 | 7 | // border 8 | export const BORDER = 'van-hairline'; 9 | export const BORDER_TOP = `${BORDER}--top`; 10 | export const BORDER_LEFT = `${BORDER}--left`; 11 | export const BORDER_BOTTOM = `${BORDER}--bottom`; 12 | export const BORDER_SURROUND = `${BORDER}--surround`; 13 | export const BORDER_TOP_BOTTOM = `${BORDER}--top-bottom`; 14 | export const BORDER_UNSET_TOP_BOTTOM = `${BORDER}-unset--top-bottom`; 15 | -------------------------------------------------------------------------------- /packages/vant-cli/site/mobile/App.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 17 | 18 | 31 | -------------------------------------------------------------------------------- /src/utils/create/i18n.ts: -------------------------------------------------------------------------------- 1 | import { get, isFunction } from '..'; 2 | import { camelize } from '../format/string'; 3 | import locale from '../../locale'; 4 | 5 | export function createI18N(name: string) { 6 | const prefix = camelize(name) + '.'; 7 | 8 | return function (path: string, ...args: any[]): string { 9 | const messages = locale.messages(); 10 | const message = get(messages, prefix + path) || get(messages, path); 11 | 12 | return isFunction(message) ? message(...args) : message; 13 | }; 14 | } 15 | 16 | export type Translate = ReturnType; 17 | -------------------------------------------------------------------------------- /packages/vant-touch-emulator/README.md: -------------------------------------------------------------------------------- 1 | # Vant Touch Emulator 2 | 3 | 在桌面端上模拟移动端 touch 事件,实现方式来自于 [hammerjs/touchemulator](https://github.com/hammerjs/touchemulator). 4 | 5 | ## Install 6 | 7 | #### NPM 8 | 9 | ```shell 10 | npm i @vant/touch-emulator -S 11 | ``` 12 | 13 | #### YARN 14 | 15 | ```shell 16 | yarn add @vant/touch-emulator 17 | ``` 18 | 19 | ## 使用指南 20 | 21 | 直接在代码中引入模块即可,模块会自动完成初始化并生效 22 | 23 | ```js 24 | import '@vant/touch-emulator'; 25 | ``` 26 | 27 | ## CDN 引入 28 | 29 | ```html 30 | 31 | ``` 32 | -------------------------------------------------------------------------------- /src/goods-action/index.js: -------------------------------------------------------------------------------- 1 | import { createNamespace } from '../utils'; 2 | import { ParentMixin } from '../mixins/relation'; 3 | 4 | const [createComponent, bem] = createNamespace('goods-action'); 5 | 6 | export default createComponent({ 7 | mixins: [ParentMixin('vanGoodsAction')], 8 | 9 | props: { 10 | safeAreaInsetBottom: { 11 | type: Boolean, 12 | default: true, 13 | }, 14 | }, 15 | 16 | render() { 17 | return ( 18 |
    19 | {this.slots()} 20 |
    21 | ); 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /src/swipe-cell/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-swipe-cell { 4 | position: relative; 5 | overflow: hidden; 6 | cursor: grab; 7 | 8 | &__wrapper { 9 | transition-timing-function: cubic-bezier(0.18, 0.89, 0.32, 1); 10 | transition-property: transform; 11 | } 12 | 13 | &__left, 14 | &__right { 15 | position: absolute; 16 | top: 0; 17 | height: 100%; 18 | } 19 | 20 | &__left { 21 | left: 0; 22 | transform: translate3d(-100%, 0, 0); 23 | } 24 | 25 | &__right { 26 | right: 0; 27 | transform: translate3d(100%, 0, 0); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/vant-cli/site/mobile/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import DemoBlock from './components/DemoBlock'; 3 | import DemoSection from './components/DemoSection'; 4 | import { router } from './router'; 5 | import App from './App'; 6 | import '@vant/touch-emulator'; 7 | 8 | if (process.env.NODE_ENV !== 'production') { 9 | Vue.config.productionTip = false; 10 | } 11 | 12 | Vue.component(DemoBlock.name, DemoBlock); 13 | Vue.component(DemoSection.name, DemoSection); 14 | 15 | setTimeout(() => { 16 | new Vue({ 17 | el: '#app', 18 | render: h => h(App), 19 | router, 20 | }); 21 | }, 0); 22 | -------------------------------------------------------------------------------- /packages/vant-cli/src/config/postcss.config.ts: -------------------------------------------------------------------------------- 1 | import { getPostcssConfig } from '../common'; 2 | 3 | type PostcssConfig = object & { 4 | plugins?: object; 5 | }; 6 | 7 | function mergePostcssConfig(config1: PostcssConfig, config2: PostcssConfig) { 8 | const plugins = { 9 | ...config1.plugins, 10 | ...config2.plugins, 11 | }; 12 | 13 | return { 14 | ...config1, 15 | ...config2, 16 | plugins, 17 | }; 18 | } 19 | 20 | const DEFAULT_CONFIG = { 21 | plugins: { 22 | autoprefixer: {}, 23 | }, 24 | }; 25 | 26 | module.exports = mergePostcssConfig(DEFAULT_CONFIG, getPostcssConfig()); 27 | -------------------------------------------------------------------------------- /packages/vant-markdown-vetur/src/web-types.ts: -------------------------------------------------------------------------------- 1 | import { VueTag, Options } from './type'; 2 | 3 | // create web-types.json to provide autocomplete in JetBrains IDEs 4 | export function genWebTypes(tags: VueTag[], options: Options) { 5 | return { 6 | $schema: 7 | 'https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json', 8 | framework: 'vue', 9 | name: options.name, 10 | version: options.version, 11 | contributions: { 12 | html: { 13 | tags, 14 | attributes: [], 15 | 'types-syntax': 'typescript', 16 | }, 17 | }, 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /packages/vant-waterfall/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vant/waterfall", 3 | "version": "1.0.2", 4 | "description": "vant waterfall component", 5 | "main": "lib/index.js", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "scripts": { 10 | "build": "npx babel src --out-dir lib", 11 | "release": "npm run build && npm publish" 12 | }, 13 | "license": "MIT", 14 | "repository": "https://github.com/youzan/vant/tree/dev/packages/vant-waterfall", 15 | "devDependencies": { 16 | "@babel/cli": "^7.8.4", 17 | "@babel/core": "^7.9.0", 18 | "@babel/preset-env": "^7.9.5" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/vant-markdown-loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vant/markdown-loader", 3 | "version": "2.3.0", 4 | "description": "Simple and fast vue markdown loader", 5 | "main": "src/index.js", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "license": "MIT", 10 | "repository": "https://github.com/youzan/vant/tree/dev/packages/vant-markdown-loader", 11 | "dependencies": { 12 | "front-matter": "^3.0.2", 13 | "highlight.js": "^9.17.1", 14 | "loader-utils": "^1.2.3", 15 | "markdown-it": "^10.0.0", 16 | "markdown-it-anchor": "^5.2.5", 17 | "transliteration": "^2.1.7" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/pull-refresh/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-pull-refresh { 4 | overflow: hidden; 5 | user-select: none; 6 | 7 | &__track { 8 | position: relative; 9 | height: 100%; 10 | transition-property: transform; 11 | } 12 | 13 | &__head { 14 | position: absolute; 15 | left: 0; 16 | width: 100%; 17 | height: @pull-refresh-head-height; 18 | overflow: hidden; 19 | color: @pull-refresh-head-text-color; 20 | font-size: @pull-refresh-head-font-size; 21 | line-height: @pull-refresh-head-height; 22 | text-align: center; 23 | transform: translateY(-100%); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/index-anchor/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-index-anchor { 4 | z-index: @index-anchor-z-index; 5 | box-sizing: border-box; 6 | padding: @index-anchor-padding; 7 | color: @index-anchor-text-color; 8 | font-weight: @index-anchor-font-weight; 9 | font-size: @index-anchor-font-size; 10 | line-height: @index-anchor-line-height; 11 | background-color: @index-anchor-background-color; 12 | 13 | &--sticky { 14 | position: fixed; 15 | top: 0; 16 | right: 0; 17 | left: 0; 18 | color: @index-anchor-sticky-text-color; 19 | background-color: @index-anchor-sticky-background-color; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/generators/templates/src/demo-button/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | 32 | -------------------------------------------------------------------------------- /packages/vant-markdown-loader/src/link-open.js: -------------------------------------------------------------------------------- 1 | // add target="_blank" to all links 2 | module.exports = function linkOpen(md) { 3 | const defaultRender = 4 | md.renderer.rules.link_open || 5 | function(tokens, idx, options, env, self) { 6 | return self.renderToken(tokens, idx, options); 7 | }; 8 | 9 | md.renderer.rules.link_open = function(tokens, idx, options, env, self) { 10 | const aIndex = tokens[idx].attrIndex('target'); 11 | 12 | if (aIndex < 0) { 13 | tokens[idx].attrPush(['target', '_blank']); // add new attribute 14 | } 15 | 16 | return defaultRender(tokens, idx, options, env, self); 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /src/field/demo/Disabled.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 25 | -------------------------------------------------------------------------------- /packages/vant-stylelint-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vant/stylelint-config", 3 | "version": "1.3.0", 4 | "description": "stylelint config of vant.", 5 | "main": "index.js", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "license": "MIT", 10 | "repository": "https://github.com/youzan/vant/tree/dev/packages/vant-stylelint-config", 11 | "peerDependencies": { 12 | "stylelint": "^13.0.0" 13 | }, 14 | "dependencies": { 15 | "stylelint-config-prettier": "^8.0.1", 16 | "stylelint-config-rational-order": "^0.1.2", 17 | "stylelint-config-standard": "^20.0.0", 18 | "stylelint-order": "^4.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/field/demo/BasicUsage.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 33 | -------------------------------------------------------------------------------- /packages/vant-icons/build/build-encode.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const path = require('path'); 3 | const config = require('../src/config'); 4 | 5 | function template(fontName, ttf) { 6 | return `@font-face { 7 | font-weight: normal; 8 | font-family: '${fontName}'; 9 | font-style: normal; 10 | src: url('${ttf}') format('truetype'); 11 | } 12 | `; 13 | } 14 | 15 | module.exports = function encode(fontName, srcDir) { 16 | const ttfBase64 = fs.readFileSync(`../src/${fontName}.ttf`, 'base64'); 17 | fs.writeFileSync( 18 | path.join(srcDir, 'encode.less'), 19 | template(config.name, `data:font/ttf;base64,${ttfBase64}`) 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /src/overlay/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`class-name prop 1`] = `
    `; 4 | 5 | exports[`custom style prop 1`] = `
    `; 6 | 7 | exports[`default slot 1`] = ``; 8 | 9 | exports[`duration prop 1`] = `
    `; 10 | 11 | exports[`z-index prop 1`] = `
    `; 12 | -------------------------------------------------------------------------------- /packages/vant-markdown-vetur/src/utils.ts: -------------------------------------------------------------------------------- 1 | // myName -> my-name 2 | export function toKebabCase(input: string): string { 3 | return input.replace( 4 | /[A-Z]/g, 5 | (val, index) => (index === 0 ? '' : '-') + val.toLowerCase() 6 | ); 7 | } 8 | 9 | // name `v2.0.0` -> name 10 | export function removeVersion(str: string) { 11 | return str.replace(/`(\w|\.)+`/g, '').trim(); 12 | } 13 | 14 | // *boolean* -> boolean 15 | // _boolean_ -> boolean 16 | export function formatType(type: string) { 17 | return type.replace(/(^(\*|_))|((\*|_)$)/g, ''); 18 | } 19 | 20 | export function normalizePath(path: string): string { 21 | return path.replace(/\\/g, '/'); 22 | } 23 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import inquirer from 'inquirer'; 4 | import consola from 'consola'; 5 | import { ensureDir } from 'fs-extra'; 6 | import { VanGenerator } from './generator'; 7 | 8 | const PROMPTS = [ 9 | { 10 | type: 'input', 11 | name: 'name', 12 | message: 'Your package name', 13 | }, 14 | ]; 15 | 16 | export default async function run() { 17 | const { name } = await inquirer.prompt(PROMPTS); 18 | 19 | try { 20 | await ensureDir(name); 21 | 22 | const generator = new VanGenerator(name); 23 | generator.run(); 24 | } catch (e) { 25 | consola.error(e); 26 | } 27 | } 28 | 29 | run(); 30 | -------------------------------------------------------------------------------- /packages/vant-icons/README.md: -------------------------------------------------------------------------------- 1 | # Vant Icons 2 | 3 | ## Install 4 | 5 | #### NPM 6 | 7 | ```shell 8 | npm i @vant/icons -S 9 | ``` 10 | 11 | #### YARN 12 | 13 | ```shell 14 | yarn add @vant/icons 15 | ``` 16 | 17 | ## Document 18 | 19 | - [Usage in Vue](https://youzan.github.io/vant/#/zh-CN/icon) 20 | - [Usage in Weapp](https://youzan.github.io/vant-weapp/#/icon) 21 | 22 | ## Contribution 23 | 24 | ### Update Icons 25 | 26 | 1. Update assets/icons.sketch 27 | 2. Make a Pull Request 28 | 29 | ### Add New Icon 30 | 31 | 1. Add new icon to assets/icons.sketch 32 | 2. Add icon name to src/config.js 33 | 3. Add icon codepoints to build/codepoints.js 34 | 4. Make a Pull Request 35 | -------------------------------------------------------------------------------- /src/mixins/bind-event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bind event when mounted or activated 3 | */ 4 | import { on, off } from '../utils/dom/event'; 5 | 6 | let uid = 0; 7 | 8 | export function BindEventMixin(handler) { 9 | const key = `binded_${uid++}`; 10 | 11 | function bind() { 12 | if (!this[key]) { 13 | handler.call(this, on, true); 14 | this[key] = true; 15 | } 16 | } 17 | 18 | function unbind() { 19 | if (this[key]) { 20 | handler.call(this, off, false); 21 | this[key] = false; 22 | } 23 | } 24 | 25 | return { 26 | mounted: bind, 27 | activated: bind, 28 | deactivated: unbind, 29 | beforeDestroy: unbind, 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/index-bar/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-index-bar { 4 | &__sidebar { 5 | position: fixed; 6 | top: 50%; 7 | right: 0; 8 | z-index: @index-bar-sidebar-z-index; 9 | display: flex; 10 | flex-direction: column; 11 | text-align: center; 12 | transform: translateY(-50%); 13 | cursor: pointer; 14 | user-select: none; 15 | } 16 | 17 | &__index { 18 | padding: 0 @padding-base 0 @padding-md; 19 | font-weight: @font-weight-bold; 20 | font-size: @index-bar-index-font-size; 21 | line-height: @index-bar-index-line-height; 22 | 23 | &--active { 24 | color: @index-bar-index-active-color; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/switch/shared.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Common Switch Props 3 | */ 4 | 5 | export type SharedSwitchProps = { 6 | size?: string | number; 7 | value?: any; 8 | loading?: boolean; 9 | disabled?: boolean; 10 | activeValue: any; 11 | inactiveValue: any; 12 | activeColor?: string; 13 | inactiveColor?: string; 14 | }; 15 | 16 | export const switchProps = { 17 | size: [Number, String], 18 | value: null as any, 19 | loading: Boolean, 20 | disabled: Boolean, 21 | activeColor: String, 22 | inactiveColor: String, 23 | activeValue: { 24 | type: null as any, 25 | default: true, 26 | }, 27 | inactiveValue: { 28 | type: null as any, 29 | default: false, 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /packages/vant-markdown-vetur/README.md: -------------------------------------------------------------------------------- 1 | # Vant Markdown Vetur 2 | 3 | 将 .md 文件转换成能描述 vue 组件的 .json 文件,供 WebStorm 和 vscode 的 `vetur` 插件读取,从而可以在 vue 模版语法中拥有自动补全的功能。 4 | 5 | ## Install 6 | 7 | #### NPM 8 | 9 | ```shell 10 | npm i @vant/markdown-vetur -D 11 | ``` 12 | 13 | #### YARN 14 | 15 | ```shell 16 | yarn add @vant/markdown-vetur --dev 17 | ``` 18 | 19 | ## API 20 | 21 | #### parseAndWrite 22 | 23 | 解析目录下所有匹配的文件,并输出为 tags.json 和 attributes.json 24 | 25 | ```ts 26 | interface Options { 27 | // 需要解析的文件夹路径 28 | path: PathLike; 29 | // 文件匹配正则 30 | test: RegExp; 31 | // 输出目录 32 | outputDir: string; 33 | // 递归的目录最大深度 34 | maxDeep?: number; 35 | // 解析出来的组件名前缀 36 | tagPrefix?: string; 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /src/field/demo/Autosize.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 36 | -------------------------------------------------------------------------------- /src/field/demo/InputAlign.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 34 | -------------------------------------------------------------------------------- /src/contact-edit/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-contact-edit { 4 | padding: @contact-edit-padding; 5 | 6 | &__fields { 7 | overflow: hidden; 8 | border-radius: @contact-edit-fields-radius; 9 | 10 | .van-field__label { 11 | width: @contact-edit-field-label-width; 12 | } 13 | } 14 | 15 | &__switch-cell { 16 | margin-top: 10px; 17 | padding-top: 9px; 18 | padding-bottom: 9px; 19 | border-radius: @contact-edit-fields-radius; 20 | } 21 | 22 | &__buttons { 23 | padding: @contact-edit-buttons-padding; 24 | } 25 | 26 | .van-button { 27 | margin-bottom: @contact-edit-button-margin-bottom; 28 | font-size: @contact-edit-button-font-size; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/vant-cli/site/desktop/components/Container.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | 20 | 33 | -------------------------------------------------------------------------------- /src/loading/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`size prop 1`] = `
    `; 4 | 5 | exports[`text-size prop 1`] = `
    Text
    `; 6 | -------------------------------------------------------------------------------- /src/row/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-row { 4 | &::after { 5 | display: table; 6 | clear: both; 7 | content: ''; 8 | } 9 | 10 | &--flex { 11 | display: flex; 12 | 13 | &::after { 14 | display: none; 15 | } 16 | } 17 | 18 | &--justify-center { 19 | justify-content: center; 20 | } 21 | 22 | &--justify-end { 23 | justify-content: flex-end; 24 | } 25 | 26 | &--justify-space-between { 27 | justify-content: space-between; 28 | } 29 | 30 | &--justify-space-around { 31 | justify-content: space-around; 32 | } 33 | 34 | &--align-center { 35 | align-items: center; 36 | } 37 | 38 | &--align-bottom { 39 | align-items: flex-end; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/vant-cli/site/common/locales.js: -------------------------------------------------------------------------------- 1 | const ZH_CN = 'zh-CN'; 2 | const EN_US = 'en-US'; 3 | const CACHE_KEY = 'vant-cli-lang'; 4 | 5 | let currentLang = ZH_CN; 6 | 7 | export function getLang() { 8 | return currentLang; 9 | } 10 | 11 | export function setLang(lang) { 12 | currentLang = lang; 13 | localStorage.setItem(CACHE_KEY, lang); 14 | } 15 | 16 | export function setDefaultLang(langFromConfig) { 17 | const cached = localStorage.getItem(CACHE_KEY); 18 | 19 | if (cached) { 20 | currentLang = cached; 21 | return; 22 | } 23 | 24 | if (navigator.language && navigator.language.indexOf('zh-') !== -1) { 25 | currentLang = ZH_CN; 26 | return; 27 | } 28 | 29 | currentLang = langFromConfig || EN_US; 30 | } 31 | -------------------------------------------------------------------------------- /packages/vant-cli/site/mobile/components/DemoSection.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | 26 | 33 | -------------------------------------------------------------------------------- /src/goods-action-icon/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-goods-action-icon { 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | min-width: @goods-action-icon-width; 8 | height: @goods-action-icon-height; 9 | color: @goods-action-icon-text-color; 10 | font-size: @goods-action-icon-font-size; 11 | line-height: 1; 12 | text-align: center; 13 | background-color: @white; 14 | cursor: pointer; 15 | 16 | &:active { 17 | background-color: @goods-action-icon-active-color; 18 | } 19 | 20 | &__icon { 21 | position: relative; 22 | width: 1em; 23 | margin: 0 auto 5px; 24 | color: @goods-action-icon-color; 25 | font-size: @goods-action-icon-size; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/progress/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import Progress from '..'; 2 | import { mount, later } from '../../../test'; 3 | 4 | test('calc width', async () => { 5 | const wrapper = mount(Progress, { 6 | propsData: { 7 | showPivot: false, 8 | percentage: 100, 9 | }, 10 | }); 11 | await later(); 12 | expect(wrapper).toMatchSnapshot(); 13 | 14 | wrapper.vm.showPivot = true; 15 | wrapper.vm.pivotText = 'test'; 16 | await later(); 17 | expect(wrapper).toMatchSnapshot(); 18 | }); 19 | 20 | test('track color prop', async () => { 21 | const wrapper = mount(Progress, { 22 | propsData: { 23 | trackColor: 'green', 24 | }, 25 | }); 26 | 27 | expect(wrapper.element.style.background).toEqual('green'); 28 | }); 29 | -------------------------------------------------------------------------------- /types/picker.d.ts: -------------------------------------------------------------------------------- 1 | import { VanComponent } from './component'; 2 | 3 | export class Picker extends VanComponent { 4 | getValues(): T[]; 5 | 6 | setValues(values: T[]): void; 7 | 8 | getIndexes(): number[]; 9 | 10 | setIndexes(indexes: number[]): void; 11 | 12 | getColumnValue(columnIndex: number): T; 13 | 14 | setColumnValue(columnIndex: number, value: T): void; 15 | 16 | getColumnIndex(columnIndex: number): number; 17 | 18 | setColumnIndex(columnIndex: number, optionIndex: number): void; 19 | 20 | getColumnValues(columnIndex: number): T[]; 21 | 22 | setColumnValues(columnIndex: number, values: T[]): void; 23 | 24 | confirm(): void; 25 | } 26 | -------------------------------------------------------------------------------- /packages/vant-cli/src/compiler/gen-vetur-config.ts: -------------------------------------------------------------------------------- 1 | import markdownVetur from '@vant/markdown-vetur'; 2 | import { get } from 'lodash'; 3 | import { 4 | SRC_DIR, 5 | VETUR_DIR, 6 | getVantConfig, 7 | getPackageJson, 8 | } from '../common/constant'; 9 | 10 | // generate vetur tags & attributes 11 | export function genVeturConfig() { 12 | const pkgJson = getPackageJson(); 13 | const vantConfig = getVantConfig(); 14 | const options = get(vantConfig, 'build.vetur'); 15 | 16 | if (options) { 17 | markdownVetur.parseAndWrite({ 18 | name: vantConfig.name, 19 | path: SRC_DIR, 20 | test: /zh-CN\.md/, 21 | version: pkgJson.version, 22 | outputDir: VETUR_DIR, 23 | ...options, 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/vant-icons/build/export.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const path = require('path'); 3 | const shell = require('shelljs'); 4 | 5 | const svgDir = path.join(__dirname, '../assets/svg'); 6 | const sketch = path.join(__dirname, '../assets/icons.sketch'); 7 | const SKETCH_TOOL_DIR = 8 | '/Applications/Sketch.app/Contents/Resources/sketchtool/bin/sketchtool'; 9 | 10 | fs.removeSync(svgDir); 11 | 12 | // extract svg from sketch 13 | // should install sketchtool first 14 | // install guide: https://developer.sketchapp.com/guides/sketchtool/ 15 | shell.exec( 16 | `${SKETCH_TOOL_DIR} export slices --formats=svg --overwriting=YES --save-for-web=YES --output=${svgDir} ${sketch}` 17 | ); 18 | 19 | shell.exec('svgo ./assets/svg/*.svg'); 20 | -------------------------------------------------------------------------------- /src/circle/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-circle { 4 | position: relative; 5 | display: inline-block; 6 | text-align: center; 7 | 8 | svg { 9 | position: absolute; 10 | top: 0; 11 | left: 0; 12 | width: 100%; 13 | height: 100%; 14 | } 15 | 16 | &__layer { 17 | fill: none; 18 | stroke-linecap: round; 19 | } 20 | 21 | &__text { 22 | position: absolute; 23 | top: 50%; 24 | left: 0; 25 | box-sizing: border-box; 26 | width: 100%; 27 | padding: 0 @padding-base; 28 | color: @circle-text-color; 29 | font-weight: @circle-text-font-weight; 30 | font-size: @circle-text-font-size; 31 | line-height: @circle-text-line-height; 32 | transform: translateY(-50%); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/dropdown-item/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-dropdown-item { 4 | position: fixed; 5 | right: 0; 6 | left: 0; 7 | z-index: @dropdown-item-z-index; 8 | overflow: hidden; 9 | 10 | &__icon { 11 | display: block; 12 | line-height: inherit; 13 | } 14 | 15 | &__option { 16 | text-align: left; 17 | 18 | &--active { 19 | color: @dropdown-menu-option-active-color; 20 | 21 | .van-dropdown-item__icon { 22 | color: @dropdown-menu-option-active-color; 23 | } 24 | } 25 | } 26 | 27 | &--up { 28 | top: 0; 29 | } 30 | 31 | &--down { 32 | bottom: 0; 33 | } 34 | 35 | &__content { 36 | position: absolute; 37 | max-height: @dropdown-menu-content-max-height; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/info/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import Info from '..'; 2 | import { mount } from '../../../test'; 3 | 4 | test('should not render when info is empty string', () => { 5 | const wrapper = mount(Info, { 6 | propsData: { 7 | info: '', 8 | }, 9 | }); 10 | 11 | expect(wrapper).toMatchSnapshot(); 12 | }); 13 | 14 | test('should not render when info is empty undefined', () => { 15 | const wrapper = mount(Info, { 16 | propsData: { 17 | info: undefined, 18 | }, 19 | }); 20 | 21 | expect(wrapper).toMatchSnapshot(); 22 | }); 23 | 24 | test('should render when info is zero', () => { 25 | const wrapper = mount(Info, { 26 | propsData: { 27 | info: 0, 28 | }, 29 | }); 30 | 31 | expect(wrapper).toMatchSnapshot(); 32 | }); 33 | -------------------------------------------------------------------------------- /src/mixins/click-outside.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Listen to click outside event 3 | */ 4 | import { on, off } from '../utils/dom/event'; 5 | 6 | export const ClickOutsideMixin = (config) => ({ 7 | props: { 8 | closeOnClickOutside: { 9 | type: Boolean, 10 | default: true, 11 | }, 12 | }, 13 | 14 | data() { 15 | const clickOutsideHandler = (event) => { 16 | if (this.closeOnClickOutside && !this.$el.contains(event.target)) { 17 | this[config.method](); 18 | } 19 | }; 20 | 21 | return { clickOutsideHandler }; 22 | }, 23 | 24 | mounted() { 25 | on(document, config.event, this.clickOutsideHandler); 26 | }, 27 | 28 | beforeDestroy() { 29 | off(document, config.event, this.clickOutsideHandler); 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/generators/templates/src/demo-button/README.md: -------------------------------------------------------------------------------- 1 | # DemoButton 按钮 2 | 3 | ### 介绍 4 | 5 | DemoButton 是一个示例按钮组件 6 | 7 | ### 引入 8 | 9 | ```js 10 | import Vue from 'vue'; 11 | import { DemoButton } from '<%= name %>'; 12 | 13 | Vue.use(DemoButton); 14 | ``` 15 | 16 | ## 代码演示 17 | 18 | ### 基础用法 19 | 20 | ```html 21 | 22 | ``` 23 | 24 | ## API 25 | 26 | ### Props 27 | 28 | | 参数 | 说明 | 类型 | 默认值 | 29 | |------|------|------|------| 30 | | type | 按钮类型 | *string* | `primary` | 31 | | color `1.0.0` | 按钮颜色 | *string* | - | 32 | 33 | ### Events 34 | 35 | | 事件名 | 说明 | 回调参数 | 36 | |------|------|------| 37 | | click | 点击时触发 | event: Event | 38 | 39 | ### Slots 40 | 41 | | 名称 | 说明 | 42 | |------|------| 43 | | default | 默认插槽 | 44 | -------------------------------------------------------------------------------- /src/empty/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-empty { 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: center; 8 | box-sizing: border-box; 9 | padding: @empty-padding; 10 | 11 | &__image { 12 | width: @empty-image-size; 13 | height: @empty-image-size; 14 | 15 | img { 16 | width: 100%; 17 | height: 100%; 18 | } 19 | } 20 | 21 | &__description { 22 | margin-top: @empty-description-margin-top; 23 | padding: @empty-description-padding; 24 | color: @empty-description-color; 25 | font-size: @empty-description-font-size; 26 | line-height: @empty-description-line-height; 27 | } 28 | 29 | &__bottom { 30 | margin-top: @empty-bottom-margin-top; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/utils/deep-assign.ts: -------------------------------------------------------------------------------- 1 | import { isDef, isObject } from '.'; 2 | import { ObjectIndex } from './types'; 3 | 4 | const { hasOwnProperty } = Object.prototype; 5 | 6 | function assignKey(to: ObjectIndex, from: ObjectIndex, key: string) { 7 | const val = from[key]; 8 | 9 | if (!isDef(val)) { 10 | return; 11 | } 12 | 13 | if (!hasOwnProperty.call(to, key) || !isObject(val)) { 14 | to[key] = val; 15 | } else { 16 | // eslint-disable-next-line @typescript-eslint/no-use-before-define 17 | to[key] = deepAssign(Object(to[key]), from[key]); 18 | } 19 | } 20 | 21 | export function deepAssign(to: ObjectIndex, from: ObjectIndex): ObjectIndex { 22 | Object.keys(from).forEach((key) => { 23 | assignKey(to, from, key); 24 | }); 25 | 26 | return to; 27 | } 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Before submitting a pull request, please make sure the following is done: 2 | 3 | 1. Read the [contributing guide](https://github.com/youzan/vant/blob/dev/.github/CONTRIBUTING.md). 4 | 2. If you've added code that should be tested, add tests. 5 | 3. If you've changed APIs, update the documentation. 6 | 4. Ensure the test suite passes (`npm test`). 7 | 8 | #### Title Format 9 | 10 | type(ComponentName?):commit message 11 | 12 | Example: 13 | 14 | - docs: fix typo in quickstart 15 | - build: optimize build speed 16 | - fix(Button): incorrect style 17 | - feat(Button): add color prop 18 | 19 | Allowed Types: 20 | 21 | - fix 22 | - feat 23 | - docs 24 | - perf 25 | - test 26 | - types 27 | - build 28 | - chore 29 | - refactor 30 | - breaking change 31 | -------------------------------------------------------------------------------- /src/sku/components/SkuHeaderItem.tsx: -------------------------------------------------------------------------------- 1 | // Utils 2 | import { createNamespace } from '../../utils'; 3 | import { inherit } from '../../utils/functional'; 4 | 5 | // Types 6 | import { CreateElement, RenderContext } from 'vue/types'; 7 | import { DefaultSlots } from '../../utils/types'; 8 | 9 | export type SkuHeaderItemProps = {}; 10 | 11 | const [createComponent, bem] = createNamespace('sku-header-item'); 12 | 13 | function SkuHeader( 14 | h: CreateElement, 15 | props: SkuHeaderItemProps, 16 | slots: DefaultSlots, 17 | ctx: RenderContext 18 | ) { 19 | return ( 20 |
    21 | {slots.default && slots.default()} 22 |
    23 | ); 24 | } 25 | 26 | export default createComponent(SkuHeader); 27 | -------------------------------------------------------------------------------- /src/info/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-info { 4 | position: absolute; 5 | top: 0; 6 | right: 0; 7 | box-sizing: border-box; 8 | min-width: @info-size; 9 | padding: @info-padding; 10 | color: @info-color; 11 | font-weight: @info-font-weight; 12 | font-size: @info-font-size; 13 | font-family: @info-font-family; 14 | line-height: 1.2; 15 | text-align: center; 16 | background-color: @info-background-color; 17 | border: @info-border-width solid @white; 18 | border-radius: @info-size; 19 | transform: translate(50%, -50%); 20 | transform-origin: 100%; 21 | 22 | &--dot { 23 | width: @info-dot-size; 24 | min-width: 0; 25 | height: @info-dot-size; 26 | background-color: @info-dot-color; 27 | border-radius: 100%; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/vant-markdown-vetur/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vant/markdown-vetur", 3 | "version": "2.0.1", 4 | "description": "simple parse markdown to vue component description for vetur auto-completion", 5 | "main": "lib/index.js", 6 | "license": "MIT", 7 | "repository": "https://github.com/youzan/vant/tree/dev/packages/vant-markdown-vetur", 8 | "author": "zhangshuai", 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "files": [ 13 | "lib" 14 | ], 15 | "scripts": { 16 | "dev": "tsc --watch", 17 | "build": "tsc", 18 | "release": "npm run build && npm publish" 19 | }, 20 | "dependencies": { 21 | "fast-glob": "^3.2.2", 22 | "fs-extra": "^9.0.0" 23 | }, 24 | "devDependencies": { 25 | "@types/fs-extra": "^8.1.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/radio-group/index.js: -------------------------------------------------------------------------------- 1 | import { createNamespace } from '../utils'; 2 | import { FieldMixin } from '../mixins/field'; 3 | import { ParentMixin } from '../mixins/relation'; 4 | 5 | const [createComponent, bem] = createNamespace('radio-group'); 6 | 7 | export default createComponent({ 8 | mixins: [ParentMixin('vanRadio'), FieldMixin], 9 | 10 | props: { 11 | value: null, 12 | disabled: Boolean, 13 | direction: String, 14 | checkedColor: String, 15 | iconSize: [Number, String], 16 | }, 17 | 18 | watch: { 19 | value(value) { 20 | this.$emit('change', value); 21 | }, 22 | }, 23 | 24 | render() { 25 | return ( 26 |
    27 | {this.slots()} 28 |
    29 | ); 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /packages/vant-eslint-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vant/eslint-config", 3 | "version": "2.2.3", 4 | "description": "eslint config of vant", 5 | "main": "index.js", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "license": "MIT", 10 | "repository": "https://github.com/youzan/vant/tree/dev/packages/vant-eslint-config", 11 | "peerDependencies": { 12 | "eslint": "^6.0.0" 13 | }, 14 | "dependencies": { 15 | "@typescript-eslint/eslint-plugin": "^2.28.0", 16 | "@typescript-eslint/parser": "^2.28.0", 17 | "eslint-config-airbnb-base": "^14.1.0", 18 | "eslint-config-prettier": "^6.10.1", 19 | "eslint-plugin-import": "^2.20.2", 20 | "eslint-plugin-vue": "^6.2.2" 21 | }, 22 | "devDependencies": { 23 | "eslint": "^6.8.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/picker/shared.ts: -------------------------------------------------------------------------------- 1 | export type SharedPickerProps = { 2 | title?: string; 3 | loading?: boolean; 4 | itemHeight?: number; 5 | showToolbar?: boolean; 6 | visibleItemCount: number | string; 7 | cancelButtonText?: string; 8 | confirmButtonText?: string; 9 | }; 10 | 11 | export const DEFAULT_ITEM_HEIGHT = 44; 12 | 13 | export const pickerProps = { 14 | title: String, 15 | loading: Boolean, 16 | itemHeight: [Number, String], 17 | showToolbar: Boolean, 18 | cancelButtonText: String, 19 | confirmButtonText: String, 20 | allowHtml: { 21 | type: Boolean, 22 | default: true, 23 | }, 24 | visibleItemCount: { 25 | type: [Number, String], 26 | default: 6, 27 | }, 28 | swipeDuration: { 29 | type: [Number, String], 30 | default: 1000, 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /.github/workflows/release-tag.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 5 | 6 | name: Create Release 7 | 8 | jobs: 9 | build: 10 | name: Create Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@master 15 | - name: Create Release for Tag 16 | id: release_tag 17 | uses: yyx990803/release-tag@master 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | tag_name: ${{ github.ref }} 22 | body: | 23 | 更新内容参见 [CHANGELOG](https://youzan.github.io/vant/#/zh-CN/changelog)。 24 | 25 | Please refer to [CHANGELOG](https://youzan.github.io/vant/#/en-US/changelog) for details. 26 | -------------------------------------------------------------------------------- /src/share-sheet/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`cancel-text prop 1`] = ``; 4 | 5 | exports[`description prop 1`] = `foo`; 6 | 7 | exports[`title & description slot 1`] = ` 8 |
    9 |
    10 |

    Custom Title

    Custom Description 11 |
    12 |
    13 |
    14 | `; 15 | -------------------------------------------------------------------------------- /src/datetime-picker/utils.ts: -------------------------------------------------------------------------------- 1 | import { isNaN } from '../utils/validate/number'; 2 | 3 | export function times(n: number, iteratee: (index: number) => any[]) { 4 | let index = -1; 5 | const result = Array(n); 6 | 7 | while (++index < n) { 8 | result[index] = iteratee(index); 9 | } 10 | 11 | return result; 12 | } 13 | 14 | export function getTrueValue(value: string | undefined): number { 15 | if (!value) { 16 | return 0; 17 | } 18 | 19 | while (isNaN(parseInt(value, 10))) { 20 | if (value.length > 1) { 21 | value = value.slice(1); 22 | } else { 23 | return 0; 24 | } 25 | } 26 | 27 | return parseInt(value, 10); 28 | } 29 | 30 | export function getMonthEndDay(year: number, month: number): number { 31 | return 32 - new Date(year, month - 1, 32).getDate(); 32 | } 33 | -------------------------------------------------------------------------------- /packages/vant-waterfall/src/event.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-mutable-exports 2 | export let supportsPassive = false; 3 | 4 | try { 5 | const opts = {}; 6 | Object.defineProperty(opts, 'passive', { 7 | // eslint-disable-next-line getter-return 8 | get() { 9 | /* istanbul ignore next */ 10 | supportsPassive = true; 11 | }, 12 | }); 13 | window.addEventListener('test-passive', null, opts); 14 | // eslint-disable-next-line no-empty 15 | } catch (e) {} 16 | 17 | export function on(target, event, handler, passive = false) { 18 | target.addEventListener( 19 | event, 20 | handler, 21 | supportsPassive ? { capture: false, passive } : false 22 | ); 23 | } 24 | 25 | export function off(target, event, handler) { 26 | target.removeEventListener(event, handler); 27 | } 28 | -------------------------------------------------------------------------------- /src/field/demo/ShowWordLimit.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 38 | -------------------------------------------------------------------------------- /src/notify/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-notify { 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | box-sizing: border-box; 8 | padding: @notify-padding; 9 | color: @notify-text-color; 10 | font-size: @notify-font-size; 11 | line-height: @notify-line-height; 12 | 13 | // allow newline charactor 14 | white-space: pre-wrap; 15 | text-align: center; 16 | word-wrap: break-word; 17 | 18 | &--primary { 19 | background-color: @notify-primary-background-color; 20 | } 21 | 22 | &--success { 23 | background-color: @notify-success-background-color; 24 | } 25 | 26 | &--danger { 27 | background-color: @notify-danger-background-color; 28 | } 29 | 30 | &--warning { 31 | background-color: @notify-warning-background-color; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/radio/index.js: -------------------------------------------------------------------------------- 1 | import { createNamespace } from '../utils'; 2 | import { CheckboxMixin } from '../mixins/checkbox'; 3 | 4 | const [createComponent, bem] = createNamespace('radio'); 5 | 6 | export default createComponent({ 7 | mixins: [ 8 | CheckboxMixin({ 9 | bem, 10 | role: 'radio', 11 | parent: 'vanRadio', 12 | }), 13 | ], 14 | 15 | computed: { 16 | currentValue: { 17 | get() { 18 | return this.parent ? this.parent.value : this.value; 19 | }, 20 | 21 | set(val) { 22 | (this.parent || this).$emit('input', val); 23 | }, 24 | }, 25 | 26 | checked() { 27 | return this.currentValue === this.name; 28 | }, 29 | }, 30 | 31 | methods: { 32 | toggle() { 33 | this.currentValue = this.name; 34 | }, 35 | }, 36 | }); 37 | -------------------------------------------------------------------------------- /src/tabbar-item/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-tabbar-item { 4 | display: flex; 5 | flex: 1; 6 | flex-direction: column; 7 | align-items: center; 8 | justify-content: center; 9 | color: @tabbar-item-text-color; 10 | font-size: @tabbar-item-font-size; 11 | line-height: @tabbar-item-line-height; 12 | cursor: pointer; 13 | 14 | &__icon { 15 | position: relative; 16 | margin-bottom: @tabbar-item-margin-bottom; 17 | font-size: @tabbar-item-icon-size; 18 | 19 | .van-icon { 20 | display: block; 21 | min-width: 1em; 22 | } 23 | 24 | img { 25 | display: block; 26 | height: 20px; 27 | } 28 | } 29 | 30 | &--active { 31 | color: @tabbar-item-active-color; 32 | } 33 | 34 | .van-info { 35 | margin-top: @padding-base; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/datetime-picker/index.js: -------------------------------------------------------------------------------- 1 | import { createNamespace } from '../utils'; 2 | import TimePicker from './TimePicker'; 3 | import DatePicker from './DatePicker'; 4 | 5 | const [createComponent, bem] = createNamespace('datetime-picker'); 6 | 7 | export default createComponent({ 8 | props: { 9 | ...TimePicker.props, 10 | ...DatePicker.props, 11 | }, 12 | 13 | methods: { 14 | // @exposed-api 15 | getPicker() { 16 | return this.$refs.root.getPicker(); 17 | }, 18 | }, 19 | 20 | render() { 21 | const Component = this.type === 'time' ? TimePicker : DatePicker; 22 | 23 | return ( 24 | 32 | ); 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /packages/vant-cli/src/compiler/compile-js.ts: -------------------------------------------------------------------------------- 1 | import { transformAsync } from '@babel/core'; 2 | import { readFileSync, removeSync, outputFileSync } from 'fs-extra'; 3 | import { replaceExt } from '../common'; 4 | import { replaceCssImport } from '../common/css'; 5 | 6 | export function compileJs(filePath: string): Promise { 7 | return new Promise((resolve, reject) => { 8 | let code = readFileSync(filePath, 'utf-8'); 9 | 10 | code = replaceCssImport(code); 11 | 12 | transformAsync(code, { filename: filePath }) 13 | .then(result => { 14 | if (result) { 15 | const jsFilePath = replaceExt(filePath, '.js'); 16 | 17 | removeSync(filePath); 18 | outputFileSync(jsFilePath, result.code); 19 | resolve(); 20 | } 21 | }) 22 | .catch(reject); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /src/steps/index.js: -------------------------------------------------------------------------------- 1 | import { createNamespace } from '../utils'; 2 | import { ParentMixin } from '../mixins/relation'; 3 | 4 | const [createComponent, bem] = createNamespace('steps'); 5 | 6 | export default createComponent({ 7 | mixins: [ParentMixin('vanSteps')], 8 | 9 | props: { 10 | activeColor: String, 11 | inactiveIcon: String, 12 | inactiveColor: String, 13 | active: { 14 | type: [Number, String], 15 | default: 0, 16 | }, 17 | direction: { 18 | type: String, 19 | default: 'horizontal', 20 | }, 21 | activeIcon: { 22 | type: String, 23 | default: 'checked', 24 | }, 25 | }, 26 | 27 | render() { 28 | return ( 29 |
    30 |
    {this.slots()}
    31 |
    32 | ); 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /src/switch-cell/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`border prop 1`] = ` 4 |
    5 |
    6 |
    7 |
    8 |
    9 |
    10 |
    11 | `; 12 | 13 | exports[`cell-size prop 1`] = ` 14 |
    15 |
    16 |
    17 |
    18 |
    19 |
    20 |
    21 | `; 22 | -------------------------------------------------------------------------------- /src/switch-cell/demo/index.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 32 | -------------------------------------------------------------------------------- /packages/vant-cli/src/compiler/compile-less.ts: -------------------------------------------------------------------------------- 1 | import { render, FileManager } from 'less'; 2 | import { readFileSync } from 'fs-extra'; 3 | 4 | // less plugin to resolve tilde 5 | class TildeResolver extends FileManager { 6 | loadFile(filename: string, ...args: any[]) { 7 | filename = filename.replace('~', ''); 8 | return FileManager.prototype.loadFile.apply(this, [filename, ...args]); 9 | } 10 | } 11 | 12 | const TildeResolverPlugin = { 13 | install(lessInstance: unknown, pluginManager: any) { 14 | pluginManager.addFileManager(new TildeResolver()); 15 | }, 16 | }; 17 | 18 | export async function compileLess(filePath: string) { 19 | const source = readFileSync(filePath, 'utf-8'); 20 | const { css } = await render(source, { 21 | filename: filePath, 22 | plugins: [TildeResolverPlugin], 23 | }); 24 | 25 | return css; 26 | } 27 | -------------------------------------------------------------------------------- /src/goods-action-button/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-goods-action-button { 4 | flex: 1; 5 | height: @goods-action-button-height; 6 | font-weight: @font-weight-bold; 7 | font-size: @font-size-md; 8 | border: none; 9 | border-radius: 0; 10 | 11 | &--first { 12 | margin-left: 5px; 13 | border-top-left-radius: @border-radius-max; 14 | border-bottom-left-radius: @border-radius-max; 15 | } 16 | 17 | &--last { 18 | margin-right: 5px; 19 | border-top-right-radius: @border-radius-max; 20 | border-bottom-right-radius: @border-radius-max; 21 | } 22 | 23 | &--warning { 24 | background: @goods-action-button-warning-color; 25 | } 26 | 27 | &--danger { 28 | background: @goods-action-button-danger-color; 29 | } 30 | 31 | @media (max-width: 321px) { 32 | font-size: 13px; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/switch-cell/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import SwitchCell from '..'; 2 | import { mount } from '../../../test'; 3 | 4 | test('change event', () => { 5 | const onChange = jest.fn(); 6 | const wrapper = mount(SwitchCell, { 7 | context: { 8 | on: { 9 | change: onChange, 10 | }, 11 | }, 12 | }); 13 | 14 | wrapper.find('.van-switch').trigger('click'); 15 | 16 | expect(onChange).toHaveBeenCalledWith(true); 17 | }); 18 | 19 | test('border prop', () => { 20 | const wrapper = mount(SwitchCell, { 21 | propsData: { 22 | border: false, 23 | }, 24 | }); 25 | 26 | expect(wrapper).toMatchSnapshot(); 27 | }); 28 | 29 | test('cell-size prop', () => { 30 | const wrapper = mount(SwitchCell, { 31 | propsData: { 32 | cellSize: 'large', 33 | }, 34 | }); 35 | 36 | expect(wrapper).toMatchSnapshot(); 37 | }); 38 | -------------------------------------------------------------------------------- /src/sku/utils/time-helper.js: -------------------------------------------------------------------------------- 1 | import { padZero } from '../../utils/format/string'; 2 | 3 | // 字符串转 Date 4 | // 只处理 YYYY-MM-DD 或者 YYYY-MM-DD HH:MM 格式 5 | export function stringToDate(timeString) { 6 | if (!timeString) { 7 | return null; 8 | } 9 | return new Date(timeString.replace(/-/g, '/')); 10 | } 11 | 12 | // Date 转字符串 13 | // type: date or datetime 14 | export function dateToString(date, type = 'date') { 15 | if (!date) { 16 | return ''; 17 | } 18 | const year = date.getFullYear(); 19 | const month = date.getMonth() + 1; 20 | const day = date.getDate(); 21 | let timeString = `${year}-${padZero(month)}-${padZero(day)}`; 22 | if (type === 'datetime') { 23 | const hours = date.getHours(); 24 | const minute = date.getMinutes(); 25 | timeString += ` ${padZero(hours)}:${padZero(minute)}`; 26 | } 27 | return timeString; 28 | } 29 | -------------------------------------------------------------------------------- /packages/vant-cli/site/common/iframe-router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 同步父窗口和 iframe 的 vue-router 状态 3 | */ 4 | 5 | import { iframeReady, isMobile } from '.'; 6 | 7 | window.syncPath = function() { 8 | const router = window.vueRouter; 9 | const isInIframe = window !== window.top; 10 | const currentDir = router.history.current.path; 11 | 12 | if (isInIframe) { 13 | window.top.replacePath(currentDir); 14 | } else if (!isMobile) { 15 | const iframe = document.querySelector('iframe'); 16 | if (iframe) { 17 | iframeReady(iframe, () => { 18 | iframe.contentWindow.replacePath(currentDir); 19 | }); 20 | } 21 | } 22 | }; 23 | 24 | window.replacePath = function(path = '') { 25 | // should preserve hash for anchor 26 | if (window.vueRouter.currentRoute.path !== path) { 27 | window.vueRouter.replace(path).catch(() => {}); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /packages/vant-cli/src/common/manager.ts: -------------------------------------------------------------------------------- 1 | import execa from 'execa'; 2 | import { consola } from './logger'; 3 | import { execSync } from 'child_process'; 4 | 5 | let hasYarnCache: boolean; 6 | 7 | export function hasYarn() { 8 | if (hasYarnCache === undefined) { 9 | try { 10 | execSync('yarn --version', { stdio: 'ignore' }); 11 | hasYarnCache = true; 12 | } catch (e) { 13 | hasYarnCache = false; 14 | } 15 | } 16 | 17 | return hasYarnCache; 18 | } 19 | 20 | export async function installDependencies() { 21 | consola.info('Install Dependencies\n'); 22 | 23 | try { 24 | const manager = hasYarn() ? 'yarn' : 'npm'; 25 | 26 | await execa(manager, ['install', '--prod=false'], { 27 | stdio: 'inherit', 28 | }); 29 | 30 | console.log(''); 31 | } catch (err) { 32 | console.log(err); 33 | throw err; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/overlay/test/__snapshots__/demo.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders demo correctly 1`] = ` 4 |
    5 |
    8 | 9 |
    10 |
    13 | 18 |
    19 |
    20 | `; 21 | -------------------------------------------------------------------------------- /src/style/mixins/hairline.less: -------------------------------------------------------------------------------- 1 | @import '../var'; 2 | 3 | .hairline-common() { 4 | position: absolute; 5 | box-sizing: border-box; 6 | content: ' '; 7 | pointer-events: none; 8 | } 9 | 10 | .hairline(@color: @border-color) { 11 | .hairline-common(); 12 | 13 | top: -50%; 14 | right: -50%; 15 | bottom: -50%; 16 | left: -50%; 17 | border: 0 solid @color; 18 | transform: scale(0.5); 19 | } 20 | 21 | .hairline-top(@color: @border-color, @left: 0, @right: 0) { 22 | .hairline-common(); 23 | 24 | top: 0; 25 | right: @right; 26 | left: @left; 27 | border-top: 1px solid @color; 28 | transform: scaleY(0.5); 29 | } 30 | 31 | .hairline-bottom(@color: @border-color, @left: 0, @right: 0) { 32 | .hairline-common(); 33 | 34 | right: @right; 35 | bottom: 0; 36 | left: @left; 37 | border-bottom: 1px solid @color; 38 | transform: scaleY(0.5); 39 | } 40 | -------------------------------------------------------------------------------- /packages/vant-cli/site/common/index.js: -------------------------------------------------------------------------------- 1 | function iframeReady(iframe, callback) { 2 | const doc = iframe.contentDocument || iframe.contentWindow.document; 3 | const interval = () => { 4 | if (iframe.contentWindow.replacePath) { 5 | callback(); 6 | } else { 7 | setTimeout(() => { 8 | interval(); 9 | }, 50); 10 | } 11 | }; 12 | 13 | if (doc.readyState === 'complete') { 14 | interval(); 15 | } else { 16 | iframe.onload = interval; 17 | } 18 | } 19 | 20 | const ua = navigator.userAgent.toLowerCase(); 21 | const isMobile = /ios|iphone|ipod|ipad|android/.test(ua); 22 | 23 | export function decamelize(str, sep = '-') { 24 | return str 25 | .replace(/([a-z\d])([A-Z])/g, '$1' + sep + '$2') 26 | .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + sep + '$2') 27 | .toLowerCase(); 28 | } 29 | 30 | export { isMobile, iframeReady }; 31 | -------------------------------------------------------------------------------- /packages/vant-markdown-vetur/src/vetur.ts: -------------------------------------------------------------------------------- 1 | import { VueTag, VeturTags, VeturAttributes } from './type'; 2 | 3 | export function genVeturTags(tags: VueTag[]) { 4 | const veturTags: VeturTags = {}; 5 | 6 | tags.forEach(tag => { 7 | veturTags[tag.name] = { 8 | attributes: tag.attributes ? tag.attributes.map(item => item.name) : [], 9 | }; 10 | }); 11 | 12 | return veturTags; 13 | } 14 | 15 | export function genVeturAttributes(tags: VueTag[]) { 16 | const veturAttributes: VeturAttributes = {}; 17 | 18 | tags.forEach(tag => { 19 | if (tag.attributes) { 20 | tag.attributes.forEach(attr => { 21 | veturAttributes[`${tag.name}/${attr.name}`] = { 22 | type: attr.value.type, 23 | description: `${attr.description}, 默认值: ${attr.default}`, 24 | }; 25 | }); 26 | } 27 | }); 28 | 29 | return veturAttributes; 30 | } 31 | -------------------------------------------------------------------------------- /src/locale/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { deepAssign } from '../utils/deep-assign'; 3 | import defaultMessages from './lang/zh-CN'; 4 | 5 | declare module 'vue' { 6 | interface VueConstructor { 7 | util: { 8 | defineReactive(obj: object, key: string, value: any): void; 9 | }; 10 | } 11 | } 12 | 13 | const proto = Vue.prototype; 14 | const { defineReactive } = Vue.util; 15 | 16 | defineReactive(proto, '$vantLang', 'zh-CN'); 17 | defineReactive(proto, '$vantMessages', { 18 | 'zh-CN': defaultMessages, 19 | }); 20 | 21 | export default { 22 | messages() { 23 | return proto.$vantMessages[proto.$vantLang]; 24 | }, 25 | 26 | use(lang: string, messages?: object) { 27 | proto.$vantLang = lang; 28 | this.add({ [lang]: messages }); 29 | }, 30 | 31 | add(messages = {}) { 32 | deepAssign(proto.$vantMessages, messages); 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /src/rate/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-rate { 4 | display: inline-flex; 5 | cursor: pointer; 6 | user-select: none; 7 | 8 | &__item { 9 | position: relative; 10 | 11 | &:not(:last-child) { 12 | padding-right: @rate-icon-gutter; 13 | } 14 | } 15 | 16 | &__icon { 17 | display: block; 18 | width: 1em; 19 | color: @rate-icon-void-color; 20 | font-size: @rate-icon-size; 21 | 22 | &--half { 23 | position: absolute; 24 | top: 0; 25 | left: 0; 26 | width: 0.5em; 27 | overflow: hidden; 28 | } 29 | 30 | &--full { 31 | color: @rate-icon-full-color; 32 | } 33 | 34 | &--disabled { 35 | color: @rate-icon-disabled-color; 36 | } 37 | } 38 | 39 | &--disabled { 40 | cursor: not-allowed; 41 | } 42 | 43 | &--readonly { 44 | cursor: default; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/popup/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`close-icon prop 1`] = ` 4 |
    5 |
    6 | `; 7 | 8 | exports[`duration prop when position is center 1`] = `
    `; 9 | 10 | exports[`duration prop when position is top 1`] = `
    `; 11 | 12 | exports[`reset z-index 1`] = `
    `; 13 | 14 | exports[`round prop 1`] = `
    `; 15 | -------------------------------------------------------------------------------- /src/progress/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-progress { 4 | position: relative; 5 | height: @progress-height; 6 | background: @progress-background-color; 7 | border-radius: @progress-height; 8 | 9 | &__portion { 10 | position: absolute; 11 | left: 0; 12 | height: 100%; 13 | background: @progress-color; 14 | border-radius: inherit; 15 | } 16 | 17 | &__pivot { 18 | position: absolute; 19 | top: 50%; 20 | box-sizing: border-box; 21 | min-width: 3.6em; 22 | padding: @progress-pivot-padding; 23 | color: @progress-pivot-text-color; 24 | font-size: @progress-pivot-font-size; 25 | line-height: @progress-pivot-line-height; 26 | text-align: center; 27 | word-break: keep-all; 28 | background-color: @progress-pivot-background-color; 29 | border-radius: 1em; 30 | transform: translate(0, -50%); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/utils/format/number.ts: -------------------------------------------------------------------------------- 1 | export function range(num: number, min: number, max: number): number { 2 | return Math.min(Math.max(num, min), max); 3 | } 4 | 5 | function trimExtraChar(value: string, char: string, regExp: RegExp) { 6 | const index = value.indexOf(char); 7 | 8 | if (index === -1) { 9 | return value; 10 | } 11 | 12 | if (char === '-' && index !== 0) { 13 | return value.slice(0, index); 14 | } 15 | 16 | return value.slice(0, index + 1) + value.slice(index).replace(regExp, ''); 17 | } 18 | 19 | export function formatNumber(value: string, allowDot?: boolean) { 20 | if (allowDot) { 21 | value = trimExtraChar(value, '.', /\./g); 22 | } else { 23 | value = value.split('.')[0]; 24 | } 25 | 26 | value = trimExtraChar(value, '-', /-/g); 27 | 28 | const regExp = allowDot ? /[^-0-9.]/g : /[^-0-9]/g; 29 | 30 | return value.replace(regExp, ''); 31 | } 32 | -------------------------------------------------------------------------------- /packages/create-vant-cli-app/generators/templates/vant.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: '<%= name %>', 3 | build: { 4 | css: { 5 | preprocessor: '<%= preprocessor %>', 6 | }, 7 | site: { 8 | publicPath: '/<%= name %>/', 9 | }, 10 | }, 11 | site: { 12 | title: '<%= name %>', 13 | logo: 'https://img.yzcdn.cn/vant/logo.png', 14 | nav: [ 15 | { 16 | title: '开发指南', 17 | items: [ 18 | { 19 | path: 'home', 20 | title: '介绍', 21 | }, 22 | { 23 | path: 'quickstart', 24 | title: '快速上手', 25 | }, 26 | ], 27 | }, 28 | { 29 | title: '基础组件', 30 | items: [ 31 | { 32 | path: 'demo-button', 33 | title: 'DemoButton 按钮', 34 | }, 35 | ], 36 | }, 37 | ], 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /src/style/hairline.less: -------------------------------------------------------------------------------- 1 | @import './var'; 2 | @import './mixins/hairline'; 3 | 4 | [class*='van-hairline'] { 5 | &::after { 6 | .hairline(); 7 | } 8 | } 9 | 10 | .van-hairline { 11 | &, 12 | &--top, 13 | &--left, 14 | &--right, 15 | &--bottom, 16 | &--surround, 17 | &--top-bottom { 18 | position: relative; 19 | } 20 | 21 | &--top::after { 22 | border-top-width: @border-width-base; 23 | } 24 | 25 | &--left::after { 26 | border-left-width: @border-width-base; 27 | } 28 | 29 | &--right::after { 30 | border-right-width: @border-width-base; 31 | } 32 | 33 | &--bottom::after { 34 | border-bottom-width: @border-width-base; 35 | } 36 | 37 | &, 38 | &-unset { 39 | &--top-bottom::after { 40 | border-width: @border-width-base 0; 41 | } 42 | } 43 | 44 | &--surround::after { 45 | border-width: @border-width-base; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/vant-cli/src/commands/jest.ts: -------------------------------------------------------------------------------- 1 | import { runCLI } from 'jest'; 2 | import { setNodeEnv } from '../common'; 3 | import { genPackageEntry } from '../compiler/gen-package-entry'; 4 | import { ROOT, JEST_CONFIG_FILE, PACKAGE_ENTRY_FILE } from '../common/constant'; 5 | 6 | export function test(command: any) { 7 | setNodeEnv('test'); 8 | 9 | genPackageEntry({ 10 | outputPath: PACKAGE_ENTRY_FILE, 11 | }); 12 | 13 | const config = { 14 | rootDir: ROOT, 15 | watch: command.watch, 16 | config: JEST_CONFIG_FILE, 17 | clearCache: command.clearCache, 18 | } as any; 19 | 20 | runCLI(config, [ROOT]) 21 | .then(response => { 22 | if (!response.results.success && !command.watch) { 23 | process.exit(1); 24 | } 25 | }) 26 | .catch(err => { 27 | console.log(err); 28 | 29 | if (!command.watch) { 30 | process.exit(1); 31 | } 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /test/demo.ts: -------------------------------------------------------------------------------- 1 | import Vue, { CreateElement } from 'vue'; 2 | import '../docs/site/mobile'; 3 | import Locale from '../src/locale'; 4 | import { mount, later } from '.'; 5 | 6 | const Empty = { 7 | render(h: CreateElement): ReturnType { 8 | return h('div', [(this as any).$slots.default]); 9 | }, 10 | inheritAttrs: false, 11 | }; 12 | 13 | Vue.component('demo-block', Empty); 14 | Vue.component('demo-section', Empty); 15 | 16 | export function snapshotDemo(Demo: any, option: any = {}) { 17 | test('renders demo correctly', async () => { 18 | if (option.beforeTest) { 19 | option.beforeTest(); 20 | } 21 | 22 | if (Demo.i18n) { 23 | Locale.add(Demo.i18n); 24 | } 25 | 26 | const wrapper = mount(Demo); 27 | 28 | await later(); 29 | 30 | expect(wrapper).toMatchSnapshot(); 31 | 32 | if (option.afterTest) { 33 | option.afterTest(); 34 | } 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /src/field/demo/ShowIcon.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 43 | -------------------------------------------------------------------------------- /src/form/test/shared.js: -------------------------------------------------------------------------------- 1 | import { mount, later } from '../../../test'; 2 | 3 | export async function submitForm(wrapper) { 4 | wrapper.find('.van-button').trigger('click'); 5 | return later(); 6 | } 7 | 8 | export function mountForm(options) { 9 | return mount(options, { attachToDocument: true }); 10 | } 11 | 12 | export function getSimpleRules() { 13 | return { 14 | rulesA: [{ required: true, message: 'A failed' }], 15 | rulesB: [{ required: true, message: 'B failed' }], 16 | }; 17 | } 18 | 19 | export function mountSimpleRulesForm(options) { 20 | return mountForm({ 21 | template: ` 22 | 23 | 24 | 25 | 26 | 27 | `, 28 | data: getSimpleRules, 29 | ...options, 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /src/address-edit/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-address-edit { 4 | padding: @address-edit-padding; 5 | 6 | &__fields { 7 | overflow: hidden; 8 | border-radius: @padding-xs; 9 | 10 | .van-field__label { 11 | width: 4.1em; 12 | } 13 | } 14 | 15 | &__default { 16 | margin-top: @padding-sm; 17 | overflow: hidden; 18 | border-radius: @padding-xs; 19 | } 20 | 21 | &__buttons { 22 | padding: @address-edit-buttons-padding; 23 | 24 | .van-button { 25 | margin-bottom: @address-edit-button-margin-bottom; 26 | } 27 | } 28 | 29 | &-detail { 30 | padding: 0; 31 | 32 | &__search-item { 33 | background-color: @gray-2; 34 | } 35 | 36 | &__keyword { 37 | color: @red; 38 | } 39 | 40 | &__finish { 41 | color: @address-edit-detail-finish-color; 42 | font-size: @address-edit-detail-finish-font-size; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/sidebar/index.js: -------------------------------------------------------------------------------- 1 | import { createNamespace } from '../utils'; 2 | import { ParentMixin } from '../mixins/relation'; 3 | 4 | const [createComponent, bem] = createNamespace('sidebar'); 5 | 6 | export default createComponent({ 7 | mixins: [ParentMixin('vanSidebar')], 8 | 9 | model: { 10 | prop: 'activeKey', 11 | }, 12 | 13 | props: { 14 | activeKey: { 15 | type: [Number, String], 16 | default: 0, 17 | }, 18 | }, 19 | 20 | data() { 21 | return { 22 | index: +this.activeKey, 23 | }; 24 | }, 25 | 26 | watch: { 27 | activeKey() { 28 | this.setIndex(+this.activeKey); 29 | }, 30 | }, 31 | 32 | methods: { 33 | setIndex(index) { 34 | if (index !== this.index) { 35 | this.index = index; 36 | this.$emit('change', index); 37 | } 38 | }, 39 | }, 40 | 41 | render() { 42 | return
    {this.slots()}
    ; 43 | }, 44 | }); 45 | -------------------------------------------------------------------------------- /src/field/demo/InsertButton.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 43 | -------------------------------------------------------------------------------- /packages/vant-cli/site/common/style/base.less: -------------------------------------------------------------------------------- 1 | @import './var'; 2 | 3 | body { 4 | min-width: 1100px; 5 | margin: 0; 6 | overflow-x: auto; 7 | color: @van-doc-black; 8 | font-size: 16px; 9 | font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, 10 | Segoe UI, Arial, Roboto, 'PingFang SC', 'Hiragino Sans GB', 11 | 'Microsoft Yahei', sans-serif; 12 | background-color: @van-doc-background-color; 13 | -webkit-font-smoothing: antialiased; 14 | } 15 | 16 | p { 17 | margin: 0; 18 | } 19 | 20 | h1, 21 | h2, 22 | h3, 23 | h4, 24 | h5, 25 | h6 { 26 | margin: 0; 27 | font-size: inherit; 28 | } 29 | 30 | ul, 31 | ol { 32 | margin: 0; 33 | padding: 0; 34 | list-style: none; 35 | } 36 | 37 | a { 38 | text-decoration: none; 39 | } 40 | 41 | .van-doc-row { 42 | width: 100%; 43 | 44 | @media (min-width: @van-doc-row-max-width) { 45 | width: @van-doc-row-max-width; 46 | margin: 0 auto; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/contact-card/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-contact-card { 4 | padding: @contact-card-padding; 5 | 6 | &__value { 7 | margin-left: 5px; 8 | line-height: @contact-card-value-line-height; 9 | } 10 | 11 | &--add { 12 | .van-contact-card__value { 13 | line-height: @contact-card-add-icon-size; 14 | } 15 | 16 | .van-cell__left-icon { 17 | color: @contact-card-add-icon-color; 18 | font-size: @contact-card-add-icon-size; 19 | } 20 | } 21 | 22 | &::before { 23 | position: absolute; 24 | right: 0; 25 | bottom: 0; 26 | left: 0; 27 | height: 2px; 28 | background: repeating-linear-gradient( 29 | -45deg, 30 | #ff6c6c 0, 31 | #ff6c6c 20%, 32 | transparent 0, 33 | transparent 25%, 34 | @blue 0, 35 | @blue 45%, 36 | transparent 0, 37 | transparent 50% 38 | ); 39 | background-size: 80px; 40 | content: ''; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/form/test/__snapshots__/events.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`failed event 1`] = ` 4 |
    5 |
    6 |
    7 |
    8 |
    A failed
    9 |
    10 |
    11 |
    12 |
    13 |
    14 |
    B failed
    15 |
    16 |
    19 |
    20 | `; 21 | -------------------------------------------------------------------------------- /src/tree-select/demo/data-zh.ts: -------------------------------------------------------------------------------- 1 | const zhejiang = [ 2 | { 3 | text: '杭州', 4 | id: 1, 5 | }, 6 | { 7 | text: '温州', 8 | id: 2, 9 | }, 10 | { 11 | text: '宁波', 12 | id: 3, 13 | disabled: true, 14 | }, 15 | { 16 | text: '义乌', 17 | id: 4, 18 | }, 19 | ]; 20 | 21 | const jiangsu = [ 22 | { 23 | text: '南京', 24 | id: 5, 25 | }, 26 | { 27 | text: '无锡', 28 | id: 6, 29 | }, 30 | { 31 | text: '徐州', 32 | id: 7, 33 | }, 34 | { 35 | text: '苏州', 36 | id: 8, 37 | }, 38 | ]; 39 | 40 | const fujian = [ 41 | { 42 | text: '泉州', 43 | id: 9, 44 | }, 45 | { 46 | text: '厦门', 47 | id: 10, 48 | }, 49 | ]; 50 | 51 | export const zhCNData = [ 52 | { 53 | text: '浙江', 54 | children: zhejiang, 55 | }, 56 | { 57 | text: '江苏', 58 | children: jiangsu, 59 | }, 60 | { 61 | text: '福建', 62 | disabled: true, 63 | children: fujian, 64 | }, 65 | ]; 66 | -------------------------------------------------------------------------------- /src/utils/vnodes.ts: -------------------------------------------------------------------------------- 1 | import { VNode } from 'vue'; 2 | 3 | function flattenVNodes(vnodes: VNode[]) { 4 | const result: VNode[] = []; 5 | 6 | function traverse(vnodes: VNode[]) { 7 | vnodes.forEach((vnode) => { 8 | result.push(vnode); 9 | 10 | if (vnode.componentInstance) { 11 | traverse(vnode.componentInstance.$children.map((item) => item.$vnode)); 12 | } 13 | 14 | if (vnode.children) { 15 | traverse(vnode.children); 16 | } 17 | }); 18 | } 19 | 20 | traverse(vnodes); 21 | return result; 22 | } 23 | 24 | // sort children instances by vnodes order 25 | export function sortChildren(children: Vue[], parent: Vue) { 26 | const { componentOptions } = parent.$vnode; 27 | if (!componentOptions || !componentOptions.children) { 28 | return; 29 | } 30 | 31 | const vnodes = flattenVNodes(componentOptions.children); 32 | children.sort((a, b) => vnodes.indexOf(a.$vnode) - vnodes.indexOf(b.$vnode)); 33 | } 34 | -------------------------------------------------------------------------------- /packages/vant-markdown-loader/README.md: -------------------------------------------------------------------------------- 1 | # vant-markdown-loader 2 | 3 | Simple and fast vue markdown loader, transform markdown to vue component. 4 | 5 | ## Install 6 | 7 | ### NPM 8 | 9 | ```shell 10 | npm i @vant/markdown-loader -S 11 | ``` 12 | 13 | ### YARN 14 | 15 | ```shell 16 | yarn add @vant/markdown-loader 17 | ``` 18 | 19 | ## Options 20 | 21 | - `enableMetaData`: Default `false`. Whether to use [front-matter](https://github.com/jxson/front-matter) to extract markdown meta data 22 | 23 | - `linkOpen`: Default `true`. Whether to add target="_blank" to all links 24 | 25 | - `wrapper(html, fm)`: Format the returned content using a custom function 26 | - `html`: The result of [markdown-it](https://github.com/markdown-it/markdown-it)'s render 27 | - `fm`: See [fm(string)](https://github.com/jxson/front-matter#fmstring). If `enableMetaData` option is `false`, the value is `undefined`. 28 | - `attributes` 29 | - `body` 30 | - `frontmatter` 31 | -------------------------------------------------------------------------------- /src/empty/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import Empty from '..'; 2 | import { mount } from '../../../test'; 3 | 4 | test('image slot', () => { 5 | const wrapper = mount(Empty, { 6 | scopedSlots: { 7 | image: () => 'Custom Image', 8 | }, 9 | }); 10 | 11 | expect(wrapper).toMatchSnapshot(); 12 | }); 13 | 14 | test('description slot', () => { 15 | const wrapper = mount(Empty, { 16 | scopedSlots: { 17 | description: () => 'Custom description', 18 | }, 19 | }); 20 | 21 | expect(wrapper).toMatchSnapshot(); 22 | }); 23 | 24 | test('bottom slot', () => { 25 | const wrapper = mount(Empty, { 26 | scopedSlots: { 27 | default: () => 'Custom bottom', 28 | }, 29 | }); 30 | 31 | expect(wrapper).toMatchSnapshot(); 32 | }); 33 | 34 | test('render svg when image is network', () => { 35 | const wrapper = mount(Empty, { 36 | propsData: { 37 | image: 'network', 38 | }, 39 | }); 40 | 41 | expect(wrapper).toMatchSnapshot(); 42 | }); 43 | -------------------------------------------------------------------------------- /.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: Sync to Gitee 2 | 3 | on: 4 | push: 5 | branches: [dev] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Sync to Gitee 12 | uses: wearerequired/git-mirror-action@master 13 | env: 14 | # 注意在 Settings->Secrets 配置 GITEE_RSA_PRIVATE_KEY 15 | SSH_PRIVATE_KEY: ${{ secrets.GITEE_RSA_PRIVATE_KEY }} 16 | with: 17 | # 注意替换为你的 GitHub 源仓库地址 18 | source-repo: 'git@github.com:youzan/vant.git' 19 | # 注意替换为你的 Gitee 目标仓库地址 20 | destination-repo: 'git@gitee.com:vant-contrib/vant.git' 21 | 22 | - name: Build Gitee Pages 23 | uses: yanglbme/gitee-pages-action@master 24 | with: 25 | # 注意替换为你的 Gitee 用户名 26 | gitee-username: chenjiahan 27 | # 注意在 Settings->Secrets 配置 GITEE_PASSWORD 28 | gitee-password: ${{ secrets.GITEE_PASSWORD }} 29 | # 注意替换为你的 Gitee 仓库 30 | gitee-repo: vant-contrib/vant 31 | -------------------------------------------------------------------------------- /packages/vant-icons/build/template.tpl: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-pseudo-element-colon-notation */ 2 | /* stylelint-disable font-family-no-missing-generic-family-keyword */ 3 | @font-face { 4 | font-weight: normal; 5 | font-family: '<%= fontName %>'; 6 | font-style: normal; 7 | font-display: auto; 8 | src: url('https://img.yzcdn.cn/vant/<%= cssClass %>.woff2') format('woff2'), 9 | url('https://img.yzcdn.cn/vant/<%= cssClass %>.woff') format('woff'), 10 | url('https://img.yzcdn.cn/vant/<%= cssClass %>.ttf') format('truetype'); 11 | } 12 | 13 | .van-icon { 14 | position: relative; 15 | display: inline-block; 16 | font: normal normal normal 14px/1 '<%= fontName %>'; 17 | font-size: inherit; 18 | text-rendering: auto; 19 | -webkit-font-smoothing: antialiased; 20 | 21 | &::before { 22 | display: inline-block; 23 | } 24 | } 25 | 26 | <% _.each(glyphs, function(glyph) { %>.van-icon-<%= glyph.fileName %>::before { 27 | content: '\<%= glyph.codePoint %>'; 28 | } 29 | 30 | <% }); %> -------------------------------------------------------------------------------- /packages/vant-cli/site/common/style/var.less: -------------------------------------------------------------------------------- 1 | @van-doc-black: #323233; 2 | @van-doc-blue: #1989fa; 3 | @van-doc-purple: #8080ff; 4 | @van-doc-fuchsia: #a7419e; 5 | @van-doc-green: #4fc08d; 6 | @van-doc-text-color: #34495e; 7 | @van-doc-text-light-blue: rgba(69, 90, 100, 0.6); 8 | @van-doc-background-color: #f7f8fa; 9 | @van-doc-grey: #999; 10 | @van-doc-dark-grey: #666; 11 | @van-doc-light-grey: #ccc; 12 | @van-doc-border-color: #f1f4f8; 13 | @van-doc-code-color: #58727e; 14 | @van-doc-code-background-color: #f1f4f8; 15 | @van-doc-code-font-family: 'Source Code Pro', 'Monaco', 'Inconsolata', monospace; 16 | @van-doc-padding: 30px; 17 | @van-doc-row-max-width: 1680px; 18 | @van-doc-nav-width: 220px; 19 | @van-doc-border-radius: 12px; 20 | 21 | // header 22 | @van-doc-header-top-height: 60px; 23 | @van-doc-header-bottom-height: 50px; 24 | 25 | // simulator 26 | @van-doc-simulator-width: 360px; 27 | @van-doc-simulator-small-width: 320px; 28 | @van-doc-simulator-height: 620px; 29 | @van-doc-simulator-small-height: 560px; 30 | -------------------------------------------------------------------------------- /src/field/demo/ErrorInfo.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 45 | -------------------------------------------------------------------------------- /src/nav-bar/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`placeholder prop 1`] = ` 4 |
    5 |
    6 |
    7 |
    8 |
    9 |
    10 |
    11 | `; 12 | 13 | exports[`render left & right slot 1`] = ` 14 |
    15 |
    Custom Left
    16 |
    17 |
    Custom Right
    18 |
    19 | `; 20 | 21 | exports[`render title slot 1`] = ` 22 |
    23 |
    24 |
    Custom Title
    25 |
    26 |
    27 | `; 28 | -------------------------------------------------------------------------------- /src/notice-bar/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`icon slot 1`] = ` 4 | 9 | `; 10 | 11 | exports[`should not scroll when content width > wrap width 1`] = ` 12 | 17 | `; 18 | 19 | exports[`should scroll when content width > wrap width 1`] = ` 20 | 25 | `; 26 | -------------------------------------------------------------------------------- /src/tree-select/demo/data-en.ts: -------------------------------------------------------------------------------- 1 | const group1 = [ 2 | { 3 | text: 'Delaware', 4 | id: 1, 5 | }, 6 | { 7 | text: 'Florida', 8 | id: 2, 9 | }, 10 | { 11 | text: 'Georqia', 12 | id: 3, 13 | disabled: true, 14 | }, 15 | { 16 | text: 'Indiana', 17 | id: 4, 18 | }, 19 | ]; 20 | 21 | const group2 = [ 22 | { 23 | text: 'Alabama', 24 | id: 5, 25 | }, 26 | { 27 | text: 'Kansas', 28 | id: 6, 29 | }, 30 | { 31 | text: 'Louisiana', 32 | id: 7, 33 | }, 34 | { 35 | text: 'Texas', 36 | id: 8, 37 | }, 38 | ]; 39 | 40 | const group3 = [ 41 | { 42 | text: 'Alabama', 43 | id: 9, 44 | }, 45 | { 46 | text: 'Kansas', 47 | id: 10, 48 | }, 49 | ]; 50 | 51 | export const enUSData = [ 52 | { 53 | text: 'Group 1', 54 | children: group1, 55 | }, 56 | { 57 | text: 'Group 2', 58 | children: group2, 59 | }, 60 | { 61 | text: 'Group 3', 62 | disabled: true, 63 | children: group3, 64 | }, 65 | ]; 66 | -------------------------------------------------------------------------------- /src/cell/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`CellGroup title slot 1`] = ` 4 |
    5 |
    CustomTitle
    6 |
    7 |
    8 | `; 9 | 10 | exports[`arrow direction 1`] = ` 11 |
    12 |
    13 | `; 14 | 15 | exports[`icon-prefix prop 1`] = ` 16 |
    17 |
    18 | `; 19 | 20 | exports[`render slot 1`] = ` 21 |
    Custom Icon
    Custom Title
    Custom Label
    22 |
    Custom Extra
    23 | `; 24 | 25 | exports[`title-style prop 1`] = ` 26 |
    27 |
    title
    28 |
    29 | `; 30 | -------------------------------------------------------------------------------- /src/cell/shared.ts: -------------------------------------------------------------------------------- 1 | export type SharedCellProps = { 2 | icon?: string; 3 | size?: string; 4 | border: boolean; 5 | center?: boolean; 6 | isLink?: boolean; 7 | required?: boolean; 8 | clickable?: boolean; 9 | iconPrefix?: string; 10 | titleStyle?: any; 11 | titleClass?: any; 12 | valueClass?: any; 13 | labelClass?: any; 14 | title?: string | number; 15 | value?: string | number; 16 | label?: string | number; 17 | arrowDirection?: 'up' | 'down' | 'left' | 'right'; 18 | }; 19 | 20 | export const cellProps = { 21 | icon: String, 22 | size: String, 23 | center: Boolean, 24 | isLink: Boolean, 25 | required: Boolean, 26 | clickable: Boolean, 27 | iconPrefix: String, 28 | titleStyle: null as any, 29 | titleClass: null as any, 30 | valueClass: null as any, 31 | labelClass: null as any, 32 | title: [Number, String], 33 | value: [Number, String], 34 | label: [Number, String], 35 | arrowDirection: String, 36 | border: { 37 | type: Boolean, 38 | default: true, 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /src/collapse/index.js: -------------------------------------------------------------------------------- 1 | import { createNamespace } from '../utils'; 2 | import { ParentMixin } from '../mixins/relation'; 3 | import { BORDER_TOP_BOTTOM } from '../utils/constant'; 4 | 5 | const [createComponent, bem] = createNamespace('collapse'); 6 | 7 | export default createComponent({ 8 | mixins: [ParentMixin('vanCollapse')], 9 | 10 | props: { 11 | accordion: Boolean, 12 | value: [String, Number, Array], 13 | border: { 14 | type: Boolean, 15 | default: true, 16 | }, 17 | }, 18 | 19 | methods: { 20 | switch(name, expanded) { 21 | if (!this.accordion) { 22 | name = expanded 23 | ? this.value.concat(name) 24 | : this.value.filter((activeName) => activeName !== name); 25 | } 26 | this.$emit('change', name); 27 | this.$emit('input', name); 28 | }, 29 | }, 30 | 31 | render() { 32 | return ( 33 |
    34 | {this.slots()} 35 |
    36 | ); 37 | }, 38 | }); 39 | -------------------------------------------------------------------------------- /src/datetime-picker/test/datetime-picker.spec.js: -------------------------------------------------------------------------------- 1 | import DatetimePicker from '..'; 2 | import { mount } from '../../../test'; 3 | 4 | test('confirm & cancel event', () => { 5 | const onConfirm = jest.fn(); 6 | const onCancel = jest.fn(); 7 | 8 | const wrapper = mount(DatetimePicker, { 9 | listeners: { 10 | confirm: onConfirm, 11 | cancel: onCancel, 12 | }, 13 | }); 14 | 15 | wrapper.find('.van-picker__confirm').trigger('click'); 16 | expect(onConfirm).toHaveBeenCalledTimes(1); 17 | 18 | wrapper.find('.van-picker__cancel').trigger('click'); 19 | expect(onCancel).toHaveBeenCalledTimes(1); 20 | }); 21 | 22 | test('time type', () => { 23 | const wrapper = mount(DatetimePicker, { 24 | propsData: { 25 | type: 'time', 26 | minHour: 22, 27 | minMinute: 58, 28 | }, 29 | }); 30 | 31 | expect(wrapper).toMatchSnapshot(); 32 | }); 33 | 34 | test('getPicker method', () => { 35 | const wrapper = mount(DatetimePicker); 36 | expect(wrapper.vm.getPicker()).toBeTruthy(); 37 | }); 38 | -------------------------------------------------------------------------------- /src/utils/validate/email.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export function isEmail(value: string): boolean { 3 | const reg = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i; 4 | return reg.test(value); 5 | } 6 | -------------------------------------------------------------------------------- /src/info/index.tsx: -------------------------------------------------------------------------------- 1 | // Utils 2 | import { createNamespace, isDef } from '../utils'; 3 | import { inherit } from '../utils/functional'; 4 | 5 | // Types 6 | import { CreateElement, RenderContext } from 'vue/types'; 7 | import { DefaultSlots } from '../utils/types'; 8 | 9 | export type InfoProps = { 10 | dot?: boolean; 11 | info?: string | number; 12 | badge?: string | number; 13 | }; 14 | 15 | const [createComponent, bem] = createNamespace('info'); 16 | 17 | function Info( 18 | h: CreateElement, 19 | props: InfoProps, 20 | slots: DefaultSlots, 21 | ctx: RenderContext 22 | ) { 23 | const { dot, info } = props; 24 | const showInfo = isDef(info) && info !== ''; 25 | 26 | if (!dot && !showInfo) { 27 | return; 28 | } 29 | 30 | return ( 31 |
    32 | {dot ? '' : props.info} 33 |
    34 | ); 35 | } 36 | 37 | Info.props = { 38 | dot: Boolean, 39 | info: [Number, String], 40 | }; 41 | 42 | export default createComponent(Info); 43 | -------------------------------------------------------------------------------- /src/utils/dom/raf.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * requestAnimationFrame polyfill 3 | */ 4 | 5 | import { isServer } from '..'; 6 | 7 | let prev = Date.now(); 8 | 9 | /* istanbul ignore next */ 10 | function fallback(fn: FrameRequestCallback): number { 11 | const curr = Date.now(); 12 | const ms = Math.max(0, 16 - (curr - prev)); 13 | const id = setTimeout(fn, ms); 14 | prev = curr + ms; 15 | return id; 16 | } 17 | 18 | /* istanbul ignore next */ 19 | const root = (isServer ? global : window) as Window; 20 | 21 | /* istanbul ignore next */ 22 | const iRaf = root.requestAnimationFrame || fallback; 23 | 24 | /* istanbul ignore next */ 25 | const iCancel = root.cancelAnimationFrame || root.clearTimeout; 26 | 27 | export function raf(fn: FrameRequestCallback): number { 28 | return iRaf.call(root, fn); 29 | } 30 | 31 | // double raf for animation 32 | export function doubleRaf(fn: FrameRequestCallback): void { 33 | raf(() => { 34 | raf(fn); 35 | }); 36 | } 37 | 38 | export function cancelRaf(id: number) { 39 | iCancel.call(root, id); 40 | } 41 | -------------------------------------------------------------------------------- /docs/markdown/design.zh-CN.md: -------------------------------------------------------------------------------- 1 | # 设计资源 2 | 3 | ### 介绍 4 | 5 | 这里提供了 Vant 现有的设计资源,更多资源还在整理中。 6 | 7 | ### 组件 8 | 9 | 包含 Sketch 格式的组件设计规范、色彩规范。 10 | 11 | 12 | 13 | 下载 14 | 15 | > 提示:目前 Sketch 中部分组件仍为旧版样式,我们正在梳理新版设计规范,尽请期待! 16 | 17 | ### 图标 18 | 19 | 包含 Sketch 格式的图标库资源。 20 | 21 | 22 | 23 | 下载 24 | 25 | 44 | -------------------------------------------------------------------------------- /src/image/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-image { 4 | position: relative; 5 | display: inline-block; 6 | 7 | &--round { 8 | overflow: hidden; 9 | border-radius: 50%; 10 | 11 | img { 12 | border-radius: inherit; 13 | } 14 | } 15 | 16 | &__img, 17 | &__error, 18 | &__loading { 19 | display: block; 20 | width: 100%; 21 | height: 100%; 22 | } 23 | 24 | &__error, 25 | &__loading { 26 | position: absolute; 27 | top: 0; 28 | left: 0; 29 | display: flex; 30 | flex-direction: column; 31 | align-items: center; 32 | justify-content: center; 33 | color: @image-placeholder-text-color; 34 | font-size: @image-placeholder-font-size; 35 | background-color: @image-placeholder-background-color; 36 | } 37 | 38 | &__loading-icon { 39 | color: @image-loading-icon-color; 40 | font-size: @image-loading-icon-size; 41 | } 42 | 43 | &__error-icon { 44 | color: @image-error-icon-color; 45 | font-size: @image-error-icon-size; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/mixins/close-on-popstate.js: -------------------------------------------------------------------------------- 1 | import { on, off } from '../utils/dom/event'; 2 | import { BindEventMixin } from './bind-event'; 3 | 4 | export const CloseOnPopstateMixin = { 5 | mixins: [ 6 | BindEventMixin(function (bind, isBind) { 7 | this.handlePopstate(isBind && this.closeOnPopstate); 8 | }), 9 | ], 10 | 11 | props: { 12 | closeOnPopstate: Boolean, 13 | }, 14 | 15 | data() { 16 | return { 17 | bindStatus: false, 18 | }; 19 | }, 20 | 21 | watch: { 22 | closeOnPopstate(val) { 23 | this.handlePopstate(val); 24 | }, 25 | }, 26 | 27 | methods: { 28 | handlePopstate(bind) { 29 | /* istanbul ignore if */ 30 | if (this.$isServer) { 31 | return; 32 | } 33 | 34 | if (this.bindStatus !== bind) { 35 | this.bindStatus = bind; 36 | const action = bind ? on : off; 37 | action(window, 'popstate', () => { 38 | this.close(); 39 | this.shouldReopen = false; 40 | }); 41 | } 42 | }, 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016-present Youzan 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/nav-bar/test/__snapshots__/demo.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders demo correctly 1`] = ` 4 |
    5 |
    6 |
    7 |
    8 | 返回
    9 |
    标题
    10 |
    按钮
    11 |
    12 |
    13 |
    14 |
    15 |
    16 | 返回
    17 |
    标题
    18 |
    19 |
    20 |
    21 |
    22 |
    23 | `; 24 | -------------------------------------------------------------------------------- /src/utils/types.ts: -------------------------------------------------------------------------------- 1 | import { VNode, CreateElement, RenderContext } from 'vue'; 2 | import { InjectOptions, PropsDefinition } from 'vue/types/options'; 3 | 4 | export type EventHandler = (event: Event) => void; 5 | 6 | export type ObjectIndex = Record; 7 | 8 | export type ScopedSlot = ( 9 | props?: Props 10 | ) => VNode[] | VNode | undefined; 11 | 12 | export type DefaultSlots = { 13 | default?: ScopedSlot; 14 | }; 15 | 16 | export type ScopedSlots = DefaultSlots & { 17 | [key: string]: ScopedSlot | undefined; 18 | }; 19 | 20 | export type ModelOptions = { 21 | prop?: string; 22 | event?: string; 23 | }; 24 | 25 | export type DefaultProps = ObjectIndex; 26 | 27 | export type FunctionComponent< 28 | Props = DefaultProps, 29 | PropDefs = PropsDefinition 30 | > = { 31 | ( 32 | h: CreateElement, 33 | props: Props, 34 | slots: ScopedSlots, 35 | context: RenderContext 36 | ): VNode | undefined; 37 | props?: PropDefs; 38 | model?: ModelOptions; 39 | inject?: InjectOptions; 40 | }; 41 | -------------------------------------------------------------------------------- /packages/vant-cli/src/commands/commit-lint.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs-extra'; 2 | import { consola } from '../common/logger'; 3 | 4 | const commitRE = /^(revert: )?(fix|feat|docs|perf|test|types|style|build|chore|refactor|breaking change)(\(.+\))?: .{1,50}/; 5 | const mergeRE = /Merge /; 6 | 7 | export function commitLint() { 8 | const gitParams = process.env.HUSKY_GIT_PARAMS as string; 9 | const commitMsg = readFileSync(gitParams, 'utf-8').trim(); 10 | 11 | if (!commitRE.test(commitMsg) && !mergeRE.test(commitMsg)) { 12 | consola.error(`invalid commit message: "${commitMsg}". 13 | 14 | Proper commit message format is required for automated changelog generation. 15 | 16 | Examples: 17 | 18 | - fix(Button): incorrect style 19 | - feat(Button): incorrect style 20 | - docs(Button): fix typo 21 | 22 | Allowed Types: 23 | 24 | - fix 25 | - feat 26 | - docs 27 | - perf 28 | - test 29 | - types 30 | - build 31 | - chore 32 | - refactor 33 | - breaking change 34 | - Merge branch 'foo' into 'bar' 35 | `); 36 | process.exit(1); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/number-keyboard/DeleteIcon.tsx: -------------------------------------------------------------------------------- 1 | export default { 2 | render() { 3 | return ( 4 | 5 | 9 | 10 | ); 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/field/demo/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 43 | -------------------------------------------------------------------------------- /src/sku/index.js: -------------------------------------------------------------------------------- 1 | // Utils 2 | import lang from './lang'; 3 | import constants from './constants'; 4 | import skuHelper from './utils/sku-helper'; 5 | 6 | // Components 7 | import Sku from './Sku'; 8 | import Locale from '../locale'; 9 | import SkuActions from './components/SkuActions'; 10 | import SkuHeader from './components/SkuHeader'; 11 | import SkuHeaderItem from './components/SkuHeaderItem'; 12 | import SkuMessages from './components/SkuMessages'; 13 | import SkuStepper from './components/SkuStepper'; 14 | import SkuRow from './components/SkuRow'; 15 | import SkuRowItem from './components/SkuRowItem'; 16 | import SkuRowPropItem from './components/SkuRowPropItem'; 17 | 18 | Locale.add(lang); 19 | 20 | Sku.SkuActions = SkuActions; 21 | Sku.SkuHeader = SkuHeader; 22 | Sku.SkuHeaderItem = SkuHeaderItem; 23 | Sku.SkuMessages = SkuMessages; 24 | Sku.SkuStepper = SkuStepper; 25 | Sku.SkuRow = SkuRow; 26 | Sku.SkuRowItem = SkuRowItem; 27 | Sku.SkuRowPropItem = SkuRowPropItem; 28 | Sku.skuHelper = skuHelper; 29 | Sku.skuConstants = constants; 30 | 31 | export default Sku; 32 | -------------------------------------------------------------------------------- /src/icon/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`dot prop 1`] = ` 4 | 5 |
    6 |
    7 | `; 8 | 9 | exports[`render icon default slot 1`] = ` 10 | Default slot 11 | 12 | `; 13 | 14 | exports[`render icon with builtin icon name 1`] = ` 15 | 16 | 17 | `; 18 | 19 | exports[`render icon with local image 1`] = ` 20 | 21 | 22 | `; 23 | 24 | exports[`render icon with url name 1`] = ` 25 | 26 | 27 | `; 28 | 29 | exports[`size without unit 1`] = ` 30 | 31 | 32 | `; 33 | 34 | exports[`tag prop 1`] = ` 35 |
    36 | 37 |
    38 | `; 39 | -------------------------------------------------------------------------------- /src/nav-bar/demo/index.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 45 | -------------------------------------------------------------------------------- /src/button/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`icon-prefix prop 1`] = ` 4 | 8 | `; 9 | 10 | exports[`loading slot 1`] = ` 11 | 14 | `; 15 | 16 | exports[`loading-size prop 1`] = ` 17 | 22 | `; 23 | -------------------------------------------------------------------------------- /src/divider/test/__snapshots__/demo.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders demo correctly 1`] = ` 4 |
    5 |
    6 | 7 |
    8 |
    9 | 12 |
    13 |
    14 | 17 | 20 |
    21 |
    22 | 25 |
    26 |
    27 | 30 |
    31 |
    32 | `; 33 | -------------------------------------------------------------------------------- /packages/vant-cli/src/config/webpack.site.prd.ts: -------------------------------------------------------------------------------- 1 | import merge from 'webpack-merge'; 2 | import { get } from 'lodash'; 3 | import { WebpackConfig } from '../common/types'; 4 | import { getVantConfig, getWebpackConfig } from '../common'; 5 | import { getSiteDevBaseConfig } from './webpack.site.dev'; 6 | import { SITE_DIST_DIR } from '../common/constant'; 7 | 8 | const vantConfig = getVantConfig(); 9 | const outputDir = get(vantConfig, 'build.site.outputDir', SITE_DIST_DIR); 10 | const publicPath = get(vantConfig, 'build.site.publicPath', '/'); 11 | 12 | export function getSitePrdConfig(): WebpackConfig { 13 | return merge( 14 | getSiteDevBaseConfig(), 15 | { 16 | mode: 'production', 17 | stats: 'none', 18 | performance: { 19 | maxAssetSize: 5 * 1024 * 1024, 20 | maxEntrypointSize: 5 * 1024 * 1024, 21 | }, 22 | output: { 23 | publicPath, 24 | path: outputDir, 25 | filename: '[name].[hash:8].js', 26 | chunkFilename: 'async_[name].[chunkhash:8].js', 27 | }, 28 | }, 29 | getWebpackConfig() 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/panel/demo/index.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 26 | 27 | 42 | -------------------------------------------------------------------------------- /src/uploader/test/utils.spec.js: -------------------------------------------------------------------------------- 1 | import { isImageFile } from '../utils'; 2 | 3 | test('isImageFile', () => { 4 | expect(isImageFile({ url: 'https://a.jpg' })).toBeTruthy(); 5 | expect(isImageFile({ url: 'https://a.jpeg' })).toBeTruthy(); 6 | expect(isImageFile({ url: 'https://a.png' })).toBeTruthy(); 7 | expect(isImageFile({ url: 'https://a.svg' })).toBeTruthy(); 8 | expect(isImageFile({ url: 'https://a.gif' })).toBeTruthy(); 9 | expect(isImageFile({ url: 'https://a.webp' })).toBeTruthy(); 10 | expect(isImageFile({ url: 'https://a.jfif' })).toBeTruthy(); 11 | expect(isImageFile({ url: 'https://a.bmp' })).toBeTruthy(); 12 | expect(isImageFile({ url: 'https://a.dpg' })).toBeTruthy(); 13 | expect(isImageFile({ file: { type: 'image/jpg' } })).toBeTruthy(); 14 | expect(isImageFile({ file: { type: 'application/pdf' } })).toBeFalsy(); 15 | expect(isImageFile({ content: 'data:image/xxx' })).toBeTruthy(); 16 | expect(isImageFile({ content: 'data:application/xxx' })).toBeFalsy(); 17 | expect(isImageFile({ isImage: true })).toBeTruthy(); 18 | expect(isImageFile({})).toBeFalsy(); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/vant-cli/site/mobile/components/DemoBlock.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 21 | 22 | 52 | -------------------------------------------------------------------------------- /packages/vant-icons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vant/icons", 3 | "version": "1.2.5", 4 | "description": "vant icons", 5 | "main": "./src/config.js", 6 | "files": [ 7 | "src" 8 | ], 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "scripts": { 13 | "export": "node ./build/export.js", 14 | "build": "npm run export && gulp --gulpfile ./build/build-iconfont.js", 15 | "release": "npm run build && release-it" 16 | }, 17 | "license": "MIT", 18 | "repository": "https://github.com/youzan/vant/tree/dev/packages/vant-icons", 19 | "devDependencies": { 20 | "fast-glob": "^3.2.2", 21 | "fs-extra": "^9.0.1", 22 | "gulp": "^4.0.2", 23 | "gulp-iconfont": "^10.0.3", 24 | "gulp-iconfont-css": "^3.0.0", 25 | "md5-file": "^5.0.0", 26 | "release-it": "^13.6.2", 27 | "shelljs": "^0.8.4", 28 | "svgo": "1.2.2" 29 | }, 30 | "release-it": { 31 | "git": { 32 | "tag": false, 33 | "commitMessage": "chore: release @vant/icons ${version}", 34 | "addUntrackedFiles": true, 35 | "requireCleanWorkingDir": false 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/form/demo/FieldTypeCalendar.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 53 | -------------------------------------------------------------------------------- /src/mixins/portal.js: -------------------------------------------------------------------------------- 1 | function getElement(selector) { 2 | if (typeof selector === 'string') { 3 | return document.querySelector(selector); 4 | } 5 | 6 | return selector(); 7 | } 8 | 9 | export function PortalMixin({ ref, afterPortal } = {}) { 10 | return { 11 | props: { 12 | getContainer: [String, Function], 13 | }, 14 | 15 | watch: { 16 | getContainer: 'portal', 17 | }, 18 | 19 | mounted() { 20 | if (this.getContainer) { 21 | this.portal(); 22 | } 23 | }, 24 | 25 | methods: { 26 | portal() { 27 | const { getContainer } = this; 28 | const el = ref ? this.$refs[ref] : this.$el; 29 | 30 | let container; 31 | if (getContainer) { 32 | container = getElement(getContainer); 33 | } else if (this.$parent) { 34 | container = this.$parent.$el; 35 | } 36 | 37 | if (container && container !== el.parentNode) { 38 | container.appendChild(el); 39 | } 40 | 41 | if (afterPortal) { 42 | afterPortal.call(this); 43 | } 44 | }, 45 | }, 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /src/calendar/test/utils.spec.js: -------------------------------------------------------------------------------- 1 | import { compareDay, compareMonth, getNextDay, calcDateNum } from '../utils'; 2 | 3 | const date1 = new Date(2010, 0, 1); 4 | const date2 = new Date(2010, 0, 2); 5 | const date3 = new Date(2010, 1, 1); 6 | const date4 = new Date(2011, 0, 1); 7 | 8 | test('compareMonth', () => { 9 | expect(compareMonth(date1, date1)).toEqual(0); 10 | expect(compareMonth(date1, date2)).toEqual(0); 11 | expect(compareMonth(date2, date3)).toEqual(-1); 12 | expect(compareMonth(date1, date4)).toEqual(-1); 13 | expect(compareMonth(date4, date1)).toEqual(1); 14 | }); 15 | 16 | test('compareDay', () => { 17 | expect(compareDay(date1, date1)).toEqual(0); 18 | expect(compareDay(date1, date2)).toEqual(-1); 19 | expect(compareDay(date2, date3)).toEqual(-1); 20 | expect(compareDay(date4, date1)).toEqual(1); 21 | }); 22 | 23 | test('getNextDay', () => { 24 | expect(getNextDay(date1).getDate()).toEqual(2); 25 | expect(getNextDay(date2).getDate()).toEqual(3); 26 | }); 27 | 28 | test('calcDateNum', () => { 29 | expect(calcDateNum([date1, date2])).toEqual(2); 30 | expect(calcDateNum([date1, date3])).toEqual(32); 31 | }); 32 | -------------------------------------------------------------------------------- /src/field/demo/FormatValue.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 50 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | export { createNamespace } from './create'; 4 | export { addUnit } from './format/unit'; 5 | 6 | export const inBrowser = typeof window !== 'undefined'; 7 | export const isServer: boolean = Vue.prototype.$isServer; 8 | 9 | // eslint-disable-next-line @typescript-eslint/no-empty-function 10 | export function noop() {} 11 | 12 | export function isDef(val: unknown): boolean { 13 | return val !== undefined && val !== null; 14 | } 15 | 16 | export function isFunction(val: unknown): val is Function { 17 | return typeof val === 'function'; 18 | } 19 | 20 | export function isObject(val: unknown): val is Record { 21 | return val !== null && typeof val === 'object'; 22 | } 23 | 24 | export function isPromise(val: unknown): val is Promise { 25 | return isObject(val) && isFunction(val.then) && isFunction(val.catch); 26 | } 27 | 28 | export function get(object: any, path: string): any { 29 | const keys = path.split('.'); 30 | let result = object; 31 | 32 | keys.forEach((key) => { 33 | result = isDef(result[key]) ? result[key] : ''; 34 | }); 35 | 36 | return result; 37 | } 38 | -------------------------------------------------------------------------------- /src/locale/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # 国际化 2 | 3 | ### 介绍 4 | 5 | Vant 默认采用中文作为语言,如果需要使用其他语言,可以参考下面的方案。 6 | 7 | ### 多语言切换 8 | 9 | Vant 通过 Locale 组件实现多语言支持,使用 `Locale.use` 方法可以切换当前使用的语言。 10 | 11 | ```js 12 | import { Locale } from 'vant'; 13 | import enUS from 'vant/lib/locale/lang/en-US'; 14 | 15 | Locale.use('en-US', enUS); 16 | ``` 17 | 18 | ### 修改默认文案 19 | 20 | 通过 `Locale.add` 方法可以实现文案的修改和扩展,示例如下: 21 | 22 | ```js 23 | import { Locale } from 'vant'; 24 | 25 | const messages = { 26 | 'zh-CN': { 27 | vanPicker: { 28 | confirm: '关闭', // 将'确认'修改为'关闭' 29 | }, 30 | }, 31 | }; 32 | 33 | Locale.add(messages); 34 | ``` 35 | 36 | ### 配置文件 37 | 38 | 目前支持的语言: 39 | 40 | | 语言 | 文件名 | 41 | | -------------- | ------ | 42 | | 简体中文 | zh-CN | 43 | | 繁體中文(港) | zh-HK | 44 | | 繁體中文(台) | zh-TW | 45 | | 英语 | en-US | 46 | | 土耳其语 | tr-TR | 47 | | 西班牙语 | es-ES | 48 | | 日语 | ja-JP | 49 | | 罗马尼亚语 | ro-RO | 50 | | 挪威语 | nb-NO | 51 | 52 | > 在 [这里](https://github.com/youzan/vant/tree/dev/src/locale/lang) 查看所有的 i18n 配置文件。 53 | 54 | ### Sku 组件 55 | 56 | 语言包中默认不包含 Sku 业务组件的语言配置,因此如果有 Sku 组件的国际化需求,请自行配置国际化文案。 57 | -------------------------------------------------------------------------------- /types/notify.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { VanComponent } from './component'; 3 | 4 | export type NotifyMessage = string | number; 5 | 6 | export type NotifyOptions = { 7 | type?: 'primary' | 'success' | 'danger' | 'warning'; 8 | value?: boolean; 9 | color?: string; 10 | message?: NotifyMessage; 11 | duration?: number; 12 | className?: any; 13 | background?: string; 14 | onClose?: (() => void) | null; 15 | onOpened?: (() => void) | null; 16 | onClick?: ((event: Event) => void) | null; 17 | }; 18 | 19 | export interface VanNotify extends Vue { 20 | message: NotifyMessage; 21 | color: string; 22 | background: string; 23 | duration: number; 24 | } 25 | 26 | export interface Notify { 27 | (message: NotifyOptions | NotifyMessage): VanNotify; 28 | clear(): void; 29 | install(): void; 30 | currentOptions: NotifyOptions; 31 | defaultOptions: NotifyOptions; 32 | setDefaultOptions(options: NotifyOptions): void; 33 | resetDefaultOptions(): void; 34 | Component: typeof VanComponent; 35 | } 36 | 37 | declare module 'vue/types/vue' { 38 | interface Vue { 39 | $notify: Notify; 40 | } 41 | } 42 | 43 | export const Notify: Notify; 44 | -------------------------------------------------------------------------------- /packages/vant-cli/src/compiler/compile-style.ts: -------------------------------------------------------------------------------- 1 | import { parse } from 'path'; 2 | import { readFileSync, writeFileSync } from 'fs'; 3 | import { replaceExt } from '../common'; 4 | import { compileCss } from './compile-css'; 5 | import { compileLess } from './compile-less'; 6 | import { compileSass } from './compile-sass'; 7 | import { consola } from '../common/logger'; 8 | 9 | async function compileFile(filePath: string) { 10 | const parsedPath = parse(filePath); 11 | 12 | try { 13 | if (parsedPath.ext === '.less') { 14 | const source = await compileLess(filePath); 15 | return await compileCss(source); 16 | } 17 | 18 | if (parsedPath.ext === '.scss') { 19 | const source = await compileSass(filePath); 20 | return await compileCss(source); 21 | } 22 | 23 | const source = readFileSync(filePath, 'utf-8'); 24 | return await compileCss(source); 25 | } catch (err) { 26 | consola.error('Compile style failed: ' + filePath); 27 | throw err; 28 | } 29 | } 30 | 31 | export async function compileStyle(filePath: string) { 32 | const css = await compileFile(filePath); 33 | 34 | writeFileSync(replaceExt(filePath, '.css'), css); 35 | } 36 | -------------------------------------------------------------------------------- /src/utils/test/bem.spec.js: -------------------------------------------------------------------------------- 1 | import { createBEM } from '../create/bem'; 2 | 3 | test('bem', () => { 4 | const bem = createBEM('button'); 5 | 6 | expect(bem()).toEqual('button'); 7 | 8 | expect(bem('text')).toEqual('button__text'); 9 | 10 | expect(bem({ disabled: false })).toEqual('button'); 11 | 12 | expect(bem({ disabled: true })).toEqual('button button--disabled'); 13 | 14 | expect(bem('text', { disabled: true })).toEqual( 15 | 'button__text button__text--disabled' 16 | ); 17 | 18 | expect(bem(['disabled', 'primary'])).toEqual( 19 | 'button button--disabled button--primary' 20 | ); 21 | 22 | expect(bem([])).toEqual('button'); 23 | 24 | expect(bem(null)).toEqual('button'); 25 | 26 | expect(bem([null])).toEqual('button'); 27 | 28 | expect(bem(['disabled', ''])).toEqual('button button--disabled'); 29 | 30 | expect(bem(['disabled', undefined])).toEqual('button button--disabled'); 31 | 32 | expect(bem('text', ['disabled', 'primary'])).toEqual( 33 | 'button__text button__text--disabled button__text--primary' 34 | ); 35 | 36 | expect(bem('text', [{ disabled: true }, 'primary'])).toEqual( 37 | 'button__text button__text--disabled button__text--primary' 38 | ); 39 | }); 40 | -------------------------------------------------------------------------------- /src/contact-list/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-contact-list { 4 | box-sizing: border-box; 5 | height: 100%; 6 | padding-bottom: 80px; 7 | 8 | &__item { 9 | padding: @contact-list-item-padding; 10 | } 11 | 12 | &__item-value { 13 | display: flex; 14 | align-items: center; 15 | padding-right: @padding-xl; 16 | padding-left: @padding-xs; 17 | } 18 | 19 | &__item-tag { 20 | flex: none; 21 | margin-left: @padding-xs; 22 | padding-top: 0; 23 | padding-bottom: 0; 24 | line-height: 1.4em; 25 | } 26 | 27 | &__group { 28 | box-sizing: border-box; 29 | height: 100%; 30 | overflow-y: scroll; 31 | -webkit-overflow-scrolling: touch; 32 | } 33 | 34 | &__edit { 35 | font-size: @contact-list-edit-icon-size; 36 | } 37 | 38 | &__bottom { 39 | position: fixed; 40 | right: 0; 41 | bottom: 0; 42 | left: 0; 43 | z-index: @contact-list-add-button-z-index; 44 | padding: 0 @padding-md; 45 | padding-bottom: constant(safe-area-inset-bottom); 46 | padding-bottom: env(safe-area-inset-bottom); 47 | background-color: @white; 48 | } 49 | 50 | &__add { 51 | height: 40px; 52 | margin: 5px 0; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/grid/index.js: -------------------------------------------------------------------------------- 1 | import { createNamespace, addUnit } from '../utils'; 2 | import { BORDER_TOP } from '../utils/constant'; 3 | import { ParentMixin } from '../mixins/relation'; 4 | 5 | const [createComponent, bem] = createNamespace('grid'); 6 | 7 | export default createComponent({ 8 | mixins: [ParentMixin('vanGrid')], 9 | 10 | props: { 11 | square: Boolean, 12 | gutter: [Number, String], 13 | iconSize: [Number, String], 14 | direction: String, 15 | clickable: Boolean, 16 | columnNum: { 17 | type: [Number, String], 18 | default: 4, 19 | }, 20 | center: { 21 | type: Boolean, 22 | default: true, 23 | }, 24 | border: { 25 | type: Boolean, 26 | default: true, 27 | }, 28 | }, 29 | 30 | computed: { 31 | style() { 32 | const { gutter } = this; 33 | 34 | if (gutter) { 35 | return { 36 | paddingLeft: addUnit(gutter), 37 | }; 38 | } 39 | }, 40 | }, 41 | 42 | render() { 43 | return ( 44 |
    48 | {this.slots()} 49 |
    50 | ); 51 | }, 52 | }); 53 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - uses: actions/setup-node@v1 11 | with: 12 | node-version: '12.x' 13 | 14 | - name: Install dependencies 15 | uses: bahmutov/npm-install@v1 16 | 17 | - name: Run linter 18 | run: npm run lint 19 | 20 | test: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v1 24 | - uses: actions/setup-node@v1 25 | with: 26 | node-version: '12.x' 27 | 28 | - name: Install dependencies 29 | uses: bahmutov/npm-install@v1 30 | 31 | - name: Run test cases 32 | run: npm test 33 | 34 | - name: Upload coverage to Codecov 35 | uses: codecov/codecov-action@v1 36 | with: 37 | token: ${{ secrets.CODECOV_TOKEN }} 38 | 39 | build: 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@v1 43 | - uses: actions/setup-node@v1 44 | with: 45 | node-version: '12.x' 46 | 47 | - name: Install dependencies 48 | uses: bahmutov/npm-install@v1 49 | 50 | - name: Build 51 | run: npm run build 52 | -------------------------------------------------------------------------------- /src/contact-card/test/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ContactList render 1`] = ` 4 |
    5 |
    6 |
    7 | 8 |
    test,123123213
    9 | 13 |
    14 |
    15 |
    18 |
    19 | `; 20 | -------------------------------------------------------------------------------- /src/utils/create/bem.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * bem helper 3 | * b() // 'button' 4 | * b('text') // 'button__text' 5 | * b({ disabled }) // 'button button--disabled' 6 | * b('text', { disabled }) // 'button__text button__text--disabled' 7 | * b(['disabled', 'primary']) // 'button button--disabled button--primary' 8 | */ 9 | 10 | export type Mod = string | { [key: string]: any }; 11 | export type Mods = Mod | Mod[]; 12 | 13 | function gen(name: string, mods?: Mods): string { 14 | if (!mods) { 15 | return ''; 16 | } 17 | 18 | if (typeof mods === 'string') { 19 | return ` ${name}--${mods}`; 20 | } 21 | 22 | if (Array.isArray(mods)) { 23 | return mods.reduce((ret, item) => ret + gen(name, item), ''); 24 | } 25 | 26 | return Object.keys(mods).reduce( 27 | (ret, key) => ret + (mods[key] ? gen(name, key) : ''), 28 | '' 29 | ); 30 | } 31 | 32 | export function createBEM(name: string) { 33 | return function (el?: Mods, mods?: Mods): Mods { 34 | if (el && typeof el !== 'string') { 35 | mods = el; 36 | el = ''; 37 | } 38 | 39 | el = el ? `${name}__${el}` : name; 40 | 41 | return `${el}${gen(el, mods)}`; 42 | }; 43 | } 44 | 45 | export type BEM = ReturnType; 46 | -------------------------------------------------------------------------------- /packages/vant-waterfall/src/scroll.js: -------------------------------------------------------------------------------- 1 | // get nearest scroll element 2 | // http://w3help.org/zh-cn/causes/SD9013 3 | // http://stackoverflow.com/questions/17016740/onscroll-function-is-not-working-for-chrome 4 | export function getScrollEventTarget(element, rootParent = window) { 5 | let node = element; 6 | while ( 7 | node && 8 | node.tagName !== 'HTML' && 9 | node.tagName !== 'BODY' && 10 | node.nodeType === 1 && 11 | node !== rootParent 12 | ) { 13 | const { overflowY } = window.getComputedStyle(node); 14 | if (overflowY === 'scroll' || overflowY === 'auto') { 15 | return node; 16 | } 17 | node = node.parentNode; 18 | } 19 | return rootParent; 20 | } 21 | 22 | export function getScrollTop(element) { 23 | return 'scrollTop' in element ? element.scrollTop : element.pageYOffset; 24 | } 25 | 26 | // get distance from element top to page top 27 | export function getElementTop(element) { 28 | return ( 29 | (element === window ? 0 : element.getBoundingClientRect().top) + 30 | getScrollTop(window) 31 | ); 32 | } 33 | 34 | export function getVisibleHeight(element) { 35 | return element === window 36 | ? element.innerHeight 37 | : element.getBoundingClientRect().height; 38 | } 39 | -------------------------------------------------------------------------------- /src/skeleton/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-skeleton { 4 | display: flex; 5 | padding: 0 @padding-md; 6 | 7 | &__avatar { 8 | flex-shrink: 0; 9 | margin-right: @padding-md; 10 | background-color: @skeleton-avatar-background-color; 11 | 12 | &--round { 13 | border-radius: @border-radius-max; 14 | } 15 | } 16 | 17 | &__content { 18 | width: 100%; 19 | } 20 | 21 | &__avatar + &__content { 22 | padding-top: @padding-xs; 23 | } 24 | 25 | &__row, 26 | &__title { 27 | height: @skeleton-row-height; 28 | background-color: @skeleton-row-background-color; 29 | } 30 | 31 | &__title { 32 | margin: 0; 33 | } 34 | 35 | &__row { 36 | &:not(:first-child) { 37 | margin-top: @skeleton-row-margin-top; 38 | } 39 | } 40 | 41 | &__title + &__row { 42 | margin-top: 20px; 43 | } 44 | 45 | &--animate { 46 | animation: van-skeleton-blink @skeleton-animation-duration ease-in-out 47 | infinite; 48 | } 49 | 50 | &--round { 51 | .van-skeleton__row, 52 | .van-skeleton__title { 53 | border-radius: @border-radius-max; 54 | } 55 | } 56 | } 57 | 58 | @keyframes van-skeleton-blink { 59 | 50% { 60 | opacity: 0.6; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/tag/test/index.spec.js: -------------------------------------------------------------------------------- 1 | import Tag from '..'; 2 | import { mount } from '../../../test'; 3 | 4 | test('click event', () => { 5 | const click = jest.fn(); 6 | const wrapper = mount(Tag, { 7 | context: { 8 | on: { 9 | click, 10 | }, 11 | }, 12 | }); 13 | 14 | wrapper.trigger('click'); 15 | expect(click).toHaveBeenCalledTimes(1); 16 | }); 17 | 18 | test('close event', () => { 19 | const close = jest.fn(); 20 | const wrapper = mount(Tag, { 21 | propsData: { 22 | closeable: true, 23 | }, 24 | context: { 25 | on: { 26 | close, 27 | }, 28 | }, 29 | }); 30 | 31 | wrapper.find('.van-tag__close').trigger('click'); 32 | expect(close).toHaveBeenCalledTimes(1); 33 | }); 34 | 35 | test('should not trigger click event when close', () => { 36 | const close = jest.fn(); 37 | const click = jest.fn(); 38 | 39 | const wrapper = mount(Tag, { 40 | propsData: { 41 | closeable: true, 42 | }, 43 | context: { 44 | on: { 45 | close, 46 | click, 47 | }, 48 | }, 49 | }); 50 | 51 | wrapper.find('.van-tag__close').trigger('click'); 52 | expect(close).toHaveBeenCalledTimes(1); 53 | expect(click).toHaveBeenCalledTimes(0); 54 | }); 55 | -------------------------------------------------------------------------------- /src/form/demo/FieldTypeDatetimePicker.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 59 | -------------------------------------------------------------------------------- /src/tree-select/index.less: -------------------------------------------------------------------------------- 1 | @import '../style/var'; 2 | 3 | .van-tree-select { 4 | position: relative; 5 | display: flex; 6 | font-size: @tree-select-font-size; 7 | user-select: none; 8 | 9 | &__nav { 10 | flex: 1; 11 | overflow-y: auto; 12 | background-color: @tree-select-nav-background-color; 13 | -webkit-overflow-scrolling: touch; 14 | 15 | &-item { 16 | padding: @tree-select-nav-item-padding; 17 | } 18 | } 19 | 20 | &__content { 21 | flex: 2; 22 | overflow-y: auto; 23 | background-color: @tree-select-content-background-color; 24 | -webkit-overflow-scrolling: touch; 25 | } 26 | 27 | &__item { 28 | position: relative; 29 | padding: 0 32px 0 @padding-md; 30 | font-weight: @font-weight-bold; 31 | line-height: @tree-select-item-height; 32 | cursor: pointer; 33 | 34 | &--active { 35 | color: @tree-select-item-active-color; 36 | } 37 | 38 | &--disabled { 39 | color: @tree-select-item-disabled-color; 40 | cursor: not-allowed; 41 | } 42 | } 43 | 44 | &__selected { 45 | position: absolute; 46 | top: 50%; 47 | right: @padding-md; 48 | margin-top: -@padding-xs; 49 | font-size: @tree-select-item-selected-size; 50 | } 51 | } 52 | --------------------------------------------------------------------------------