├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── README-zh_CN.md ├── README.md ├── build ├── build.js ├── check-versions.js ├── dev-client.js ├── dev-server.js ├── git-release.sh ├── utils.js ├── webpack.base.conf.js ├── webpack.dev.conf.js ├── webpack.prod.conf.js └── webpack.prod.min.conf.js ├── components ├── alert │ ├── alert.vue │ ├── index.js │ └── style │ │ └── index.less ├── badge │ ├── badge.vue │ ├── index.js │ └── style │ │ └── index.less ├── breadcrumb │ ├── breadcrumb-item.vue │ ├── breadcrumb.vue │ ├── index.js │ └── style │ │ └── index.less ├── button │ ├── button-group.vue │ ├── button.vue │ ├── index.js │ └── style │ │ ├── index.less │ │ └── mixin.less ├── card │ ├── card.js │ ├── index.js │ └── style │ │ └── index.less ├── checkbox │ ├── checkbox-group.vue │ ├── checkbox.vue │ ├── index.js │ ├── store.js │ └── style │ │ ├── index.less │ │ └── mixin.less ├── collapse │ ├── collapse.vue │ ├── index.js │ ├── panel.vue │ ├── store.js │ └── style │ │ └── index.less ├── dropdown │ ├── dropdown-item.vue │ ├── dropdown-menu.vue │ ├── dropdown.vue │ ├── index.js │ └── style │ │ └── index.less ├── grid │ ├── col.vue │ ├── index.js │ ├── row.vue │ └── style │ │ ├── index.less │ │ └── mixin.less ├── icon │ ├── icon.vue │ └── index.js ├── index.js ├── input-number │ ├── index.js │ ├── input-number.vue │ └── style │ │ └── index.less ├── input │ ├── index.js │ ├── input.vue │ └── style │ │ ├── index.less │ │ ├── mixin.less │ │ └── search-input.less ├── menu │ ├── enum.js │ ├── index.js │ ├── menu-item-group.vue │ ├── menu-item.vue │ ├── menu.vue │ ├── mixins.js │ ├── store.js │ ├── style │ │ └── index.less │ └── sub-menu.vue ├── message │ ├── index.js │ ├── message.js │ ├── node.js │ ├── nodes.js │ └── style │ │ └── index.less ├── mixins │ ├── popper.js │ └── tooltip.js ├── modal │ ├── confirm.js │ ├── index.js │ ├── modal.js │ └── style │ │ ├── confirm.less │ │ ├── index.less │ │ └── modal.less ├── notification │ ├── index.js │ ├── node.js │ ├── nodes.js │ ├── notification.js │ └── style │ │ └── index.less ├── pagination │ ├── index.js │ ├── mixins.js │ ├── options.js │ ├── pager.js │ ├── pagination.js │ ├── simple-pager.js │ └── style │ │ └── index.less ├── popconfirm │ ├── index.js │ └── popconfirm.js ├── popover │ ├── index.js │ ├── popover.js │ └── style │ │ └── index.less ├── radio │ ├── index.js │ ├── radio-button.vue │ ├── radio-group.vue │ ├── radio-mixin.js │ ├── radio.vue │ ├── store.js │ └── style │ │ └── index.less ├── rate │ ├── index.js │ ├── rate.js │ └── style │ │ └── index.less ├── select │ ├── index.js │ ├── option-group.vue │ ├── option.vue │ ├── select.vue │ └── style │ │ └── index.less ├── style │ ├── color │ │ ├── bezierEasing.less │ │ ├── colorPalette.less │ │ ├── colors.less │ │ └── tinyColor.less │ ├── core │ │ ├── base.less │ │ ├── iconfont.less │ │ ├── index.less │ │ ├── motion.less │ │ ├── motion │ │ │ ├── fade.less │ │ │ ├── move.less │ │ │ ├── other.less │ │ │ ├── slide.less │ │ │ ├── swing.less │ │ │ └── zoom.less │ │ └── normalize.less │ ├── index.less │ ├── mixins │ │ ├── clearfix.less │ │ ├── compatibility.less │ │ ├── iconfont.less │ │ ├── index.less │ │ ├── motion.less │ │ ├── opacity.less │ │ └── size.less │ └── themes │ │ └── default.less ├── switch │ ├── index.js │ ├── style │ │ └── index.less │ └── switch.vue ├── tabs │ ├── index.js │ ├── store.js │ ├── style │ │ ├── card-style.less │ │ └── index.less │ ├── tab-nav.js │ ├── tab-pane.js │ └── tabs.js ├── tag │ ├── index.js │ ├── style │ │ └── index.less │ └── tag.vue ├── tooltip │ ├── index.js │ ├── style │ │ └── index.less │ └── tooltip.js ├── transition │ ├── collapse.js │ ├── fade.js │ ├── index.js │ ├── move-up.js │ ├── slide-up.js │ ├── zoom-big-fast.js │ └── zoom.js └── utils │ ├── base-store.js │ ├── calcTextareaHeight.js │ ├── clickoutside.js │ ├── dom.js │ └── keycode.js ├── config ├── dev.env.js ├── index.js └── prod.env.js └── package.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2"], 3 | "plugins": ["transform-vue-jsx"], 4 | "comments": false 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | config/*.js 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | mocha: true, 7 | es6: true, 8 | }, 9 | parser: 'babel-eslint', 10 | parserOptions: { 11 | sourceType: 'module' 12 | }, 13 | extends: 'airbnb-base', 14 | // required to lint *.vue files 15 | plugins: [ 16 | 'html' 17 | ], 18 | // check if imports actually resolve 19 | 'settings': { 20 | 'import/resolver': { 21 | 'webpack': { 22 | 'config': 'build/webpack.base.conf.js' 23 | } 24 | } 25 | }, 26 | // add your custom rules here 27 | 'rules': { 28 | // don't require .vue extension when importing 29 | 'import/extensions': ['error', 'always', { 30 | 'js': 'never', 31 | 'vue': 'never' 32 | }], 33 | // 结尾禁用逗号 34 | 'comma-dangle': ['error', 'never'], 35 | // 结尾禁用分号 36 | 'semi': ['error', 'never'], 37 | // 允许使用一元操作符 ++ 和 -- 38 | 'no-plusplus': 'off', 39 | // 可以对函数参数再赋值 40 | 'no-param-reassign': 0, 41 | // allow debugger during development 42 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 43 | // 禁止未使用过的变量,但函数中的参数不检查 44 | 'no-unused-vars': ["error", { "vars": "all", "args": "none" }], 45 | // 允许优先级相同的使用不同的操作符 46 | 'no-mixed-operators': ["error", {"allowSamePrecedence": true}], 47 | // 允许标识符中有悬空下划线 48 | 'no-underscore-dangle': 0 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | /.idea 6 | /node_modules.zip 7 | build/release.sh 8 | -------------------------------------------------------------------------------- /README-zh_CN.md: -------------------------------------------------------------------------------- 1 | # Ant Design of Vue 2 | 3 | [![npm package](https://img.shields.io/npm/v/antv.svg?style=flat-square)](https://www.npmjs.org/package/antv) 4 | 5 | 一套企业级的 UI 设计语言和 Vue 实现。 6 | 7 | [README in English](README.md) 8 | 9 | ## 特性 10 | 11 | - 提炼自企业级中后台产品的交互语言和视觉风格。 12 | - 基于卓越的 [Ant Design](https://ant.design) 实现的高质量 Vue 组件。 13 | - 基于 npm + webpack + babel + [Vue2.0](https://vuejs.org) 的工作流。 14 | 15 | ## 支持环境 16 | 17 | * 现代浏览器和 IE9 及以上。 18 | 19 | ## 安装 20 | 21 | ### 使用 npm 安装 22 | 23 | **我们推荐使用 npm 的方式进行开发**,不仅可在开发环境轻松调试,也可放心地在生产环境打包部署使用,享受整个生态圈和工具链带来的诸多好处。 24 | 25 | ```bash 26 | $ npm install antv --save 27 | ``` 28 | 29 | 如果你的网络环境不佳,推荐使用 [cnpm](https://github.com/cnpm/cnpm)。 30 | 31 | ### 浏览器引入 32 | 33 | 在浏览器中使用 `script` 和 `link` 标签直接引入文件,并使用全局变量 `antv`。 34 | 35 | 我们在 npm 发布包内的 `antv/dist` 目录下提供了 `antv.js` `antv.css` 以及 `antv.min.js` `antv.min.css`。 36 | 37 | ## 示例 38 | 39 | ```jsx 40 | import Vue from 'vue' 41 | import Antv from 'antv' 42 | import 'antv/dist/antv.css' 43 | 44 | Vue.use(Antv) 45 | 46 | new Vue({ 47 | el: '#app', 48 | render() { 49 | return (Primary) 50 | } 51 | }) 52 | ``` 53 | 54 | ## 链接 55 | 56 | - [Antd](http://ant.design/) 57 | - [Element UI](http://element.eleme.io) 58 | - [UI 组件库](/docs/vue/introduce) 59 | - [设计规范速查手册](https://github.com/ant-design/ant-design/wiki/Ant-Design-%E8%AE%BE%E8%AE%A1%E5%9F%BA%E7%A1%80%E7%AE%80%E7%89%88) 60 | - [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design) 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ant Design of Vue 2 | 3 | [![npm package](https://img.shields.io/npm/v/antv.svg?style=flat-square)](https://www.npmjs.org/package/antv) 4 | 5 | An enterprise-class UI design language and Vue-based implementation. 6 | 7 | [中文 README](README-zh_CN.md) 8 | 9 | ## Features 10 | 11 | - An enterprise-class UI design language for web applications. 12 | - A high-quality Vue components based on superior [Ant Design](https://ant.design). 13 | - A npm + webpack + babel + [Vue2.0](https://vuejs.org) front-end development workflow. 14 | 15 | ## Environment Support 16 | 17 | * Browser: Modern browsers and Internet Explorer 9+ 18 | 19 | ## Installation 20 | 21 | ### Using npm 22 | 23 | **We recommend using npm or yarn to install**,it not only makes development easier,but also allow you to take advantage of the rich ecosystem of javascript packages and tooling. 24 | 25 | ```bash 26 | $ npm install antv --save 27 | ``` 28 | 29 | If you are in a bad network enviornment,you can try other registers and tools like [cnpm](https://github.com/cnpm/cnpm). 30 | 31 | ### Import in Browser 32 | 33 | Add `script` and `link` tags in your browser and use the global variable `antv`. 34 | 35 | We provide `antv.js` `antv.css` and `antv.min.js` `antv.min.css` under `antv/dist` in antv's npm package. 36 | 37 | ## Usage 38 | 39 | ```jsx 40 | import Vue from 'vue' 41 | import Antv from 'antv' 42 | import 'antv/dist/antv.css' 43 | 44 | Vue.use(Antv) 45 | 46 | new Vue({ 47 | el: '#app', 48 | render() { 49 | return (Primary) 50 | } 51 | }) 52 | ``` 53 | 54 | ## Links 55 | 56 | - [Antd](http://ant.design/) 57 | - [Element UI](http://element.eleme.io) 58 | - [UI library](/docs/Vue/introduce) 59 | - [Design Code Quick Guide](https://github.com/ant-design/ant-design/wiki/Ant-Design-%E8%AE%BE%E8%AE%A1%E5%9F%BA%E7%A1%80%E7%AE%80%E7%89%88) 60 | - [Awesome Ant Design](https://github.com/websemantics/awesome-ant-design) 61 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | // https://github.com/shelljs/shelljs 2 | require('./check-versions')() 3 | require('shelljs/global') 4 | env.NODE_ENV = 'production' 5 | 6 | var path = require('path') 7 | var config = require('../config') 8 | var ora = require('ora') 9 | var webpack = require('webpack') 10 | var webpackConfig = require('./webpack.prod.conf') 11 | var prodWebpackConfig = require('./webpack.prod.min.conf') 12 | 13 | console.log( 14 | ' Tip:\n' + 15 | ' Built files are meant to be served over an HTTP server.\n' + 16 | ' Opening index.html over file:// won\'t work.\n' 17 | ) 18 | 19 | var spinner = ora('building for production...') 20 | spinner.start() 21 | 22 | var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory) 23 | rm('-rf', assetsPath) 24 | mkdir('-p', assetsPath) 25 | cp('-R', 'static/*', assetsPath) 26 | 27 | webpack([webpackConfig, prodWebpackConfig], function (err, stats) { 28 | spinner.stop() 29 | if (err) throw err 30 | process.stdout.write(stats.toString({ 31 | colors: true, 32 | modules: false, 33 | children: false, 34 | chunks: false, 35 | chunkModules: false 36 | }) + '\n') 37 | }) 38 | -------------------------------------------------------------------------------- /build/check-versions.js: -------------------------------------------------------------------------------- 1 | var semver = require('semver') 2 | var chalk = require('chalk') 3 | var packageConfig = require('../package.json') 4 | var exec = function (cmd) { 5 | return require('child_process') 6 | .execSync(cmd).toString().trim() 7 | } 8 | 9 | var versionRequirements = [ 10 | { 11 | name: 'node', 12 | currentVersion: semver.clean(process.version), 13 | versionRequirement: packageConfig.engines.node 14 | }, 15 | { 16 | name: 'npm', 17 | currentVersion: exec('npm --version'), 18 | versionRequirement: packageConfig.engines.npm 19 | } 20 | ] 21 | 22 | module.exports = function () { 23 | var warnings = [] 24 | for (var i = 0; i < versionRequirements.length; i++) { 25 | var mod = versionRequirements[i] 26 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 27 | warnings.push(mod.name + ': ' + 28 | chalk.red(mod.currentVersion) + ' should be ' + 29 | chalk.green(mod.versionRequirement) 30 | ) 31 | } 32 | } 33 | 34 | if (warnings.length) { 35 | console.log('') 36 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 37 | console.log() 38 | for (var i = 0; i < warnings.length; i++) { 39 | var warning = warnings[i] 40 | console.log(' ' + warning) 41 | } 42 | console.log() 43 | process.exit(1) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /build/dev-client.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | require('eventsource-polyfill') 3 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 4 | 5 | hotClient.subscribe(function (event) { 6 | if (event.action === 'reload') { 7 | window.location.reload() 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /build/dev-server.js: -------------------------------------------------------------------------------- 1 | require('./check-versions')() 2 | var config = require('../config') 3 | if (!process.env.NODE_ENV) process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) 4 | var path = require('path') 5 | var express = require('express') 6 | var webpack = require('webpack') 7 | var opn = require('opn') 8 | var proxyMiddleware = require('http-proxy-middleware') 9 | var webpackConfig = require('./webpack.dev.conf') 10 | 11 | // default port where dev server listens for incoming traffic 12 | var port = process.env.PORT || config.dev.port 13 | // Define HTTP proxies to your custom API backend 14 | // https://github.com/chimurai/http-proxy-middleware 15 | var proxyTable = config.dev.proxyTable 16 | 17 | var app = express() 18 | var compiler = webpack(webpackConfig) 19 | 20 | var devMiddleware = require('webpack-dev-middleware')(compiler, { 21 | publicPath: webpackConfig.output.publicPath, 22 | stats: { 23 | colors: true, 24 | chunks: false 25 | } 26 | }) 27 | 28 | var hotMiddleware = require('webpack-hot-middleware')(compiler) 29 | // force page reload when html-webpack-plugin template changes 30 | compiler.plugin('compilation', function (compilation) { 31 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { 32 | hotMiddleware.publish({ action: 'reload' }) 33 | cb() 34 | }) 35 | }) 36 | 37 | // proxy api requests 38 | Object.keys(proxyTable).forEach(function (context) { 39 | var options = proxyTable[context] 40 | if (typeof options === 'string') { 41 | options = { target: options } 42 | } 43 | app.use(proxyMiddleware(context, options)) 44 | }) 45 | 46 | // handle fallback for HTML5 history API 47 | app.use(require('connect-history-api-fallback')()) 48 | 49 | // serve webpack bundle output 50 | app.use(devMiddleware) 51 | 52 | // enable hot-reload and state-preserving 53 | // compilation error display 54 | app.use(hotMiddleware) 55 | 56 | // serve pure static assets 57 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) 58 | app.use(staticPath, express.static('./static')) 59 | 60 | module.exports = app.listen(port, function (err) { 61 | if (err) { 62 | console.log(err) 63 | return 64 | } 65 | var uri = 'http://localhost:' + port 66 | console.log('Listening at ' + uri + '\n') 67 | 68 | // when env is testing, don't need open it 69 | if (process.env.NODE_ENV !== 'testing') { 70 | opn(uri) 71 | } 72 | }) 73 | -------------------------------------------------------------------------------- /build/git-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | git checkout dev 3 | 4 | if test -n "$(git status --porcelain)"; then 5 | echo 'Unclean working tree. Commit or stash changes first.' >&2; 6 | exit 128; 7 | fi 8 | 9 | if ! git fetch --quiet 2>/dev/null; then 10 | echo 'There was a problem fetching your branch. Run `git fetch` to see more...' >&2; 11 | exit 128; 12 | fi 13 | 14 | if test "0" != "$(git rev-list --count --left-only @'{u}'...HEAD)"; then 15 | echo 'Remote history differ. Please pull changes.' >&2; 16 | exit 128; 17 | fi 18 | 19 | echo 'No conflicts.' >&2; 20 | -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | 5 | exports.assetsPath = function (_path) { 6 | var assetsSubDirectory = process.env.NODE_ENV === 'production' 7 | ? config.build.assetsSubDirectory 8 | : config.dev.assetsSubDirectory 9 | return path.posix.join(assetsSubDirectory, _path) 10 | } 11 | 12 | exports.cssLoaders = function (options) { 13 | options = options || {} 14 | // generate loader string to be used with extract text plugin 15 | function generateLoaders (loaders) { 16 | var sourceLoader = loaders.map(function (loader) { 17 | var extraParamChar 18 | if (/\?/.test(loader)) { 19 | loader = loader.replace(/\?/, '-loader?') 20 | extraParamChar = '&' 21 | } else { 22 | loader = loader + '-loader' 23 | extraParamChar = '?' 24 | } 25 | return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '') 26 | }).join('!') 27 | 28 | // Extract CSS when that option is specified 29 | // (which is the case during production build) 30 | if (options.extract) { 31 | return ExtractTextPlugin.extract('vue-style-loader', sourceLoader) 32 | } else { 33 | return ['vue-style-loader', sourceLoader].join('!') 34 | } 35 | } 36 | 37 | // http://vuejs.github.io/vue-loader/en/configurations/extract-css.html 38 | return { 39 | css: generateLoaders(['css']), 40 | postcss: generateLoaders(['css']), 41 | less: generateLoaders(['css', 'less']), 42 | sass: generateLoaders(['css', 'sass?indentedSyntax']), 43 | scss: generateLoaders(['css', 'sass']), 44 | stylus: generateLoaders(['css', 'stylus']), 45 | styl: generateLoaders(['css', 'stylus']) 46 | } 47 | } 48 | 49 | // Generate loaders for standalone style files (outside of .vue) 50 | exports.styleLoaders = function (options) { 51 | var output = [] 52 | var loaders = exports.cssLoaders(options) 53 | for (var extension in loaders) { 54 | var loader = loaders[extension] 55 | output.push({ 56 | test: new RegExp('\\.' + extension + '$'), 57 | loader: loader 58 | }) 59 | } 60 | return output 61 | } 62 | -------------------------------------------------------------------------------- /build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var utils = require('./utils') 4 | var projectRoot = path.resolve(__dirname, '../') 5 | 6 | var env = process.env.NODE_ENV 7 | // check env & config/index.js to decide weither to enable CSS Sourcemaps for the 8 | // various preprocessor loaders added to vue-loader at the end of this file 9 | var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap) 10 | var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap) 11 | var useCssSourceMap = cssSourceMapDev || cssSourceMapProd 12 | 13 | module.exports = { 14 | entry: { 15 | antv: './components' 16 | }, 17 | output: { 18 | path: config.build.assetsRoot, 19 | publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, 20 | filename: '[name].js' 21 | }, 22 | resolve: { 23 | extensions: ['', '.js', '.vue'], 24 | fallback: [path.join(__dirname, '../node_modules')], 25 | alias: { 26 | 'vue$': 'vue/dist/vue', 27 | 'src': path.resolve(__dirname, '../src'), 28 | 'assets': path.resolve(__dirname, '../src/assets'), 29 | 'components': path.resolve(__dirname, '../src/components') 30 | } 31 | }, 32 | resolveLoader: { 33 | fallback: [path.join(__dirname, '../node_modules')] 34 | }, 35 | module: { 36 | preLoaders: [ 37 | { 38 | test: /\.vue$/, 39 | loader: 'eslint', 40 | include: projectRoot, 41 | exclude: /node_modules/ 42 | }, 43 | { 44 | test: /\.js$/, 45 | loader: 'eslint', 46 | include: projectRoot, 47 | exclude: /node_modules/ 48 | } 49 | ], 50 | loaders: [ 51 | { 52 | test: /\.vue$/, 53 | loader: 'vue' 54 | }, 55 | { 56 | test: /\.js$/, 57 | loader: 'babel', 58 | include: projectRoot, 59 | exclude: /node_modules/ 60 | }, 61 | { 62 | test: /\.json$/, 63 | loader: 'json' 64 | }, 65 | { 66 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 67 | loader: 'url', 68 | query: { 69 | limit: 10000, 70 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 71 | } 72 | }, 73 | { 74 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 75 | loader: 'url', 76 | query: { 77 | limit: 10000, 78 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 79 | } 80 | } 81 | ] 82 | }, 83 | eslint: { 84 | formatter: require('eslint-friendly-formatter') 85 | }, 86 | vue: { 87 | preserveWhitespace: false, 88 | loaders: utils.cssLoaders({ sourceMap: useCssSourceMap }), 89 | postcss: [ 90 | require('autoprefixer')({ 91 | browsers: ['last 2 versions'] 92 | }) 93 | ] 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | var config = require('../config') 2 | var webpack = require('webpack') 3 | var merge = require('webpack-merge') 4 | var utils = require('./utils') 5 | var baseWebpackConfig = require('./webpack.base.conf') 6 | var HtmlWebpackPlugin = require('html-webpack-plugin') 7 | 8 | // add hot-reload related code to entry chunks 9 | Object.keys(baseWebpackConfig.entry).forEach(function (name) { 10 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) 11 | }) 12 | 13 | module.exports = merge(baseWebpackConfig, { 14 | entry: { 15 | antv: './test/main.js' 16 | }, 17 | module: { 18 | loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) 19 | }, 20 | // eval-source-map is faster for development 21 | devtool: '#inline-source-map', 22 | plugins: [ 23 | new webpack.DefinePlugin({ 24 | 'process.env': config.dev.env 25 | }), 26 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 27 | new webpack.optimize.OccurenceOrderPlugin(), 28 | new webpack.OldWatchingPlugin(), 29 | new webpack.HotModuleReplacementPlugin(), 30 | new webpack.NoErrorsPlugin(), 31 | // https://github.com/ampedandwired/html-webpack-plugin 32 | new HtmlWebpackPlugin({ 33 | filename: './test/index.html', 34 | template: './test/index.html', 35 | inject: true 36 | }) 37 | ] 38 | }) 39 | -------------------------------------------------------------------------------- /build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var utils = require('./utils') 4 | var webpack = require('webpack') 5 | var merge = require('webpack-merge') 6 | var baseWebpackConfig = require('./webpack.base.conf') 7 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 8 | var env = config.build.env 9 | 10 | var webpackConfig = merge(baseWebpackConfig, { 11 | module: { 12 | loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true }) 13 | }, 14 | devtool: config.build.productionSourceMap ? '#source-map' : false, 15 | output: { 16 | path: config.build.assetsRoot, 17 | filename: utils.assetsPath('[name].js'), 18 | library: 'antv', 19 | libraryTarget: 'umd', 20 | umdNamedDefine: true 21 | }, 22 | externals: { 23 | vue: { 24 | root: 'Vue', 25 | commonjs: 'vue', 26 | commonjs2: 'vue', 27 | amd: 'vue' 28 | } 29 | }, 30 | vue: { 31 | loaders: utils.cssLoaders({ 32 | sourceMap: false, 33 | extract: true 34 | }) 35 | }, 36 | plugins: [ 37 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 38 | new webpack.DefinePlugin({ 39 | 'process.env': env 40 | }), 41 | new webpack.optimize.OccurrenceOrderPlugin(), 42 | // extract css into its own file 43 | new ExtractTextPlugin(utils.assetsPath('[name].css')), 44 | ] 45 | }) 46 | 47 | module.exports = webpackConfig 48 | -------------------------------------------------------------------------------- /build/webpack.prod.min.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var utils = require('./utils') 3 | var config = require('../config') 4 | var webpack = require('webpack') 5 | var merge = require('webpack-merge') 6 | var prodWebpackConfig = require('./webpack.prod.conf') 7 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 8 | 9 | var webpackConfig = merge(prodWebpackConfig, { 10 | output: { 11 | path: config.build.assetsRoot, 12 | filename: utils.assetsPath('[name].min.js'), 13 | library: 'antv', 14 | libraryTarget: 'umd', 15 | umdNamedDefine: true 16 | }, 17 | plugins: [ 18 | new webpack.optimize.UglifyJsPlugin({ 19 | compress: { 20 | warnings: false 21 | } 22 | }), 23 | new ExtractTextPlugin(utils.assetsPath('[name].min.css')) 24 | ] 25 | }) 26 | 27 | if (config.build.productionGzip) { 28 | var CompressionWebpackPlugin = require('compression-webpack-plugin') 29 | 30 | webpackConfig.plugins.push( 31 | new CompressionWebpackPlugin({ 32 | asset: '[path].gz[query]', 33 | algorithm: 'gzip', 34 | test: new RegExp( 35 | '\\.(' + 36 | config.build.productionGzipExtensions.join('|') + 37 | ')$' 38 | ), 39 | threshold: 10240, 40 | minRatio: 0.8 41 | }) 42 | ) 43 | } 44 | 45 | module.exports = webpackConfig 46 | -------------------------------------------------------------------------------- /components/alert/alert.vue: -------------------------------------------------------------------------------- 1 | 19 | 84 | -------------------------------------------------------------------------------- /components/alert/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Alert from './alert' 4 | 5 | export default Alert 6 | -------------------------------------------------------------------------------- /components/alert/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | 3 | @alert-prefix-cls: ~"@{ant-prefix}-alert"; 4 | 5 | .@{alert-prefix-cls} { 6 | position: relative; 7 | padding: 8px 48px 8px 38px; 8 | border-radius: @border-radius-base; 9 | color: @text-color; 10 | font-size: @font-size-base; 11 | line-height: 16px; 12 | margin-bottom: 10px; 13 | 14 | &&-no-icon { 15 | padding: 8px 48px 8px 16px; 16 | } 17 | 18 | &-icon { 19 | font-size: @font-size-lg; 20 | top: 9.5px; 21 | left: 16px; 22 | position: absolute; 23 | } 24 | 25 | &-description { 26 | font-size: @font-size-base; 27 | line-height: 21px; 28 | display: none; 29 | } 30 | 31 | &-success { 32 | border: @border-width-base @border-style-base @green-2; 33 | background-color: @green-1; 34 | .@{alert-prefix-cls}-icon { 35 | color: @success-color; 36 | } 37 | } 38 | 39 | &-info { 40 | border: @border-width-base @border-style-base @primary-2; 41 | background-color: @primary-1; 42 | .@{alert-prefix-cls}-icon { 43 | color: @primary-color; 44 | } 45 | } 46 | 47 | &-warning { 48 | border: @border-width-base @border-style-base @yellow-2; 49 | background-color: @yellow-1; 50 | .@{alert-prefix-cls}-icon { 51 | color: @warning-color; 52 | } 53 | } 54 | 55 | &-error { 56 | border: @border-width-base @border-style-base @red-2; 57 | background-color: @red-1; 58 | .@{alert-prefix-cls}-icon { 59 | color: @error-color; 60 | } 61 | } 62 | 63 | &-close-icon { 64 | font-size: @font-size-base; 65 | position: absolute; 66 | right: 16px; 67 | top: 10px; 68 | height: 12px; 69 | line-height: 12px; 70 | overflow: hidden; 71 | cursor: pointer; 72 | 73 | .@{iconfont-css-prefix}-cross { 74 | color: @text-color-secondary; 75 | transition: color .3s ease; 76 | &:hover { 77 | color: #404040; 78 | } 79 | } 80 | } 81 | 82 | &-close-text { 83 | position: absolute; 84 | right: 16px; 85 | } 86 | 87 | &-with-description { 88 | padding: 16px 16px 16px 60px; 89 | position: relative; 90 | border-radius: @border-radius-base; 91 | margin-bottom: 10px; 92 | color: @text-color; 93 | line-height: 1.5; 94 | } 95 | 96 | &-with-description&-no-icon { 97 | padding: 16px; 98 | } 99 | 100 | &-with-description &-icon { 101 | position: absolute; 102 | top: 16px; 103 | left: 20px; 104 | font-size: 24px; 105 | } 106 | 107 | &-with-description &-close-icon { 108 | position: absolute; 109 | top: 16px; 110 | right: 16px; 111 | cursor: pointer; 112 | font-size: @font-size-base; 113 | } 114 | 115 | &-with-description &-message { 116 | font-size: @font-size-lg; 117 | color: @heading-color; 118 | display: block; 119 | margin-bottom: 4px; 120 | } 121 | 122 | &-with-description &-description { 123 | display: block; 124 | } 125 | 126 | &&-close { 127 | height: 0 !important; 128 | margin: 0; 129 | padding-top: 0; 130 | padding-bottom: 0; 131 | transition: all .3s @ease-in-out-circ; 132 | transform-origin: 50% 0; 133 | } 134 | 135 | &-slide-up-leave { 136 | animation: antAlertSlideUpOut .3s @ease-in-out-circ; 137 | animation-fill-mode: both; 138 | } 139 | 140 | &-banner { 141 | border-radius: 0; 142 | border: 0; 143 | margin-bottom: 0; 144 | } 145 | } 146 | 147 | @keyframes antAlertSlideUpIn { 148 | 0% { 149 | opacity: 0; 150 | transform-origin: 0% 0%; 151 | transform: scaleY(0); 152 | } 153 | 100% { 154 | opacity: 1; 155 | transform-origin: 0% 0%; 156 | transform: scaleY(1); 157 | } 158 | } 159 | 160 | @keyframes antAlertSlideUpOut { 161 | 0% { 162 | opacity: 1; 163 | transform-origin: 0% 0%; 164 | transform: scaleY(1); 165 | } 166 | 100% { 167 | opacity: 0; 168 | transform-origin: 0% 0%; 169 | transform: scaleY(0); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /components/badge/badge.vue: -------------------------------------------------------------------------------- 1 | 20 | 87 | -------------------------------------------------------------------------------- /components/badge/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Badge from './badge' 4 | 5 | export default Badge 6 | -------------------------------------------------------------------------------- /components/badge/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | 3 | @badge-prefix-cls: ~"@{ant-prefix}-badge"; 4 | @number-prefix-cls: ~"@{ant-prefix}-scroll-number"; 5 | 6 | .@{badge-prefix-cls} { 7 | position: relative; 8 | display: inline-block; 9 | line-height: 1; 10 | vertical-align: middle; 11 | 12 | &-count { 13 | position: absolute; 14 | transform: translateX(-50%); 15 | top: -@badge-height / 2; 16 | height: @badge-height; 17 | border-radius: @badge-height / 2; 18 | min-width: @badge-height; 19 | background: @highlight-color; 20 | color: #fff; 21 | line-height: @badge-height; 22 | text-align: center; 23 | padding: 0 6px; 24 | font-size: @font-size-base; 25 | white-space: nowrap; 26 | transform-origin: -10% center; 27 | font-family: tahoma; 28 | box-shadow: 0 0 0 1px #fff; 29 | a, 30 | a:hover { 31 | color: #fff; 32 | } 33 | } 34 | 35 | &-dot { 36 | position: absolute; 37 | transform: translateX(-50%); 38 | transform-origin: 0 center; 39 | top: -@badge-dot-size / 2; 40 | height: @badge-dot-size; 41 | width: @badge-dot-size; 42 | border-radius: 100%; 43 | background: @highlight-color; 44 | z-index: 10; 45 | box-shadow: 0 0 0 1px #fff; 46 | } 47 | 48 | &-status { 49 | line-height: inherit; 50 | vertical-align: baseline; 51 | 52 | &-dot { 53 | width: 8px; 54 | height: 8px; 55 | display: inline-block; 56 | border-radius: 50%; 57 | } 58 | &-success { 59 | background-color: @success-color; 60 | } 61 | &-processing { 62 | background-color: @primary-color; 63 | position: relative; 64 | &:after { 65 | position: absolute; 66 | top: 0; 67 | left: 0; 68 | width: 100%; 69 | height: 100%; 70 | border-radius: 50%; 71 | border: 1px solid @primary-color; 72 | content: ''; 73 | animation: antStatusProcessing 1.2s infinite ease-in-out; 74 | } 75 | } 76 | &-default { 77 | background-color: @normal-color; 78 | } 79 | &-error { 80 | background-color: @error-color; 81 | } 82 | &-warning { 83 | background-color: @warning-color; 84 | } 85 | &-text { 86 | color: @text-color; 87 | font-size: @font-size-base; 88 | margin-left: 8px; 89 | } 90 | } 91 | 92 | &-zoom-appear, 93 | &-zoom-enter { 94 | animation: antZoomBadgeIn .3s @ease-out-back; 95 | animation-fill-mode: both; 96 | } 97 | 98 | &-zoom-leave { 99 | animation: antZoomBadgeOut .3s @ease-in-back; 100 | animation-fill-mode: both; 101 | } 102 | 103 | &-not-a-wrapper &-count { 104 | top: auto; 105 | display: block; 106 | position: relative; 107 | transform: none!important; 108 | } 109 | } 110 | 111 | @keyframes antStatusProcessing { 112 | 0% { 113 | transform: scale(0.8); 114 | opacity: 0.5; 115 | } 116 | 100% { 117 | transform: scale(2.4); 118 | opacity: 0; 119 | } 120 | } 121 | 122 | .@{number-prefix-cls} { 123 | overflow: hidden; 124 | &-only { 125 | display: inline-block; 126 | transition: transform .3s @ease-in-out; 127 | height: @badge-height; 128 | > p { 129 | height: @badge-height; 130 | } 131 | } 132 | // for IE8/9 display 133 | &.not-support-css-animation &-only { 134 | > p { 135 | display: none; 136 | &.current { 137 | display: block; 138 | } 139 | } 140 | } 141 | } 142 | 143 | @keyframes antZoomBadgeIn { 144 | 0% { 145 | opacity: 0; 146 | transform: scale(0) translateX(-50%); 147 | } 148 | 100% { 149 | transform: scale(1) translateX(-50%); 150 | } 151 | } 152 | 153 | @keyframes antZoomBadgeOut { 154 | 0% { 155 | transform: scale(1) translateX(-50%); 156 | } 157 | 100% { 158 | opacity: 0; 159 | transform: scale(0) translateX(-50%); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /components/breadcrumb/breadcrumb-item.vue: -------------------------------------------------------------------------------- 1 | 12 | 27 | -------------------------------------------------------------------------------- /components/breadcrumb/breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 11 | 30 | -------------------------------------------------------------------------------- /components/breadcrumb/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Breadcrumb from './breadcrumb' 4 | import BreadcrumbItem from './breadcrumb-item' 5 | 6 | Breadcrumb.Item = BreadcrumbItem 7 | 8 | export default Breadcrumb 9 | -------------------------------------------------------------------------------- /components/breadcrumb/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | 3 | @breadcrumb-prefix-cls: ~"@{ant-prefix}-breadcrumb"; 4 | 5 | .@{breadcrumb-prefix-cls} { 6 | color: @text-color; 7 | font-size: @font-size-base; 8 | 9 | a { 10 | color: @text-color; 11 | transition: color .3s; 12 | &:hover { 13 | color: @primary-5; 14 | } 15 | } 16 | 17 | & > span:last-child { 18 | font-weight: bold; 19 | color: @text-color; 20 | } 21 | 22 | & > span:last-child &-separator { 23 | display: none; 24 | } 25 | 26 | &-separator { 27 | margin: 0 8px; 28 | color: rgba(0, 0, 0, 0.3); 29 | } 30 | 31 | &-link { 32 | > .@{iconfont-css-prefix} + span { 33 | margin-left: 4px; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /components/button/button-group.vue: -------------------------------------------------------------------------------- 1 | 6 | 33 | -------------------------------------------------------------------------------- /components/button/button.vue: -------------------------------------------------------------------------------- 1 | 7 | 58 | -------------------------------------------------------------------------------- /components/button/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Button from './button' 4 | import ButtonGroup from './button-group' 5 | 6 | Button.Group = ButtonGroup 7 | export default Button 8 | -------------------------------------------------------------------------------- /components/button/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | @import "./mixin"; 4 | 5 | @btn-prefix-cls: ~"@{ant-prefix}-btn"; 6 | 7 | // for compatibile 8 | @btn-ghost-color : @text-color; 9 | @btn-ghost-bg : transparent; 10 | @btn-ghost-border : @border-color-base; 11 | 12 | // Button styles 13 | // ----------------------------- 14 | .@{btn-prefix-cls} { 15 | .btn; 16 | .btn-default; 17 | 18 | &-primary { 19 | .btn-primary; 20 | 21 | .@{btn-prefix-cls}-group &:not(:first-child):not(:last-child) { 22 | border-right-color: @btn-group-border; 23 | border-left-color: @btn-group-border; 24 | 25 | &:disabled { 26 | border-color: @btn-default-border; 27 | } 28 | } 29 | 30 | .@{btn-prefix-cls}-group &:first-child { 31 | &:not(:last-child) { 32 | border-right-color: @btn-group-border; 33 | &[disabled] { 34 | border-right-color: @btn-default-border; 35 | } 36 | } 37 | } 38 | 39 | .@{btn-prefix-cls}-group &:last-child:not(:first-child), 40 | .@{btn-prefix-cls}-group & + & { 41 | border-left-color: @btn-group-border; 42 | &[disabled] { 43 | border-left-color: @btn-default-border; 44 | } 45 | } 46 | } 47 | 48 | &-ghost { 49 | .btn-ghost; 50 | } 51 | 52 | &-dashed { 53 | .btn-dashed; 54 | } 55 | 56 | &-danger { 57 | .btn-danger; 58 | } 59 | 60 | &-circle, 61 | &-circle-outline { 62 | .btn-circle(@btn-prefix-cls); 63 | } 64 | 65 | &:before { 66 | position: absolute; 67 | top: -1px; 68 | left: -1px; 69 | bottom: -1px; 70 | right: -1px; 71 | background: #fff; 72 | opacity: 0.35; 73 | content: ''; 74 | border-radius: inherit; 75 | z-index: 1; 76 | transition: opacity .2s; 77 | pointer-events: none; 78 | display: none; 79 | } 80 | 81 | &&-loading:before { 82 | display: block; 83 | } 84 | 85 | .@{iconfont-css-prefix} { 86 | transition: all .3s @ease-in-out; 87 | } 88 | 89 | &&-loading:not(&-circle):not(&-circle-outline) { 90 | padding-left: 29px; 91 | pointer-events: none; 92 | position: relative; 93 | .@{iconfont-css-prefix} { 94 | margin-left: -14px; 95 | } 96 | } 97 | 98 | &-sm&-loading:not(&-circle):not(&-circle-outline) { 99 | padding-left: 24px; 100 | .@{iconfont-css-prefix} { 101 | margin-left: -17px; 102 | } 103 | } 104 | 105 | &-group { 106 | .btn-group(@btn-prefix-cls); 107 | } 108 | 109 | &:not(&-circle):not(&-circle-outline)&-icon-only { 110 | padding-left: 8px; 111 | padding-right: 8px; 112 | } 113 | 114 | // http://stackoverflow.com/a/21281554/3040605 115 | &:focus > span, 116 | &:active > span { 117 | position: relative; 118 | } 119 | 120 | // To ensure that a space will be placed between character and `Icon`. 121 | > .@{iconfont-css-prefix} + span, 122 | > span + .@{iconfont-css-prefix} { 123 | margin-left: 0.5em; 124 | } 125 | 126 | &-clicked:after { 127 | content: ''; 128 | position: absolute; 129 | top: -1px; 130 | left: -1px; 131 | bottom: -1px; 132 | right: -1px; 133 | border-radius: inherit; 134 | border: 0 solid @primary-color; 135 | opacity: 0.4; 136 | animation: buttonEffect .4s; 137 | display: block; 138 | } 139 | 140 | &-danger&-clicked:after { 141 | border-color: @btn-danger-bg; 142 | } 143 | 144 | &-background-ghost { 145 | background: transparent!important; 146 | border-color: #fff; 147 | color: #fff; 148 | } 149 | 150 | &-background-ghost&-primary { 151 | .button-variant-other(@primary-color; transparent; @primary-color); 152 | } 153 | } 154 | 155 | @keyframes buttonEffect { 156 | to { 157 | opacity: 0; 158 | top: -6px; 159 | left: -6px; 160 | bottom: -6px; 161 | right: -6px; 162 | border-width: 6px; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /components/card/card.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'VCard', 3 | 4 | props: { 5 | title: null, 6 | extra: null, 7 | bordered: { 8 | type: Boolean, 9 | default: true 10 | }, 11 | bodyStyle: Object, 12 | loading: { 13 | type: Boolean, 14 | default: false 15 | } 16 | }, 17 | 18 | render() { 19 | const vLoading = (

20 |

21 |

22 |

23 |

24 |

25 |

26 |

27 |

) 29 | return ( 30 |
31 | { this.title ?

{this.title}

: null} 32 |
{this.extra || this.$slots.extra}
33 |
{ this.loading ? vLoading : this.$slots.default}
35 |
) 36 | }, 37 | 38 | computed: { 39 | classes() { 40 | return { 41 | 'ant-card-bordered': this.bordered 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /components/card/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Card from './card' 4 | 5 | export default Card 6 | -------------------------------------------------------------------------------- /components/card/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | 3 | @card-prefix-cls: ~"@{ant-prefix}-card"; 4 | 5 | .@{card-prefix-cls} { 6 | background: @component-background; 7 | border-radius: @border-radius-base; 8 | font-size: @font-size-base; 9 | position: relative; 10 | overflow: hidden; 11 | transition: all .3s; 12 | 13 | &:hover { 14 | box-shadow: @box-shadow-base; 15 | border-color: transparent; 16 | } 17 | 18 | &-bordered { 19 | border: @border-width-base @border-style-base @border-color-split; 20 | } 21 | 22 | &-head { 23 | height: 48px; 24 | line-height: 48px; 25 | border-bottom: @border-width-base @border-style-base @border-color-split; 26 | padding: 0 24px; 27 | 28 | &-title { 29 | font-size: @font-size-lg; 30 | display: inline-block; 31 | text-overflow: ellipsis; 32 | width: 100%; 33 | overflow: hidden; 34 | white-space: nowrap; 35 | } 36 | } 37 | 38 | &-extra { 39 | position: absolute; 40 | right: 24px; 41 | top: 14px; 42 | } 43 | 44 | &-body { 45 | padding: 24px; 46 | } 47 | 48 | &-loading &-body { 49 | user-select: none; 50 | } 51 | 52 | &-loading-block { 53 | display: inline-block; 54 | margin: 5px 1% 0; 55 | height: 14px; 56 | border-radius: @border-radius-sm; 57 | background: linear-gradient(90deg, rgba(207, 216, 220, .2), rgba(207, 216, 220, .4), rgba(207, 216, 220, .2)); 58 | animation: card-loading 1.4s ease infinite; 59 | background-size: 600% 600%; 60 | } 61 | } 62 | 63 | @keyframes card-loading { 64 | 0%, 65 | 100% { 66 | background-position: 0 50%; 67 | } 68 | 50% { 69 | background-position: 100% 50%; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /components/checkbox/checkbox-group.vue: -------------------------------------------------------------------------------- 1 | 6 | 36 | 37 | -------------------------------------------------------------------------------- /components/checkbox/checkbox.vue: -------------------------------------------------------------------------------- 1 | 18 | 76 | -------------------------------------------------------------------------------- /components/checkbox/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Checkbox from './checkbox' 4 | import CheckboxGroup from './checkbox-group' 5 | 6 | Checkbox.Group = CheckboxGroup 7 | export default Checkbox 8 | -------------------------------------------------------------------------------- /components/checkbox/store.js: -------------------------------------------------------------------------------- 1 | import BaseStore from '../utils/base-store' 2 | 3 | class Store extends BaseStore { 4 | constructor(component, initialState = {}) { 5 | super(component, initialState) 6 | 7 | this.state = { 8 | value: [], 9 | disabled: false 10 | } 11 | 12 | this.setState(initialState) 13 | } 14 | } 15 | 16 | export default Store 17 | -------------------------------------------------------------------------------- /components/checkbox/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "./mixin"; 3 | 4 | .antCheckboxFn(); 5 | -------------------------------------------------------------------------------- /components/checkbox/style/mixin.less: -------------------------------------------------------------------------------- 1 | @import "../../style/mixins/index"; 2 | 3 | .antCheckboxFn(@checkbox-prefix-cls: ~"@{ant-prefix}-checkbox") { 4 | @checkbox-inner-prefix-cls: ~"@{checkbox-prefix-cls}-inner"; 5 | // 一般状态 6 | .@{checkbox-prefix-cls} { 7 | white-space: nowrap; 8 | cursor: pointer; 9 | outline: none; 10 | display: inline-block; 11 | line-height: 1; 12 | position: relative; 13 | vertical-align: middle; 14 | 15 | .@{checkbox-prefix-cls}-wrapper:hover &, 16 | &:hover &-inner, 17 | &-input:focus + &-inner { 18 | border-color: @primary-color; 19 | } 20 | 21 | &-inner { 22 | position: relative; 23 | top: 0; 24 | left: 0; 25 | display: block; 26 | width: 14px; 27 | height: 14px; 28 | border: @border-width-base @border-style-base @border-color-base; 29 | border-radius: @border-radius-sm; 30 | background-color: #fff; 31 | transition: all .3s; 32 | 33 | &:after { 34 | transform: rotate(45deg) scale(0); 35 | position: absolute; 36 | left: 4px; 37 | top: 1px; 38 | display: table; 39 | width: 5px; 40 | height: 8px; 41 | border: 2px solid #fff; 42 | border-top: 0; 43 | border-left: 0; 44 | content: ' '; 45 | transition: all .1s @ease-in-back; 46 | } 47 | } 48 | 49 | &-input { 50 | position: absolute; 51 | left: 0; 52 | z-index: 1; 53 | cursor: pointer; 54 | .opacity(0); 55 | top: 0; 56 | bottom: 0; 57 | right: 0; 58 | width: 100%; 59 | height: 100%; 60 | } 61 | } 62 | 63 | // 半选状态 64 | .@{checkbox-prefix-cls}-indeterminate .@{checkbox-inner-prefix-cls}:after { 65 | content: ' '; 66 | transform: scale(1); 67 | position: absolute; 68 | left: 2px; 69 | top: 5px; 70 | width: 8px; 71 | height: 1px; 72 | } 73 | 74 | // 选中状态 75 | .@{checkbox-prefix-cls}-checked .@{checkbox-inner-prefix-cls}:after { 76 | transform: rotate(45deg) scale(1); 77 | position: absolute; 78 | left: 4px; 79 | top: 1px; 80 | display: table; 81 | width: 5px; 82 | height: 8px; 83 | border: 2px solid #fff; 84 | border-top: 0; 85 | border-left: 0; 86 | content: ' '; 87 | transition: all .2s @ease-out-back .1s; 88 | } 89 | 90 | .@{checkbox-prefix-cls}-checked, 91 | .@{checkbox-prefix-cls}-indeterminate { 92 | .@{checkbox-inner-prefix-cls} { 93 | background-color: @primary-color; 94 | border-color: @primary-color; 95 | } 96 | } 97 | 98 | .@{checkbox-prefix-cls}-disabled { 99 | &.@{checkbox-prefix-cls}-checked { 100 | .@{checkbox-inner-prefix-cls}:after { 101 | animation-name: none; 102 | border-color: @disabled-color; 103 | } 104 | } 105 | 106 | .@{checkbox-inner-prefix-cls} { 107 | border-color: @border-color-base!important; 108 | background-color: #f3f3f3; 109 | &:after { 110 | animation-name: none; 111 | border-color: #f3f3f3; 112 | } 113 | } 114 | 115 | & + span { 116 | color: @disabled-color; 117 | cursor: not-allowed; 118 | } 119 | } 120 | 121 | .@{checkbox-prefix-cls}-wrapper { 122 | cursor: pointer; 123 | font-size: @font-size-base; 124 | display: inline-block; 125 | &:not(:last-child) { 126 | margin-right: 8px; 127 | } 128 | } 129 | 130 | .@{checkbox-prefix-cls}-wrapper + span, 131 | .@{checkbox-prefix-cls} + span { 132 | padding-left: 8px; 133 | padding-right: 8px; 134 | vertical-align: middle; 135 | } 136 | 137 | .@{checkbox-prefix-cls}-group { 138 | font-size: @font-size-base; 139 | &-item { 140 | display: inline-block; 141 | } 142 | } 143 | 144 | @ie8: \0screen; 145 | 146 | // IE8 hack for https://github.com/ant-design/ant-design/issues/2148 147 | @media @ie8 { 148 | .@{checkbox-prefix-cls}-checked .@{checkbox-prefix-cls}-inner:before, 149 | .@{checkbox-prefix-cls}-checked .@{checkbox-prefix-cls}-inner:after { 150 | .iconfont-font("\e632"); 151 | font-weight: bold; 152 | font-size: 8px; 153 | border: 0; 154 | color: #fff; 155 | left: 2px; 156 | top: 3px; 157 | position: absolute; 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /components/collapse/collapse.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 45 | -------------------------------------------------------------------------------- /components/collapse/index.js: -------------------------------------------------------------------------------- 1 | import Collapse from './collapse' 2 | import Panel from './panel' 3 | 4 | Collapse.Panel = Panel 5 | 6 | export default Collapse 7 | -------------------------------------------------------------------------------- /components/collapse/panel.vue: -------------------------------------------------------------------------------- 1 | 21 | 46 | -------------------------------------------------------------------------------- /components/collapse/store.js: -------------------------------------------------------------------------------- 1 | import BaseStore from '../utils/base-store' 2 | 3 | class Mutations { 4 | openPanel({ indexs }) { 5 | indexs.forEach((index) => { 6 | if (this.state.activeIndexs.indexOf(index) > -1) return 7 | if (this.state.accordion) { 8 | this.state.activeIndexs = [index] 9 | } else { 10 | this.state.activeIndexs.push(index) 11 | } 12 | }) 13 | this.mutations.activeChange.call(this) 14 | } 15 | 16 | closePanel({ indexs }) { 17 | indexs.forEach((index) => { 18 | const i = this.state.activeIndexs.indexOf(index) 19 | if (i > -1) { 20 | this.state.activeIndexs.splice(i, 1) 21 | } 22 | }) 23 | this.mutations.activeChange.call(this) 24 | } 25 | 26 | activeChange() { 27 | this.component.$children.forEach((node) => { 28 | node.active = this.state.activeIndexs.indexOf(node.index) > -1 29 | }) 30 | this.component.$emit('input', this.state.activeIndexs) 31 | this.component.$emit('onChange', this.state.activeIndexs) 32 | } 33 | } 34 | 35 | class Store extends BaseStore { 36 | constructor(component, initialState = {}) { 37 | super(component, initialState) 38 | 39 | this.state = { 40 | activeIndexs: [], 41 | accordion: false 42 | } 43 | this.setState(initialState) 44 | 45 | this.mutations = new Mutations() 46 | } 47 | } 48 | 49 | export default Store 50 | -------------------------------------------------------------------------------- /components/collapse/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | 4 | @collapse-prefix-cls: ~"@{ant-prefix}-collapse"; 5 | 6 | .collapse-close() { 7 | .iconfont-size-under-12px(9px, 0); 8 | } 9 | .collapse-open() { 10 | .iconfont-size-under-12px(9px, 90deg); 11 | } 12 | 13 | .@{collapse-prefix-cls} { 14 | background-color: @background-color-base; 15 | border-radius: @border-radius-base; 16 | border: @border-width-base @border-style-base @border-color-base; 17 | border-bottom: 0; 18 | 19 | & > &-item { 20 | border-bottom: @border-width-base @border-style-base @border-color-base; 21 | > .@{collapse-prefix-cls}-header { 22 | height: 38px; 23 | line-height: 38px; 24 | padding-left: 32px; 25 | color: @text-color; 26 | cursor: pointer; 27 | position: relative; 28 | transition: all .3s; 29 | 30 | &:active { 31 | background-color: #eee!important; 32 | } 33 | 34 | .arrow { 35 | .collapse-close(); 36 | .iconfont-mixin(); 37 | position: absolute; 38 | color: @text-color-secondary; 39 | display: inline-block; 40 | font-weight: bold; 41 | line-height: 40px; 42 | vertical-align: middle; 43 | transition: transform 0.24s; 44 | top: 0; 45 | left: 16px; 46 | /* stylelint-disable declaration-block-no-duplicate-properties */ 47 | top: ~"16px \9"; 48 | left: ~"0 \9"; 49 | /* stylelint-enable declaration-block-no-duplicate-properties */ 50 | &:before { 51 | content: "\E61F"; 52 | } 53 | } 54 | } 55 | } 56 | 57 | &-anim-active { 58 | transition: height .2s @ease-out; 59 | } 60 | 61 | &-content { 62 | overflow: hidden; 63 | color: @text-color; 64 | padding: 0 16px; 65 | background-color: @component-background; 66 | 67 | & > &-box { 68 | padding-top: 16px; 69 | padding-bottom: 16px; 70 | } 71 | 72 | &-inactive { 73 | display: none; 74 | } 75 | } 76 | 77 | &-item:last-child { 78 | > .@{collapse-prefix-cls}-content { 79 | border-radius: 0 0 @border-radius-base @border-radius-base; 80 | } 81 | } 82 | 83 | & > &-item > &-header[aria-expanded="true"] { 84 | .arrow { 85 | .collapse-open(); 86 | } 87 | } 88 | 89 | &-borderless { 90 | background-color: @component-background; 91 | border: 0; 92 | } 93 | 94 | &-borderless > &-item-active { 95 | border: 0; 96 | } 97 | 98 | &-borderless > &-item > &-content { 99 | background-color: transparent; 100 | border-top: @border-width-base @border-style-base @border-color-base; 101 | } 102 | 103 | &-borderless > &-item > &-header { 104 | transition: all .3s; 105 | &:hover { 106 | background-color: @background-color-base; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /components/dropdown/dropdown-item.vue: -------------------------------------------------------------------------------- 1 | 6 | 25 | -------------------------------------------------------------------------------- /components/dropdown/dropdown-menu.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | 23 | 28 | -------------------------------------------------------------------------------- /components/dropdown/dropdown.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 19 | 20 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /components/dropdown/index.js: -------------------------------------------------------------------------------- 1 | import Dropdown from './dropdown' 2 | import DropdownItem from './dropdown-item' 3 | import DropdownMenu from './dropdown-menu' 4 | 5 | Dropdown.DropdownItem = DropdownItem 6 | Dropdown.DropdownMenu = DropdownMenu 7 | 8 | export default Dropdown 9 | -------------------------------------------------------------------------------- /components/grid/col.vue: -------------------------------------------------------------------------------- 1 | 6 | 37 | -------------------------------------------------------------------------------- /components/grid/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Row from './row' 4 | import Col from './col' 5 | 6 | export { Row, Col } 7 | -------------------------------------------------------------------------------- /components/grid/row.vue: -------------------------------------------------------------------------------- 1 | 6 | 47 | -------------------------------------------------------------------------------- /components/grid/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | @import "./mixin"; 4 | 5 | // Grid system 6 | .@{ant-prefix}-row { 7 | .make-row(); 8 | display: block; 9 | } 10 | 11 | .@{ant-prefix}-row-flex { 12 | display: flex; 13 | flex-direction: row; 14 | flex-wrap: wrap; 15 | 16 | &:before, 17 | &:after { 18 | display: flex; 19 | } 20 | } 21 | 22 | // x轴原点 23 | .@{ant-prefix}-row-flex-start { 24 | justify-content: flex-start; 25 | } 26 | 27 | // x轴居中 28 | .@{ant-prefix}-row-flex-center { 29 | justify-content: center; 30 | } 31 | 32 | // x轴反方向 33 | .@{ant-prefix}-row-flex-end { 34 | justify-content: flex-end; 35 | } 36 | 37 | // x轴平分 38 | .@{ant-prefix}-row-flex-space-between { 39 | justify-content: space-between; 40 | } 41 | 42 | // x轴有间隔地平分 43 | .@{ant-prefix}-row-flex-space-around { 44 | justify-content: space-around; 45 | } 46 | 47 | // 顶部对齐 48 | .@{ant-prefix}-row-flex-top { 49 | align-items: flex-start; 50 | } 51 | 52 | // 居中对齐 53 | .@{ant-prefix}-row-flex-middle { 54 | align-items: center; 55 | } 56 | 57 | // 底部对齐 58 | .@{ant-prefix}-row-flex-bottom { 59 | align-items: flex-end; 60 | } 61 | 62 | .@{ant-prefix}-col { 63 | position: relative; 64 | display: block; 65 | } 66 | 67 | .make-grid-columns(); 68 | .make-grid(); 69 | 70 | // Extra small grid 71 | // 72 | // Columns, offsets, pushes, and pulls for extra small devices like 73 | // smartphones. 74 | 75 | .make-grid(-xs); 76 | 77 | // Small grid 78 | // 79 | // Columns, offsets, pushes, and pulls for the small device range, from phones 80 | // to tablets. 81 | 82 | @media (min-width: @screen-sm-min) { 83 | .make-grid(-sm); 84 | } 85 | 86 | // Medium grid 87 | // 88 | // Columns, offsets, pushes, and pulls for the desktop device range. 89 | 90 | @media (min-width: @screen-md-min) { 91 | .make-grid(-md); 92 | } 93 | 94 | // Large grid 95 | // 96 | // Columns, offsets, pushes, and pulls for the large desktop device range. 97 | 98 | @media (min-width: @screen-lg-min) { 99 | .make-grid(-lg); 100 | } 101 | 102 | // Extra Large grid 103 | // 104 | // Columns, offsets, pushes, and pulls for the full hd device range. 105 | 106 | @media (min-width: @screen-xl-min) { 107 | .make-grid(-xl); 108 | } 109 | -------------------------------------------------------------------------------- /components/grid/style/mixin.less: -------------------------------------------------------------------------------- 1 | @import "../../style/mixins/index"; 2 | 3 | // mixins for grid system 4 | // ------------------------ 5 | .make-row(@gutter: @grid-gutter-width) { 6 | position: relative; 7 | margin-left: (@gutter / -2); 8 | margin-right: (@gutter / -2); 9 | height: auto; 10 | .clearfix; 11 | } 12 | 13 | .make-grid-columns() { 14 | .col(@index) { 15 | @item: ~".@{ant-prefix}-col-@{index}, .@{ant-prefix}-col-xs-@{index}, .@{ant-prefix}-col-sm-@{index}, .@{ant-prefix}-col-md-@{index}, .@{ant-prefix}-col-lg-@{index}"; 16 | .col((@index + 1), @item); 17 | } 18 | .col(@index, @list) when (@index =< @grid-columns) { 19 | @item: ~".@{ant-prefix}-col-@{index}, .@{ant-prefix}-col-xs-@{index}, .@{ant-prefix}-col-sm-@{index}, .@{ant-prefix}-col-md-@{index}, .@{ant-prefix}-col-lg-@{index}"; 20 | .col((@index + 1), ~"@{list}, @{item}"); 21 | } 22 | .col(@index, @list) when (@index > @grid-columns) { 23 | @{list} { 24 | position: relative; 25 | // Prevent columns from collapsing when empty 26 | min-height: 1px; 27 | padding-left: (@grid-gutter-width / 2); 28 | padding-right: (@grid-gutter-width / 2); 29 | } 30 | } 31 | .col(1); 32 | } 33 | 34 | .float-grid-columns(@class) { 35 | .col(@index) { // initial 36 | @item: ~".@{ant-prefix}-col@{class}-@{index}"; 37 | .col((@index + 1), @item); 38 | } 39 | .col(@index, @list) when (@index =< @grid-columns) { // general 40 | @item: ~".@{ant-prefix}-col@{class}-@{index}"; 41 | .col((@index + 1), ~"@{list}, @{item}"); 42 | } 43 | .col(@index, @list) when (@index > @grid-columns) { // terminal 44 | @{list} { 45 | float: left; 46 | flex: 0 0 auto; 47 | } 48 | } 49 | .col(1); // kickstart it 50 | } 51 | 52 | // lesshint false 53 | .loop-grid-columns(@index, @class) when (@index > 0) { 54 | .@{ant-prefix}-col@{class}-@{index} { 55 | display: block; 56 | width: percentage((@index / @grid-columns)); 57 | } 58 | .@{ant-prefix}-col@{class}-push-@{index} { 59 | left: percentage((@index / @grid-columns)); 60 | } 61 | .@{ant-prefix}-col@{class}-pull-@{index} { 62 | right: percentage((@index / @grid-columns)); 63 | } 64 | .@{ant-prefix}-col@{class}-offset-@{index} { 65 | margin-left: percentage((@index / @grid-columns)); 66 | } 67 | .@{ant-prefix}-col@{class}-order-@{index} { 68 | order: @index; 69 | } 70 | .loop-grid-columns((@index - 1), @class); 71 | } 72 | 73 | .loop-grid-columns(@index, @class) when (@index = 0) { 74 | .@{ant-prefix}-col@{class}-@{index} { 75 | display: none; 76 | } 77 | .@{ant-prefix}-col-push-@{index} { 78 | left: auto; 79 | } 80 | .@{ant-prefix}-col-pull-@{index} { 81 | right: auto; 82 | } 83 | .@{ant-prefix}-col@{class}-push-@{index} { 84 | left: auto; 85 | } 86 | .@{ant-prefix}-col@{class}-pull-@{index} { 87 | right: auto; 88 | } 89 | .@{ant-prefix}-col@{class}-offset-@{index} { 90 | margin-left: 0; 91 | } 92 | .@{ant-prefix}-col@{class}-order-@{index} { 93 | order: 0; 94 | } 95 | } 96 | 97 | .make-grid(@class: ~'') { 98 | .float-grid-columns(@class); 99 | .loop-grid-columns(@grid-columns, @class); 100 | } 101 | -------------------------------------------------------------------------------- /components/icon/icon.vue: -------------------------------------------------------------------------------- 1 | 4 | 34 | -------------------------------------------------------------------------------- /components/icon/index.js: -------------------------------------------------------------------------------- 1 | import Icon from './icon' 2 | 3 | export default Icon 4 | -------------------------------------------------------------------------------- /components/index.js: -------------------------------------------------------------------------------- 1 | import { Col, Row } from './grid' 2 | import Button from './button' 3 | import Icon from './icon' 4 | import Menu from './menu' 5 | import Pagination from './pagination' 6 | import Tabs from './tabs' 7 | import Breadcrumb from './breadcrumb' 8 | import Dropdown from './dropdown' 9 | import Input from './input' 10 | import InputNumber from './input-number' 11 | import Rate from './rate' 12 | import Radio from './radio' 13 | import Checkbox from './checkbox' 14 | import Switch from './switch' 15 | import Select from './select' 16 | import Badge from './badge' 17 | import Card from './card' 18 | import Collapse from './collapse' 19 | import Tooltip from './tooltip' 20 | import Popover from './popover' 21 | import Tag from './tag' 22 | import Alert from './alert' 23 | import Modal from './modal' 24 | import Message from './message' 25 | import Notification from './notification' 26 | import Popconfirm from './popconfirm' 27 | 28 | import VTransition from './transition' 29 | 30 | const components = { 31 | Menu, 32 | SubMenu: Menu.SubMenu, 33 | MenuItemGroup: Menu.ItemGroup, 34 | MenuItem: Menu.Item, 35 | Pagination, 36 | Tabs, 37 | TabPane: Tabs.Pane, 38 | Breadcrumb, 39 | BreadcrumbItem: Breadcrumb.Item, 40 | Dropdown, 41 | DropdownItem: Dropdown.DropdownItem, 42 | DropdownMenu: Dropdown.DropdownMenu, 43 | Col, 44 | Row, 45 | Button, 46 | ButtonGroup: Button.Group, 47 | Icon, 48 | Input, 49 | InputNumber, 50 | Rate, 51 | Radio, 52 | RadioButton: Radio.RadioButton, 53 | RadioGroup: Radio.Group, 54 | Checkbox, 55 | CheckboxGroup: Checkbox.Group, 56 | Switch, 57 | Select, 58 | Option: Select.Option, 59 | OptionGroup: Select.OptionGroup, 60 | Badge, 61 | Card, 62 | Collapse, 63 | Panel: Collapse.Panel, 64 | Tooltip, 65 | Popover, 66 | Tag, 67 | Alert, 68 | Modal, 69 | Popconfirm, 70 | VTransition 71 | } 72 | 73 | Object.keys(components).forEach((key) => { 74 | if (!components[key].install && components[key].name) { 75 | components[key].install = (Vue) => { 76 | Vue.component(components[key].name, components[key]) 77 | } 78 | } 79 | }) 80 | 81 | const install = (Vue) => { 82 | if (install.installed) return 83 | 84 | Object.keys(components).forEach((key) => { 85 | if (components[key].install) { 86 | Vue.use(components[key]) 87 | } 88 | }) 89 | 90 | Vue.prototype.$message = Message 91 | Vue.prototype.$modal = Modal 92 | Vue.prototype.$notification = Notification 93 | } 94 | 95 | export default { 96 | install, 97 | ...components 98 | } 99 | -------------------------------------------------------------------------------- /components/input-number/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import InputNumber from './input-number' 4 | 5 | export default InputNumber 6 | -------------------------------------------------------------------------------- /components/input-number/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | @import "../../input/style/mixin"; 4 | 5 | @input-number-prefix-cls: ~"@{ant-prefix}-input-number"; 6 | 7 | .handler-disabled() { 8 | opacity: 0.72; 9 | color: #ccc !important; 10 | cursor: not-allowed; 11 | } 12 | 13 | .@{input-number-prefix-cls} { 14 | .input(); 15 | margin: 0; 16 | padding: 0; 17 | font-size: @font-size-base; 18 | height: @input-height-base; 19 | display: inline-block; 20 | border: @border-width-base @border-style-base @border-color-base; 21 | border-radius: @border-radius-base; 22 | width: 80px; 23 | 24 | &-handler { 25 | text-align: center; 26 | line-height: 0; 27 | height: 50%; 28 | overflow: hidden; 29 | color: @text-color-secondary; 30 | position: relative; 31 | transition: all 0.1s linear; 32 | display: block; 33 | width: 100%; 34 | font-weight: bold; 35 | &:active { 36 | background: #f4f4f4; 37 | } 38 | &:hover &-up-inner, 39 | &:hover &-down-inner { 40 | color: @primary-5; 41 | } 42 | } 43 | 44 | &-handler-up-inner, 45 | &-handler-down-inner { 46 | .iconfont-mixin(); 47 | line-height: 12px; 48 | user-select: none; 49 | position: absolute; 50 | width: 12px; 51 | height: 12px; 52 | transition: all 0.1s linear; 53 | .iconfont-size-under-12px(7px); 54 | right: 4px; 55 | color: @text-color-secondary; 56 | } 57 | 58 | &:hover { 59 | .hover(); 60 | } 61 | 62 | &-focused { 63 | .active(); 64 | } 65 | 66 | &-disabled { 67 | .disabled(); 68 | } 69 | 70 | &-input { 71 | width: 100%; 72 | text-align: left; 73 | outline: 0; 74 | -moz-appearance: textfield; 75 | line-height: @input-height-base - 2px; 76 | height: @input-height-base - 2px; 77 | transition: all 0.3s linear; 78 | color: @input-color; 79 | border: 0; 80 | border-radius: @border-radius-base; 81 | padding: 0 7px; 82 | 83 | &[disabled] { 84 | .disabled(); 85 | } 86 | } 87 | 88 | &-lg { 89 | padding: 0; 90 | 91 | input { 92 | height: @input-height-lg - 2px; 93 | line-height: @input-height-lg - 2px; 94 | } 95 | } 96 | 97 | &-sm { 98 | padding: 0; 99 | 100 | input { 101 | height: @input-height-sm - 2px; 102 | line-height: @input-height-sm - 2px; 103 | } 104 | } 105 | 106 | &-handler-wrap { 107 | border-left: @border-width-base @border-style-base @border-color-base; 108 | width: 22px; 109 | height: 100%; 110 | background: @component-background; 111 | position: absolute; 112 | top: 0; 113 | right: 0; 114 | opacity: 0; 115 | border-radius: 0 @border-radius-base @border-radius-base 0; 116 | transition: opacity 0.24s linear 0.1s; 117 | } 118 | 119 | &-handler-wrap:hover &-handler { 120 | height: 40%; 121 | } 122 | 123 | &:hover &-handler-wrap { 124 | opacity: 1; 125 | } 126 | 127 | &-handler-up { 128 | cursor: pointer; 129 | &-inner { 130 | top: 50%; 131 | margin-top: -6px; 132 | &:before { 133 | text-align: center; 134 | content: "\e61e"; 135 | } 136 | } 137 | &:hover { 138 | height: 60%!important; 139 | } 140 | } 141 | 142 | &-handler-down { 143 | border-top: @border-width-base @border-style-base @border-color-base; 144 | top: -1px; 145 | cursor: pointer; 146 | &-inner { 147 | top: 50%; 148 | margin-top: -6px; 149 | &:before { 150 | text-align: center; 151 | content: "\e61d"; 152 | } 153 | } 154 | &:hover { 155 | height: 60%!important; 156 | } 157 | } 158 | 159 | &-handler-down-disabled, 160 | &-handler-up-disabled, 161 | &-disabled { 162 | .@{input-number-prefix-cls}-handler-down-inner, 163 | .@{input-number-prefix-cls}-handler-up-inner { 164 | .handler-disabled(); 165 | } 166 | } 167 | 168 | &-disabled { 169 | .@{input-number-prefix-cls}-input { 170 | opacity: 0.72; 171 | cursor: not-allowed; 172 | background-color: #f3f3f3; 173 | } 174 | .@{input-number-prefix-cls}-handler-wrap { 175 | display: none; 176 | } 177 | .@{input-number-prefix-cls}-handler { 178 | .handler-disabled(); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /components/input/index.js: -------------------------------------------------------------------------------- 1 | import Input from './input' 2 | 3 | export default Input 4 | -------------------------------------------------------------------------------- /components/input/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | @import "./mixin"; 4 | @import "./search-input"; 5 | 6 | // Input styles 7 | .@{ant-prefix}-input { 8 | .input; 9 | } 10 | 11 | //== Style for input-group: input with label, with button or dropdown... 12 | .@{ant-prefix}-input-group { 13 | .input-group(~"@{ant-prefix}-input"); 14 | } 15 | 16 | // Input with suffix 17 | .@{ant-prefix}-input-preSuffix-wrapper { 18 | .input-preSuffix-wrapper(~"@{ant-prefix}-input"); 19 | } 20 | -------------------------------------------------------------------------------- /components/input/style/search-input.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | @import "../../button/style/mixin"; 4 | @import "./mixin"; 5 | 6 | .@{ant-prefix}-input-search-icon { 7 | cursor: pointer; 8 | transition: all .3s; 9 | font-size: 14px; 10 | &:hover { 11 | color: @input-hover-border-color; 12 | } 13 | } 14 | 15 | // code blow is keeped for compatibility 16 | // for this demo: http://1x.ant.design/components/select/#components-select-demo-search-box 17 | // do not delete until 3.x 18 | .@{ant-prefix}-search-input-wrapper { 19 | display: inline-block; 20 | vertical-align: middle; 21 | } 22 | 23 | .@{ant-prefix}-search-input { 24 | &.@{ant-prefix}-input-group .@{ant-prefix}-input:first-child, 25 | &.@{ant-prefix}-input-group .@{ant-prefix}-select:first-child { 26 | border-radius: @border-radius-base; 27 | position: absolute; 28 | top: -1px; 29 | width: 100%; 30 | } 31 | 32 | &.@{ant-prefix}-input-group .@{ant-prefix}-input:first-child { 33 | padding-right: 36px; 34 | } 35 | 36 | .@{ant-prefix}-search-btn { 37 | .btn-default; 38 | border-radius: 0 @border-radius-base - 1 @border-radius-base - 1 0; 39 | left: -1px; 40 | position: relative; 41 | border-width: 0 0 0 1px; 42 | z-index: 2; 43 | padding-left: 8px; 44 | padding-right: 8px; 45 | &:hover { 46 | border-color: @border-color-base; 47 | } 48 | } 49 | &&-focus .@{ant-prefix}-search-btn-noempty, 50 | &:hover .@{ant-prefix}-search-btn-noempty { 51 | .btn-primary; 52 | } 53 | .@{ant-prefix}-select-combobox { 54 | .@{ant-prefix}-select-selection__rendered { 55 | margin-right: 29px; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /components/menu/enum.js: -------------------------------------------------------------------------------- 1 | export default { 2 | MODE: { 3 | VERTICAL: 'vertical', 4 | HORIZONTAL: 'horizontal', 5 | INLINE: 'inline' 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /components/menu/index.js: -------------------------------------------------------------------------------- 1 | import Menu from './menu' 2 | import SubMenu from './sub-menu' 3 | import MenuItemGroup from './menu-item-group' 4 | import MenuItem from './menu-item' 5 | 6 | Menu.SubMenu = SubMenu 7 | Menu.ItemGroup = MenuItemGroup 8 | Menu.Item = MenuItem 9 | 10 | export default Menu 11 | -------------------------------------------------------------------------------- /components/menu/menu-item-group.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 41 | -------------------------------------------------------------------------------- /components/menu/menu-item.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 60 | -------------------------------------------------------------------------------- /components/menu/menu.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | 109 | -------------------------------------------------------------------------------- /components/menu/mixins.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | computed: { 3 | indexPath() { 4 | let parent = this.$parent 5 | const path = [this.index] 6 | while (parent.$options.name !== 'VMenu') { 7 | if (parent.index) { 8 | path.unshift(parent.index) 9 | } 10 | parent = parent.$parent 11 | } 12 | return path 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /components/menu/store.js: -------------------------------------------------------------------------------- 1 | import BaseStore from '../utils/base-store' 2 | 3 | class Mutations { 4 | openMenu({ index, indexPath = [] }) { 5 | if (this.state.openIndexs.indexOf(index) > -1) return 6 | if (this.state.uniqueOpened) { 7 | this.state.openIndexs = [].concat(indexPath) 8 | } else { 9 | this.state.openIndexs.push(index) 10 | } 11 | this.mutations.openChange.call(this) 12 | } 13 | 14 | closeMenu({ index }) { 15 | const i = this.state.openIndexs.indexOf(index) 16 | if (i > -1) { 17 | this.state.openIndexs.splice(i, 1) 18 | this.mutations.openChange.call(this) 19 | } 20 | } 21 | 22 | selectMenu({ index }) { 23 | this.state.selectedIndex = index 24 | 25 | let item 26 | const deepNodes = (nodes) => { 27 | nodes.forEach((node) => { 28 | if (node.$options.name === 'VMenuItem') { 29 | node.selected = node.index === this.state.selectedIndex 30 | if (node.selected) item = node 31 | } else if (node.$options.name === 'VSubMenu') { 32 | let selected = false 33 | const deepNode = (childNodes) => { 34 | childNodes.forEach((childNode) => { 35 | if (childNode.$options.name === 'VMenuItem') { 36 | childNode.selected = childNode.index === this.state.selectedIndex 37 | if (childNode.selected) selected = true 38 | } else { 39 | deepNode(childNode.$children) 40 | } 41 | }) 42 | } 43 | deepNode(node.$children) 44 | node.selected = selected 45 | deepNodes(node.$children) 46 | } else { 47 | deepNodes(node.$children) 48 | } 49 | }) 50 | } 51 | 52 | deepNodes(this.component.$children) 53 | if (item) { 54 | if (this.state.router) this.component.$router.push({ name: this.state.selectedIndex }) 55 | this.component.$emit('onSelect', this.state.selectedIndex, item) 56 | } 57 | } 58 | 59 | openChange() { 60 | const deepNodes = (nodes) => { 61 | nodes.forEach((node) => { 62 | if (node.$options.name === 'VSubMenu') { 63 | node.opened = this.state.openIndexs.indexOf(node.index) > -1 64 | } 65 | deepNodes(node.$children) 66 | }) 67 | } 68 | deepNodes(this.component.$children) 69 | this.component.$emit('onOpenChange', this.state.openIndexs) 70 | } 71 | } 72 | 73 | class Store extends BaseStore { 74 | constructor(component, initialState = {}) { 75 | super(component, initialState) 76 | 77 | this.state = { 78 | ENUM_MODE: { 79 | VERTICAL: 'vertical', 80 | HORIZONTAL: 'horizontal', 81 | INLINE: 'inline' 82 | }, 83 | uniqueOpened: false, 84 | openIndexs: [], 85 | selectedIndex: '', 86 | router: false 87 | } 88 | this.setState(initialState) 89 | 90 | this.mutations = new Mutations() 91 | } 92 | } 93 | 94 | export default Store 95 | -------------------------------------------------------------------------------- /components/menu/sub-menu.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 119 | -------------------------------------------------------------------------------- /components/message/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Message from './message' 4 | 5 | export default Message 6 | -------------------------------------------------------------------------------- /components/message/message.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import VNodes from './nodes' 4 | 5 | const VMessage = Vue.extend(VNodes) 6 | 7 | let instance 8 | let instancePool = [] // 保持生成的实例用于destroy 9 | let defaultDuration = 1.5 // 自动关闭延时 10 | let defaultTop = 16 // 消息距离顶部的位置 11 | const noop = () => { 12 | } 13 | 14 | function getInstance() { 15 | if (instance) return instance 16 | const message = new VMessage() 17 | message.top = defaultTop 18 | instance = message.$mount() 19 | instancePool.push(instance) 20 | document.body.appendChild(instance.$el) 21 | return instance 22 | } 23 | 24 | class Message { 25 | static config({ top = defaultTop, duration = defaultDuration }) { 26 | // 对比新旧实例的配置,配置改变了需删除旧的 27 | if (instance && instance.top !== top) { 28 | instance = null 29 | } 30 | defaultTop = top 31 | defaultDuration = duration 32 | } 33 | 34 | static destroy() { 35 | if (instancePool.length) { 36 | instancePool.forEach((_instance) => { 37 | _instance.visible = false 38 | _instance.$nextTick(() => { 39 | _instance.$destroy() 40 | _instance = null 41 | }) 42 | }) 43 | instance = null 44 | instancePool = [] 45 | } 46 | } 47 | } 48 | 49 | ['info', 'success', 'error', 'warning', 'loading'].forEach((type) => { 50 | Message[type] = (content, duration = defaultDuration, onClose = noop) => { 51 | getInstance().add({ type, content, duration, onClose }) 52 | } 53 | }) 54 | 55 | 56 | export default Message 57 | -------------------------------------------------------------------------------- /components/message/node.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'VNode', 3 | 4 | props: { 5 | index: String, 6 | content: String, 7 | duration: Number, 8 | type: { 9 | type: String, 10 | default: 'info' 11 | }, 12 | onClose: Function 13 | }, 14 | 15 | render() { 16 | return ( 17 |
18 |
19 |
20 | 21 | { this.content } 22 |
23 |
24 |
25 |
) 26 | }, 27 | 28 | mounted() { 29 | if (this.duration) { 30 | setTimeout(() => { 31 | this.$parent.remove(this.index) 32 | if (this.onClose) { 33 | this.$nextTick(() => { 34 | this.onClose.call(this, this.index) 35 | }) 36 | } 37 | }, this.duration * 1000) 38 | } 39 | }, 40 | 41 | computed: { 42 | iconCls() { 43 | const icon = { 44 | info: 'info', 45 | success: 'check', 46 | warning: 'exclamation', 47 | error: 'cross' 48 | }[this.type] 49 | return { 50 | [`anticon-${icon}-circle`]: !!icon, 51 | 'anticon-spin anticon-loading': this.type === 'loading' 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /components/message/nodes.js: -------------------------------------------------------------------------------- 1 | import VNode from './node' 2 | 3 | let seed = 0 4 | const now = Date.now() 5 | 6 | function getUuid() { 7 | return `message_${now}_${seed++}` 8 | } 9 | 10 | export default { 11 | name: 'VNodes', 12 | 13 | data() { 14 | return { 15 | top: 24, 16 | nodes: [], 17 | visible: true 18 | } 19 | }, 20 | 21 | methods: { 22 | add(node) { 23 | node.index = getUuid() 24 | if (this.nodes.some(n => n.index === node.index)) return 25 | this.nodes.push(node) 26 | }, 27 | 28 | remove(index) { 29 | this.nodes = this.nodes.filter(node => node.index !== index) 30 | } 31 | }, 32 | 33 | render() { 34 | const vnodes = this.nodes.map(node => 35 | (())) 36 | const vm = (
37 | {vnodes} 38 |
) 39 | return (this.visible ? vm : null) 40 | }, 41 | 42 | components: { 43 | VNode 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /components/message/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | 3 | @message-prefix-cls: ~"@{ant-prefix}-message"; 4 | 5 | .@{message-prefix-cls} { 6 | font-size: @font-size-base; 7 | position: fixed; 8 | z-index: @zindex-message; 9 | width: 100%; 10 | top: 16px; 11 | left: 0; 12 | pointer-events: none; 13 | 14 | &-notice { 15 | padding: 8px; 16 | text-align: center; 17 | &:first-child { 18 | margin-top: -8px; 19 | } 20 | } 21 | 22 | &-notice-content { 23 | padding: 8px 16px; 24 | border-radius: @border-radius-base; 25 | box-shadow: @shadow-2; 26 | background: @component-background; 27 | display: inline-block; 28 | pointer-events: all; 29 | } 30 | 31 | &-success .@{iconfont-css-prefix} { 32 | color: @success-color; 33 | } 34 | 35 | &-error .@{iconfont-css-prefix} { 36 | color: @error-color; 37 | } 38 | 39 | &-warning .@{iconfont-css-prefix} { 40 | color: @warning-color; 41 | } 42 | 43 | &-info .@{iconfont-css-prefix}, 44 | &-loading .@{iconfont-css-prefix} { 45 | color: @primary-color; 46 | } 47 | 48 | .@{iconfont-css-prefix} { 49 | margin-right: 8px; 50 | font-size: @font-size-lg; 51 | top: 1px; 52 | position: relative; 53 | } 54 | 55 | &-notice.move-up-leave.move-up-leave-active { 56 | animation-name: MessageMoveOut; 57 | overflow: hidden; 58 | animation-duration: .3s; 59 | } 60 | } 61 | 62 | @keyframes MessageMoveOut { 63 | 0% { 64 | opacity: 1; 65 | max-height: 150px; 66 | padding: 8px; 67 | } 68 | 100% { 69 | opacity: 0; 70 | max-height: 0; 71 | padding: 0; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /components/modal/confirm.js: -------------------------------------------------------------------------------- 1 | import Modal from './modal' 2 | 3 | export default { 4 | props: { 5 | title: [String, Object], 6 | content: [String, Object], 7 | okText: { 8 | type: String, 9 | default: '确定' 10 | }, 11 | cancelText: { 12 | type: String, 13 | default: '取消' 14 | }, 15 | maskClosable: { 16 | type: Boolean, 17 | default: false 18 | }, 19 | width: { 20 | type: Number, 21 | default: 416 22 | }, 23 | type: { 24 | type: String, 25 | default: 'confirm' 26 | }, 27 | iconType: { 28 | type: String, 29 | default: 'question-circle' 30 | }, 31 | okCancel: { 32 | type: Boolean, 33 | default: false 34 | }, 35 | onOk: Function, 36 | onCancel: Function 37 | }, 38 | 39 | data() { 40 | return { 41 | visible: false 42 | } 43 | }, 44 | 45 | render() { 46 | const modalData = { 47 | ref: 'modal', 48 | props: { 49 | value: this.visible, 50 | title: null, 51 | closable: false, 52 | footer: null, 53 | wrapClassName: `ant-confirm ant-confirm-${this.type}`, 54 | ...this.$props 55 | }, 56 | on: { 57 | onCancel: this.close.bind(this, 'cancel'), 58 | afterClose: this.afterClose 59 | } 60 | } 61 | 62 | return ( 63 |
64 |
65 | 66 | {this.title} 67 |
{this.content}
68 |
69 |
70 | {this.okCancel ? 71 | Cancel : null} 72 | Ok 73 |
74 |
75 |
) 76 | }, 77 | 78 | mounted() { 79 | document.body.appendChild(this.$el) 80 | this.visible = true 81 | }, 82 | 83 | methods: { 84 | close(type) { 85 | this.visible = false 86 | if (type === 'cancel' && this.onCancel) { 87 | this.onCancel() 88 | } else if (type === 'ok' && this.onOk) { 89 | this.onOk() 90 | } 91 | }, 92 | 93 | afterClose() { 94 | document.body.removeChild(this.$el) 95 | this.$destroy() 96 | } 97 | }, 98 | 99 | components: { 100 | Modal 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /components/modal/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import '../style/index.less' 4 | import './style/index.less' 5 | 6 | // style dependencies 7 | import '../button/style/index.less' 8 | 9 | 10 | import Modal from './modal' 11 | import Confirm from './confirm' 12 | 13 | const VConfirm = Vue.extend(Confirm) 14 | 15 | function getInstance(props) { 16 | const confirm = new VConfirm() 17 | Object.assign(confirm, props) 18 | return confirm.$mount() 19 | } 20 | 21 | Modal.confirm = function (props = {}) { 22 | props.okCancel = true 23 | return getInstance(props) 24 | } 25 | 26 | Modal.info = function (props = {}) { 27 | props.type = 'info' 28 | props.iconType = 'info-circle' 29 | return getInstance(props) 30 | } 31 | 32 | Modal.success = function (props = {}) { 33 | props.type = 'success' 34 | props.iconType = 'check-circle' 35 | return getInstance(props) 36 | } 37 | 38 | Modal.error = function (props = {}) { 39 | props.type = 'error' 40 | props.iconType = 'cross-circle' 41 | return getInstance(props) 42 | } 43 | 44 | Modal.warning = function (props = {}) { 45 | props.type = 'warning' 46 | props.iconType = 'exclamation-circle' 47 | return getInstance(props) 48 | } 49 | 50 | export default Modal 51 | -------------------------------------------------------------------------------- /components/modal/style/confirm.less: -------------------------------------------------------------------------------- 1 | @import "../../style/mixins/index"; 2 | 3 | @confirm-prefix-cls: ~"@{ant-prefix}-confirm"; 4 | 5 | .@{confirm-prefix-cls} { 6 | .@{ant-prefix}-modal-header { 7 | display: none; 8 | } 9 | 10 | .@{ant-prefix}-modal-close { 11 | display: none; 12 | } 13 | 14 | .@{ant-prefix}-modal-body { 15 | padding: 30px 40px; 16 | } 17 | 18 | &-body-wrapper { 19 | .clearfix(); 20 | } 21 | 22 | &-body { 23 | .@{confirm-prefix-cls}-title { 24 | color: @text-color; 25 | font-weight: bold; 26 | font-size: @font-size-lg; 27 | } 28 | 29 | .@{confirm-prefix-cls}-content { 30 | margin-left: 42px; 31 | font-size: @font-size-base; 32 | color: @text-color; 33 | margin-top: 8px; 34 | } 35 | 36 | > .@{iconfont-css-prefix} { 37 | font-size: 24px; 38 | margin-right: 16px; 39 | padding: 0 1px; 40 | float: left; 41 | } 42 | } 43 | 44 | .@{confirm-prefix-cls}-btns { 45 | margin-top: 30px; 46 | float: right; 47 | 48 | button + button { 49 | margin-left: 10px; 50 | margin-bottom: 0; 51 | } 52 | } 53 | 54 | &-error &-body > .@{iconfont-css-prefix} { 55 | color: @error-color; 56 | } 57 | 58 | &-warning &-body > .@{iconfont-css-prefix}, 59 | &-confirm &-body > .@{iconfont-css-prefix} { 60 | color: @warning-color; 61 | } 62 | 63 | &-info &-body > .@{iconfont-css-prefix} { 64 | color: @primary-color; 65 | } 66 | 67 | &-success &-body > .@{iconfont-css-prefix} { 68 | color: @success-color; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /components/modal/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "./modal"; 3 | @import "./confirm"; 4 | -------------------------------------------------------------------------------- /components/modal/style/modal.less: -------------------------------------------------------------------------------- 1 | @dialog-prefix-cls: ~"@{ant-prefix}-modal"; 2 | 3 | .@{dialog-prefix-cls} { 4 | position: relative; 5 | width: auto; 6 | margin: 0 auto; 7 | top: 100px; 8 | padding-bottom: 24px; 9 | 10 | &-wrap { 11 | position: fixed; 12 | overflow: auto; 13 | top: 0; 14 | right: 0; 15 | bottom: 0; 16 | left: 0; 17 | z-index: @zindex-modal; 18 | -webkit-overflow-scrolling: touch; 19 | outline: 0; 20 | } 21 | 22 | &-title { 23 | margin: 0; 24 | font-size: @font-size-lg; 25 | line-height: 21px; 26 | font-weight: bold; 27 | } 28 | 29 | &-content { 30 | position: relative; 31 | background-color: @component-background; 32 | border: 0; 33 | border-radius: @border-radius-base; 34 | background-clip: padding-box; 35 | box-shadow: @shadow-2; 36 | } 37 | 38 | &-close { 39 | cursor: pointer; 40 | border: 0; 41 | background: transparent; 42 | position: absolute; 43 | right: 16px; 44 | top: 16px; 45 | z-index: 10; 46 | font-weight: 700; 47 | line-height: 1; 48 | text-decoration: none; 49 | transition: color .3s ease; 50 | color: @text-color-secondary; 51 | outline: 0; 52 | 53 | &-x { 54 | display: block; 55 | font-style: normal; 56 | vertical-align: baseline; 57 | text-align: center; 58 | text-transform: none; 59 | text-rendering: auto; 60 | width: 14px; 61 | height: 14px; 62 | font-size: @font-size-lg; 63 | line-height: 1; 64 | 65 | &:before { 66 | content: "\e633"; 67 | display: block; 68 | font-family: "anticon" !important; 69 | } 70 | } 71 | 72 | &:focus, 73 | &:hover { 74 | color: #444; 75 | text-decoration: none; 76 | } 77 | } 78 | 79 | &-header { 80 | padding: 14px 16px; 81 | border-radius: @border-radius-base @border-radius-base 0 0; 82 | background: @component-background; 83 | color: @text-color; 84 | border-bottom: @border-width-base @border-style-base @border-color-split; 85 | } 86 | 87 | &-body { 88 | padding: 16px; 89 | font-size: @font-size-base; 90 | line-height: 1.5; 91 | } 92 | 93 | &-footer { 94 | border-top: @border-width-base @border-style-base @border-color-split; 95 | padding: 10px 16px 10px 10px; 96 | text-align: right; 97 | border-radius: 0 0 @border-radius-base @border-radius-base; 98 | button + button { 99 | margin-left: 8px; 100 | margin-bottom: 0; 101 | } 102 | } 103 | 104 | &.zoom-enter, 105 | &.zoom-appear { 106 | animation-duration: @animation-duration-slow; 107 | transform: none; // reset scale avoid mousePosition bug 108 | opacity: 0; 109 | } 110 | 111 | &-mask { 112 | position: fixed; 113 | top: 0; 114 | right: 0; 115 | left: 0; 116 | bottom: 0; 117 | background-color: #373737; 118 | background-color: rgba(55, 55, 55, 0.6); // lesshint duplicateProperty: false 119 | height: 100%; 120 | z-index: @zindex-modal-mask; 121 | filter: ~"alpha(opacity=50)"; 122 | 123 | &-hidden { 124 | display: none; 125 | } 126 | } 127 | 128 | &-open { 129 | overflow: hidden; 130 | } 131 | } 132 | 133 | @media (max-width: 768px) { 134 | .@{dialog-prefix-cls} { 135 | width: auto !important; 136 | margin: 10px; 137 | } 138 | .vertical-center-modal { 139 | .@{dialog-prefix-cls} { 140 | flex: 1; 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /components/notification/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Message from './notification' 4 | 5 | export default Message 6 | -------------------------------------------------------------------------------- /components/notification/node.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'VNode', 3 | 4 | props: { 5 | index: String, 6 | icon: [String, Object], 7 | message: [String, Object], 8 | description: String, 9 | duration: Number, 10 | type: { 11 | type: String, 12 | default: 'open' 13 | }, 14 | onClose: Function 15 | }, 16 | 17 | render() { 18 | return ( 19 |
20 |
21 |
22 | {this.vicon ? {this.vicon} : null} 23 |
{ this.message }
24 |
{ this.description }
25 |
26 |
27 | 29 |
30 |
) 31 | }, 32 | 33 | mounted() { 34 | if (this.duration) { 35 | this.closeTimer = setTimeout(this.close, this.duration * 1000) 36 | } 37 | }, 38 | 39 | computed: { 40 | vicon() { 41 | if (this.icon) return this.icon 42 | return this.type !== 'open' ? () : '' 43 | }, 44 | 45 | iconCls() { 46 | const icon = { 47 | info: 'info', 48 | success: 'check', 49 | warning: 'exclamation', 50 | error: 'cross' 51 | }[this.type] 52 | return { 53 | [`anticon-${icon}-circle-o ant-notification-notice-icon ant-notification-notice-icon-${this.type}`]: !!icon, 54 | 'anticon-spin anticon-loading': this.type === 'loading' 55 | } 56 | } 57 | }, 58 | 59 | methods: { 60 | clearCloseTimer() { 61 | if (this.closeTimer) { 62 | clearTimeout(this.closeTimer) 63 | this.closeTimer = null 64 | } 65 | }, 66 | 67 | close() { 68 | this.clearCloseTimer() 69 | this.$parent.remove(this.index) 70 | if (this.onClose) { 71 | this.$nextTick(() => { 72 | this.onClose.call(this, this.index) 73 | }) 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /components/notification/nodes.js: -------------------------------------------------------------------------------- 1 | import VNode from './node' 2 | 3 | let seed = 0 4 | const now = Date.now() 5 | 6 | function getUuid() { 7 | return `notification_${now}_${seed++}` 8 | } 9 | 10 | export default { 11 | name: 'VNodes', 12 | 13 | data() { 14 | return { 15 | top: 24, 16 | bottom: 24, 17 | placement: 'topRight', 18 | nodes: [], 19 | visible: true 20 | } 21 | }, 22 | 23 | methods: { 24 | add(node) { 25 | this.placement = node.placement 26 | node.index = getUuid() 27 | if (this.nodes.some(n => n.index === node.index)) return 28 | this.nodes.push(node) 29 | }, 30 | 31 | remove(index) { 32 | this.nodes = this.nodes.filter(node => node.index !== index) 33 | } 34 | }, 35 | 36 | computed: { 37 | /** 38 | * 将placement转换为页面上四个角的位置 39 | * @returns {*} 40 | */ 41 | getPlacementStyle() { 42 | let style 43 | switch (this.placement) { 44 | case 'topLeft': 45 | style = { 46 | left: 0, 47 | top: `${this.top}px`, 48 | bottom: 'auto' 49 | } 50 | break 51 | case 'bottomLeft': 52 | style = { 53 | left: 0, 54 | top: 'auto', 55 | bottom: `${this.bottom}px` 56 | } 57 | break 58 | case 'bottomRight': 59 | style = { 60 | right: 0, 61 | top: 'auto', 62 | bottom: `${this.bottom}px` 63 | } 64 | break 65 | default: 66 | style = { 67 | right: 0, 68 | top: `${this.top}px`, 69 | bottom: 'auto' 70 | } 71 | } 72 | return style 73 | }, 74 | 75 | vdata() { 76 | return { 77 | class: [`ant-notification-${this.placement}`], 78 | style: this.getPlacementStyle 79 | } 80 | } 81 | }, 82 | 83 | render() { 84 | // 每个node增加key属性,每次nodes数据变化时强制替换组件,避免vue重复使用组件导致组件内使用setTimeout时引用错误 85 | const vnodes = this.nodes.map(node => 86 | (())) 87 | const vm = ( 88 |
89 | {vnodes} 90 |
) 91 | return (this.visible ? vm : null) 92 | }, 93 | 94 | components: { 95 | VNode 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /components/notification/notification.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import VNodes from './nodes' 4 | 5 | const VNotification = Vue.extend(VNodes) 6 | 7 | let instance 8 | let instancePool = [] // 保持生成的实例用于destroy 9 | let defaultDuration = 4.5 // 自动关闭延时 10 | let defaultTop = 24 // 消息距离顶部的位置 11 | let defaultBottom = 24 // 消息距离底部的位置 12 | let defaultPlacement = 'topRight' // 弹出位置,可选 topLeft topRight bottomLeft bottomRight 13 | const noop = () => { 14 | } 15 | 16 | function getInstance() { 17 | if (instance) return instance 18 | const notification = new VNotification() 19 | notification.top = defaultTop 20 | notification.bottom = defaultBottom 21 | notification.placement = defaultPlacement 22 | instance = notification.$mount() 23 | instancePool.push(instance) 24 | document.body.appendChild(instance.$el) 25 | return instance 26 | } 27 | 28 | class Notification { 29 | static config({ 30 | top = defaultTop, 31 | bottom = defaultBottom, 32 | placement = defaultPlacement, 33 | duration = defaultDuration 34 | }) { 35 | // 对比新旧实例的配置,配置改变了需删除旧的 36 | if (instance && 37 | (instance.top !== top || instance.bottom !== bottom || instance.placement !== placement)) { 38 | instance = null 39 | } 40 | defaultTop = top 41 | defaultBottom = bottom 42 | defaultPlacement = placement 43 | defaultDuration = duration 44 | } 45 | 46 | static destroy() { 47 | if (instancePool.length) { 48 | instancePool.forEach((_instance) => { 49 | _instance.visible = false 50 | _instance.$nextTick(() => { 51 | _instance.$destroy() 52 | _instance = null 53 | }) 54 | }) 55 | instance = null 56 | instancePool = [] 57 | } 58 | } 59 | } 60 | 61 | ['open', 'info', 'success', 'error', 'warning'].forEach((type) => { 62 | Notification[type] = ({ 63 | icon, 64 | message, 65 | description, 66 | duration = defaultDuration, 67 | placement = defaultPlacement, 68 | onClose = noop 69 | }) => { 70 | // 若旧实例的位置和新实例的位置不同,需删除旧的 71 | if (instance && instance.placement !== placement) { 72 | instance = null 73 | } 74 | getInstance().add({ type, icon, message, description, duration, placement, onClose }) 75 | } 76 | }) 77 | 78 | export default Notification 79 | -------------------------------------------------------------------------------- /components/notification/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | 4 | @notification-prefix-cls: ~"@{ant-prefix}-notification"; 5 | @notification-width: 335px; 6 | @notification-padding: 16px; 7 | @notification-margin-bottom: 10px; 8 | 9 | .@{notification-prefix-cls} { 10 | position: fixed; 11 | z-index: @zindex-notification; 12 | width: @notification-width; 13 | margin-right: 24px; 14 | 15 | &-topLeft, 16 | &-bottomLeft { 17 | margin-left: 24px; 18 | margin-right: 0; 19 | 20 | .@{notification-prefix-cls}-fade-enter.@{notification-prefix-cls}-fade-enter-active, 21 | .@{notification-prefix-cls}-fade-appear.@{notification-prefix-cls}-fade-appear-active { 22 | animation-name: NotificationLeftFadeIn; 23 | } 24 | } 25 | 26 | &-notice { 27 | padding: @notification-padding; 28 | border-radius: @border-radius-base; 29 | box-shadow: @shadow-2; 30 | background: @component-background; 31 | line-height: 1.5; 32 | position: relative; 33 | margin-bottom: @notification-margin-bottom; 34 | overflow: hidden; 35 | 36 | &-message { 37 | font-size: @font-size-lg; 38 | color: @heading-color; 39 | margin-bottom: 4px; 40 | line-height: 20px; 41 | } 42 | 43 | &-description { 44 | font-size: @font-size-base; 45 | } 46 | 47 | &-closable &-message { 48 | padding-right: 24px; 49 | } 50 | 51 | &-with-icon &-message { 52 | font-size: @font-size-lg; 53 | margin-left: 48px; 54 | margin-bottom: 4px; 55 | } 56 | 57 | &-with-icon &-description { 58 | margin-left: 48px; 59 | font-size: @font-size-base; 60 | } 61 | 62 | &-icon { 63 | float: left; 64 | font-size: 32px; 65 | line-height: 32px; 66 | 67 | &-success { 68 | color: @success-color; 69 | } 70 | &-info { 71 | color: @primary-color; 72 | } 73 | &-warning { 74 | color: @warning-color; 75 | } 76 | &-error { 77 | color: @error-color; 78 | } 79 | } 80 | 81 | &-close-x:after { 82 | font-size: @font-size-base; 83 | content: "\e633"; 84 | font-family: "anticon"; 85 | cursor: pointer; 86 | } 87 | 88 | &-close { 89 | position: absolute; 90 | right: 16px; 91 | top: 10px; 92 | color: @text-color-secondary; 93 | outline: none; 94 | &:hover { 95 | color: #404040; 96 | } 97 | } 98 | 99 | &-btn { 100 | float: right; 101 | margin-top: 16px; 102 | } 103 | } 104 | 105 | .notification-fade-effect { 106 | animation-duration: 0.24s; 107 | animation-fill-mode: both; 108 | animation-timing-function: @ease-in-out; 109 | } 110 | 111 | &-fade-enter, 112 | &-fade-appear { 113 | opacity: 0; 114 | .notification-fade-effect(); 115 | animation-play-state: paused; 116 | } 117 | 118 | &-fade-leave { 119 | .notification-fade-effect(); 120 | animation-duration: 0.2s; 121 | animation-play-state: paused; 122 | } 123 | 124 | &-fade-enter&-fade-enter-active, 125 | &-fade-appear&-fade-appear-active { 126 | animation-name: NotificationFadeIn; 127 | animation-play-state: running; 128 | } 129 | 130 | &-fade-leave&-fade-leave-active { 131 | animation-name: NotificationFadeOut; 132 | animation-play-state: running; 133 | } 134 | } 135 | 136 | @keyframes NotificationFadeIn { 137 | 0% { 138 | opacity: 0; 139 | left: @notification-width; 140 | } 141 | 100% { 142 | left: 0; 143 | opacity: 1; 144 | } 145 | } 146 | 147 | @keyframes NotificationLeftFadeIn { 148 | 0% { 149 | opacity: 0; 150 | right: @notification-width; 151 | } 152 | 100% { 153 | right: 0; 154 | opacity: 1; 155 | } 156 | } 157 | 158 | @keyframes NotificationFadeOut { 159 | 0% { 160 | opacity: 1; 161 | margin-bottom: @notification-margin-bottom; 162 | padding-top: @notification-padding; 163 | padding-bottom: @notification-padding; 164 | max-height: 150px; 165 | } 166 | 100% { 167 | opacity: 0; 168 | margin-bottom: 0; 169 | padding-top: 0; 170 | padding-bottom: 0; 171 | max-height: 0; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /components/pagination/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Pagination from './pagination' 4 | 5 | export default Pagination 6 | -------------------------------------------------------------------------------- /components/pagination/mixins.js: -------------------------------------------------------------------------------- 1 | import KEYCODE from '../utils/keycode' 2 | 3 | export default { 4 | props: { 5 | page: Number, 6 | allPages: Number 7 | }, 8 | 9 | methods: { 10 | onKeyDown(e) { 11 | const key = e.keyCode 12 | if (!((!e.shiftKey && (key >= KEYCODE.ZERO && 13 | key <= KEYCODE.NINE)) || 14 | key === KEYCODE.LEFT || 15 | key === KEYCODE.RIGHT || 16 | key === KEYCODE.DELETE || 17 | key === KEYCODE.BACKSPACE)) { 18 | e.preventDefault() 19 | } 20 | }, 21 | 22 | onKeyUp(e) { 23 | const _val = e.target.value 24 | let val 25 | 26 | if (_val === '') { 27 | val = _val 28 | } else if (isNaN(Number(_val))) { 29 | val = this.page 30 | } else { 31 | val = Number(_val) 32 | } 33 | 34 | if (e.keyCode === KEYCODE.ENTER) { 35 | this.handleChange(val) 36 | } else if (e.keyCode === KEYCODE.UP) { 37 | this.handleChange(val - 1) 38 | } else if (e.keyCode === KEYCODE.DOWN) { 39 | this.handleChange(val + 1) 40 | } 41 | }, 42 | 43 | handleChange(page) { 44 | this.$emit('onChange', page) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /components/pagination/options.js: -------------------------------------------------------------------------------- 1 | import Mixins from './mixins' 2 | 3 | export default { 4 | name: 'VOptions', 5 | 6 | mixins: [Mixins], 7 | 8 | props: { 9 | showQuickJumper: Boolean, 10 | showSizeChanger: Boolean, 11 | pageSizeOptions: Array, 12 | pageSize: Number, 13 | size: String 14 | }, 15 | 16 | data() { 17 | return { 18 | currentPageSize: this.pageSize 19 | } 20 | }, 21 | 22 | render() { 23 | const options = this.pageSizeOptions.map(size => 24 | ({size} / page)) 25 | return (
26 | { 27 | this.showSizeChanger ? 28 | 30 | {options} 31 | 32 | : null 33 | } 34 | { 35 | this.showQuickJumper ? 36 |
Goto 37 | 44 |
45 | : null 46 | } 47 |
) 48 | }, 49 | 50 | methods: { 51 | onSelect(option) { 52 | this.$emit('onSizeChange', option) 53 | console.log(option) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /components/pagination/pager.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'VPager', 3 | 4 | props: { 5 | page: { 6 | type: Number, 7 | default: 1 8 | }, 9 | allPages: Number 10 | }, 11 | 12 | render() { 13 | const { page, allPages } = this 14 | const pagers = [] 15 | const pageBufferSize = 2 16 | 17 | if (allPages < 5 + (pageBufferSize * 2)) { 18 | for (let i = 1; i <= allPages; i++) { 19 | pagers.push(i) 20 | } 21 | } else { 22 | let left = Math.max(2, page - pageBufferSize) 23 | let right = Math.min(page + pageBufferSize, allPages - 1) 24 | 25 | if (page + pageBufferSize > allPages) { 26 | left = allPages - (pageBufferSize * 2) 27 | } 28 | 29 | if (page - pageBufferSize <= 0) { 30 | right = 1 + (pageBufferSize * 2) 31 | } 32 | pagers.push(1) 33 | if (left - pageBufferSize >= 1 && left !== 2) { 34 | pagers.push('prev') 35 | } 36 | 37 | for (let i = left; i <= right; i++) { 38 | pagers.push(i) 39 | } 40 | 41 | if (right + pageBufferSize <= allPages && right !== allPages - 1) { 42 | pagers.push('next') 43 | } 44 | pagers.push(allPages) 45 | } 46 | 47 | const pagerVM = pagers.map((pager) => { 48 | if (pager === 'prev' || pager === 'next') { 49 | const nextPrevData = { 50 | class: `ant-pagination-jump-${pager}`, 51 | on: { 52 | click: this.jumpNextPrev.bind(this, pager) 53 | } 54 | } 55 | return (
  • ) 56 | } 57 | const pagerData = { 58 | class: [ 59 | [`ant-pagination-item-${pager}`], 60 | { 61 | 'ant-pagination-item-active': page === pager 62 | } 63 | ], 64 | on: { 65 | click: this.onChange.bind(this, pager) 66 | } 67 | } 68 | return (
  • {pager}
  • ) 69 | }) 70 | return () 71 | }, 72 | 73 | methods: { 74 | onChange(page) { 75 | this.$emit('onChange', page) 76 | }, 77 | 78 | jumpNextPrev(page) { 79 | let jumpPage = page 80 | if (page === 'prev') { 81 | jumpPage = Math.max(1, this.page - 5) 82 | } else { 83 | jumpPage = Math.min(this.allPages, this.page + 5) 84 | } 85 | this.onChange(jumpPage) 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /components/pagination/pagination.js: -------------------------------------------------------------------------------- 1 | import VPager from './pager' 2 | import VOptions from './options' 3 | import VSimplePager from './simple-pager' 4 | 5 | export default { 6 | name: 'VPagination', 7 | 8 | props: { 9 | value: { 10 | type: Number, 11 | default: 1 12 | }, 13 | total: Number, 14 | size: { 15 | type: String, 16 | default: '' 17 | }, 18 | pageSize: { 19 | type: Number, 20 | default: 10 21 | }, 22 | simple: { 23 | type: Boolean, 24 | default: false 25 | }, 26 | showSizeChanger: { 27 | type: Boolean, 28 | default: false 29 | }, 30 | showQuickJumper: { 31 | type: Boolean, 32 | default: false 33 | }, 34 | showTotal: Function, 35 | pageSizeOptions: { 36 | type: Array, 37 | default() { 38 | return ['10', '20', '30', '40'] 39 | } 40 | } 41 | }, 42 | 43 | data() { 44 | return { 45 | currentPage: this.value, 46 | currentPageSize: this.pageSize, 47 | inputValue: this.value 48 | } 49 | }, 50 | 51 | render() { 52 | const { simple, currentPage, pageSize, total, allPages, showTotal } = this 53 | const pagerData = { 54 | props: { 55 | allPages, 56 | page: currentPage 57 | }, 58 | on: { 59 | onChange: this.handleChange 60 | } 61 | } 62 | 63 | let pager = null 64 | 65 | if (simple) { 66 | pager = () 67 | } else { 68 | pager = () 69 | } 70 | 71 | const optionsData = { 72 | props: { 73 | page: currentPage, 74 | ...this.$props 75 | }, 76 | on: { 77 | onChange: this.handleChange, 78 | onSizeChange: this.onSizeChange 79 | } 80 | } 81 | 82 | const paginationData = { 83 | class: { 84 | mini: this.size === 'small', 85 | 'ant-pagination-simple': this.simple 86 | } 87 | } 88 | 89 | let totalText 90 | if (showTotal) { 91 | totalText = ( 92 | Total {showTotal( 93 | total, 94 | [ 95 | ((currentPage - 1) * pageSize) + 1, 96 | currentPage * pageSize > total ? total : currentPage * pageSize 97 | ] 98 | )} items) 99 | } 100 | 101 | return () 116 | }, 117 | 118 | computed: { 119 | allPages() { 120 | return Math.ceil(this.total / this.currentPageSize) 121 | } 122 | }, 123 | 124 | methods: { 125 | onSizeChange(pageSize) { 126 | this.currentPageSize = pageSize 127 | this.currentPage = this.currentPage > this.allPages ? this.allPages : this.currentPage 128 | this.$emit('onShowSizeChange', this.currentPage, pageSize) 129 | }, 130 | 131 | prev() { 132 | if (!this.hasPrev()) return 133 | this.handleChange(this.currentPage - 1) 134 | }, 135 | next() { 136 | if (!this.hasNext()) return 137 | this.handleChange(this.currentPage + 1) 138 | }, 139 | hasPrev() { 140 | return this.currentPage > 1 141 | }, 142 | hasNext() { 143 | return this.currentPage < this.allPages 144 | }, 145 | handleChange(page) { 146 | if (this.isValid(page)) { 147 | if (page > this.allPages) { 148 | page = this.allPages 149 | } 150 | 151 | this.currentPage = page 152 | this.inputValue = page 153 | this.$emit('input', page) 154 | this.$emit('onChange', page, this.pageSize) 155 | 156 | return page 157 | } 158 | 159 | return this.currentPage 160 | }, 161 | 162 | isValid(page) { 163 | return typeof page === 'number' && page >= 1 && page !== this.currentPage 164 | } 165 | }, 166 | 167 | components: { 168 | VPager, 169 | VOptions, 170 | VSimplePager 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /components/pagination/simple-pager.js: -------------------------------------------------------------------------------- 1 | import Mixins from './mixins' 2 | 3 | export default { 4 | name: 'VSimplePager', 5 | 6 | mixins: [Mixins], 7 | 8 | render() { 9 | return ( 10 |
  • 11 | 18 | 19 | {this.allPages} 20 |
  • ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /components/popconfirm/index.js: -------------------------------------------------------------------------------- 1 | import Popconfirm from './popconfirm' 2 | 3 | export default Popconfirm 4 | -------------------------------------------------------------------------------- /components/popconfirm/popconfirm.js: -------------------------------------------------------------------------------- 1 | import '../popover/style/index.less' 2 | import '../button/style/index.less' 3 | 4 | import TooltipMixin from '../mixins/tooltip' 5 | 6 | export default { 7 | name: 'VPopconfirm', 8 | 9 | mixins: [TooltipMixin], 10 | 11 | props: { 12 | okText: { 13 | type: String, 14 | default: 'Yes' 15 | }, 16 | cancelText: { 17 | type: String, 18 | default: 'No' 19 | }, 20 | trigger: { 21 | type: String, 22 | default: 'click' 23 | } 24 | }, 25 | 26 | data() { 27 | return { 28 | prefixCls: 'ant-popover' 29 | } 30 | }, 31 | 32 | methods: { 33 | onConfirm(e) { 34 | this.closePopper() 35 | this.$emit('onConfirm', e) 36 | }, 37 | 38 | onCancel(e) { 39 | this.closePopper() 40 | this.$emit('onCancel', e) 41 | } 42 | }, 43 | 44 | components: { 45 | /** 46 | * 定义函数化组件用于插入到popperVM 47 | * 实际浮层显示的内容 48 | */ 49 | VTooltipContent: { 50 | functional: true, 51 | 52 | render(h, context) { 53 | const data = context.data 54 | return ( 55 |
    56 |
    57 |
    58 |
    59 | 60 |
    { data.title || data.$slots.title}
    61 |
    62 |
    63 | 65 | { data.cancelText } 66 | 67 | 69 | { data.okText } 70 | 71 |
    72 |
    73 |
    74 |
    ) 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /components/popover/index.js: -------------------------------------------------------------------------------- 1 | import Popover from './popover' 2 | 3 | export default Popover 4 | -------------------------------------------------------------------------------- /components/popover/popover.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | import TooltipMixin from '../mixins/tooltip' 3 | 4 | export default { 5 | name: 'VPopover', 6 | 7 | mixins: [TooltipMixin], 8 | 9 | props: { 10 | content: [String, Object] 11 | }, 12 | 13 | data() { 14 | return { 15 | prefixCls: 'ant-popover' 16 | } 17 | }, 18 | 19 | components: { 20 | /** 21 | * 定义函数化组件用于插入到popperVM 22 | * 实际浮层显示的内容 23 | */ 24 | VTooltipContent: { 25 | functional: true, 26 | 27 | render(h, context) { 28 | const data = context.data 29 | return ( 30 |
    31 |
    32 |
    { data.title || data.$slots.title}
    33 |
    { data.content || data.$slots.content}
    34 |
    35 |
    ) 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /components/radio/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Radio from './radio' 4 | import RadioButton from './radio-button' 5 | import RadioGroup from './radio-group' 6 | 7 | Radio.RadioButton = RadioButton 8 | Radio.Group = RadioGroup 9 | export default Radio 10 | -------------------------------------------------------------------------------- /components/radio/radio-button.vue: -------------------------------------------------------------------------------- 1 | 18 | 43 | 44 | -------------------------------------------------------------------------------- /components/radio/radio-group.vue: -------------------------------------------------------------------------------- 1 | 7 | 42 | -------------------------------------------------------------------------------- /components/radio/radio-mixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | value: [String, Number, Boolean], 4 | label: [String, Number, Boolean], 5 | disabled: Boolean 6 | }, 7 | 8 | data() { 9 | return { 10 | store: {} 11 | } 12 | }, 13 | 14 | computed: { 15 | model: { 16 | get() { 17 | return this.store.state ? this.store.state.value : this.value 18 | }, 19 | 20 | set(val) { 21 | if (this.store.state) { 22 | this.store.state.value = val 23 | } else { 24 | this.$emit('input', val) 25 | } 26 | } 27 | }, 28 | 29 | isDisabled() { 30 | return this.store.state ? this.store.state.disabled || this.disabled : this.disabled 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /components/radio/radio.vue: -------------------------------------------------------------------------------- 1 | 18 | 42 | -------------------------------------------------------------------------------- /components/radio/store.js: -------------------------------------------------------------------------------- 1 | import BaseStore from '../utils/base-store' 2 | 3 | class Store extends BaseStore { 4 | constructor(component, initialState = {}) { 5 | super(component, initialState) 6 | 7 | this.state = { 8 | value: null, 9 | disabled: false 10 | } 11 | 12 | this.setState(initialState) 13 | } 14 | } 15 | 16 | export default Store 17 | -------------------------------------------------------------------------------- /components/rate/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Rate from './rate' 4 | 5 | export default Rate 6 | -------------------------------------------------------------------------------- /components/rate/rate.js: -------------------------------------------------------------------------------- 1 | import Dom from '../utils/dom' 2 | 3 | export default { 4 | name: 'VRate', 5 | 6 | props: { 7 | count: { 8 | type: Number, 9 | default: 5 10 | }, 11 | value: Number, 12 | disabled: { 13 | type: Boolean, 14 | default: false 15 | }, 16 | allowHalf: { 17 | type: Boolean, 18 | default: false 19 | }, 20 | character: [String, Object] 21 | }, 22 | 23 | data() { 24 | return { 25 | hoverValue: undefined 26 | } 27 | }, 28 | 29 | render() { 30 | const stars = [] 31 | for (let i = 0; i < this.count; i++) { 32 | const starData = { 33 | ref: `star_${i}`, 34 | on: { 35 | click: this.onClick.bind(this, i), 36 | mousemove: this.onHover.bind(this, i) 37 | }, 38 | class: this.getClass(i) 39 | } 40 | 41 | stars.push((
  • 42 |
    {this.getCharacterVM()}
    43 |
    {this.getCharacterVM()}
    44 |
  • )) 45 | } 46 | 47 | const rateData = { 48 | on: { 49 | mouseleave: this.onMouseLeave 50 | }, 51 | class: { 52 | 'ant-rate-disabled': this.disabled 53 | } 54 | } 55 | return ( 56 | 57 | {this.$slots.default} 58 | ) 59 | }, 60 | 61 | methods: { 62 | getCharacterVM() { 63 | return !this.character ? () : this.character 64 | }, 65 | 66 | getClass(index) { 67 | const { value, hoverValue, allowHalf } = this 68 | const starValue = index + 1 69 | const currentValue = hoverValue === undefined ? value : hoverValue 70 | if (allowHalf && currentValue + 0.5 === starValue) { 71 | return 'ant-rate-star ant-rate-star-half ant-rate-star-active' 72 | } 73 | return starValue <= currentValue ? 'ant-rate-star ant-rate-star-full' : 'ant-rate-star ant-rate-star-zero' 74 | }, 75 | 76 | onClick(index, e) { 77 | if (this.disabled) return 78 | const value = this.getStarValue(index, e.pageX) 79 | this.onMouseLeave() 80 | this.$emit('input', value) 81 | this.$emit('onChange', value) 82 | }, 83 | 84 | onHover(index, e) { 85 | if (this.disabled) return 86 | this.hoverValue = this.getStarValue(index, e.pageX) 87 | this.$emit('onHoverChange', this.hoverValue) 88 | }, 89 | 90 | onMouseLeave() { 91 | if (this.disabled) return 92 | this.hoverValue = undefined 93 | this.$emit('onHoverChange', this.hoverValue) 94 | }, 95 | 96 | getStarValue(index, x) { 97 | let value = index + 1 98 | if (this.allowHalf) { 99 | const leftEdge = Dom.getOffset(this.$refs.star_0).left 100 | const width = Dom.getOffset(this.$refs.star_1).left - leftEdge 101 | if ((x - leftEdge - (width * index)) < width / 2) { 102 | value -= 0.5 103 | } 104 | } 105 | return value 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /components/rate/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | 4 | @rate-prefix-cls: ~"@{ant-prefix}-rate"; 5 | 6 | .@{rate-prefix-cls} { 7 | margin: 0; 8 | padding: 0; 9 | list-style: none; 10 | font-size: 20px; 11 | display: inline-block; 12 | vertical-align: middle; 13 | 14 | &-disabled &-star { 15 | cursor: not-allowed; 16 | &:hover { 17 | transform: scale(1); 18 | } 19 | } 20 | 21 | &-star { 22 | margin: 0; 23 | padding: 0; 24 | display: inline-block; 25 | margin-right: 8px; 26 | position: relative; 27 | transition: all .3s; 28 | color: @rate-star-bg; 29 | cursor: pointer; 30 | 31 | &-first, 32 | &-second { 33 | user-select: none; 34 | transition: all .3s; 35 | } 36 | 37 | &:hover { 38 | transform: scale(1.1); 39 | } 40 | 41 | &-first { 42 | position: absolute; 43 | left: 0; 44 | top: 0; 45 | width: 50%; 46 | height: 100%; 47 | overflow: hidden; 48 | opacity: 0; 49 | } 50 | 51 | &-half &-first, 52 | &-half &-second { 53 | opacity: 1; 54 | } 55 | 56 | &-half &-first, 57 | &-full &-second { 58 | color: @rate-star-color; 59 | } 60 | 61 | &-half:hover &-first, 62 | &-full:hover &-second { 63 | color: tint(@rate-star-color, 20%); 64 | } 65 | } 66 | 67 | &-text { 68 | margin-left: 8px; 69 | vertical-align: middle; 70 | display: inline-block; 71 | font-size: @font-size-base; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /components/select/index.js: -------------------------------------------------------------------------------- 1 | import Select from './select' 2 | import Option from './option' 3 | import OptionGroup from './option-group' 4 | 5 | Select.Option = Option 6 | Select.OptionGroup = OptionGroup 7 | 8 | export default Select 9 | -------------------------------------------------------------------------------- /components/select/option-group.vue: -------------------------------------------------------------------------------- 1 | 6 | 22 | -------------------------------------------------------------------------------- /components/select/option.vue: -------------------------------------------------------------------------------- 1 | 6 | 25 | -------------------------------------------------------------------------------- /components/style/color/bezierEasing.less: -------------------------------------------------------------------------------- 1 | .bezierEasingMixin() { 2 | @functions: ~`(function() { 3 | var NEWTON_ITERATIONS = 4; 4 | var NEWTON_MIN_SLOPE = 0.001; 5 | var SUBDIVISION_PRECISION = 0.0000001; 6 | var SUBDIVISION_MAX_ITERATIONS = 10; 7 | 8 | var kSplineTableSize = 11; 9 | var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); 10 | 11 | var float32ArraySupported = typeof Float32Array === 'function'; 12 | 13 | function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } 14 | function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } 15 | function C (aA1) { return 3.0 * aA1; } 16 | 17 | // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. 18 | function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } 19 | 20 | // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. 21 | function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } 22 | 23 | function binarySubdivide (aX, aA, aB, mX1, mX2) { 24 | var currentX, currentT, i = 0; 25 | do { 26 | currentT = aA + (aB - aA) / 2.0; 27 | currentX = calcBezier(currentT, mX1, mX2) - aX; 28 | if (currentX > 0.0) { 29 | aB = currentT; 30 | } else { 31 | aA = currentT; 32 | } 33 | } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); 34 | return currentT; 35 | } 36 | 37 | function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) { 38 | for (var i = 0; i < NEWTON_ITERATIONS; ++i) { 39 | var currentSlope = getSlope(aGuessT, mX1, mX2); 40 | if (currentSlope === 0.0) { 41 | return aGuessT; 42 | } 43 | var currentX = calcBezier(aGuessT, mX1, mX2) - aX; 44 | aGuessT -= currentX / currentSlope; 45 | } 46 | return aGuessT; 47 | } 48 | 49 | var BezierEasing = function (mX1, mY1, mX2, mY2) { 50 | if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { 51 | throw new Error('bezier x values must be in [0, 1] range'); 52 | } 53 | 54 | // Precompute samples table 55 | var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); 56 | if (mX1 !== mY1 || mX2 !== mY2) { 57 | for (var i = 0; i < kSplineTableSize; ++i) { 58 | sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); 59 | } 60 | } 61 | 62 | function getTForX (aX) { 63 | var intervalStart = 0.0; 64 | var currentSample = 1; 65 | var lastSample = kSplineTableSize - 1; 66 | 67 | for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) { 68 | intervalStart += kSampleStepSize; 69 | } 70 | --currentSample; 71 | 72 | // Interpolate to provide an initial guess for t 73 | var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]); 74 | var guessForT = intervalStart + dist * kSampleStepSize; 75 | 76 | var initialSlope = getSlope(guessForT, mX1, mX2); 77 | if (initialSlope >= NEWTON_MIN_SLOPE) { 78 | return newtonRaphsonIterate(aX, guessForT, mX1, mX2); 79 | } else if (initialSlope === 0.0) { 80 | return guessForT; 81 | } else { 82 | return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2); 83 | } 84 | } 85 | 86 | return function BezierEasing (x) { 87 | if (mX1 === mY1 && mX2 === mY2) { 88 | return x; // linear 89 | } 90 | // Because JavaScript number are imprecise, we should guarantee the extremes are right. 91 | if (x === 0) { 92 | return 0; 93 | } 94 | if (x === 1) { 95 | return 1; 96 | } 97 | return calcBezier(getTForX(x), mY1, mY2); 98 | }; 99 | }; 100 | 101 | this.colorEasing = BezierEasing(0.26, 0.09, 0.37, 0.18); 102 | })()`; 103 | } 104 | // It is hacky way to make this function will be compiled preferentially by less 105 | // resolve error: `ReferenceError: colorPalette is not defined` 106 | // https://github.com/ant-design/ant-motion/issues/44 107 | .bezierEasingMixin(); 108 | -------------------------------------------------------------------------------- /components/style/color/colorPalette.less: -------------------------------------------------------------------------------- 1 | @import "bezierEasing"; 2 | @import "tinyColor"; 3 | 4 | // We create a very complex algorithm which take the place of original tint/shade color system 5 | // to make sure no one can understand it 👻 6 | // and create an entire color palette magicly by inputing just a single primary color. 7 | // We are using bezier-curve easing function and some color manipulations like tint/shade/darken/spin 8 | .colorPaletteMixin() { 9 | @functions: ~`(function() { 10 | var warmDark = 0.5; // warm color darken radio 11 | var warmRotate = -26; // warm color rotate degree 12 | var coldDark = 0.55; // cold color darken radio 13 | var coldRotate = 10; // cold color rotate degree 14 | var getShadeColor = function(c) { 15 | var shadeColor = tinycolor(c); 16 | // warm and cold color will darken in different radio, and rotate in different degree 17 | // warmer color 18 | if (shadeColor.toRgb().r > shadeColor.toRgb().b) { 19 | return shadeColor.darken(shadeColor.toHsl().l * warmDark * 100).spin(warmRotate).toHexString(); 20 | } 21 | // colder color 22 | return shadeColor.darken(shadeColor.toHsl().l * coldDark * 100).spin(coldRotate).toHexString(); 23 | } 24 | var primaryEasing = colorEasing(0.6); 25 | this.colorPalette = function(color, index) { 26 | var currentEasing = colorEasing(index * 0.1); 27 | // return light colors after tint 28 | if (index <= 6) { 29 | return tinycolor.mix( 30 | '#ffffff', 31 | color, 32 | currentEasing * 100 / primaryEasing 33 | ).toHexString(); 34 | } 35 | return tinycolor.mix( 36 | getShadeColor(color), 37 | color, 38 | (1 - (currentEasing - primaryEasing) / (1 - primaryEasing)) * 100 39 | ).toHexString(); 40 | }; 41 | })()`; 42 | } 43 | // It is hacky way to make this function will be compiled preferentially by less 44 | // resolve error: `ReferenceError: colorPalette is not defined` 45 | // https://github.com/ant-design/ant-motion/issues/44 46 | .colorPaletteMixin(); 47 | -------------------------------------------------------------------------------- /components/style/color/colors.less: -------------------------------------------------------------------------------- 1 | @import 'colorPalette'; 2 | 3 | // color palettes 4 | @blue-1: color(~`colorPalette("@{blue-6}", 1)`); 5 | @blue-2: color(~`colorPalette("@{blue-6}", 2)`); 6 | @blue-3: color(~`colorPalette("@{blue-6}", 3)`); 7 | @blue-4: color(~`colorPalette("@{blue-6}", 4)`); 8 | @blue-5: color(~`colorPalette("@{blue-6}", 5)`); 9 | @blue-6: #108ee9; 10 | @blue-7: color(~`colorPalette("@{blue-6}", 7)`); 11 | @blue-8: color(~`colorPalette("@{blue-6}", 8)`); 12 | @blue-9: color(~`colorPalette("@{blue-6}", 9)`); 13 | @blue-10: color(~`colorPalette("@{blue-6}", 10)`); 14 | 15 | @purple-1: color(~`colorPalette("@{purple-6}", 1)`); 16 | @purple-2: color(~`colorPalette("@{purple-6}", 2)`); 17 | @purple-3: color(~`colorPalette("@{purple-6}", 3)`); 18 | @purple-4: color(~`colorPalette("@{purple-6}", 4)`); 19 | @purple-5: color(~`colorPalette("@{purple-6}", 5)`); 20 | @purple-6: #7265e6; 21 | @purple-7: color(~`colorPalette("@{purple-6}", 7)`); 22 | @purple-8: color(~`colorPalette("@{purple-6}", 8)`); 23 | @purple-9: color(~`colorPalette("@{purple-6}", 9)`); 24 | @purple-10: color(~`colorPalette("@{purple-6}", 10)`); 25 | 26 | @cyan-1: color(~`colorPalette("@{cyan-6}", 1)`); 27 | @cyan-2: color(~`colorPalette("@{cyan-6}", 2)`); 28 | @cyan-3: color(~`colorPalette("@{cyan-6}", 3)`); 29 | @cyan-4: color(~`colorPalette("@{cyan-6}", 4)`); 30 | @cyan-5: color(~`colorPalette("@{cyan-6}", 5)`); 31 | @cyan-6: #00a2ae; 32 | @cyan-7: color(~`colorPalette("@{cyan-6}", 7)`); 33 | @cyan-8: color(~`colorPalette("@{cyan-6}", 8)`); 34 | @cyan-9: color(~`colorPalette("@{cyan-6}", 9)`); 35 | @cyan-10: color(~`colorPalette("@{cyan-6}", 10)`); 36 | 37 | @green-1: color(~`colorPalette("@{green-6}", 1)`); 38 | @green-2: color(~`colorPalette("@{green-6}", 2)`); 39 | @green-3: color(~`colorPalette("@{green-6}", 3)`); 40 | @green-4: color(~`colorPalette("@{green-6}", 4)`); 41 | @green-5: color(~`colorPalette("@{green-6}", 5)`); 42 | @green-6: #00a854; 43 | @green-7: color(~`colorPalette("@{green-6}", 7)`); 44 | @green-8: color(~`colorPalette("@{green-6}", 8)`); 45 | @green-9: color(~`colorPalette("@{green-6}", 9)`); 46 | @green-10: color(~`colorPalette("@{green-6}", 10)`); 47 | 48 | @pink-1: color(~`colorPalette("@{pink-6}", 1)`); 49 | @pink-2: color(~`colorPalette("@{pink-6}", 2)`); 50 | @pink-3: color(~`colorPalette("@{pink-6}", 3)`); 51 | @pink-4: color(~`colorPalette("@{pink-6}", 4)`); 52 | @pink-5: color(~`colorPalette("@{pink-6}", 5)`); 53 | @pink-6: #f5317f; 54 | @pink-7: color(~`colorPalette("@{pink-6}", 7)`); 55 | @pink-8: color(~`colorPalette("@{pink-6}", 8)`); 56 | @pink-9: color(~`colorPalette("@{pink-6}", 9)`); 57 | @pink-10: color(~`colorPalette("@{pink-6}", 10)`); 58 | 59 | @red-1: color(~`colorPalette("@{red-6}", 1)`); 60 | @red-2: color(~`colorPalette("@{red-6}", 2)`); 61 | @red-3: color(~`colorPalette("@{red-6}", 3)`); 62 | @red-4: color(~`colorPalette("@{red-6}", 4)`); 63 | @red-5: color(~`colorPalette("@{red-6}", 5)`); 64 | @red-6: #f04134; 65 | @red-7: color(~`colorPalette("@{red-6}", 7)`); 66 | @red-8: color(~`colorPalette("@{red-6}", 8)`); 67 | @red-9: color(~`colorPalette("@{red-6}", 9)`); 68 | @red-10: color(~`colorPalette("@{red-6}", 10)`); 69 | 70 | @orange-1: color(~`colorPalette("@{orange-6}", 1)`); 71 | @orange-2: color(~`colorPalette("@{orange-6}", 2)`); 72 | @orange-3: color(~`colorPalette("@{orange-6}", 3)`); 73 | @orange-4: color(~`colorPalette("@{orange-6}", 4)`); 74 | @orange-5: color(~`colorPalette("@{orange-6}", 5)`); 75 | @orange-6: #f56a00; 76 | @orange-7: color(~`colorPalette("@{orange-6}", 7)`); 77 | @orange-8: color(~`colorPalette("@{orange-6}", 8)`); 78 | @orange-9: color(~`colorPalette("@{orange-6}", 9)`); 79 | @orange-10: color(~`colorPalette("@{orange-6}", 10)`); 80 | 81 | @yellow-1: color(~`colorPalette("@{yellow-6}", 1)`); 82 | @yellow-2: color(~`colorPalette("@{yellow-6}", 2)`); 83 | @yellow-3: color(~`colorPalette("@{yellow-6}", 3)`); 84 | @yellow-4: color(~`colorPalette("@{yellow-6}", 4)`); 85 | @yellow-5: color(~`colorPalette("@{yellow-6}", 5)`); 86 | @yellow-6: #ffbf00; 87 | @yellow-7: color(~`colorPalette("@{yellow-6}", 7)`); 88 | @yellow-8: color(~`colorPalette("@{yellow-6}", 8)`); 89 | @yellow-9: color(~`colorPalette("@{yellow-6}", 9)`); 90 | @yellow-10: color(~`colorPalette("@{yellow-6}", 10)`); 91 | -------------------------------------------------------------------------------- /components/style/core/base.less: -------------------------------------------------------------------------------- 1 | @import "./normalize.less"; 2 | 3 | * { 4 | box-sizing: border-box; 5 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); // remove tap highlight color for mobile safari 6 | } 7 | 8 | *:before, 9 | *:after { 10 | box-sizing: border-box; 11 | } 12 | 13 | // HTML & Body reset 14 | html, body { 15 | .square(100%); 16 | } 17 | 18 | body { 19 | font-family: @font-family; 20 | font-size: @font-size-base; 21 | line-height: @line-height-base; 22 | color: @text-color; 23 | background-color: @body-background; 24 | } 25 | 26 | // unify the setting of elements's margin and padding for browsers 27 | body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td,hr,button,article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section { 28 | margin: 0; 29 | padding: 0; 30 | } 31 | 32 | // Reset fonts for relevant elements 33 | button,input,select,textarea { 34 | font-family: inherit; 35 | font-size: inherit; 36 | line-height: inherit; 37 | color: inherit; 38 | } 39 | 40 | ul, 41 | ol { 42 | list-style: none; 43 | } 44 | 45 | // Remove the clear button of a text input control in IE10+ 46 | input::-ms-clear, input::-ms-reveal { 47 | display: none; 48 | } 49 | 50 | ::selection { 51 | background: @primary-color; 52 | color: #fff; 53 | } 54 | 55 | // Links 56 | a { 57 | color: @link-color; 58 | background: transparent; 59 | text-decoration: none; 60 | outline: none; 61 | cursor: pointer; 62 | transition: color .3s ease; 63 | 64 | &:hover { 65 | color: @link-hover-color; 66 | } 67 | 68 | &:active { 69 | color: @link-active-color; 70 | } 71 | 72 | &:active, 73 | &:hover { 74 | outline: 0; 75 | text-decoration: none; 76 | } 77 | 78 | &[disabled] { 79 | color: @disabled-color; 80 | cursor: not-allowed; 81 | pointer-events: none; 82 | } 83 | } 84 | 85 | .@{ant-prefix}-divider { 86 | margin: 0 6px; 87 | display: inline-block; 88 | height: 8px; 89 | width: 1px; 90 | background: #ccc; 91 | } 92 | 93 | code, 94 | kbd, 95 | pre, 96 | samp { 97 | font-family: @code-family; 98 | } 99 | 100 | // Utility classes 101 | .clearfix { 102 | .clearfix(); 103 | } 104 | -------------------------------------------------------------------------------- /components/style/core/index.less: -------------------------------------------------------------------------------- 1 | @import "../mixins/index"; 2 | @import "base"; 3 | @import "iconfont"; 4 | @import "motion"; 5 | -------------------------------------------------------------------------------- /components/style/core/motion.less: -------------------------------------------------------------------------------- 1 | @import "../mixins/motion"; 2 | @import "motion/fade"; 3 | @import "motion/move"; 4 | @import "motion/other"; 5 | @import "motion/slide"; 6 | @import "motion/swing"; 7 | @import "motion/zoom"; 8 | 9 | // For common/openAnimation 10 | .ant-motion-collapse { 11 | overflow: hidden; 12 | &-active { 13 | transition: height .12s, opacity .12s; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /components/style/core/motion/fade.less: -------------------------------------------------------------------------------- 1 | .fade-motion(@className, @keyframeName) { 2 | .make-motion(@className, @keyframeName); 3 | .@{className}-enter, 4 | .@{className}-appear { 5 | opacity: 0; 6 | animation-timing-function: linear; 7 | } 8 | .@{className}-leave { 9 | animation-timing-function: linear; 10 | } 11 | } 12 | 13 | .fade-motion(fade, antFade); 14 | 15 | @keyframes antFadeIn { 16 | 0% { 17 | opacity: 0; 18 | } 19 | 100% { 20 | opacity: 1; 21 | } 22 | } 23 | 24 | @keyframes antFadeOut { 25 | 0% { 26 | opacity: 1; 27 | } 28 | 100% { 29 | opacity: 0; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /components/style/core/motion/move.less: -------------------------------------------------------------------------------- 1 | .move-motion(@className, @keyframeName) { 2 | .make-motion(@className, @keyframeName); 3 | .@{className}-enter, 4 | .@{className}-appear { 5 | opacity: 0; 6 | animation-timing-function: @ease-out-circ; 7 | } 8 | .@{className}-leave { 9 | animation-timing-function: @ease-in-circ; 10 | } 11 | } 12 | 13 | .move-motion(move-up, antMoveUp); 14 | .move-motion(move-down, antMoveDown); 15 | .move-motion(move-left, antMoveLeft); 16 | .move-motion(move-right, antMoveRight); 17 | 18 | @keyframes antMoveDownIn { 19 | 0% { 20 | transform-origin: 0 0; 21 | transform: translateY(100%); 22 | opacity: 0; 23 | } 24 | 100% { 25 | transform-origin: 0 0; 26 | transform: translateY(0%); 27 | opacity: 1; 28 | } 29 | } 30 | 31 | @keyframes antMoveDownOut { 32 | 0% { 33 | transform-origin: 0 0; 34 | transform: translateY(0%); 35 | opacity: 1; 36 | } 37 | 100% { 38 | transform-origin: 0 0; 39 | transform: translateY(100%); 40 | opacity: 0; 41 | } 42 | } 43 | 44 | @keyframes antMoveLeftIn { 45 | 0% { 46 | transform-origin: 0 0; 47 | transform: translateX(-100%); 48 | opacity: 0; 49 | } 50 | 100% { 51 | transform-origin: 0 0; 52 | transform: translateX(0%); 53 | opacity: 1; 54 | } 55 | } 56 | 57 | @keyframes antMoveLeftOut { 58 | 0% { 59 | transform-origin: 0 0; 60 | transform: translateX(0%); 61 | opacity: 1; 62 | } 63 | 100% { 64 | transform-origin: 0 0; 65 | transform: translateX(-100%); 66 | opacity: 0; 67 | } 68 | } 69 | 70 | @keyframes antMoveRightIn { 71 | 0% { 72 | opacity: 0; 73 | transform-origin: 0 0; 74 | transform: translateX(100%); 75 | } 76 | 100% { 77 | opacity: 1; 78 | transform-origin: 0 0; 79 | transform: translateX(0%); 80 | } 81 | } 82 | 83 | @keyframes antMoveRightOut { 84 | 0% { 85 | transform-origin: 0 0; 86 | transform: translateX(0%); 87 | opacity: 1; 88 | } 89 | 100% { 90 | transform-origin: 0 0; 91 | transform: translateX(100%); 92 | opacity: 0; 93 | } 94 | } 95 | 96 | @keyframes antMoveUpIn { 97 | 0% { 98 | transform-origin: 0 0; 99 | transform: translateY(-100%); 100 | opacity: 0; 101 | } 102 | 100% { 103 | transform-origin: 0 0; 104 | transform: translateY(0%); 105 | opacity: 1; 106 | } 107 | } 108 | 109 | @keyframes antMoveUpOut { 110 | 0% { 111 | transform-origin: 0 0; 112 | transform: translateY(0%); 113 | opacity: 1; 114 | } 115 | 100% { 116 | transform-origin: 0 0; 117 | transform: translateY(-100%); 118 | opacity: 0; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /components/style/core/motion/other.less: -------------------------------------------------------------------------------- 1 | @keyframes loadingCircle { 2 | 0% { 3 | transform-origin: 50% 50%; 4 | transform: rotate(0deg); 5 | } 6 | 100% { 7 | transform-origin: 50% 50%; 8 | transform: rotate(360deg); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /components/style/core/motion/slide.less: -------------------------------------------------------------------------------- 1 | .slide-motion(@className, @keyframeName) { 2 | .make-motion(@className, @keyframeName); 3 | .@{className}-enter, 4 | .@{className}-appear { 5 | opacity: 0; 6 | animation-timing-function: @ease-out-quint; 7 | } 8 | .@{className}-leave { 9 | animation-timing-function: @ease-in-quint; 10 | } 11 | } 12 | 13 | .slide-motion(slide-up, antSlideUp); 14 | .slide-motion(slide-down, antSlideDown); 15 | .slide-motion(slide-left, antSlideLeft); 16 | .slide-motion(slide-right, antSlideRight); 17 | 18 | @keyframes antSlideUpIn { 19 | 0% { 20 | opacity: 0; 21 | transform-origin: 0% 0%; 22 | transform: scaleY(.8); 23 | } 24 | 100% { 25 | opacity: 1; 26 | transform-origin: 0% 0%; 27 | transform: scaleY(1); 28 | } 29 | } 30 | 31 | @keyframes antSlideUpOut { 32 | 0% { 33 | opacity: 1; 34 | transform-origin: 0% 0%; 35 | transform: scaleY(1); 36 | } 37 | 100% { 38 | opacity: 0; 39 | transform-origin: 0% 0%; 40 | transform: scaleY(.8); 41 | } 42 | } 43 | 44 | @keyframes antSlideDownIn { 45 | 0% { 46 | opacity: 0; 47 | transform-origin: 100% 100%; 48 | transform: scaleY(.8); 49 | } 50 | 100% { 51 | opacity: 1; 52 | transform-origin: 100% 100%; 53 | transform: scaleY(1); 54 | } 55 | } 56 | 57 | @keyframes antSlideDownOut { 58 | 0% { 59 | opacity: 1; 60 | transform-origin: 100% 100%; 61 | transform: scaleY(1); 62 | } 63 | 100% { 64 | opacity: 0; 65 | transform-origin: 100% 100%; 66 | transform: scaleY(.8); 67 | } 68 | } 69 | 70 | @keyframes antSlideLeftIn { 71 | 0% { 72 | opacity: 0; 73 | transform-origin: 0% 0%; 74 | transform: scaleX(.8); 75 | } 76 | 100% { 77 | opacity: 1; 78 | transform-origin: 0% 0%; 79 | transform: scaleX(1); 80 | } 81 | } 82 | 83 | @keyframes antSlideLeftOut { 84 | 0% { 85 | opacity: 1; 86 | transform-origin: 0% 0%; 87 | transform: scaleX(1); 88 | } 89 | 100% { 90 | opacity: 0; 91 | transform-origin: 0% 0%; 92 | transform: scaleX(.8); 93 | } 94 | } 95 | 96 | @keyframes antSlideRightIn { 97 | 0% { 98 | opacity: 0; 99 | transform-origin: 100% 0%; 100 | transform: scaleX(.8); 101 | } 102 | 100% { 103 | opacity: 1; 104 | transform-origin: 100% 0%; 105 | transform: scaleX(1); 106 | } 107 | } 108 | 109 | @keyframes antSlideRightOut { 110 | 0% { 111 | opacity: 1; 112 | transform-origin: 100% 0%; 113 | transform: scaleX(1); 114 | } 115 | 100% { 116 | opacity: 0; 117 | transform-origin: 100% 0%; 118 | transform: scaleX(.8); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /components/style/core/motion/swing.less: -------------------------------------------------------------------------------- 1 | .swing-motion(@className, @keyframeName) { 2 | .@{className}-enter, 3 | .@{className}-appear { 4 | .motion-common(); 5 | animation-play-state: paused; 6 | } 7 | .@{className}-enter.@{className}-enter-active, 8 | .@{className}-appear.@{className}-appear-active { 9 | animation-name: ~"@{keyframeName}In"; 10 | animation-play-state: running; 11 | } 12 | } 13 | 14 | .swing-motion(swing, antSwing); 15 | 16 | @keyframes antSwingIn { 17 | 0%, 18 | 100% { 19 | transform: translateX(0); 20 | } 21 | 20% { 22 | transform: translateX(-10px); 23 | } 24 | 40% { 25 | transform: translateX(10px); 26 | } 27 | 60% { 28 | transform: translateX(-5px); 29 | } 30 | 80% { 31 | transform: translateX(5px); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /components/style/core/motion/zoom.less: -------------------------------------------------------------------------------- 1 | .zoom-motion(@className, @keyframeName, @duration: @animation-duration-base) { 2 | .make-motion(@className, @keyframeName, @duration); 3 | .@{className}-enter, 4 | .@{className}-appear { 5 | transform: scale(0); // need this by yiminghe 6 | animation-timing-function: @ease-out-circ; 7 | } 8 | .@{className}-leave { 9 | animation-timing-function: @ease-in-out-circ; 10 | } 11 | } 12 | 13 | // For Modal, Select choosen item 14 | .zoom-motion(zoom, antZoom); 15 | // For Popover, Popconfirm, Dropdown 16 | .zoom-motion(zoom-big, antZoomBig); 17 | // For Tooltip 18 | .zoom-motion(zoom-big-fast, antZoomBig, @animation-duration-fast); 19 | 20 | .zoom-motion(zoom-up, antZoomUp); 21 | .zoom-motion(zoom-down, antZoomDown); 22 | .zoom-motion(zoom-left, antZoomLeft); 23 | .zoom-motion(zoom-right, antZoomRight); 24 | 25 | @keyframes antZoomIn { 26 | 0% { 27 | opacity: 0; 28 | transform: scale(0.2); 29 | } 30 | 100% { 31 | opacity: 1; 32 | transform: scale(1); 33 | } 34 | } 35 | 36 | @keyframes antZoomOut { 37 | 0% { 38 | transform: scale(1); 39 | } 40 | 100% { 41 | opacity: 0; 42 | transform: scale(0.2); 43 | } 44 | } 45 | 46 | @keyframes antZoomBigIn { 47 | 0% { 48 | opacity: 0; 49 | transform: scale(.8); 50 | } 51 | 100% { 52 | transform: scale(1); 53 | } 54 | } 55 | 56 | @keyframes antZoomBigOut { 57 | 0% { 58 | transform: scale(1); 59 | } 60 | 100% { 61 | opacity: 0; 62 | transform: scale(.8); 63 | } 64 | } 65 | 66 | @keyframes antZoomUpIn { 67 | 0% { 68 | opacity: 0; 69 | transform-origin: 50% 0%; 70 | transform: scale(.8); 71 | } 72 | 100% { 73 | transform-origin: 50% 0%; 74 | transform: scale(1); 75 | } 76 | } 77 | 78 | @keyframes antZoomUpOut { 79 | 0% { 80 | transform-origin: 50% 0%; 81 | transform: scale(1); 82 | } 83 | 100% { 84 | opacity: 0; 85 | transform-origin: 50% 0%; 86 | transform: scale(.8); 87 | } 88 | } 89 | 90 | @keyframes antZoomLeftIn { 91 | 0% { 92 | opacity: 0; 93 | transform-origin: 0% 50%; 94 | transform: scale(.8); 95 | } 96 | 100% { 97 | transform-origin: 0% 50%; 98 | transform: scale(1); 99 | } 100 | } 101 | 102 | @keyframes antZoomLeftOut { 103 | 0% { 104 | transform-origin: 0% 50%; 105 | transform: scale(1); 106 | } 107 | 100% { 108 | opacity: 0; 109 | transform-origin: 0% 50%; 110 | transform: scale(.8); 111 | } 112 | } 113 | 114 | @keyframes antZoomRightIn { 115 | 0% { 116 | opacity: 0; 117 | transform-origin: 100% 50%; 118 | transform: scale(.8); 119 | } 120 | 100% { 121 | transform-origin: 100% 50%; 122 | transform: scale(1); 123 | } 124 | } 125 | 126 | @keyframes antZoomRightOut { 127 | 0% { 128 | transform-origin: 100% 50%; 129 | transform: scale(1); 130 | } 131 | 100% { 132 | opacity: 0; 133 | transform-origin: 100% 50%; 134 | transform: scale(.8); 135 | } 136 | } 137 | 138 | @keyframes antZoomDownIn { 139 | 0% { 140 | opacity: 0; 141 | transform-origin: 50% 100%; 142 | transform: scale(.8); 143 | } 144 | 100% { 145 | transform-origin: 50% 100%; 146 | transform: scale(1); 147 | } 148 | } 149 | 150 | @keyframes antZoomDownOut { 151 | 0% { 152 | transform-origin: 50% 100%; 153 | transform: scale(1); 154 | } 155 | 100% { 156 | opacity: 0; 157 | transform-origin: 50% 100%; 158 | transform: scale(.8); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /components/style/index.less: -------------------------------------------------------------------------------- 1 | @import "./themes/default"; 2 | @import "./core/index"; 3 | -------------------------------------------------------------------------------- /components/style/mixins/clearfix.less: -------------------------------------------------------------------------------- 1 | // mixins for clearfix 2 | // ------------------------ 3 | .clearfix() { 4 | zoom: 1; 5 | &:before, 6 | &:after { 7 | content: " "; 8 | display: table; 9 | } 10 | &:after { 11 | clear: both; 12 | visibility: hidden; 13 | font-size: 0; 14 | height: 0; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /components/style/mixins/compatibility.less: -------------------------------------------------------------------------------- 1 | // Compatibility for browsers. 2 | 3 | // rotate for ie8 and blow 4 | .ie-rotate(@rotation) { 5 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; 6 | } 7 | 8 | // rotate for ie8 and blow 9 | // degrees unit 10 | .ie-rotate-via-degrees(@degrees) { 11 | /* IE6-IE8 */ 12 | @radians: ~`parseInt("@{degrees}") * Math.PI * 2 / 360`; 13 | @costheta: ~`Math.cos("@{radians}")`; 14 | @sintheta: ~`Math.sin("@{radians}")`; 15 | @negsintheta: ~`"@{sintheta}" * -1`; 16 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=@{costheta}, M12=@{negsintheta}, M21=@{sintheta}, M22=@{costheta})"; 17 | zoom: 1; 18 | 19 | :root & { 20 | filter: none; 21 | } 22 | } 23 | 24 | // support rotate for all browsers 25 | .cross-rotate(@degrees) { 26 | .rotate(@degrees); 27 | .ie-rotate-via-degrees(@degrees); 28 | } 29 | 30 | // Placeholder text 31 | .placeholder(@color: @input-placeholder-color) { 32 | // Firefox 33 | &::-moz-placeholder { 34 | color: @color; 35 | opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526 36 | } 37 | // Internet Explorer 10+ 38 | &:-ms-input-placeholder { 39 | color: @color; 40 | } 41 | // Safari and Chrome 42 | &::-webkit-input-placeholder { 43 | color: @color; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /components/style/mixins/iconfont.less: -------------------------------------------------------------------------------- 1 | .iconfont-mixin() { 2 | display: inline-block; 3 | font-style: normal; 4 | vertical-align: baseline; 5 | text-align: center; 6 | text-transform: none; 7 | line-height: 1; 8 | text-rendering: optimizeLegibility; 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | &:before { 12 | display: block; 13 | font-family: "anticon" !important; 14 | } 15 | } 16 | 17 | .iconfont-font(@content) { 18 | font-family: 'anticon'; 19 | text-rendering: optimizeLegibility; 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | content: @content; 23 | } 24 | 25 | // for iconfont font size 26 | // fix chrome 12px bug, support ie 27 | .iconfont-size-under-12px(@size, @rotate: 0deg) { 28 | display: inline-block; 29 | @font-scale: unit(@size / 12px); 30 | font-size: @font-size-base; 31 | // ie8-9 32 | font-size: ~"@{size} \9"; // lesshint duplicateProperty: false 33 | transform: scale(@font-scale) rotate(@rotate); 34 | .ie-rotate-via-degrees(@rotate); 35 | :root & { 36 | font-size: @font-size-base; // reset ie9 and above 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /components/style/mixins/index.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------------------------------- 3 | @import "opacity"; 4 | @import "size"; 5 | @import "compatibility"; 6 | @import "clearfix"; 7 | @import "iconfont"; 8 | @import "motion"; 9 | -------------------------------------------------------------------------------- /components/style/mixins/motion.less: -------------------------------------------------------------------------------- 1 | @import '../themes/default'; 2 | 3 | .motion-common(@duration: @animation-duration-base) { 4 | animation-duration: @duration; 5 | animation-fill-mode: both; 6 | } 7 | 8 | .motion-common-leave(@duration: @animation-duration-base) { 9 | animation-duration: @duration; 10 | animation-fill-mode: both; 11 | } 12 | 13 | .make-motion(@className, @keyframeName, @duration: @animation-duration-base) { 14 | .@{className}-enter, 15 | .@{className}-appear { 16 | .motion-common(@duration); 17 | animation-play-state: paused; 18 | } 19 | .@{className}-leave { 20 | .motion-common-leave(@duration); 21 | animation-play-state: paused; 22 | } 23 | .@{className}-enter.@{className}-enter-active, 24 | .@{className}-appear.@{className}-appear-active { 25 | animation-name: ~"@{keyframeName}In"; 26 | animation-play-state: running; 27 | } 28 | .@{className}-leave.@{className}-leave-active { 29 | animation-name: ~"@{keyframeName}Out"; 30 | animation-play-state: running; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /components/style/mixins/opacity.less: -------------------------------------------------------------------------------- 1 | // Opacity 2 | 3 | .opacity(@opacity) { 4 | opacity: @opacity; 5 | // IE8 filter 6 | @opacity-ie: (@opacity * 100); 7 | filter: ~"alpha(opacity=@{opacity-ie})"; 8 | } 9 | -------------------------------------------------------------------------------- /components/style/mixins/size.less: -------------------------------------------------------------------------------- 1 | // Sizing shortcuts 2 | 3 | .size(@width; @height) { 4 | width: @width; 5 | height: @height; 6 | } 7 | 8 | .square(@size) { 9 | .size(@size; @size); 10 | } 11 | -------------------------------------------------------------------------------- /components/switch/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Switch from './switch' 4 | 5 | export default Switch 6 | -------------------------------------------------------------------------------- /components/switch/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | 4 | @switch-prefix-cls: ~"@{ant-prefix}-switch"; 5 | @switch-duration: .3s; 6 | 7 | .@{switch-prefix-cls} { 8 | position: relative; 9 | display: inline-block; 10 | box-sizing: border-box; 11 | height: 22px; 12 | min-width: 44px; 13 | line-height: 20px; 14 | vertical-align: middle; 15 | border-radius: 20px; 16 | border: 1px solid #ccc; 17 | background-color: @disabled-color; 18 | cursor: pointer; 19 | transition: all @switch-duration; 20 | user-select: none; 21 | 22 | &-inner { 23 | color: #fff; 24 | font-size: @font-size-base; 25 | margin-left: 24px; 26 | margin-right: 6px; 27 | display: block; 28 | } 29 | 30 | &:after { 31 | position: absolute; 32 | width: 18px; 33 | height: 18px; 34 | left: 1px; 35 | top: 1px; 36 | 37 | border-radius: 18px; 38 | background-color: @component-background; 39 | content: " "; 40 | cursor: pointer; 41 | transition: all @switch-duration, width @switch-duration; 42 | } 43 | 44 | &:active:after { 45 | width: 24px; 46 | } 47 | 48 | &:focus { 49 | box-shadow: 0 0 0 2px fade(@primary-color, 20%); 50 | outline: 0; 51 | } 52 | 53 | &:focus:hover { 54 | box-shadow: none; 55 | } 56 | 57 | &-small { 58 | height: 14px; 59 | min-width: 28px; 60 | line-height: 12px; 61 | 62 | .@{switch-prefix-cls}-inner { 63 | margin-left: 18px; 64 | margin-right: 3px; 65 | } 66 | 67 | &:after { 68 | width: 12px; 69 | height: 12px; 70 | top: 0; 71 | left: 0.5px; 72 | } 73 | 74 | &:active:after { 75 | width: 16px; 76 | } 77 | } 78 | 79 | &-small&-checked { 80 | &:after { 81 | left: 100%; 82 | margin-left: -12.5px; 83 | } 84 | 85 | .@{switch-prefix-cls}-inner { 86 | margin-left: 3px; 87 | margin-right: 18px; 88 | } 89 | } 90 | 91 | &-small:active&-checked:after { 92 | margin-left: -16.5px; 93 | } 94 | 95 | &-checked { 96 | border-color: @primary-color; 97 | background-color: @primary-color; 98 | 99 | .@{switch-prefix-cls}-inner { 100 | margin-left: 6px; 101 | margin-right: 24px; 102 | } 103 | 104 | &:after { 105 | left: 100%; 106 | margin-left: -19px; 107 | } 108 | 109 | &:active:after { 110 | margin-left: -25px; 111 | } 112 | } 113 | 114 | &-disabled { 115 | cursor: not-allowed; 116 | background: #f4f4f4; 117 | border-color: #f4f4f4; 118 | 119 | &:after { 120 | background: #ccc; 121 | cursor: not-allowed; 122 | } 123 | 124 | .@{switch-prefix-cls}-inner { 125 | color: @disabled-color; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /components/switch/switch.vue: -------------------------------------------------------------------------------- 1 | 13 | 49 | -------------------------------------------------------------------------------- /components/tabs/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Tabs from './tabs' 4 | import TabPane from './tab-pane' 5 | 6 | Tabs.Pane = TabPane 7 | 8 | export default Tabs 9 | -------------------------------------------------------------------------------- /components/tabs/store.js: -------------------------------------------------------------------------------- 1 | import BaseStore from '../utils/base-store' 2 | 3 | class Mutations { 4 | onTabClick(index) { 5 | this.state.activeIndex = index 6 | 7 | this.component.$emit('onTabClick', index) 8 | this.component.$emit('input', index) 9 | } 10 | } 11 | 12 | class Store extends BaseStore { 13 | constructor(component, initialState = {}) { 14 | super(component, initialState) 15 | 16 | this.state = { ...initialState } 17 | 18 | this.setState(initialState) 19 | 20 | this.mutations = new Mutations() 21 | } 22 | } 23 | 24 | export default Store 25 | -------------------------------------------------------------------------------- /components/tabs/style/card-style.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | @tab-prefix-cls: ~"@{ant-prefix}-tabs"; 4 | 5 | // card style 6 | .@{tab-prefix-cls} { 7 | &&-card > &-bar &-nav-container { 8 | height: 32px; 9 | } 10 | &&-card > &-bar &-ink-bar { 11 | visibility: hidden; 12 | } 13 | &&-card > &-bar &-tab { 14 | margin: 0; 15 | border: @border-width-base @border-style-base @border-color-base; 16 | border-bottom: 0; 17 | border-radius: @border-radius-base @border-radius-base 0 0; 18 | transition: all 0.3s @ease-in-out; 19 | background: #f9f9f9; 20 | margin-right: 2px; 21 | } 22 | &&-card > &-bar &-tab { 23 | padding: 5px 16px 4px; 24 | transition: all 0.3s @ease-in-out; 25 | } 26 | &&-card > &-bar &-tab-active { 27 | background: @component-background; 28 | transform: translateZ(0); 29 | border-color: @border-color-base; 30 | color: @primary-color; 31 | } 32 | &&-card > &-bar &-tab-active { 33 | padding-bottom: 5px; 34 | transform: translateZ(0); 35 | } 36 | &&-card > &-bar &-nav-wrap { 37 | margin-bottom: 0; 38 | } 39 | &&-card > &-bar &-tab .@{iconfont-css-prefix}-close { 40 | margin-right: 0; 41 | color: @text-color-secondary; 42 | transition: all 0.3s @ease-in-out; 43 | .iconfont-size-under-12px(9px); 44 | transform-origin: 100% 50%; 45 | width: 0; 46 | text-align: right; 47 | vertical-align: middle; 48 | overflow: hidden; 49 | &:hover { 50 | color: #404040; 51 | font-weight: bold; 52 | } 53 | } 54 | 55 | &&-editable-card > &-bar &-tab > div { 56 | transition: all 0.3s @ease-in-out; 57 | } 58 | &&-editable-card > &-bar &-tab:not(&-tab-active):hover > div:not(&-tab-unclosable) { 59 | margin-left: -8px; 60 | margin-right: -8px; 61 | } 62 | 63 | &&-card > &-bar &-tab-active .@{iconfont-css-prefix}-close, 64 | &&-card > &-bar &-tab:hover .@{iconfont-css-prefix}-close { 65 | width: 16px; 66 | transform: translateZ(0); 67 | } 68 | 69 | &-extra-content { 70 | float: right; 71 | line-height: 32px; 72 | 73 | .@{tab-prefix-cls}-new-tab { 74 | width: 20px; 75 | height: 20px; 76 | line-height: 20px; 77 | text-align: center; 78 | cursor: pointer; 79 | border-radius: @border-radius-base; 80 | border: @border-width-base @border-style-base @border-color-base; 81 | font-size: @font-size-base; 82 | .iconfont-size-under-12px(10px); 83 | color: @text-color-secondary; 84 | transition: color 0.3s ease; 85 | &:hover { 86 | color: #404040; 87 | } 88 | } 89 | } 90 | 91 | // https://github.com/ant-design/ant-design/issues/4669 92 | &-vertical&-card > .@{tab-prefix-cls}-bar { 93 | .@{tab-prefix-cls}-nav-container { 94 | height: auto; 95 | } 96 | .@{tab-prefix-cls}-tab { 97 | border-bottom: @border-width-base @border-style-base @border-color-base; 98 | margin-bottom: 8px; 99 | &-active { 100 | padding-bottom: 4px; 101 | } 102 | &:last-child { 103 | margin-bottom: 8px; 104 | } 105 | } 106 | } 107 | 108 | &-vertical&-card&-left > .@{tab-prefix-cls}-bar { 109 | .@{tab-prefix-cls}-nav-wrap { 110 | margin-right: 0; 111 | } 112 | .@{tab-prefix-cls}-tab { 113 | border-right: 0; 114 | border-radius: @border-radius-base 0 0 @border-radius-base; 115 | margin-right: 1px; 116 | &-active { 117 | margin-right: -1px; 118 | padding-right: 18px; 119 | } 120 | } 121 | } 122 | 123 | &-vertical&-card&-right > .@{tab-prefix-cls}-bar { 124 | .@{tab-prefix-cls}-nav-wrap { 125 | margin-left: 0; 126 | } 127 | .@{tab-prefix-cls}-tab { 128 | border-left: 0; 129 | border-radius: 0 @border-radius-base @border-radius-base 0; 130 | margin-left: 1px; 131 | &-active { 132 | margin-left: -1px; 133 | padding-left: 18px; 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /components/tabs/tab-pane.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'VTabPane', 3 | 4 | props: { 5 | index: String, 6 | disabled: { 7 | type: Boolean, 8 | default: false 9 | }, 10 | closable: { 11 | type: Boolean, 12 | default: true 13 | }, 14 | tab: [String, Object] 15 | }, 16 | data() { 17 | return { 18 | store: this.$parent.store 19 | } 20 | }, 21 | render() { 22 | const activeIndex = this.store.state.activeIndex 23 | const data = { 24 | class: { 25 | 'ant-tabs-tabpane-active': activeIndex === this.index, 26 | 'ant-tabs-tabpane-inactive': activeIndex !== this.index 27 | } 28 | } 29 | return (
    {this.$slots.default}
    ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /components/tabs/tabs.js: -------------------------------------------------------------------------------- 1 | import VTabNav from './tab-nav' 2 | import Store from './store' 3 | 4 | export default { 5 | name: 'VTabs', 6 | 7 | props: { 8 | value: String, 9 | type: { 10 | type: String, 11 | default: 'line' 12 | }, 13 | size: String, 14 | hideAdd: Boolean, 15 | tabBarExtraContent: null 16 | }, 17 | 18 | data() { 19 | const store = new Store(this, { activeIndex: this.value, injected: true, ...this.$props }) 20 | return { 21 | store 22 | } 23 | }, 24 | 25 | watch: { 26 | value() { 27 | this.store.state.activeIndex = this.value 28 | } 29 | }, 30 | 31 | render() { 32 | const panes = this.$slots.default || [] 33 | const tabsData = { 34 | class: { 35 | 'ant-tabs-mini': this.size === 'small', 36 | 'ant-tabs-card': this.type === 'editable-card', 37 | [`ant-tabs-${this.type}`]: !!this.type 38 | } 39 | } 40 | const tabNavData = { 41 | props: { 42 | panes 43 | } 44 | } 45 | const panesIndex = panes.findIndex(pane => 46 | (pane.componentOptions.propsData.index === this.store.state.activeIndex)) 47 | 48 | const style = { 49 | 'margin-left': `${-panesIndex * 100}%` 50 | } 51 | return (
    52 | 53 |
    54 | {panes} 55 |
    56 |
    ) 57 | }, 58 | 59 | components: { 60 | VTabNav 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /components/tag/index.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | 3 | import Tag from './tag' 4 | 5 | export default Tag 6 | -------------------------------------------------------------------------------- /components/tag/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | 4 | @tag-prefix-cls: ~"@{ant-prefix}-tag"; 5 | 6 | .@{tag-prefix-cls} { 7 | display: inline-block; 8 | line-height: 20px; 9 | height: 22px; 10 | padding: 0 8px; 11 | border-radius: @border-radius-base; 12 | border: @border-width-base @border-style-base @border-color-split; 13 | background: @background-color-base; 14 | font-size: @font-size-base; 15 | transition: all 0.3s @ease-in-out-circ; 16 | opacity: 1; 17 | margin-right: 8px; 18 | cursor: pointer; 19 | white-space: nowrap; 20 | 21 | &:hover { 22 | opacity: 0.85; 23 | } 24 | 25 | &, 26 | a, 27 | a:hover { 28 | color: @text-color; 29 | } 30 | 31 | &-text { 32 | a:first-child:last-child { 33 | display: inline-block; 34 | margin: 0 -8px; 35 | padding: 0 8px; 36 | } 37 | } 38 | 39 | .@{iconfont-css-prefix}-cross { 40 | .iconfont-size-under-12px(10px); 41 | cursor: pointer; 42 | font-weight: bold; 43 | margin-left: 3px; 44 | transition: all 0.3s ease; 45 | opacity: 0.66; 46 | 47 | &:hover { 48 | opacity: 1; 49 | } 50 | } 51 | 52 | &-has-color { 53 | border-color: transparent; 54 | &, 55 | a, 56 | a:hover, 57 | .@{iconfont-css-prefix}-cross, 58 | .@{iconfont-css-prefix}-cross:hover { 59 | color: #fff; 60 | } 61 | } 62 | 63 | &-checkable { 64 | background-color: transparent; 65 | border-color: transparent; 66 | &:hover, 67 | &:active, 68 | &-checked { 69 | color: #fff; 70 | } 71 | &:hover { 72 | background-color: @primary-5; 73 | } 74 | &-checked { 75 | background-color: @primary-6; 76 | } 77 | &:active { 78 | background-color: @primary-7; 79 | } 80 | } 81 | 82 | &-close { 83 | width: 0 !important; 84 | padding: 0; 85 | margin: 0; 86 | } 87 | 88 | &-zoom-enter, 89 | &-zoom-appear { 90 | animation: antFadeIn .2s @ease-in-out-circ; 91 | animation-fill-mode: both; 92 | } 93 | 94 | &-zoom-leave { 95 | animation: antZoomOut .3s @ease-in-out-circ; 96 | animation-fill-mode: both; 97 | } 98 | 99 | @colors: pink, red, orange, yellow, cyan, green, blue, purple; 100 | 101 | // mixin to iterate over colors and create CSS class for each one 102 | .make-color-classes(@i: length(@colors)) when (@i > 0) { 103 | .make-color-classes(@i - 1); 104 | @color: extract(@colors, @i); 105 | @lightColor: "@{color}-2"; 106 | @darkColor: "@{color}-6"; 107 | &-@{color} { 108 | color: @@darkColor; 109 | background: @@lightColor; 110 | border-color: @@lightColor; 111 | } 112 | &-@{color}-inverse { 113 | background: @@darkColor; 114 | border-color: @@darkColor; 115 | color: #fff; 116 | } 117 | } 118 | 119 | .make-color-classes(); 120 | } 121 | -------------------------------------------------------------------------------- /components/tag/tag.vue: -------------------------------------------------------------------------------- 1 | 15 | 72 | -------------------------------------------------------------------------------- /components/tooltip/index.js: -------------------------------------------------------------------------------- 1 | import Tooltip from './tooltip' 2 | 3 | export default Tooltip 4 | -------------------------------------------------------------------------------- /components/tooltip/style/index.less: -------------------------------------------------------------------------------- 1 | @import "../../style/themes/default"; 2 | @import "../../style/mixins/index"; 3 | 4 | @tooltip-prefix-cls: ~"@{ant-prefix}-tooltip"; 5 | 6 | // Base class 7 | .@{tooltip-prefix-cls} { 8 | position: absolute; 9 | z-index: @zindex-tooltip; 10 | display: block; 11 | visibility: visible; 12 | font-size: @font-size-base; 13 | line-height: @line-height-base; 14 | 15 | &-hidden { 16 | display: none; 17 | } 18 | 19 | &-placement-top, 20 | &-placement-topLeft, 21 | &-placement-topRight { 22 | padding: @tooltip-arrow-width 0 @tooltip-distance 0; 23 | } 24 | &-placement-right, 25 | &-placement-rightTop, 26 | &-placement-rightBottom { 27 | padding: 0 @tooltip-arrow-width 0 @tooltip-distance; 28 | } 29 | &-placement-bottom, 30 | &-placement-bottomLeft, 31 | &-placement-bottomRight { 32 | padding: @tooltip-distance 0 @tooltip-arrow-width 0; 33 | } 34 | &-placement-left, 35 | &-placement-leftTop, 36 | &-placement-leftBottom { 37 | padding: 0 @tooltip-distance 0 @tooltip-arrow-width; 38 | } 39 | } 40 | 41 | // Wrapper for the tooltip content 42 | .@{tooltip-prefix-cls}-inner { 43 | max-width: @tooltip-max-width; 44 | padding: 8px 10px; 45 | color: @tooltip-color; 46 | text-align: left; 47 | text-decoration: none; 48 | background-color: @tooltip-bg; 49 | border-radius: @border-radius-base; 50 | box-shadow: @box-shadow-base; 51 | min-height: 34px; 52 | } 53 | 54 | // Arrows 55 | .@{tooltip-prefix-cls}-arrow { 56 | position: absolute; 57 | width: 0; 58 | height: 0; 59 | border-color: transparent; 60 | border-style: solid; 61 | } 62 | 63 | .@{tooltip-prefix-cls} { 64 | &-placement-top &-arrow, 65 | &-placement-topLeft &-arrow, 66 | &-placement-topRight &-arrow { 67 | bottom: @tooltip-distance - @tooltip-arrow-width; 68 | border-width: @tooltip-arrow-width @tooltip-arrow-width 0; 69 | border-top-color: @tooltip-arrow-color; 70 | } 71 | 72 | &-placement-top &-arrow { 73 | left: 50%; 74 | margin-left: -@tooltip-arrow-width; 75 | } 76 | 77 | &-placement-topLeft &-arrow { 78 | left: 16px; 79 | } 80 | 81 | &-placement-topRight &-arrow { 82 | right: 16px; 83 | } 84 | 85 | &-placement-right &-arrow, 86 | &-placement-rightTop &-arrow, 87 | &-placement-rightBottom &-arrow { 88 | left: @tooltip-distance - @tooltip-arrow-width; 89 | border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0; 90 | border-right-color: @tooltip-arrow-color; 91 | } 92 | 93 | &-placement-right &-arrow { 94 | top: 50%; 95 | margin-top: -@tooltip-arrow-width; 96 | } 97 | 98 | &-placement-rightTop &-arrow { 99 | top: 8px; 100 | } 101 | 102 | &-placement-rightBottom &-arrow { 103 | bottom: 8px; 104 | } 105 | 106 | &-placement-left &-arrow, 107 | &-placement-leftTop &-arrow, 108 | &-placement-leftBottom &-arrow { 109 | right: @tooltip-distance - @tooltip-arrow-width; 110 | border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width; 111 | border-left-color: @tooltip-arrow-color; 112 | } 113 | 114 | &-placement-left &-arrow { 115 | top: 50%; 116 | margin-top: -@tooltip-arrow-width; 117 | } 118 | 119 | &-placement-leftTop &-arrow { 120 | top: 8px; 121 | } 122 | 123 | &-placement-leftBottom &-arrow { 124 | bottom: 8px; 125 | } 126 | 127 | &-placement-bottom &-arrow, 128 | &-placement-bottomLeft &-arrow, 129 | &-placement-bottomRight &-arrow { 130 | top: @tooltip-distance - @tooltip-arrow-width; 131 | border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; 132 | border-bottom-color: @tooltip-arrow-color; 133 | } 134 | 135 | &-placement-bottom &-arrow { 136 | left: 50%; 137 | margin-left: -@tooltip-arrow-width; 138 | } 139 | 140 | &-placement-bottomLeft &-arrow { 141 | left: 16px; 142 | } 143 | 144 | &-placement-bottomRight &-arrow { 145 | right: 16px; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /components/tooltip/tooltip.js: -------------------------------------------------------------------------------- 1 | import './style/index.less' 2 | import TooltipMixin from '../mixins/tooltip' 3 | 4 | export default { 5 | name: 'VTooltip', 6 | 7 | mixins: [TooltipMixin], 8 | 9 | data() { 10 | return { 11 | prefixCls: 'ant-tooltip' 12 | } 13 | }, 14 | 15 | components: { 16 | /** 17 | * 定义函数化组件用于插入到popperVM 18 | * 实际浮层显示的内容 19 | */ 20 | VTooltipContent: { 21 | functional: true, 22 | 23 | render(h, context) { 24 | return (
    { context.data.title }
    ) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /components/transition/collapse.js: -------------------------------------------------------------------------------- 1 | class Transition { 2 | constructor() { 3 | this.props = { 4 | 'enter-active-class': 'ant-motion-collapse ant-motion-collapse-active', 5 | 'leave-active-class': 'ant-motion-collapse ant-motion-collapse-active' 6 | } 7 | this.on = { 8 | beforeEnter(el) { 9 | // 获取隐藏的元素的高度 10 | // const oldStyle = { 11 | // visibility: el.style.visibility, 12 | // position: el.style.position, 13 | // display: el.style.display 14 | // } 15 | // el.style.visibility = 'hidden' 16 | // el.style.position = 'absolute' 17 | // el.style.display = '' 18 | // el.dataset.offsetHeight = el.offsetHeight 19 | // Object.keys(oldStyle).forEach((key) => { 20 | // el.style[key] = oldStyle[key] 21 | // }) 22 | el.style.height = '0px' 23 | }, 24 | enter(el) { 25 | el.style.display = '' 26 | el.style.height = `${el.scrollHeight}px` 27 | }, 28 | afterEnter(el) { 29 | el.style.display = '' 30 | el.style.height = '' 31 | }, 32 | beforeLeave(el) { 33 | el.style.height = `${el.offsetHeight}px` 34 | }, 35 | leave(el) { 36 | // todo: 为什么一定要setTimeout呢··· 37 | // setTimeout(() => { 38 | // el.style.height = '0px' 39 | // }, 0) 40 | // todo: 一个奇怪的现象,必须要调用一次el的属性,否则没有过渡效果,不知道是否有兼容问题 41 | let h = el.scrollHeight 42 | h = '0px' 43 | el.style.height = h 44 | }, 45 | afterLeave(el) { 46 | el.style.height = '' 47 | } 48 | } 49 | } 50 | } 51 | 52 | export default Transition 53 | -------------------------------------------------------------------------------- /components/transition/fade.js: -------------------------------------------------------------------------------- 1 | class Transition { 2 | constructor({ prefixCls }) { 3 | const cls = prefixCls ? `${prefixCls}-` : '' 4 | this.props = { 5 | 'enter-active-class': `${cls}fade-enter ${cls}fade-enter-active`, 6 | 'leave-active-class': `${cls}fade-leave ${cls}fade-leave-active` 7 | } 8 | } 9 | } 10 | 11 | export default Transition 12 | -------------------------------------------------------------------------------- /components/transition/index.js: -------------------------------------------------------------------------------- 1 | import Collapse from './collapse' 2 | import SideUp from './slide-up' 3 | import ZoomBigFast from './zoom-big-fast' 4 | import MoveUp from './move-up' 5 | import Fade from './fade' 6 | import Zoom from './zoom' 7 | 8 | export default { 9 | name: 'VTransition', 10 | functional: true, 11 | render(createElement, context) { 12 | const attrs = context.data.attrs 13 | const type = attrs.type 14 | let data = {} 15 | switch (type) { 16 | case 'collapse': 17 | data = new Collapse(attrs) 18 | break 19 | case 'slide-up': 20 | data = new SideUp(attrs) 21 | break 22 | case 'zoom-big-fast': 23 | data = new ZoomBigFast(attrs) 24 | break 25 | case 'move-up': 26 | data = new MoveUp(attrs) 27 | break 28 | case 'fade': 29 | data = new Fade(attrs) 30 | break 31 | case 'zoom': 32 | data = new Zoom(attrs) 33 | break 34 | default: 35 | } 36 | data = Object.assign({}, context.data, data) 37 | return createElement('transition', data, context.children) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /components/transition/move-up.js: -------------------------------------------------------------------------------- 1 | class Transition { 2 | constructor() { 3 | this.props = { 4 | 'enter-active-class': 'move-up-enter move-up-enter-active', 5 | 'leave-active-class': 'move-up-leave move-up-leave-active' 6 | } 7 | } 8 | } 9 | 10 | export default Transition 11 | -------------------------------------------------------------------------------- /components/transition/slide-up.js: -------------------------------------------------------------------------------- 1 | class Transition { 2 | constructor({ prefixCls }) { 3 | const cls = prefixCls ? `${prefixCls}-` : '' 4 | this.props = { 5 | 'enter-active-class': `${cls}slide-up-enter ${cls}slide-up-enter-active`, 6 | 'leave-active-class': `${cls}slide-up-leave ${cls}slide-up-leave-active` 7 | } 8 | } 9 | } 10 | 11 | export default Transition 12 | -------------------------------------------------------------------------------- /components/transition/zoom-big-fast.js: -------------------------------------------------------------------------------- 1 | class Transition { 2 | constructor() { 3 | this.props = { 4 | 'enter-active-class': 'zoom-big-fast-enter zoom-big-fast-enter-active', 5 | 'leave-active-class': 'zoom-big-fast-leave zoom-big-fast-leave-active' 6 | } 7 | } 8 | } 9 | 10 | export default Transition 11 | -------------------------------------------------------------------------------- /components/transition/zoom.js: -------------------------------------------------------------------------------- 1 | class Transition { 2 | constructor({ prefixCls }) { 3 | const cls = prefixCls ? `${prefixCls}-` : '' 4 | this.props = { 5 | 'enter-active-class': `${cls}zoom-enter ${cls}zoom-enter-active`, 6 | 'leave-active-class': `${cls}zoom-leave ${cls}zoom-leave-active` 7 | } 8 | } 9 | } 10 | 11 | export default Transition 12 | -------------------------------------------------------------------------------- /components/utils/base-store.js: -------------------------------------------------------------------------------- 1 | class BaseStore { 2 | constructor(component) { 3 | if (!component) { 4 | throw new Error('component is required.') 5 | } 6 | this.component = component 7 | 8 | this.state = {} 9 | } 10 | 11 | setState(state) { 12 | this.state.injected = false // 标记store是否注入,为false时自动将store注入到所有子组件中 13 | 14 | Object.keys(state).forEach((prop) => { 15 | if ({}.hasOwnProperty.call(this.state, prop)) { 16 | this.state[prop] = state[prop] 17 | } 18 | }) 19 | 20 | this.component.$nextTick(() => { 21 | if (!this.state.injected) this.reject() 22 | }) 23 | } 24 | 25 | reject() { 26 | const deepNodes = (nodes) => { 27 | nodes.forEach((node) => { 28 | node.store = this 29 | deepNodes(node.$children) 30 | }) 31 | } 32 | 33 | deepNodes(this.component.$children) 34 | this.state.injected = true 35 | } 36 | 37 | commit(name, ...args) { 38 | if (!this.state.injected) this.reject() 39 | const mutations = this.mutations 40 | if (mutations[name]) { 41 | mutations[name].apply(this, args) 42 | } 43 | } 44 | } 45 | 46 | export default BaseStore 47 | -------------------------------------------------------------------------------- /components/utils/calcTextareaHeight.js: -------------------------------------------------------------------------------- 1 | // Thanks to 2 | // https://github.com/andreypopp/react-textarea-autosize/ 3 | // https://github.com/ElemeFE/element/blob/master/packages/input/src/calcTextareaHeight.js 4 | 5 | let hiddenTextarea 6 | 7 | const HIDDEN_STYLE = ` 8 | height:0 !important; 9 | visibility:hidden !important; 10 | overflow:hidden !important; 11 | position:absolute !important; 12 | z-index:-1000 !important; 13 | top:0 !important; 14 | right:0 !important; 15 | ` 16 | 17 | const CONTEXT_STYLE = [ 18 | 'letter-spacing', 19 | 'line-height', 20 | 'padding-top', 21 | 'padding-bottom', 22 | 'font-family', 23 | 'font-weight', 24 | 'font-size', 25 | 'text-rendering', 26 | 'text-transform', 27 | 'width', 28 | 'text-indent', 29 | 'padding-left', 30 | 'padding-right', 31 | 'border-width', 32 | 'box-sizing' 33 | ] 34 | 35 | function calculateNodeStyling(node) { 36 | const style = window.getComputedStyle(node) 37 | 38 | const boxSizing = style.getPropertyValue('box-sizing') 39 | 40 | const paddingSize = ( 41 | parseFloat(style.getPropertyValue('padding-bottom')) + 42 | parseFloat(style.getPropertyValue('padding-top')) 43 | ) 44 | 45 | const borderSize = ( 46 | parseFloat(style.getPropertyValue('border-bottom-width')) + 47 | parseFloat(style.getPropertyValue('border-top-width')) 48 | ) 49 | 50 | const contextStyle = CONTEXT_STYLE 51 | .map(name => `${name}:${style.getPropertyValue(name)}`) 52 | .join(';') 53 | 54 | return { contextStyle, paddingSize, borderSize, boxSizing } 55 | } 56 | 57 | export default function calcTextareaHeight(targetNode, minRows = null, maxRows = null) { 58 | if (!hiddenTextarea) { 59 | hiddenTextarea = document.createElement('textarea') 60 | document.body.appendChild(hiddenTextarea) 61 | } 62 | 63 | const { 64 | paddingSize, 65 | borderSize, 66 | boxSizing, 67 | contextStyle 68 | } = calculateNodeStyling(targetNode) 69 | 70 | hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`) 71 | hiddenTextarea.value = targetNode.value || targetNode.placeholder || '' 72 | 73 | let height = hiddenTextarea.scrollHeight 74 | let minHeight = -Infinity 75 | let maxHeight = Infinity 76 | 77 | if (boxSizing === 'border-box') { 78 | height += borderSize 79 | } else if (boxSizing === 'content-box') { 80 | height -= paddingSize 81 | } 82 | 83 | hiddenTextarea.value = '' 84 | const singleRowHeight = hiddenTextarea.scrollHeight - paddingSize 85 | 86 | if (minRows !== null) { 87 | minHeight = singleRowHeight * minRows 88 | if (boxSizing === 'border-box') { 89 | minHeight = minHeight + paddingSize + borderSize 90 | } 91 | height = Math.max(minHeight, height) 92 | } 93 | if (maxRows !== null) { 94 | maxHeight = singleRowHeight * maxRows 95 | if (boxSizing === 'border-box') { 96 | maxHeight = maxHeight + paddingSize + borderSize 97 | } 98 | height = Math.min(maxHeight, height) 99 | } 100 | 101 | return { 102 | height: `${height}px`, 103 | minHeight: `${minHeight}px`, 104 | maxHeight: `${maxHeight}px` 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /components/utils/clickoutside.js: -------------------------------------------------------------------------------- 1 | // https://github.com/ElemeFE/element/blob/dev/src/utils/clickoutside.js 2 | 3 | const nodeList = [] 4 | const ctx = '@@clickoutsideContext' 5 | 6 | window.document.addEventListener('mousedown', (e) => { 7 | nodeList.forEach(node => node[ctx].documentHandler(e)) 8 | }) 9 | 10 | /** 11 | * v-clickoutside 12 | * @desc 点击元素外面才会触发的事件 13 | * @example 14 | * ```vue 15 | *
    16 | * ``` 17 | */ 18 | export default { 19 | bind(el, binding, vnode) { 20 | const id = nodeList.push(el) - 1 21 | const documentHandler = (e) => { 22 | if (!vnode.context || 23 | el.contains(e.target) || 24 | (vnode.context.popper && 25 | vnode.context.popper.contains(e.target))) return 26 | 27 | if (binding.expression && el[ctx].methodName && vnode.context[el[ctx].methodName]) { 28 | vnode.context[el[ctx].methodName]() 29 | } else if (el[ctx].bindingFn) { 30 | el[ctx].bindingFn() 31 | } 32 | } 33 | el[ctx] = { 34 | id, 35 | documentHandler, 36 | methodName: binding.expression, 37 | bindingFn: binding.value 38 | } 39 | }, 40 | 41 | update(el, binding) { 42 | el[ctx].methodName = binding.expression 43 | el[ctx].bindingFn = binding.value 44 | }, 45 | 46 | unbind(el) { 47 | nodeList.some((node, index) => { 48 | if (node[ctx].id === el[ctx].id) { 49 | nodeList.splice(index, 1) 50 | return true 51 | } 52 | return false 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /components/utils/dom.js: -------------------------------------------------------------------------------- 1 | let cached 2 | 3 | class Dom { 4 | /** 5 | * 获取元素的offset:{top, left} 6 | * @param elem 7 | * @returns {{top: number, left: number}} 8 | */ 9 | static getOffset(elem) { 10 | if (!elem.getClientRects().length) return { top: 0, left: 0 } 11 | 12 | const rect = elem.getBoundingClientRect() 13 | const doc = elem.ownerDocument 14 | const docElem = doc.documentElement 15 | const win = doc.defaultView 16 | 17 | return { 18 | top: rect.top + win.pageYOffset - docElem.clientTop, 19 | left: rect.left + win.pageXOffset - docElem.clientLeft 20 | } 21 | } 22 | 23 | /** 24 | * 设置transform,浏览器兼容处理 25 | * @param style 26 | * @param v 27 | */ 28 | static setTransform(style, v) { 29 | style.transform = v 30 | style.webkitTransform = v 31 | style.mozTransform = v 32 | } 33 | 34 | static setTransformOrigin(node, value) { 35 | const style = node.style 36 | const list = ['Webkit', 'Moz', 'Ms', 'ms'] 37 | list.forEach((prefix) => { 38 | style[`${prefix}TransformOrigin`] = value 39 | }) 40 | style.transformOrigin = value 41 | } 42 | 43 | static getScrollBarSize(fresh) { 44 | if (fresh || cached === undefined) { 45 | const inner = document.createElement('div') 46 | inner.style.width = '100%' 47 | inner.style.height = '200px' 48 | 49 | const outer = document.createElement('div') 50 | const outerStyle = outer.style 51 | 52 | outerStyle.position = 'absolute' 53 | outerStyle.top = 0 54 | outerStyle.left = 0 55 | outerStyle.pointerEvents = 'none' 56 | outerStyle.visibility = 'hidden' 57 | outerStyle.width = '200px' 58 | outerStyle.height = '150px' 59 | outerStyle.overflow = 'hidden' 60 | 61 | outer.appendChild(inner) 62 | 63 | document.body.appendChild(outer) 64 | 65 | const widthContained = inner.offsetWidth 66 | outer.style.overflow = 'scroll' 67 | let widthScroll = inner.offsetWidth 68 | 69 | if (widthContained === widthScroll) { 70 | widthScroll = outer.clientWidth 71 | } 72 | 73 | document.body.removeChild(outer) 74 | 75 | cached = widthContained - widthScroll 76 | } 77 | return cached 78 | } 79 | } 80 | 81 | export default Dom 82 | -------------------------------------------------------------------------------- /components/utils/keycode.js: -------------------------------------------------------------------------------- 1 | export default { 2 | ZERO: 48, 3 | NINE: 57, 4 | 5 | NUMPAD_ZERO: 96, 6 | NUMPAD_NINE: 105, 7 | 8 | ESC: 27, 9 | TAB: 9, 10 | BACKSPACE: 8, 11 | DELETE: 46, 12 | ENTER: 13, 13 | 14 | LEFT: 37, 15 | UP: 38, 16 | RIGHT: 39, 17 | DOWN: 40 18 | } 19 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | // index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: '', 10 | assetsPublicPath: '/', 11 | productionSourceMap: false, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'] 18 | }, 19 | dev: { 20 | env: require('./dev.env'), 21 | port: 8080, 22 | assetsRoot: path.resolve(__dirname, '../dist'), 23 | assetsSubDirectory: '', 24 | assetsPublicPath: '/', 25 | proxyTable: {}, 26 | // CSS Sourcemaps off by default because relative paths are "buggy" 27 | // with this option, according to the CSS-Loader README 28 | // (https://github.com/webpack/css-loader#sourcemaps) 29 | // In our experience, they generally work as expected, 30 | // just be aware of this issue when enabling this option. 31 | cssSourceMap: false 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "antv", 3 | "version": "0.2.2", 4 | "description": "Ant Design of Vue", 5 | "author": "FlashHK ", 6 | "main": "dist/antv", 7 | "files": [ 8 | "dist" 9 | ], 10 | "directories": { 11 | "test": "test" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/ant-design-vue/antv.git" 16 | }, 17 | "keywords": [ 18 | "antd", 19 | "ant-design", 20 | "vue", 21 | "vue-component", 22 | "ui", 23 | "component", 24 | "components", 25 | "framework", 26 | "frontend" 27 | ], 28 | "license": "ISC", 29 | "bugs": { 30 | "url": "https://github.com/ant-design-vue/antv/issues" 31 | }, 32 | "homepage": "https://github.com/ant-design-vue/antv#readme", 33 | "scripts": { 34 | "bootstrap": "npm i", 35 | "dev": "node build/dev-server.js", 36 | "build": "node build/build.js", 37 | "lint": "eslint components --ext .js,.vue src", 38 | "pub": "sh build/git-release.sh && sh build/release.sh" 39 | }, 40 | "dependencies": { 41 | "popper.js": "^0.6.4", 42 | "vue": "^2.2.0", 43 | "vue-router": "^2.0.0-rc.7" 44 | }, 45 | "devDependencies": { 46 | "autoprefixer": "^6.4.0", 47 | "babel-core": "^6.0.0", 48 | "babel-eslint": "^7.0.0", 49 | "babel-loader": "^6.0.0", 50 | "babel-plugin-transform-runtime": "^6.0.0", 51 | "babel-helper-vue-jsx-merge-props": "^2.0.2", 52 | "babel-plugin-syntax-jsx": "^6.8.0", 53 | "babel-plugin-transform-vue-jsx": "^3.4.2", 54 | "babel-preset-es2015": "^6.0.0", 55 | "babel-preset-stage-2": "^6.0.0", 56 | "babel-register": "^6.0.0", 57 | "chalk": "^1.1.3", 58 | "connect-history-api-fallback": "^1.1.0", 59 | "css-loader": "^0.25.0", 60 | "eslint": "^3.7.1", 61 | "eslint-friendly-formatter": "^2.0.5", 62 | "eslint-loader": "^1.5.0", 63 | "eslint-plugin-html": "^1.3.0", 64 | "eslint-config-airbnb-base": "^8.0.0", 65 | "eslint-import-resolver-webpack": "^0.6.0", 66 | "eslint-plugin-import": "^1.16.0", 67 | "eventsource-polyfill": "^0.9.6", 68 | "express": "^4.13.3", 69 | "extract-text-webpack-plugin": "^1.0.1", 70 | "file-loader": "^0.9.0", 71 | "function-bind": "^1.0.2", 72 | "html-webpack-plugin": "^2.8.1", 73 | "http-proxy-middleware": "^0.17.2", 74 | "json-loader": "^0.5.4", 75 | "semver": "^5.3.0", 76 | "opn": "^4.0.2", 77 | "ora": "^0.3.0", 78 | "shelljs": "^0.7.4", 79 | "url-loader": "^0.5.7", 80 | "vue-loader": "^11.1.4", 81 | "vue-template-compiler": "^2.1.0", 82 | "vue-style-loader": "^1.0.0", 83 | "webpack": "^1.13.2", 84 | "webpack-dev-middleware": "^1.8.3", 85 | "webpack-hot-middleware": "^2.12.2", 86 | "webpack-merge": "^0.14.1", 87 | "less": "^2.7.1", 88 | "less-loader": "^2.2.3" 89 | }, 90 | "engines": { 91 | "node": ">= 4.0.0", 92 | "npm": ">= 3.0.0" 93 | } 94 | } 95 | --------------------------------------------------------------------------------