style: lint add semi and prettier

This commit is contained in:
tangjinzhou 2019-01-12 11:33:27 +08:00
parent b4d9bfd8d9
commit ffb2a593b1
1264 changed files with 38265 additions and 36569 deletions

View File

@ -3,3 +3,7 @@ node_modules/
**/style/
*.html
/components/test/*
es/
lib/
site-dist/
dist/

View File

@ -7,12 +7,13 @@
"es6": true
},
"parser": "babel-eslint",
"extends": ["plugin:vue-libs/recommended"],
"extends": ["plugin:vue-libs/recommended", "prettier"],
"rules": {
"comma-dangle": [2, "always-multiline"],
"no-var": "error",
"no-unused-vars": "warn",
"camelcase": "off",
"no-extra-boolean-cast": "off"
"no-extra-boolean-cast": "off",
"semi": ["error", "always"]
}
}

9
.prettierignore Normal file
View File

@ -0,0 +1,9 @@
**/*.md
**/*.svg
**/*.ejs
**/*.html
package.json
es/**
lib/**
site-dist/**
dist/**

19
.prettierrc Normal file
View File

@ -0,0 +1,19 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"overrides": [
{
"files": ".prettierrc",
"options": {
"parser": "json"
}
},
{
"files": ".stylelintrc",
"options": {
"parser": "json"
}
}
]
}

View File

@ -1,6 +1,5 @@
{
"extends": "stylelint-config-standard",
"ignoreFiles": "./components/**",
"extends": ["stylelint-config-standard", "stylelint-config-prettier"],
"rules": {
"comment-empty-line-before": null,
"declaration-empty-line-before": null,

View File

@ -1,27 +1,27 @@
#!/usr/bin/env node
'use strict'
'use strict';
require('colorful').colorful()
require('colorful').colorful();
const program = require('commander')
const packageInfo = require('../../package.json')
const program = require('commander');
const packageInfo = require('../../package.json');
program
.version(packageInfo.version)
.command('run [name]', 'run specified task')
.parse(process.argv)
.parse(process.argv);
// https://github.com/tj/commander.js/pull/260
const proc = program.runningCommand
const proc = program.runningCommand;
if (proc) {
proc.on('close', process.exit.bind(process))
proc.on('close', process.exit.bind(process));
proc.on('error', () => {
process.exit(1)
})
process.exit(1);
});
}
const subCmd = program.args[0]
const subCmd = program.args[0];
if (!subCmd || subCmd !== 'run') {
program.help()
program.help();
}

View File

@ -1,26 +1,26 @@
#!/usr/bin/env node
'use strict'
'use strict';
require('colorful').colorful()
const gulp = require('gulp')
const program = require('commander')
require('colorful').colorful();
const gulp = require('gulp');
const program = require('commander');
program.on('--help', () => {
console.log(' Usage:'.to.bold.blue.color)
console.log()
})
console.log(' Usage:'.to.bold.blue.color);
console.log();
});
program.parse(process.argv)
program.parse(process.argv);
const task = program.args[0]
const task = program.args[0];
if (!task) {
program.help()
program.help();
} else {
console.log('antd-tools run', task)
console.log('antd-tools run', task);
require('../gulpfile')
require('../gulpfile');
gulp.start(task)
gulp.start(task);
}

View File

@ -1,6 +1,6 @@
'use strict'
'use strict';
module.exports = function (modules) {
module.exports = function(modules) {
const plugins = [
require.resolve('babel-plugin-transform-vue-jsx'),
require.resolve('babel-plugin-transform-es3-member-expression-literals'),
@ -8,25 +8,31 @@ module.exports = function (modules) {
require.resolve('babel-plugin-transform-object-assign'),
require.resolve('babel-plugin-transform-object-rest-spread'),
require.resolve('babel-plugin-transform-class-properties'),
]
plugins.push([require.resolve('babel-plugin-transform-runtime'), {
polyfill: false,
}])
];
plugins.push([
require.resolve('babel-plugin-transform-runtime'),
{
polyfill: false,
},
]);
return {
presets: [
[require.resolve('babel-preset-env'), {
modules,
targets: {
browsers: [
'last 2 versions',
'Firefox ESR',
'> 1%',
'ie >= 9',
'iOS >= 8',
'Android >= 4',
],
[
require.resolve('babel-preset-env'),
{
modules,
targets: {
browsers: [
'last 2 versions',
'Firefox ESR',
'> 1%',
'ie >= 9',
'iOS >= 8',
'Android >= 4',
],
},
},
}],
],
],
plugins,
env: {
@ -34,5 +40,5 @@ module.exports = function (modules) {
plugins: [require.resolve('babel-plugin-istanbul')],
},
},
}
}
};
};

View File

@ -1,14 +1,14 @@
const path = require('path')
const webpack = require('webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
const deepAssign = require('deep-assign')
const chalk = require('chalk')
const postcssConfig = require('./postcssConfig')
const distFileBaseName = 'antd'
module.exports = function (modules) {
const pkg = require(path.join(process.cwd(), 'package.json'))
const babelConfig = require('./getBabelCommonConfig')(modules || false)
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const deepAssign = require('deep-assign');
const chalk = require('chalk');
const postcssConfig = require('./postcssConfig');
const distFileBaseName = 'antd';
module.exports = function(modules) {
const pkg = require(path.join(process.cwd(), 'package.json'));
const babelConfig = require('./getBabelCommonConfig')(modules || false);
const pluginImportOptions = [
{
@ -16,17 +16,12 @@ module.exports = function (modules) {
libraryName: 'antd',
libraryDirectory: 'components',
},
]
];
// if (distFileBaseName !== 'antd') { pluginImportOptions.push({ style:
// 'css', libraryDirectory: 'components', libraryName: 'antd', }) }
babelConfig
.plugins
.push([
require.resolve('babel-plugin-import'),
pluginImportOptions,
])
babelConfig.plugins.push([require.resolve('babel-plugin-import'), pluginImportOptions]);
const config = {
devtool: 'source-map',
@ -37,14 +32,10 @@ module.exports = function (modules) {
},
resolve: {
modules: [
'node_modules', path.join(__dirname, '../node_modules'),
],
extensions: [
'.js', '.jsx', '.vue', '.md', '.json',
],
modules: ['node_modules', path.join(__dirname, '../node_modules')],
extensions: ['.js', '.jsx', '.vue', '.md', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
vue$: 'vue/dist/vue.esm.js',
'@': process.cwd(),
},
},
@ -86,12 +77,14 @@ module.exports = function (modules) {
},
},
],
}, {
},
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: babelConfig,
}, {
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: [
@ -100,13 +93,15 @@ module.exports = function (modules) {
options: {
sourceMap: true,
},
}, {
},
{
loader: 'postcss-loader',
options: Object.assign({}, postcssConfig, { sourceMap: true }),
},
],
}),
}, {
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
use: [
@ -115,10 +110,12 @@ module.exports = function (modules) {
options: {
sourceMap: true,
},
}, {
},
{
loader: 'postcss-loader',
options: Object.assign({}, postcssConfig, { sourceMap: true }),
}, {
},
{
loader: 'less-loader',
options: {
sourceMap: true,
@ -140,23 +137,23 @@ Copyright 2017-present, ant-design-vue.
All rights reserved.
`),
new webpack.ProgressPlugin((percentage, msg, addInfo) => {
const stream = process.stderr
const stream = process.stderr;
if (stream.isTTY && percentage < 0.71) {
stream.cursorTo(0)
stream.write(`📦 ${chalk.magenta(msg)} (${chalk.magenta(addInfo)})`)
stream.clearLine(1)
stream.cursorTo(0);
stream.write(`📦 ${chalk.magenta(msg)} (${chalk.magenta(addInfo)})`);
stream.clearLine(1);
} else if (percentage === 1) {
console.log(chalk.green('\nwebpack: bundle build is now finished.'))
console.log(chalk.green('\nwebpack: bundle build is now finished.'));
}
}),
],
}
};
if (process.env.RUN_ENV === 'PRODUCTION') {
const entry = ['./index']
const entry = ['./index'];
config.entry = {
[`${distFileBaseName}.min`]: entry,
}
};
config.externals = {
vue: {
root: 'Vue',
@ -164,47 +161,41 @@ All rights reserved.
commonjs: 'vue',
amd: 'vue',
},
}
config.output.library = distFileBaseName
config.output.libraryTarget = 'umd'
};
config.output.library = distFileBaseName;
config.output.libraryTarget = 'umd';
const uncompressedConfig = deepAssign({}, config)
const uncompressedConfig = deepAssign({}, config);
config.plugins = config
.plugins
.concat([
new webpack
.optimize
.UglifyJsPlugin({
sourceMap: true,
output: {
ascii_only: true,
},
compress: {
warnings: false,
},
}),
new webpack
.optimize
.ModuleConcatenationPlugin(),
new webpack.LoaderOptionsPlugin({ minimize: true }),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'),
}),
])
config.plugins = config.plugins.concat([
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
output: {
ascii_only: true,
},
compress: {
warnings: false,
},
}),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.LoaderOptionsPlugin({ minimize: true }),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'),
}),
]);
uncompressedConfig.entry = {
[distFileBaseName]: entry,
}
};
uncompressedConfig
.plugins
.push(new webpack.DefinePlugin({
uncompressedConfig.plugins.push(
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
}))
}),
);
return [config, uncompressedConfig]
return [config, uncompressedConfig];
}
return config
}
return config;
};

View File

@ -1,57 +1,57 @@
'use strict'
'use strict';
// const install = require('./install')
const runCmd = require('./runCmd')
const getBabelCommonConfig = require('./getBabelCommonConfig')
const merge2 = require('merge2')
const { execSync } = require('child_process')
const through2 = require('through2')
const transformLess = require('./transformLess')
const webpack = require('webpack')
const babel = require('gulp-babel')
const argv = require('minimist')(process.argv.slice(2))
const GitHub = require('@octokit/rest')
const runCmd = require('./runCmd');
const getBabelCommonConfig = require('./getBabelCommonConfig');
const merge2 = require('merge2');
const { execSync } = require('child_process');
const through2 = require('through2');
const transformLess = require('./transformLess');
const webpack = require('webpack');
const babel = require('gulp-babel');
const argv = require('minimist')(process.argv.slice(2));
const GitHub = require('@octokit/rest');
const packageJson = require(`${process.cwd()}/package.json`)
const packageJson = require(`${process.cwd()}/package.json`);
// const getNpm = require('./getNpm')
// const selfPackage = require('../package.json')
const chalk = require('chalk')
const getNpmArgs = require('./utils/get-npm-args')
const getChangelog = require('./utils/getChangelog')
const path = require('path')
const chalk = require('chalk');
const getNpmArgs = require('./utils/get-npm-args');
const getChangelog = require('./utils/getChangelog');
const path = require('path');
// const watch = require('gulp-watch')
const gulp = require('gulp')
const fs = require('fs')
const rimraf = require('rimraf')
const replaceLib = require('./replaceLib')
const stripCode = require('gulp-strip-code')
const compareVersions = require('compare-versions')
const gulp = require('gulp');
const fs = require('fs');
const rimraf = require('rimraf');
const replaceLib = require('./replaceLib');
const stripCode = require('gulp-strip-code');
const compareVersions = require('compare-versions');
const cwd = process.cwd()
const libDir = path.join(cwd, 'lib')
const esDir = path.join(cwd, 'es')
const cwd = process.cwd();
const libDir = path.join(cwd, 'lib');
const esDir = path.join(cwd, 'es');
function dist (done) {
rimraf.sync(path.join(cwd, 'dist'))
process.env.RUN_ENV = 'PRODUCTION'
const webpackConfig = require(path.join(cwd, 'webpack.build.config.js'))
function dist(done) {
rimraf.sync(path.join(cwd, 'dist'));
process.env.RUN_ENV = 'PRODUCTION';
const webpackConfig = require(path.join(cwd, 'webpack.build.config.js'));
webpack(webpackConfig, (err, stats) => {
if (err) {
console.error(err.stack || err)
console.error(err.stack || err);
if (err.details) {
console.error(err.details)
console.error(err.details);
}
return
return;
}
const info = stats.toJson()
const info = stats.toJson();
if (stats.hasErrors()) {
console.error(info.errors)
console.error(info.errors);
}
if (stats.hasWarnings()) {
console.warn(info.warnings)
console.warn(info.warnings);
}
const buildInfo = stats.toString({
@ -62,109 +62,126 @@ function dist (done) {
chunkModules: false,
hash: false,
version: false,
})
console.log(buildInfo)
done(0)
})
});
console.log(buildInfo);
done(0);
});
}
function babelify (js, modules) {
const babelConfig = getBabelCommonConfig(modules)
babelConfig.babelrc = false
delete babelConfig.cacheDirectory
function babelify(js, modules) {
const babelConfig = getBabelCommonConfig(modules);
babelConfig.babelrc = false;
delete babelConfig.cacheDirectory;
if (modules === false) {
babelConfig.plugins.push(replaceLib)
babelConfig.plugins.push(replaceLib);
} else {
babelConfig.plugins.push(require.resolve('babel-plugin-add-module-exports'))
babelConfig.plugins.push(require.resolve('babel-plugin-add-module-exports'));
}
let stream = js.pipe(babel(babelConfig))
.pipe(through2.obj(function z (file, encoding, next) {
this.push(file.clone())
let stream = js.pipe(babel(babelConfig)).pipe(
through2.obj(function z(file, encoding, next) {
this.push(file.clone());
if (file.path.match(/\/style\/index\.(js|jsx)$/)) {
const content = file.contents.toString(encoding)
file.contents = Buffer.from(content
.replace(/\/style\/?'/g, '/style/css\'')
.replace(/\.less/g, '.css'))
file.path = file.path.replace(/index\.(js|jsx)$/, 'css.js')
this.push(file)
next()
const content = file.contents.toString(encoding);
file.contents = Buffer.from(
content.replace(/\/style\/?'/g, "/style/css'").replace(/\.less/g, '.css'),
);
file.path = file.path.replace(/index\.(js|jsx)$/, 'css.js');
this.push(file);
next();
} else {
next()
next();
}
}))
}),
);
if (modules === false) {
stream = stream.pipe(stripCode({
start_comment: '@remove-on-es-build-begin',
end_comment: '@remove-on-es-build-end',
}))
stream = stream.pipe(
stripCode({
start_comment: '@remove-on-es-build-begin',
end_comment: '@remove-on-es-build-end',
}),
);
}
return stream.pipe(gulp.dest(modules === false ? esDir : libDir))
return stream.pipe(gulp.dest(modules === false ? esDir : libDir));
}
function compile (modules) {
rimraf.sync(modules !== false ? libDir : esDir)
const less = gulp.src(['components/**/*.less'])
.pipe(through2.obj(function (file, encoding, next) {
this.push(file.clone())
if (file.path.match(/\/style\/index\.less$/) || file.path.match(/\/style\/v2-compatible-reset\.less$/)) {
transformLess(file.path).then((css) => {
file.contents = Buffer.from(css)
file.path = file.path.replace(/\.less$/, '.css')
this.push(file)
next()
}).catch((e) => {
console.error(e)
})
} else {
next()
}
}))
.pipe(gulp.dest(modules === false ? esDir : libDir))
const assets = gulp.src(['components/**/*.@(png|svg)']).pipe(gulp.dest(modules === false ? esDir : libDir))
function compile(modules) {
rimraf.sync(modules !== false ? libDir : esDir);
const less = gulp
.src(['components/**/*.less'])
.pipe(
through2.obj(function(file, encoding, next) {
this.push(file.clone());
if (
file.path.match(/\/style\/index\.less$/) ||
file.path.match(/\/style\/v2-compatible-reset\.less$/)
) {
transformLess(file.path)
.then(css => {
file.contents = Buffer.from(css);
file.path = file.path.replace(/\.less$/, '.css');
this.push(file);
next();
})
.catch(e => {
console.error(e);
});
} else {
next();
}
}),
)
.pipe(gulp.dest(modules === false ? esDir : libDir));
const assets = gulp
.src(['components/**/*.@(png|svg)'])
.pipe(gulp.dest(modules === false ? esDir : libDir));
const source = [
'components/**/*.js',
'components/**/*.jsx',
'!components/*/__tests__/*',
]
const jsFilesStream = babelify(gulp.src(source), modules)
return merge2([less, jsFilesStream, assets])
const source = ['components/**/*.js', 'components/**/*.jsx', '!components/*/__tests__/*'];
const jsFilesStream = babelify(gulp.src(source), modules);
return merge2([less, jsFilesStream, assets]);
}
function tag () {
console.log('tagging')
const { version } = packageJson
execSync(`git config --global user.email ${process.env.GITHUB_USER_EMAIL}`)
execSync(`git config --global user.name ${process.env.GITHUB_USER_NAME}`)
execSync(`git tag ${version}`)
execSync(`git push https://${process.env.GITHUB_TOKEN}@github.com/vueComponent/ant-design-vue.git ${version}:${version}`)
execSync(`git push https://${process.env.GITHUB_TOKEN}@github.com/vueComponent/ant-design-vue.git master:master`)
console.log('tagged')
function tag() {
console.log('tagging');
const { version } = packageJson;
execSync(`git config --global user.email ${process.env.GITHUB_USER_EMAIL}`);
execSync(`git config --global user.name ${process.env.GITHUB_USER_NAME}`);
execSync(`git tag ${version}`);
execSync(
`git push https://${
process.env.GITHUB_TOKEN
}@github.com/vueComponent/ant-design-vue.git ${version}:${version}`,
);
execSync(
`git push https://${
process.env.GITHUB_TOKEN
}@github.com/vueComponent/ant-design-vue.git master:master`,
);
console.log('tagged');
}
function githubRelease (done) {
function githubRelease(done) {
const changlogFiles = [
path.join(cwd, 'CHANGELOG.en-US.md'),
path.join(cwd, 'CHANGELOG.zh-CN.md'),
]
console.log('creating release on GitHub')
];
console.log('creating release on GitHub');
if (!process.env.GITHUB_TOKEN) {
console.log('no GitHub token found, skip')
return
console.log('no GitHub token found, skip');
return;
}
if (!changlogFiles.every(file => fs.existsSync(file))) {
console.log('no changelog found, skip')
return
console.log('no changelog found, skip');
return;
}
const github = new GitHub()
const github = new GitHub();
github.authenticate({
type: 'oauth',
token: process.env.GITHUB_TOKEN,
})
const date = new Date()
const { version } = packageJson
const enChangelog = getChangelog(changlogFiles[0], version)
const cnChangelog = getChangelog(changlogFiles[1], version)
});
const date = new Date();
const { version } = packageJson;
const enChangelog = getChangelog(changlogFiles[0], version);
const cnChangelog = getChangelog(changlogFiles[1], version);
const changelog = [
`\`${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}\``,
enChangelog,
@ -172,161 +189,164 @@ function githubRelease (done) {
'---',
'\n',
cnChangelog,
].join('\n')
].join('\n');
const [_, owner, repo] = execSync('git remote get-url origin') // eslint-disable-line
.toString()
.match(/github.com[:/](.+)\/(.+)\.git/)
github.repos.createRelease({
owner,
repo,
tag_name: version,
name: version,
body: changelog,
}).then(() => {
done()
})
.match(/github.com[:/](.+)\/(.+)\.git/);
github.repos
.createRelease({
owner,
repo,
tag_name: version,
name: version,
body: changelog,
})
.then(() => {
done();
});
}
gulp.task('tag', (done) => {
tag()
githubRelease(done)
})
gulp.task('tag', done => {
tag();
githubRelease(done);
});
gulp.task('check-git', (done) => {
gulp.task('check-git', done => {
runCmd('git', ['status', '--porcelain'], (code, result) => {
if (/^\?\?/m.test(result)) {
return done(`There are untracked files in the working tree.\n${result}
`)
`);
}
if (/^([ADRM]| [ADRM])/m.test(result)) {
return done(`There are uncommitted changes in the working tree.\n${result}
`)
`);
}
return done()
})
})
return done();
});
});
function publish (tagString, done) {
let args = ['publish', '--with-antd-tools']
function publish(tagString, done) {
let args = ['publish', '--with-antd-tools'];
if (tagString) {
args = args.concat(['--tag', tagString])
args = args.concat(['--tag', tagString]);
}
const publishNpm = process.env.PUBLISH_NPM_CLI || 'npm'
runCmd(publishNpm, args, (code) => {
tag()
const publishNpm = process.env.PUBLISH_NPM_CLI || 'npm';
runCmd(publishNpm, args, code => {
tag();
githubRelease(() => {
done(code)
})
})
done(code);
});
});
}
function pub (done) {
dist((code) => {
function pub(done) {
dist(code => {
if (code) {
done(code)
return
done(code);
return;
}
const notOk = !packageJson.version.match(/^\d+\.\d+\.\d+$/)
let tagString
const notOk = !packageJson.version.match(/^\d+\.\d+\.\d+$/);
let tagString;
if (argv['npm-tag']) {
tagString = argv['npm-tag']
tagString = argv['npm-tag'];
}
if (!tagString && notOk) {
tagString = 'next'
tagString = 'next';
}
if (packageJson.scripts['pre-publish']) {
runCmd('npm', ['run', 'pre-publish'], (code2) => {
runCmd('npm', ['run', 'pre-publish'], code2 => {
if (code2) {
done(code2)
return
done(code2);
return;
}
publish(tagString, done)
})
publish(tagString, done);
});
} else {
publish(tagString, done)
publish(tagString, done);
}
})
});
}
gulp.task('dist', ['compile'], (done) => {
dist(done)
})
gulp.task('compile', ['compile-with-es'], (done) => {
compile()
.on('finish', function () {
done()
})
})
gulp.task('compile-with-es', (done) => {
compile(false)
.on('finish', function () {
done()
})
})
gulp.task('dist', ['compile'], done => {
dist(done);
});
gulp.task('compile', ['compile-with-es'], done => {
compile().on('finish', function() {
done();
});
});
gulp.task('compile-with-es', done => {
compile(false).on('finish', function() {
done();
});
});
gulp.task('pub', ['check-git', 'compile'], (done) => {
gulp.task('pub', ['check-git', 'compile'], done => {
if (!process.env.GITHUB_TOKEN) {
console.log('no GitHub token found, skip')
console.log('no GitHub token found, skip');
} else {
pub(done)
pub(done);
}
})
});
gulp.task('pub-with-ci', (done) => {
gulp.task('pub-with-ci', done => {
if (!process.env.NPM_TOKEN) {
console.log('no NPM token found, skip')
console.log('no NPM token found, skip');
} else {
const github = new GitHub()
const github = new GitHub();
github.authenticate({
type: 'oauth',
token: process.env.GITHUB_TOKEN,
})
});
const [_, owner, repo] = execSync('git remote get-url origin') // eslint-disable-line
.toString()
.match(/github.com[:/](.+)\/(.+)\.git/)
.match(/github.com[:/](.+)\/(.+)\.git/);
const getLatestRelease = github.repos.getLatestRelease({
owner,
repo,
})
});
const getCommits = github.repos.getCommits({
owner,
repo,
per_page: 1,
})
});
Promise.all([getLatestRelease, getCommits]).then(([latestRelease, commits]) => {
const preVersion = latestRelease.data.tag_name
const { version } = packageJson
const [_, newVersion] = commits.data[0].commit.message.trim().match(/bump (.+)/) || []
if (compareVersions(version, preVersion) === 1 && newVersion && newVersion.trim() === version) {
gulp.run('pub', (err) => {
err && console.log('err', err)
done()
})
const preVersion = latestRelease.data.tag_name;
const { version } = packageJson;
const [_, newVersion] = commits.data[0].commit.message.trim().match(/bump (.+)/) || []; // eslint-disable-line
if (
compareVersions(version, preVersion) === 1 &&
newVersion &&
newVersion.trim() === version
) {
gulp.run('pub', err => {
err && console.log('err', err);
done();
});
} else {
console.log('donot need publish' + version)
console.log('donot need publish' + version);
}
})
});
}
})
});
function reportError () {
console.log(chalk.bgRed('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'))
console.log(chalk.bgRed('!! `npm publish` is forbidden for this package. !!'))
console.log(chalk.bgRed('!! Use `npm run pub` instead. !!'))
console.log(chalk.bgRed('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'))
function reportError() {
console.log(chalk.bgRed('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'));
console.log(chalk.bgRed('!! `npm publish` is forbidden for this package. !!'));
console.log(chalk.bgRed('!! Use `npm run pub` instead. !!'));
console.log(chalk.bgRed('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'));
}
gulp.task('guard', (done) => {
const npmArgs = getNpmArgs()
gulp.task('guard', done => {
const npmArgs = getNpmArgs();
if (npmArgs) {
for (let arg = npmArgs.shift(); arg; arg = npmArgs.shift()) {
if (/^pu(b(l(i(sh?)?)?)?)?$/.test(arg) && npmArgs.indexOf('--with-antd-tools') < 0) {
reportError()
done(1)
return
reportError();
done(1);
return;
}
}
}
done()
})
done();
});

View File

@ -1,18 +1,11 @@
const rucksack = require('rucksack-css')
const autoprefixer = require('autoprefixer')
const rucksack = require('rucksack-css');
const autoprefixer = require('autoprefixer');
module.exports = {
plugins: [
rucksack(),
autoprefixer({
browsers: [
'last 2 versions',
'Firefox ESR',
'> 1%',
'ie >= 9',
'iOS >= 8',
'Android >= 4',
],
browsers: ['last 2 versions', 'Firefox ESR', '> 1%', 'ie >= 9', 'iOS >= 8', 'Android >= 4'],
}),
],
}
};

View File

@ -1,27 +1,27 @@
'use strict'
'use strict';
const { join, dirname } = require('path')
const fs = require('fs')
const { join, dirname } = require('path');
const fs = require('fs');
const cwd = process.cwd()
const cwd = process.cwd();
function replacePath (path) {
function replacePath(path) {
if (path.node.source && /\/lib\//.test(path.node.source.value)) {
const esModule = path.node.source.value.replace('/lib/', '/es/')
const esPath = dirname(join(cwd, `node_modules/${esModule}`))
const esModule = path.node.source.value.replace('/lib/', '/es/');
const esPath = dirname(join(cwd, `node_modules/${esModule}`));
if (fs.existsSync(esPath)) {
path.node.source.value = esModule
path.node.source.value = esModule;
}
}
}
function replaceLib () {
function replaceLib() {
return {
visitor: {
ImportDeclaration: replacePath,
ExportNamedDeclaration: replacePath,
},
}
};
}
module.exports = replaceLib
module.exports = replaceLib;

View File

@ -1,20 +1,20 @@
'use strict'
'use strict';
const getRunCmdEnv = require('./utils/getRunCmdEnv')
const getRunCmdEnv = require('./utils/getRunCmdEnv');
function runCmd (cmd, _args, fn) {
const args = _args || []
function runCmd(cmd, _args, fn) {
const args = _args || [];
const runner = require('child_process').spawn(cmd, args, {
// keep color
stdio: 'inherit',
env: getRunCmdEnv(),
})
});
runner.on('close', (code) => {
runner.on('close', code => {
if (fn) {
fn(code)
fn(code);
}
})
});
}
module.exports = runCmd
module.exports = runCmd;

View File

@ -1,33 +1,32 @@
const less = require('less')
const { readFileSync } = require('fs')
const path = require('path')
const postcss = require('postcss')
const NpmImportPlugin = require('less-plugin-npm-import')
const postcssConfig = require('./postcssConfig')
const less = require('less');
const { readFileSync } = require('fs');
const path = require('path');
const postcss = require('postcss');
const NpmImportPlugin = require('less-plugin-npm-import');
const postcssConfig = require('./postcssConfig');
function transformLess (lessFile, config = {}) {
const { cwd = process.cwd() } = config
const resolvedLessFile = path.resolve(cwd, lessFile)
function transformLess(lessFile, config = {}) {
const { cwd = process.cwd() } = config;
const resolvedLessFile = path.resolve(cwd, lessFile);
let data = readFileSync(resolvedLessFile, 'utf-8')
data = data.replace(/^\uFEFF/, '')
let data = readFileSync(resolvedLessFile, 'utf-8');
data = data.replace(/^\uFEFF/, '');
// Do less compile
const lessOpts = {
paths: [path.dirname(resolvedLessFile)],
filename: resolvedLessFile,
plugins: [
new NpmImportPlugin({ prefix: '~' }),
],
}
return less.render(data, lessOpts)
.then((result) => {
const source = result.css
return postcss(postcssConfig.plugins).process(source)
})
.then((r) => {
return r.css
plugins: [new NpmImportPlugin({ prefix: '~' })],
};
return less
.render(data, lessOpts)
.then(result => {
const source = result.css;
return postcss(postcssConfig.plugins).process(source);
})
.then(r => {
return r.css;
});
}
module.exports = transformLess
module.exports = transformLess;

View File

@ -1,18 +1,18 @@
'use strict'
'use strict';
// NOTE: the following code was partially adopted from https://github.com/iarna/in-publish
module.exports = function getNpmArgs () {
let npmArgv = null
module.exports = function getNpmArgs() {
let npmArgv = null;
try {
npmArgv = JSON.parse(process.env.npm_config_argv)
npmArgv = JSON.parse(process.env.npm_config_argv);
} catch (err) {
return null
return null;
}
if (typeof npmArgv !== 'object' || !npmArgv.cooked || !Array.isArray(npmArgv.cooked)) {
return null
return null;
}
return npmArgv.cooked
}
return npmArgv.cooked;
};

View File

@ -1,23 +1,26 @@
const fs = require('fs')
const fs = require('fs');
module.exports = function getChangelog (file, version) {
const lines = fs.readFileSync(file).toString().split('\n')
const changeLog = []
const startPattern = new RegExp(`^## ${version}`)
const stopPattern = /^## / // 前一个版本
const skipPattern = /^`/ // 日期
let begin = false
module.exports = function getChangelog(file, version) {
const lines = fs
.readFileSync(file)
.toString()
.split('\n');
const changeLog = [];
const startPattern = new RegExp(`^## ${version}`);
const stopPattern = /^## /; // 前一个版本
const skipPattern = /^`/; // 日期
let begin = false;
for (let i = 0; i < lines.length; i += 1) {
const line = lines[i]
const line = lines[i];
if (begin && stopPattern.test(line)) {
break
break;
}
if (begin && line && !skipPattern.test(line)) {
changeLog.push(line)
changeLog.push(line);
}
if (!begin) {
begin = startPattern.test(line)
begin = startPattern.test(line);
}
}
return changeLog.join('\n')
}
return changeLog.join('\n');
};

View File

@ -1,14 +1,14 @@
'use strict'
'use strict';
const path = require('path')
const path = require('path');
module.exports = function getRunCmdEnv () {
const env = {}
Object.keys(process.env).forEach((key) => {
env[key] = process.env[key]
})
module.exports = function getRunCmdEnv() {
const env = {};
Object.keys(process.env).forEach(key => {
env[key] = process.env[key];
});
// make sure `antd-tools/node_modules/.bin` in the PATH env
const nodeModulesBinDir = path.join(__dirname, '../../node_modules/.bin')
env.PATH = env.PATH ? `${nodeModulesBinDir}:${env.PATH}` : nodeModulesBinDir
return env
}
const nodeModulesBinDir = path.join(__dirname, '../../node_modules/.bin');
env.PATH = env.PATH ? `${nodeModulesBinDir}:${env.PATH}` : nodeModulesBinDir;
return env;
};

View File

@ -1,4 +1,3 @@
export default {
// directives: {
// ref: {
@ -14,27 +13,28 @@ export default {
// },
// },
methods: {
setState (state, callback) {
const newState = typeof state === 'function' ? state(this.$data, this.$props) : state
setState(state, callback) {
const newState = typeof state === 'function' ? state(this.$data, this.$props) : state;
// if (this.getDerivedStateFromProps) {
// Object.assign(newState, this.getDerivedStateFromProps(getOptionProps(this), { ...this.$data, ...newState }, true) || {})
// }
Object.assign(this.$data, newState)
Object.assign(this.$data, newState);
this.$nextTick(() => {
callback && callback()
})
callback && callback();
});
},
__emit () { // 直接调用listeners底层组件不需要vueTool记录events
const args = [].slice.call(arguments, 0)
const filterEvent = []
const eventName = args[0]
__emit() {
// 直接调用listeners底层组件不需要vueTool记录events
const args = [].slice.call(arguments, 0);
const filterEvent = [];
const eventName = args[0];
if (args.length && this.$listeners[eventName]) {
if (filterEvent.includes(eventName)) {
this.$emit(eventName, ...args.slice(1))
this.$emit(eventName, ...args.slice(1));
} else {
this.$listeners[eventName](...args.slice(1))
this.$listeners[eventName](...args.slice(1));
}
}
},
},
}
};

View File

@ -1,6 +1,5 @@
import Vue from 'vue'
import PropTypes from './vue-types'
import Vue from 'vue';
import PropTypes from './vue-types';
export default {
props: {
@ -14,42 +13,42 @@ export default {
children: PropTypes.func.isRequired,
},
mounted () {
mounted() {
if (this.autoMount) {
this.renderComponent()
this.renderComponent();
}
},
updated () {
updated() {
if (this.autoMount) {
this.renderComponent()
this.renderComponent();
}
},
beforeDestroy () {
beforeDestroy() {
if (this.autoDestroy) {
this.removeContainer()
this.removeContainer();
}
},
methods: {
removeContainer () {
removeContainer() {
if (this.container) {
this._component && this._component.$destroy()
this.container.parentNode.removeChild(this.container)
this.container = null
this._component && this._component.$destroy();
this.container.parentNode.removeChild(this.container);
this.container = null;
}
},
renderComponent (props = {}, ready) {
const { visible, forceRender, getContainer, parent } = this
const self = this
renderComponent(props = {}, ready) {
const { visible, forceRender, getContainer, parent } = this;
const self = this;
if (visible || parent.$refs._component || forceRender) {
let el = this.componentEl
let el = this.componentEl;
if (!this.container) {
this.container = getContainer()
el = document.createElement('div')
this.componentEl = el
this.container.appendChild(el)
this.container = getContainer();
el = document.createElement('div');
this.componentEl = el;
this.container.appendChild(el);
}
if (!this._component) {
@ -59,36 +58,35 @@ export default {
},
parent: self.parent,
el: el,
mounted () {
mounted() {
this.$nextTick(() => {
if (ready) {
ready.call(self)
ready.call(self);
}
})
});
},
updated () {
updated() {
this.$nextTick(() => {
if (ready) {
ready.call(self)
ready.call(self);
}
})
});
},
render () {
return self.getComponent(this.comProps)
render() {
return self.getComponent(this.comProps);
},
})
});
} else {
this._component.comProps = props
this._component.comProps = props;
}
}
},
},
render () {
render() {
return this.children({
renderComponent: this.renderComponent,
removeContainer: this.removeContainer,
})
});
},
}
};

View File

@ -1,5 +1,5 @@
import addDOMEventListener from 'add-dom-event-listener'
import addDOMEventListener from 'add-dom-event-listener';
export default function addEventListenerWrap (target, eventType, cb, option) {
return addDOMEventListener(target, eventType, cb, option)
export default function addEventListenerWrap(target, eventType, cb, option) {
return addDOMEventListener(target, eventType, cb, option);
}

View File

@ -4,23 +4,23 @@
* Add class with compatibility for SVG since classList is not supported on
* SVG elements in IE
*/
export function addClass (el, cls) {
export function addClass(el, cls) {
/* istanbul ignore if */
if (!cls || !(cls = cls.trim())) {
return
return;
}
/* istanbul ignore else */
if (el.classList) {
if (cls.indexOf(' ') > -1) {
cls.split(/\s+/).forEach(c => el.classList.add(c))
cls.split(/\s+/).forEach(c => el.classList.add(c));
} else {
el.classList.add(cls)
el.classList.add(cls);
}
} else {
const cur = ` ${el.getAttribute('class') || ''} `
const cur = ` ${el.getAttribute('class') || ''} `;
if (cur.indexOf(' ' + cls + ' ') < 0) {
el.setAttribute('class', (cur + cls).trim())
el.setAttribute('class', (cur + cls).trim());
}
}
}
@ -29,33 +29,33 @@ export function addClass (el, cls) {
* Remove class with compatibility for SVG since classList is not supported on
* SVG elements in IE
*/
export function removeClass (el, cls) {
export function removeClass(el, cls) {
/* istanbul ignore if */
if (!cls || !(cls = cls.trim())) {
return
return;
}
/* istanbul ignore else */
if (el.classList) {
if (cls.indexOf(' ') > -1) {
cls.split(/\s+/).forEach(c => el.classList.remove(c))
cls.split(/\s+/).forEach(c => el.classList.remove(c));
} else {
el.classList.remove(cls)
el.classList.remove(cls);
}
if (!el.classList.length) {
el.removeAttribute('class')
el.removeAttribute('class');
}
} else {
let cur = ` ${el.getAttribute('class') || ''} `
const tar = ' ' + cls + ' '
let cur = ` ${el.getAttribute('class') || ''} `;
const tar = ' ' + cls + ' ';
while (cur.indexOf(tar) >= 0) {
cur = cur.replace(tar, ' ')
cur = cur.replace(tar, ' ');
}
cur = cur.trim()
cur = cur.trim();
if (cur) {
el.setAttribute('class', cur)
el.setAttribute('class', cur);
} else {
el.removeAttribute('class')
el.removeAttribute('class');
}
}
}

View File

@ -1,11 +1,11 @@
export default function contains (root, n) {
let node = n
export default function contains(root, n) {
let node = n;
while (node) {
if (node === root) {
return true
return true;
}
node = node.parentNode
node = node.parentNode;
}
return false
return false;
}

View File

@ -1,11 +1,10 @@
export function antDecorator (Vue) {
return Vue.directive('decorator', {
})
export function antDecorator(Vue) {
return Vue.directive('decorator', {});
}
export default {
// just for tag
install: (Vue, options) => {
antDecorator(Vue)
antDecorator(Vue);
},
}
};

View File

@ -425,17 +425,20 @@ const KeyCode = {
* WIN_IME
*/
WIN_IME: 229,
}
};
/*
whether text and modified key is entered at the same time.
*/
KeyCode.isTextModifyingKeyEvent = function isTextModifyingKeyEvent (e) {
const keyCode = e.keyCode
if (e.altKey && !e.ctrlKey || e.metaKey ||
// Function keys don't generate text
keyCode >= KeyCode.F1 && keyCode <= KeyCode.F12) {
return false
KeyCode.isTextModifyingKeyEvent = function isTextModifyingKeyEvent(e) {
const keyCode = e.keyCode;
if (
(e.altKey && !e.ctrlKey) ||
e.metaKey ||
// Function keys don't generate text
(keyCode >= KeyCode.F1 && keyCode <= KeyCode.F12)
) {
return false;
}
// The following keys are quite harmless, even in combination with
@ -464,34 +467,31 @@ KeyCode.isTextModifyingKeyEvent = function isTextModifyingKeyEvent (e) {
case KeyCode.UP:
case KeyCode.WIN_KEY:
case KeyCode.WIN_KEY_RIGHT:
return false
return false;
default:
return true
return true;
}
}
};
/*
whether character is entered.
*/
KeyCode.isCharacterKey = function isCharacterKey (keyCode) {
if (keyCode >= KeyCode.ZERO &&
keyCode <= KeyCode.NINE) {
return true
KeyCode.isCharacterKey = function isCharacterKey(keyCode) {
if (keyCode >= KeyCode.ZERO && keyCode <= KeyCode.NINE) {
return true;
}
if (keyCode >= KeyCode.NUM_ZERO &&
keyCode <= KeyCode.NUM_MULTIPLY) {
return true
if (keyCode >= KeyCode.NUM_ZERO && keyCode <= KeyCode.NUM_MULTIPLY) {
return true;
}
if (keyCode >= KeyCode.A &&
keyCode <= KeyCode.Z) {
return true
if (keyCode >= KeyCode.A && keyCode <= KeyCode.Z) {
return true;
}
// Safari sends zero key code for non-latin characters.
if (window.navigation.userAgent.indexOf('WebKit') !== -1 && keyCode === 0) {
return true
return true;
}
switch (keyCode) {
@ -512,10 +512,10 @@ KeyCode.isCharacterKey = function isCharacterKey (keyCode) {
case KeyCode.OPEN_SQUARE_BRACKET:
case KeyCode.BACKSLASH:
case KeyCode.CLOSE_SQUARE_BRACKET:
return true
return true;
default:
return false
return false;
}
}
};
export default KeyCode
export default KeyCode;

View File

@ -1,10 +1,10 @@
export default {
methods: {
setState (state, callback) {
Object.assign(this.$data, state)
setState(state, callback) {
Object.assign(this.$data, state);
this.$nextTick(() => {
callback && callback()
})
callback && callback();
});
},
},
}
};

View File

@ -1,9 +1,9 @@
import { antInput } from './antInputDirective'
import { antDecorator } from './FormDecoratorDirective'
import { antInput } from './antInputDirective';
import { antDecorator } from './FormDecoratorDirective';
export default {
install: (Vue, options) => {
antInput(Vue)
antDecorator(Vue)
antInput(Vue);
antDecorator(Vue);
},
}
};

View File

@ -3,76 +3,71 @@
* properties to Elements.
*/
export const inBrowser = typeof window !== 'undefined'
export const UA = inBrowser && window.navigator.userAgent.toLowerCase()
export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
function makeMap (
str,
expectsLowerCase
) {
const map = Object.create(null)
const list = str.split(',')
export const inBrowser = typeof window !== 'undefined';
export const UA = inBrowser && window.navigator.userAgent.toLowerCase();
export const isIE9 = UA && UA.indexOf('msie 9.0') > 0;
function makeMap(str, expectsLowerCase) {
const map = Object.create(null);
const list = str.split(',');
for (let i = 0; i < list.length; i++) {
map[list[i]] = true
map[list[i]] = true;
}
return expectsLowerCase
? val => map[val.toLowerCase()]
: val => map[val]
return expectsLowerCase ? val => map[val.toLowerCase()] : val => map[val];
}
const isTextInputType = makeMap('text,number,password,search,email,tel,url')
const isTextInputType = makeMap('text,number,password,search,email,tel,url');
function onCompositionStart (e) {
e.target.composing = true
function onCompositionStart(e) {
e.target.composing = true;
}
function onCompositionEnd (e) {
function onCompositionEnd(e) {
// prevent triggering an input event for no reason
if (!e.target.composing) return
e.target.composing = false
trigger(e.target, 'input')
if (!e.target.composing) return;
e.target.composing = false;
trigger(e.target, 'input');
}
function trigger (el, type) {
const e = document.createEvent('HTMLEvents')
e.initEvent(type, true, true)
el.dispatchEvent(e)
function trigger(el, type) {
const e = document.createEvent('HTMLEvents');
e.initEvent(type, true, true);
el.dispatchEvent(e);
}
/* istanbul ignore if */
if (isIE9) {
// http://www.matts411.com/post/internet-explorer-9-oninput/
document.addEventListener('selectionchange', () => {
const el = document.activeElement
const el = document.activeElement;
if (el && el.vmodel) {
trigger(el, 'input')
trigger(el, 'input');
}
})
});
}
export function antInput (Vue) {
export function antInput(Vue) {
return Vue.directive('ant-input', {
inserted (el, binding, vnode, oldVnode) {
inserted(el, binding, vnode, oldVnode) {
if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
if (!binding.modifiers || !binding.modifiers.lazy) {
el.addEventListener('compositionstart', onCompositionStart)
el.addEventListener('compositionend', onCompositionEnd)
el.addEventListener('compositionstart', onCompositionStart);
el.addEventListener('compositionend', onCompositionEnd);
// Safari < 10.2 & UIWebView doesn't fire compositionend when
// switching focus before confirming composition choice
// this also fixes the issue where some browsers e.g. iOS Chrome
// fires "change" instead of "input" on autocomplete.
el.addEventListener('change', onCompositionEnd)
el.addEventListener('change', onCompositionEnd);
/* istanbul ignore if */
if (isIE9) {
el.vmodel = true
el.vmodel = true;
}
}
}
},
})
});
}
export default {
install: (Vue, options) => {
antInput(Vue)
antInput(Vue);
},
}
};

View File

@ -1,4 +1,4 @@
// https://github.com/moment/moment/issues/3650
export default function callMoment (moment, ...args) {
return (moment.default || moment)(...args)
export default function callMoment(moment, ...args) {
return (moment.default || moment)(...args);
}

View File

@ -6,17 +6,17 @@
*
* @returns {function|null}
*/
export default function createChainedFunction () {
const args = [].slice.call(arguments, 0)
export default function createChainedFunction() {
const args = [].slice.call(arguments, 0);
if (args.length === 1) {
return args[0]
return args[0];
}
return function chainedFunction () {
return function chainedFunction() {
for (let i = 0; i < args.length; i++) {
if (args[i] && args[i].apply) {
args[i].apply(this, arguments)
args[i].apply(this, arguments);
}
}
}
};
}

View File

@ -14,7 +14,7 @@ const START_EVENT_NAME_MAP = {
OAnimation: 'oAnimationStart',
msAnimation: 'MSAnimationStart',
},
}
};
const END_EVENT_NAME_MAP = {
transitionend: {
@ -32,99 +32,99 @@ const END_EVENT_NAME_MAP = {
OAnimation: 'oAnimationEnd',
msAnimation: 'MSAnimationEnd',
},
}
};
const startEvents = []
const endEvents = []
const startEvents = [];
const endEvents = [];
function detectEvents () {
const testEl = document.createElement('div')
const style = testEl.style
function detectEvents() {
const testEl = document.createElement('div');
const style = testEl.style;
if (!('AnimationEvent' in window)) {
delete START_EVENT_NAME_MAP.animationstart.animation
delete END_EVENT_NAME_MAP.animationend.animation
delete START_EVENT_NAME_MAP.animationstart.animation;
delete END_EVENT_NAME_MAP.animationend.animation;
}
if (!('TransitionEvent' in window)) {
delete START_EVENT_NAME_MAP.transitionstart.transition
delete END_EVENT_NAME_MAP.transitionend.transition
delete START_EVENT_NAME_MAP.transitionstart.transition;
delete END_EVENT_NAME_MAP.transitionend.transition;
}
function process (EVENT_NAME_MAP, events) {
function process(EVENT_NAME_MAP, events) {
for (const baseEventName in EVENT_NAME_MAP) {
if (EVENT_NAME_MAP.hasOwnProperty(baseEventName)) {
const baseEvents = EVENT_NAME_MAP[baseEventName]
const baseEvents = EVENT_NAME_MAP[baseEventName];
for (const styleName in baseEvents) {
if (styleName in style) {
events.push(baseEvents[styleName])
break
events.push(baseEvents[styleName]);
break;
}
}
}
}
}
process(START_EVENT_NAME_MAP, startEvents)
process(END_EVENT_NAME_MAP, endEvents)
process(START_EVENT_NAME_MAP, startEvents);
process(END_EVENT_NAME_MAP, endEvents);
}
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
detectEvents()
detectEvents();
}
function addEventListener (node, eventName, eventListener) {
node.addEventListener(eventName, eventListener, false)
function addEventListener(node, eventName, eventListener) {
node.addEventListener(eventName, eventListener, false);
}
function removeEventListener (node, eventName, eventListener) {
node.removeEventListener(eventName, eventListener, false)
function removeEventListener(node, eventName, eventListener) {
node.removeEventListener(eventName, eventListener, false);
}
const TransitionEvents = {
// Start events
startEvents,
addStartEventListener (node, eventListener) {
addStartEventListener(node, eventListener) {
if (startEvents.length === 0) {
window.setTimeout(eventListener, 0)
return
window.setTimeout(eventListener, 0);
return;
}
startEvents.forEach((startEvent) => {
addEventListener(node, startEvent, eventListener)
})
startEvents.forEach(startEvent => {
addEventListener(node, startEvent, eventListener);
});
},
removeStartEventListener (node, eventListener) {
removeStartEventListener(node, eventListener) {
if (startEvents.length === 0) {
return
return;
}
startEvents.forEach((startEvent) => {
removeEventListener(node, startEvent, eventListener)
})
startEvents.forEach(startEvent => {
removeEventListener(node, startEvent, eventListener);
});
},
// End events
endEvents,
addEndEventListener (node, eventListener) {
addEndEventListener(node, eventListener) {
if (endEvents.length === 0) {
window.setTimeout(eventListener, 0)
return
window.setTimeout(eventListener, 0);
return;
}
endEvents.forEach((endEvent) => {
addEventListener(node, endEvent, eventListener)
})
endEvents.forEach(endEvent => {
addEventListener(node, endEvent, eventListener);
});
},
removeEndEventListener (node, eventListener) {
removeEndEventListener(node, eventListener) {
if (endEvents.length === 0) {
return
return;
}
endEvents.forEach((endEvent) => {
removeEventListener(node, endEvent, eventListener)
})
endEvents.forEach(endEvent => {
removeEventListener(node, endEvent, eventListener);
});
},
}
};
export default TransitionEvents
export default TransitionEvents;

View File

@ -1,181 +1,181 @@
// https://github.com/yiminghe/css-animation 1.5.0
import Event from './Event'
import classes from 'component-classes'
import { requestAnimationTimeout, cancelAnimationTimeout } from '../requestAnimationTimeout'
import Event from './Event';
import classes from 'component-classes';
import { requestAnimationTimeout, cancelAnimationTimeout } from '../requestAnimationTimeout';
const isCssAnimationSupported = Event.endEvents.length !== 0
const capitalPrefixes = ['Webkit',
const isCssAnimationSupported = Event.endEvents.length !== 0;
const capitalPrefixes = [
'Webkit',
'Moz',
'O',
// ms is special .... !
'ms']
const prefixes = ['-webkit-', '-moz-', '-o-', 'ms-', '']
'ms',
];
const prefixes = ['-webkit-', '-moz-', '-o-', 'ms-', ''];
function getStyleProperty (node, name) {
function getStyleProperty(node, name) {
// old ff need null, https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
const style = window.getComputedStyle(node, null)
let ret = ''
const style = window.getComputedStyle(node, null);
let ret = '';
for (let i = 0; i < prefixes.length; i++) {
ret = style.getPropertyValue(prefixes[i] + name)
ret = style.getPropertyValue(prefixes[i] + name);
if (ret) {
break
break;
}
}
return (ret)
return ret;
}
function fixBrowserByTimeout (node) {
function fixBrowserByTimeout(node) {
if (isCssAnimationSupported) {
const transitionDelay = parseFloat(getStyleProperty(node, 'transition-delay')) || 0
const transitionDuration = parseFloat(getStyleProperty(node, 'transition-duration')) || 0
const animationDelay = parseFloat(getStyleProperty(node, 'animation-delay')) || 0
const animationDuration = parseFloat(getStyleProperty(node, 'animation-duration')) || 0
const time = Math.max(transitionDuration + transitionDelay, animationDuration + animationDelay)
const transitionDelay = parseFloat(getStyleProperty(node, 'transition-delay')) || 0;
const transitionDuration = parseFloat(getStyleProperty(node, 'transition-duration')) || 0;
const animationDelay = parseFloat(getStyleProperty(node, 'animation-delay')) || 0;
const animationDuration = parseFloat(getStyleProperty(node, 'animation-duration')) || 0;
const time = Math.max(transitionDuration + transitionDelay, animationDuration + animationDelay);
// sometimes, browser bug
node.rcEndAnimTimeout = setTimeout(() => {
node.rcEndAnimTimeout = null
node.rcEndAnimTimeout = null;
if (node.rcEndListener) {
node.rcEndListener()
node.rcEndListener();
}
}, (time) * 1000 + 200)
}, time * 1000 + 200);
}
}
function clearBrowserBugTimeout (node) {
function clearBrowserBugTimeout(node) {
if (node.rcEndAnimTimeout) {
clearTimeout(node.rcEndAnimTimeout)
node.rcEndAnimTimeout = null
clearTimeout(node.rcEndAnimTimeout);
node.rcEndAnimTimeout = null;
}
}
const cssAnimation = (node, transitionName, endCallback) => {
const nameIsObj = typeof transitionName === 'object'
const className = nameIsObj ? transitionName.name : transitionName
const activeClassName = nameIsObj ? transitionName.active : `${transitionName}-active`
let end = endCallback
let start
let active
const nodeClasses = classes(node)
const nameIsObj = typeof transitionName === 'object';
const className = nameIsObj ? transitionName.name : transitionName;
const activeClassName = nameIsObj ? transitionName.active : `${transitionName}-active`;
let end = endCallback;
let start;
let active;
const nodeClasses = classes(node);
if (endCallback && Object.prototype.toString.call(endCallback) === '[object Object]') {
end = endCallback.end
start = endCallback.start
active = endCallback.active
end = endCallback.end;
start = endCallback.start;
active = endCallback.active;
}
if (node.rcEndListener) {
node.rcEndListener()
node.rcEndListener();
}
node.rcEndListener = (e) => {
node.rcEndListener = e => {
if (e && e.target !== node) {
return
return;
}
if (node.rcAnimTimeout) {
cancelAnimationTimeout(node.rcAnimTimeout)
node.rcAnimTimeout = null
cancelAnimationTimeout(node.rcAnimTimeout);
node.rcAnimTimeout = null;
}
clearBrowserBugTimeout(node)
clearBrowserBugTimeout(node);
nodeClasses.remove(className)
nodeClasses.remove(activeClassName)
nodeClasses.remove(className);
nodeClasses.remove(activeClassName);
Event.removeEndEventListener(node, node.rcEndListener)
node.rcEndListener = null
Event.removeEndEventListener(node, node.rcEndListener);
node.rcEndListener = null;
// Usually this optional end is used for informing an owner of
// a leave animation and telling it to remove the child.
if (end) {
end()
end();
}
}
};
Event.addEndEventListener(node, node.rcEndListener)
Event.addEndEventListener(node, node.rcEndListener);
if (start) {
start()
start();
}
nodeClasses.add(className)
nodeClasses.add(className);
node.rcAnimTimeout = requestAnimationTimeout(() => {
node.rcAnimTimeout = null
nodeClasses.add(activeClassName)
node.rcAnimTimeout = null;
nodeClasses.add(activeClassName);
if (active) {
requestAnimationTimeout(active, 0)
requestAnimationTimeout(active, 0);
}
fixBrowserByTimeout(node)
fixBrowserByTimeout(node);
// 30ms for firefox
}, 30)
}, 30);
return {
stop () {
stop() {
if (node.rcEndListener) {
node.rcEndListener()
node.rcEndListener();
}
},
}
}
};
};
cssAnimation.style = (node, style, callback) => {
if (node.rcEndListener) {
node.rcEndListener()
node.rcEndListener();
}
node.rcEndListener = (e) => {
node.rcEndListener = e => {
if (e && e.target !== node) {
return
return;
}
if (node.rcAnimTimeout) {
cancelAnimationTimeout(node.rcAnimTimeout)
node.rcAnimTimeout = null
cancelAnimationTimeout(node.rcAnimTimeout);
node.rcAnimTimeout = null;
}
clearBrowserBugTimeout(node)
clearBrowserBugTimeout(node);
Event.removeEndEventListener(node, node.rcEndListener)
node.rcEndListener = null
Event.removeEndEventListener(node, node.rcEndListener);
node.rcEndListener = null;
// Usually this optional callback is used for informing an owner of
// a leave animation and telling it to remove the child.
if (callback) {
callback()
callback();
}
}
};
Event.addEndEventListener(node, node.rcEndListener)
Event.addEndEventListener(node, node.rcEndListener);
node.rcAnimTimeout = requestAnimationTimeout(() => {
for (const s in style) {
if (style.hasOwnProperty(s)) {
node.style[s] = style[s]
node.style[s] = style[s];
}
}
node.rcAnimTimeout = null
fixBrowserByTimeout(node)
}, 0)
}
node.rcAnimTimeout = null;
fixBrowserByTimeout(node);
}, 0);
};
cssAnimation.setTransition = (node, p, value) => {
let property = p
let v = value
let property = p;
let v = value;
if (value === undefined) {
v = property
property = ''
v = property;
property = '';
}
property = property || ''
capitalPrefixes.forEach((prefix) => {
node.style[`${prefix}Transition${property}`] = v
})
}
property = property || '';
capitalPrefixes.forEach(prefix => {
node.style[`${prefix}Transition${property}`] = v;
});
};
cssAnimation.isCssAnimationSupported = isCssAnimationSupported
cssAnimation.isCssAnimationSupported = isCssAnimationSupported;
export {
isCssAnimationSupported,
}
export { isCssAnimationSupported };
export default cssAnimation
export default cssAnimation;

View File

@ -1,4 +1,4 @@
export const inBrowser = typeof window !== 'undefined'
export const UA = inBrowser && window.navigator.userAgent.toLowerCase()
export const isIE = UA && /msie|trident/.test(UA)
export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
export const inBrowser = typeof window !== 'undefined';
export const UA = inBrowser && window.navigator.userAgent.toLowerCase();
export const isIE = UA && /msie|trident/.test(UA);
export const isIE9 = UA && UA.indexOf('msie 9.0') > 0;

View File

@ -1,30 +1,30 @@
export function getComponentLocale (props, context, componentName, getDefaultLocale) {
let locale = {}
export function getComponentLocale(props, context, componentName, getDefaultLocale) {
let locale = {};
if (context && context.antLocale && context.antLocale[componentName]) {
locale = context.antLocale[componentName]
locale = context.antLocale[componentName];
} else {
const defaultLocale = getDefaultLocale()
const defaultLocale = getDefaultLocale();
// TODO: make default lang of antd be English
// https://github.com/ant-design/ant-design/issues/6334
locale = defaultLocale.default || defaultLocale
locale = defaultLocale.default || defaultLocale;
}
const result = {
...locale,
...props.locale,
}
};
result.lang = {
...locale.lang,
...props.locale.lang,
}
return result
};
return result;
}
export function getLocaleCode (context) {
const localeCode = context.antLocale && context.antLocale.locale
export function getLocaleCode(context) {
const localeCode = context.antLocale && context.antLocale.locale;
// Had use LocaleProvide but didn't set locale
if (context.antLocale && context.antLocale.exist && !localeCode) {
return 'zh-cn'
return 'zh-cn';
}
return localeCode
return localeCode;
}

View File

@ -1,44 +1,46 @@
const availablePrefixs = ['moz', 'ms', 'webkit']
const availablePrefixs = ['moz', 'ms', 'webkit'];
function requestAnimationFramePolyfill () {
let lastTime = 0
return function (callback) {
const currTime = new Date().getTime()
const timeToCall = Math.max(0, 16 - (currTime - lastTime))
const id = window.setTimeout(function () { callback(currTime + timeToCall) }, timeToCall)
lastTime = currTime + timeToCall
return id
}
function requestAnimationFramePolyfill() {
let lastTime = 0;
return function(callback) {
const currTime = new Date().getTime();
const timeToCall = Math.max(0, 16 - (currTime - lastTime));
const id = window.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
export default function getRequestAnimationFrame () {
export default function getRequestAnimationFrame() {
if (typeof window === 'undefined') {
return () => {}
return () => {};
}
if (window.requestAnimationFrame) {
// https://github.com/vuejs/vue/issues/4465
return window.requestAnimationFrame.bind(window)
return window.requestAnimationFrame.bind(window);
}
const prefix = availablePrefixs.filter(key => `${key}RequestAnimationFrame` in window)[0]
const prefix = availablePrefixs.filter(key => `${key}RequestAnimationFrame` in window)[0];
return prefix
? window[`${prefix}RequestAnimationFrame`]
: requestAnimationFramePolyfill()
return prefix ? window[`${prefix}RequestAnimationFrame`] : requestAnimationFramePolyfill();
}
export function cancelRequestAnimationFrame (id) {
export function cancelRequestAnimationFrame(id) {
if (typeof window === 'undefined') {
return null
return null;
}
if (window.cancelAnimationFrame) {
return window.cancelAnimationFrame(id)
return window.cancelAnimationFrame(id);
}
const prefix = availablePrefixs.filter(key =>
`${key}CancelAnimationFrame` in window || `${key}CancelRequestAnimationFrame` in window,
)[0]
const prefix = availablePrefixs.filter(
key => `${key}CancelAnimationFrame` in window || `${key}CancelRequestAnimationFrame` in window,
)[0];
return prefix
? (window[`${prefix}CancelAnimationFrame`] || window[`${prefix}CancelRequestAnimationFrame`]).call(this, id)
: clearTimeout(id)
? (
window[`${prefix}CancelAnimationFrame`] || window[`${prefix}CancelRequestAnimationFrame`]
).call(this, id)
: clearTimeout(id);
}

View File

@ -1,17 +1,17 @@
export default function getScroll (target, top) {
export default function getScroll(target, top) {
if (typeof window === 'undefined') {
return 0
return 0;
}
const prop = top ? 'pageYOffset' : 'pageXOffset'
const method = top ? 'scrollTop' : 'scrollLeft'
const isWindow = target === window
const prop = top ? 'pageYOffset' : 'pageXOffset';
const method = top ? 'scrollTop' : 'scrollLeft';
const isWindow = target === window;
let ret = isWindow ? target[prop] : target[method]
let ret = isWindow ? target[prop] : target[method];
// ie6,7,8 standard mode
if (isWindow && typeof ret !== 'number') {
ret = window.document.documentElement[method]
ret = window.document.documentElement[method];
}
return ret
return ret;
}

View File

@ -1,38 +1,38 @@
let cached
let cached;
export default function getScrollBarSize (fresh) {
export default function getScrollBarSize(fresh) {
if (fresh || cached === undefined) {
const inner = document.createElement('div')
inner.style.width = '100%'
inner.style.height = '200px'
const inner = document.createElement('div');
inner.style.width = '100%';
inner.style.height = '200px';
const outer = document.createElement('div')
const outerStyle = outer.style
const outer = document.createElement('div');
const outerStyle = outer.style;
outerStyle.position = 'absolute'
outerStyle.top = 0
outerStyle.left = 0
outerStyle.pointerEvents = 'none'
outerStyle.visibility = 'hidden'
outerStyle.width = '200px'
outerStyle.height = '150px'
outerStyle.overflow = 'hidden'
outerStyle.position = 'absolute';
outerStyle.top = 0;
outerStyle.left = 0;
outerStyle.pointerEvents = 'none';
outerStyle.visibility = 'hidden';
outerStyle.width = '200px';
outerStyle.height = '150px';
outerStyle.overflow = 'hidden';
outer.appendChild(inner)
outer.appendChild(inner);
document.body.appendChild(outer)
document.body.appendChild(outer);
const widthContained = inner.offsetWidth
outer.style.overflow = 'scroll'
let widthScroll = inner.offsetWidth
const widthContained = inner.offsetWidth;
outer.style.overflow = 'scroll';
let widthScroll = inner.offsetWidth;
if (widthContained === widthScroll) {
widthScroll = outer.clientWidth
widthScroll = outer.clientWidth;
}
document.body.removeChild(outer)
document.body.removeChild(outer);
cached = widthContained - widthScroll
cached = widthContained - widthScroll;
}
return cached
return cached;
}

View File

@ -1,7 +1,7 @@
import animate from './css-animation'
const noop = () => {}
import animate from './css-animation';
const noop = () => {};
const getTransitionProps = (transitionName, opt = {}) => {
const { beforeEnter, enter, afterEnter, leave, afterLeave, appear = true, tag } = opt
const { beforeEnter, enter, afterEnter, leave, afterLeave, appear = true, tag } = opt;
const transitionProps = {
props: {
appear,
@ -9,21 +9,25 @@ const getTransitionProps = (transitionName, opt = {}) => {
},
on: {
beforeEnter: beforeEnter || noop,
enter: enter || ((el, done) => {
animate(el, `${transitionName}-enter`, done)
}),
enter:
enter ||
((el, done) => {
animate(el, `${transitionName}-enter`, done);
}),
afterEnter: afterEnter || noop,
leave: leave || ((el, done) => {
animate(el, `${transitionName}-leave`, done)
}),
leave:
leave ||
((el, done) => {
animate(el, `${transitionName}-leave`, done);
}),
afterLeave: afterLeave || noop,
},
}
};
// transition-group
if (tag) {
transitionProps.tag = tag
transitionProps.tag = tag;
}
return transitionProps
}
return transitionProps;
};
export default getTransitionProps
export default getTransitionProps;

View File

@ -1,4 +1,4 @@
// https://github.com/moment/moment/issues/3650
export default function interopDefault (m) {
return m.default || m
export default function interopDefault(m) {
return m.default || m;
}

View File

@ -1,24 +1,24 @@
let animation
let animation;
function isCssAnimationSupported () {
function isCssAnimationSupported() {
if (animation !== undefined) {
return animation
return animation;
}
const domPrefixes = 'Webkit Moz O ms Khtml'.split(' ')
const elm = document.createElement('div')
const domPrefixes = 'Webkit Moz O ms Khtml'.split(' ');
const elm = document.createElement('div');
if (elm.style.animationName !== undefined) {
animation = true
animation = true;
}
if (animation !== undefined) {
for (let i = 0; i < domPrefixes.length; i++) {
if (elm.style[`${domPrefixes[i]}AnimationName`] !== undefined) {
animation = true
break
animation = true;
break;
}
}
}
animation = animation || false
return animation
animation = animation || false;
return animation;
}
export default isCssAnimationSupported
export default isCssAnimationSupported;

View File

@ -1,10 +1,12 @@
export default function isFlexSupported () {
export default function isFlexSupported() {
if (typeof window !== 'undefined' && window.document && window.document.documentElement) {
const { documentElement } = window.document
return 'flex' in documentElement.style ||
const { documentElement } = window.document;
return (
'flex' in documentElement.style ||
'webkitFlex' in documentElement.style ||
'Flex' in documentElement.style ||
'msFlex' in documentElement.style
);
}
return false
return false;
}

View File

@ -1,4 +1,4 @@
const isNumeric = (value) => {
return !isNaN(parseFloat(value)) && isFinite(value)
}
export default isNumeric
const isNumeric = value => {
return !isNaN(parseFloat(value)) && isFinite(value);
};
export default isNumeric;

View File

@ -1,64 +1,64 @@
import cssAnimation from './css-animation'
import raf from 'raf'
import cssAnimation from './css-animation';
import raf from 'raf';
function animate (node, show, done, type) {
let height
let requestAnimationFrameId
let appearRequestAnimationFrameId
function animate(node, show, done, type) {
let height;
let requestAnimationFrameId;
let appearRequestAnimationFrameId;
return cssAnimation(node, 'ant-motion-collapse', {
start () {
start() {
if (appearRequestAnimationFrameId) {
raf.cancel(appearRequestAnimationFrameId)
raf.cancel(appearRequestAnimationFrameId);
}
if (!show) {
node.style.height = `${node.offsetHeight}px`
node.style.opacity = '1'
node.style.height = `${node.offsetHeight}px`;
node.style.opacity = '1';
} else {
height = node.offsetHeight
height = node.offsetHeight;
// not get offsetHeight when appear
// set it into raf get correct offsetHeight
if (height === 0) {
appearRequestAnimationFrameId = raf(() => {
height = node.offsetHeight
node.style.height = '0px'
node.style.opacity = '0'
})
height = node.offsetHeight;
node.style.height = '0px';
node.style.opacity = '0';
});
} else {
node.style.height = '0px'
node.style.opacity = '0'
node.style.height = '0px';
node.style.opacity = '0';
}
}
},
active () {
active() {
if (requestAnimationFrameId) {
raf.cancel(requestAnimationFrameId)
raf.cancel(requestAnimationFrameId);
}
requestAnimationFrameId = raf(() => {
node.style.height = `${show ? height : 0}px`
node.style.opacity = show ? '1' : '0'
})
node.style.height = `${show ? height : 0}px`;
node.style.opacity = show ? '1' : '0';
});
},
end () {
end() {
if (appearRequestAnimationFrameId) {
raf.cancel(appearRequestAnimationFrameId)
raf.cancel(appearRequestAnimationFrameId);
}
if (requestAnimationFrameId) {
raf.cancel(requestAnimationFrameId)
raf.cancel(requestAnimationFrameId);
}
node.style.height = ''
node.style.opacity = ''
done && done()
node.style.height = '';
node.style.opacity = '';
done && done();
},
})
});
}
const animation = {
enter (node, done) {
return animate(node, true, done)
enter(node, done) {
return animate(node, true, done);
},
leave (node, done) {
return animate(node, false, done)
leave(node, done) {
return animate(node, false, done);
},
}
};
export default animation
export default animation;

View File

@ -1,264 +1,276 @@
import isPlainObject from 'lodash/isPlainObject'
import classNames from 'classnames'
function getType (fn) {
const match = fn && fn.toString().match(/^\s*function (\w+)/)
return match ? match[1] : ''
import isPlainObject from 'lodash/isPlainObject';
import classNames from 'classnames';
function getType(fn) {
const match = fn && fn.toString().match(/^\s*function (\w+)/);
return match ? match[1] : '';
}
const camelizeRE = /-(\w)/g
const camelize = (str) => {
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
}
const camelizeRE = /-(\w)/g;
const camelize = str => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''));
};
const parseStyleText = (cssText = '', camel) => {
const res = {}
const listDelimiter = /;(?![^(]*\))/g
const propertyDelimiter = /:(.+)/
cssText.split(listDelimiter).forEach(function (item) {
const res = {};
const listDelimiter = /;(?![^(]*\))/g;
const propertyDelimiter = /:(.+)/;
cssText.split(listDelimiter).forEach(function(item) {
if (item) {
const tmp = item.split(propertyDelimiter)
const tmp = item.split(propertyDelimiter);
if (tmp.length > 1) {
const k = camel ? camelize(tmp[0].trim()) : tmp[0].trim()
res[k] = tmp[1].trim()
const k = camel ? camelize(tmp[0].trim()) : tmp[0].trim();
res[k] = tmp[1].trim();
}
}
})
return res
}
});
return res;
};
const hasProp = (instance, prop) => {
const $options = instance.$options || {}
const propsData = $options.propsData || {}
return prop in propsData
}
const $options = instance.$options || {};
const propsData = $options.propsData || {};
return prop in propsData;
};
const slotHasProp = (slot, prop) => {
const $options = slot.componentOptions || {}
const propsData = $options.propsData || {}
return prop in propsData
}
const $options = slot.componentOptions || {};
const propsData = $options.propsData || {};
return prop in propsData;
};
const filterProps = (props, propsData = {}) => {
const res = {}
Object.keys(props).forEach((k) => {
const res = {};
Object.keys(props).forEach(k => {
if (k in propsData || props[k] !== undefined) {
res[k] = props[k]
res[k] = props[k];
}
})
return res
}
const getSlots = (ele) => {
let componentOptions = ele.componentOptions || {}
});
return res;
};
const getSlots = ele => {
let componentOptions = ele.componentOptions || {};
if (ele.$vnode) {
componentOptions = ele.$vnode.componentOptions || {}
componentOptions = ele.$vnode.componentOptions || {};
}
const children = ele.children || componentOptions.children || []
const slots = {}
const children = ele.children || componentOptions.children || [];
const slots = {};
children.forEach(child => {
if (!isEmptyElement(child)) {
const name = (child.data && child.data.slot) || 'default'
slots[name] = slots[name] || []
slots[name].push(child)
const name = (child.data && child.data.slot) || 'default';
slots[name] = slots[name] || [];
slots[name].push(child);
}
})
return slots
}
const getAllChildren = (ele) => {
let componentOptions = ele.componentOptions || {}
});
return slots;
};
const getAllChildren = ele => {
let componentOptions = ele.componentOptions || {};
if (ele.$vnode) {
componentOptions = ele.$vnode.componentOptions || {}
componentOptions = ele.$vnode.componentOptions || {};
}
return ele.children || componentOptions.children || []
}
const getSlotOptions = (ele) => {
if (ele.fnOptions) { // 函数式组件
return ele.fnOptions
return ele.children || componentOptions.children || [];
};
const getSlotOptions = ele => {
if (ele.fnOptions) {
// 函数式组件
return ele.fnOptions;
}
let componentOptions = ele.componentOptions
let componentOptions = ele.componentOptions;
if (ele.$vnode) {
componentOptions = ele.$vnode.componentOptions
componentOptions = ele.$vnode.componentOptions;
}
return componentOptions ? componentOptions.Ctor.options || {} : {}
}
const getOptionProps = (instance) => {
return componentOptions ? componentOptions.Ctor.options || {} : {};
};
const getOptionProps = instance => {
if (instance.componentOptions) {
const componentOptions = instance.componentOptions
const { propsData = {}, Ctor = {}} = componentOptions
const props = (Ctor.options || {}).props || {}
const res = {}
const componentOptions = instance.componentOptions;
const { propsData = {}, Ctor = {} } = componentOptions;
const props = (Ctor.options || {}).props || {};
const res = {};
for (const [k, v] of Object.entries(props)) {
const def = v.default
const def = v.default;
if (def !== undefined) {
res[k] = typeof def === 'function' && getType(v.type) !== 'Function'
? def.call(instance)
: def
res[k] =
typeof def === 'function' && getType(v.type) !== 'Function' ? def.call(instance) : def;
}
}
return { ...res, ...propsData }
return { ...res, ...propsData };
}
const { $options = {}, $props = {}} = instance
return filterProps($props, $options.propsData)
}
const { $options = {}, $props = {} } = instance;
return filterProps($props, $options.propsData);
};
const getComponentFromProp = (instance, prop, options = instance, execute = true) => {
if (instance.$createElement) {
const h = instance.$createElement
const temp = instance[prop]
const h = instance.$createElement;
const temp = instance[prop];
if (temp !== undefined) {
return typeof temp === 'function' && execute ? temp(h, options) : temp
return typeof temp === 'function' && execute ? temp(h, options) : temp;
}
return instance.$slots[prop] ||
(instance.$scopedSlots[prop] && execute && instance.$scopedSlots[prop](options)) ||
(instance.$scopedSlots[prop] && instance.$scopedSlots[prop]) ||
undefined
return (
instance.$slots[prop] ||
(instance.$scopedSlots[prop] && execute && instance.$scopedSlots[prop](options)) ||
instance.$scopedSlots[prop] ||
undefined
);
} else {
const h = instance.context.$createElement
const temp = getPropsData(instance)[prop]
const h = instance.context.$createElement;
const temp = getPropsData(instance)[prop];
if (temp !== undefined) {
return typeof temp === 'function' && execute ? temp(h, options) : temp
return typeof temp === 'function' && execute ? temp(h, options) : temp;
}
const slotsProp = []
const slotsProp = [];
const componentOptions = instance.componentOptions || {};
(componentOptions.children || []).forEach((child) => {
(componentOptions.children || []).forEach(child => {
if (child.data && child.data.slot === prop) {
if (child.tag === 'template') {
slotsProp.push(child.children)
slotsProp.push(child.children);
} else {
slotsProp.push(child)
slotsProp.push(child);
}
}
})
return slotsProp.length ? slotsProp : undefined
});
return slotsProp.length ? slotsProp : undefined;
}
}
};
const getAllProps = (ele) => {
let data = ele.data || {}
let componentOptions = ele.componentOptions || {}
const getAllProps = ele => {
let data = ele.data || {};
let componentOptions = ele.componentOptions || {};
if (ele.$vnode) {
data = ele.$vnode.data || {}
componentOptions = ele.$vnode.componentOptions || {}
data = ele.$vnode.data || {};
componentOptions = ele.$vnode.componentOptions || {};
}
return { ...data.props, ...data.attrs, ...componentOptions.propsData }
}
return { ...data.props, ...data.attrs, ...componentOptions.propsData };
};
const getPropsData = (ele) => {
let componentOptions = ele.componentOptions
const getPropsData = ele => {
let componentOptions = ele.componentOptions;
if (ele.$vnode) {
componentOptions = ele.$vnode.componentOptions
componentOptions = ele.$vnode.componentOptions;
}
return componentOptions ? componentOptions.propsData || {} : {}
}
return componentOptions ? componentOptions.propsData || {} : {};
};
const getValueByProp = (ele, prop) => {
return getPropsData(ele)[prop]
}
return getPropsData(ele)[prop];
};
const getAttrs = (ele) => {
let data = ele.data
const getAttrs = ele => {
let data = ele.data;
if (ele.$vnode) {
data = ele.$vnode.data
data = ele.$vnode.data;
}
return data ? data.attrs || {} : {}
}
return data ? data.attrs || {} : {};
};
const getKey = (ele) => {
let key = ele.key
const getKey = ele => {
let key = ele.key;
if (ele.$vnode) {
key = ele.$vnode.key
key = ele.$vnode.key;
}
return key
}
return key;
};
export function getEvents (child) {
let events = {}
export function getEvents(child) {
let events = {};
if (child.componentOptions && child.componentOptions.listeners) {
events = child.componentOptions.listeners
events = child.componentOptions.listeners;
} else if (child.data && child.data.on) {
events = child.data.on
events = child.data.on;
}
return { ...events }
return { ...events };
}
export function getClass (ele) {
let data = {}
export function getClass(ele) {
let data = {};
if (ele.data) {
data = ele.data
data = ele.data;
} else if (ele.$vnode && ele.$vnode.data) {
data = ele.$vnode.data
data = ele.$vnode.data;
}
const tempCls = data.class || {}
const staticClass = data.staticClass
let cls = {}
staticClass && staticClass.split(' ').forEach(c => { cls[c.trim()] = true })
const tempCls = data.class || {};
const staticClass = data.staticClass;
let cls = {};
staticClass &&
staticClass.split(' ').forEach(c => {
cls[c.trim()] = true;
});
if (typeof tempCls === 'string') {
tempCls.split(' ').forEach(c => { cls[c.trim()] = true })
tempCls.split(' ').forEach(c => {
cls[c.trim()] = true;
});
} else if (Array.isArray(tempCls)) {
classNames(tempCls).split(' ').forEach(c => { cls[c.trim()] = true })
classNames(tempCls)
.split(' ')
.forEach(c => {
cls[c.trim()] = true;
});
} else {
cls = { ...cls, ...tempCls }
cls = { ...cls, ...tempCls };
}
return cls
return cls;
}
export function getStyle (ele, camel) {
let data = {}
export function getStyle(ele, camel) {
let data = {};
if (ele.data) {
data = ele.data
data = ele.data;
} else if (ele.$vnode && ele.$vnode.data) {
data = ele.$vnode.data
data = ele.$vnode.data;
}
let style = data.style || data.staticStyle
let style = data.style || data.staticStyle;
if (typeof style === 'string') {
style = parseStyleText(style, camel)
} else if (camel && style) { // 驼峰化
const res = {}
Object.keys(style).forEach(k => (res[camelize(k)] = style[k]))
return res
style = parseStyleText(style, camel);
} else if (camel && style) {
// 驼峰化
const res = {};
Object.keys(style).forEach(k => (res[camelize(k)] = style[k]));
return res;
}
return style
return style;
}
export function getComponentName (opts) {
return opts && (opts.Ctor.options.name || opts.tag)
export function getComponentName(opts) {
return opts && (opts.Ctor.options.name || opts.tag);
}
export function isEmptyElement (c) {
return !(c.tag || (c.text && c.text.trim() !== ''))
export function isEmptyElement(c) {
return !(c.tag || (c.text && c.text.trim() !== ''));
}
export function filterEmpty (children = []) {
return children.filter(c => !isEmptyElement(c))
export function filterEmpty(children = []) {
return children.filter(c => !isEmptyElement(c));
}
const initDefaultProps = (propTypes, defaultProps) => {
Object.keys(defaultProps).forEach(k => {
if (propTypes[k]) {
propTypes[k].def && (propTypes[k] = propTypes[k].def(defaultProps[k]))
propTypes[k].def && (propTypes[k] = propTypes[k].def(defaultProps[k]));
} else {
throw new Error(
`not have ${k} prop`,
)
throw new Error(`not have ${k} prop`);
}
})
return propTypes
}
});
return propTypes;
};
export function mergeProps () {
const args = [].slice.call(arguments, 0)
const props = {}
export function mergeProps() {
const args = [].slice.call(arguments, 0);
const props = {};
args.forEach((p = {}, i) => {
for (const [k, v] of Object.entries(p)) {
props[k] = props[k] || {}
props[k] = props[k] || {};
if (isPlainObject(v)) {
Object.assign(props[k], v)
Object.assign(props[k], v);
} else {
props[k] = v
props[k] = v;
}
}
})
return props
});
return props;
}
function isValidElement (element) {
return element &&
function isValidElement(element) {
return (
element &&
typeof element === 'object' &&
'componentOptions' in element &&
'context' in element &&
element.tag !== undefined // remove text node
element.tag !== undefined
); // remove text node
}
export {
@ -279,5 +291,5 @@ export {
getSlots,
getAllProps,
getAllChildren,
}
export default hasProp
};
export default hasProp;

View File

@ -1,29 +1,30 @@
import PropTypes from './vue-types';
import { getOptionProps } from './props-util';
import PropTypes from './vue-types'
import { getOptionProps } from './props-util'
function getDisplayName (WrappedComponent) {
return WrappedComponent.name || 'Component'
function getDisplayName(WrappedComponent) {
return WrappedComponent.name || 'Component';
}
export default function wrapWithConnect (WrappedComponent) {
const tempProps = WrappedComponent.props || {}
const methods = WrappedComponent.methods || {}
const props = {}
Object.keys(tempProps).forEach(k => { props[k] = ({ ...k, required: false }) })
WrappedComponent.props.__propsSymbol__ = PropTypes.any
WrappedComponent.props.children = PropTypes.array.def([])
export default function wrapWithConnect(WrappedComponent) {
const tempProps = WrappedComponent.props || {};
const methods = WrappedComponent.methods || {};
const props = {};
Object.keys(tempProps).forEach(k => {
props[k] = { ...k, required: false };
});
WrappedComponent.props.__propsSymbol__ = PropTypes.any;
WrappedComponent.props.children = PropTypes.array.def([]);
const ProxyWrappedComponent = {
props,
model: WrappedComponent.model,
name: `Proxy_${getDisplayName(WrappedComponent)}`,
methods: {
getProxyWrappedInstance () {
return this.$refs.wrappedInstance
getProxyWrappedInstance() {
return this.$refs.wrappedInstance;
},
},
render () {
const { $listeners, $slots = {}, $attrs, $scopedSlots } = this
const props = getOptionProps(this)
render() {
const { $listeners, $slots = {}, $attrs, $scopedSlots } = this;
const props = getOptionProps(this);
const wrapProps = {
props: {
...props,
@ -33,24 +34,26 @@ export default function wrapWithConnect (WrappedComponent) {
},
on: $listeners,
attrs: $attrs,
}
};
if (Object.keys($scopedSlots).length) {
wrapProps.scopedSlots = $scopedSlots
wrapProps.scopedSlots = $scopedSlots;
}
const slotsKey = Object.keys($slots)
const slotsKey = Object.keys($slots);
return (
<WrappedComponent {...wrapProps} ref='wrappedInstance'>
{slotsKey.length ? slotsKey.map(name => {
return <template slot={name}>{$slots[name]}</template>
}) : null}
<WrappedComponent {...wrapProps} ref="wrappedInstance">
{slotsKey.length
? slotsKey.map(name => {
return <template slot={name}>{$slots[name]}</template>;
})
: null}
</WrappedComponent>
)
);
},
}
};
Object.keys(methods).map(m => {
ProxyWrappedComponent.methods[m] = function () {
return this.getProxyWrappedInstance()[m](...arguments)
}
})
return ProxyWrappedComponent
ProxyWrappedComponent.methods[m] = function() {
return this.getProxyWrappedInstance()[m](...arguments);
};
});
return ProxyWrappedComponent;
}

View File

@ -1,30 +1,30 @@
import raf from 'raf'
import raf from 'raf';
let id = 0
const ids = {}
let id = 0;
const ids = {};
// Support call raf with delay specified frame
export default function wrapperRaf (callback, delayFrames = 1) {
const myId = id++
let restFrames = delayFrames
export default function wrapperRaf(callback, delayFrames = 1) {
const myId = id++;
let restFrames = delayFrames;
function internalCallback () {
restFrames -= 1
function internalCallback() {
restFrames -= 1;
if (restFrames <= 0) {
callback()
delete ids[id]
callback();
delete ids[id];
} else {
ids[id] = raf(internalCallback)
ids[id] = raf(internalCallback);
}
}
ids[id] = raf(internalCallback)
ids[id] = raf(internalCallback);
return myId
return myId;
}
wrapperRaf.cancel = function (id) {
raf.cancel(ids[id])
delete ids[id]
}
wrapperRaf.cancel = function(id) {
raf.cancel(ids[id]);
delete ids[id];
};

View File

@ -1,22 +1,23 @@
import getRequestAnimationFrame, {
cancelRequestAnimationFrame as caf,
} from './getRequestAnimationFrame';
const raf = getRequestAnimationFrame();
import getRequestAnimationFrame, { cancelRequestAnimationFrame as caf } from './getRequestAnimationFrame'
const raf = getRequestAnimationFrame()
export const cancelAnimationTimeout = (frame) => caf(frame.id)
export const cancelAnimationTimeout = frame => caf(frame.id);
export const requestAnimationTimeout = (callback, delay) => {
const start = Date.now()
function timeout () {
const start = Date.now();
function timeout() {
if (Date.now() - start >= delay) {
callback.call()
callback.call();
} else {
frame.id = raf(timeout)
frame.id = raf(timeout);
}
}
const frame = {
id: raf(timeout),
}
};
return frame
}
return frame;
};

View File

@ -1,7 +1,7 @@
import PropTypes from '../vue-types'
import PropTypes from '../vue-types';
export const storeShape = PropTypes.shape({
subscribe: PropTypes.func.isRequired,
setState: PropTypes.func.isRequired,
getState: PropTypes.func.isRequired,
})
});

View File

@ -1,16 +1,15 @@
import { storeShape } from './PropTypes'
import { storeShape } from './PropTypes';
export default {
name: 'StoreProvider',
props: {
store: storeShape.isRequired,
},
provide () {
provide() {
return {
storeContext: this.$props,
}
};
},
render () {
return this.$slots.default[0]
render() {
return this.$slots.default[0];
},
}
};

View File

@ -1,84 +1,89 @@
import shallowEqual from 'shallowequal'
import omit from 'omit.js'
import { getOptionProps } from '../props-util'
import PropTypes from '../vue-types'
import proxyComponent from '../proxyComponent'
import shallowEqual from 'shallowequal';
import omit from 'omit.js';
import { getOptionProps } from '../props-util';
import PropTypes from '../vue-types';
import proxyComponent from '../proxyComponent';
function getDisplayName (WrappedComponent) {
return WrappedComponent.name || 'Component'
function getDisplayName(WrappedComponent) {
return WrappedComponent.name || 'Component';
}
const defaultMapStateToProps = () => ({})
export default function connect (mapStateToProps) {
const shouldSubscribe = !!mapStateToProps
const finnalMapStateToProps = mapStateToProps || defaultMapStateToProps
return function wrapWithConnect (WrappedComponent) {
const tempProps = omit(WrappedComponent.props || {}, ['store'])
const defaultMapStateToProps = () => ({});
export default function connect(mapStateToProps) {
const shouldSubscribe = !!mapStateToProps;
const finnalMapStateToProps = mapStateToProps || defaultMapStateToProps;
return function wrapWithConnect(WrappedComponent) {
const tempProps = omit(WrappedComponent.props || {}, ['store']);
const props = {
__propsSymbol__: PropTypes.any,
}
Object.keys(tempProps).forEach(k => { props[k] = ({ ...k, required: false }) })
};
Object.keys(tempProps).forEach(k => {
props[k] = { ...k, required: false };
});
const Connect = {
name: `Connect_${getDisplayName(WrappedComponent)}`,
props,
inject: {
storeContext: { default: {}},
storeContext: { default: {} },
},
data () {
this.store = this.storeContext.store
this.preProps = omit(getOptionProps(this), ['__propsSymbol__'])
data() {
this.store = this.storeContext.store;
this.preProps = omit(getOptionProps(this), ['__propsSymbol__']);
return {
subscribed: finnalMapStateToProps(this.store.getState(), this.$props),
}
};
},
watch: {
__propsSymbol__ () {
__propsSymbol__() {
if (mapStateToProps && mapStateToProps.length === 2) {
this.subscribed = finnalMapStateToProps(this.store.getState(), this.$props)
this.subscribed = finnalMapStateToProps(this.store.getState(), this.$props);
}
},
},
mounted () {
this.trySubscribe()
mounted() {
this.trySubscribe();
},
beforeDestroy () {
this.tryUnsubscribe()
beforeDestroy() {
this.tryUnsubscribe();
},
methods: {
handleChange () {
handleChange() {
if (!this.unsubscribe) {
return
return;
}
const props = omit(getOptionProps(this), ['__propsSymbol__'])
const nextSubscribed = finnalMapStateToProps(this.store.getState(), props)
if (!shallowEqual(this.preProps, props) || !shallowEqual(this.subscribed, nextSubscribed)) {
this.subscribed = nextSubscribed
const props = omit(getOptionProps(this), ['__propsSymbol__']);
const nextSubscribed = finnalMapStateToProps(this.store.getState(), props);
if (
!shallowEqual(this.preProps, props) ||
!shallowEqual(this.subscribed, nextSubscribed)
) {
this.subscribed = nextSubscribed;
}
},
trySubscribe () {
trySubscribe() {
if (shouldSubscribe) {
this.unsubscribe = this.store.subscribe(this.handleChange)
this.handleChange()
this.unsubscribe = this.store.subscribe(this.handleChange);
this.handleChange();
}
},
tryUnsubscribe () {
tryUnsubscribe() {
if (this.unsubscribe) {
this.unsubscribe()
this.unsubscribe = null
this.unsubscribe();
this.unsubscribe = null;
}
},
getWrappedInstance () {
return this.$refs.wrappedInstance
getWrappedInstance() {
return this.$refs.wrappedInstance;
},
},
render () {
this.preProps = { ...this.$props }
const { $listeners, $slots = {}, $attrs, $scopedSlots, subscribed, store } = this
const props = getOptionProps(this)
this.preProps = { ...omit(props, ['__propsSymbol__']) }
render() {
this.preProps = { ...this.$props };
const { $listeners, $slots = {}, $attrs, $scopedSlots, subscribed, store } = this;
const props = getOptionProps(this);
this.preProps = { ...omit(props, ['__propsSymbol__']) };
const wrapProps = {
props: {
...props,
@ -88,16 +93,16 @@ export default function connect (mapStateToProps) {
on: $listeners,
attrs: $attrs,
scopedSlots: $scopedSlots,
}
};
return (
<WrappedComponent {...wrapProps} ref='wrappedInstance'>
<WrappedComponent {...wrapProps} ref="wrappedInstance">
{Object.keys($slots).map(name => {
return <template slot={name}>{$slots[name]}</template>
return <template slot={name}>{$slots[name]}</template>;
})}
</WrappedComponent>
)
);
},
}
return proxyComponent(Connect)
}
};
return proxyComponent(Connect);
};
}

View File

@ -1,30 +1,30 @@
export default function create (initialState) {
let state = initialState
const listeners = []
export default function create(initialState) {
let state = initialState;
const listeners = [];
function setState (partial) {
state = { ...state, ...partial }
function setState(partial) {
state = { ...state, ...partial };
for (let i = 0; i < listeners.length; i++) {
listeners[i]()
listeners[i]();
}
}
function getState () {
return state
function getState() {
return state;
}
function subscribe (listener) {
listeners.push(listener)
function subscribe(listener) {
listeners.push(listener);
return function unsubscribe () {
const index = listeners.indexOf(listener)
listeners.splice(index, 1)
}
return function unsubscribe() {
const index = listeners.indexOf(listener);
listeners.splice(index, 1);
};
}
return {
setState,
getState,
subscribe,
}
};
}

View File

@ -1,5 +1,5 @@
export { default as Provider } from './Provider'
export { default as Provider } from './Provider';
export { default as connect } from './connect'
export { default as connect } from './connect';
export { default as create } from './create'
export { default as create } from './create';

View File

@ -1,45 +1,45 @@
import raf from 'raf'
import raf from 'raf';
export default function throttleByAnimationFrame (fn) {
let requestId
export default function throttleByAnimationFrame(fn) {
let requestId;
const later = args => () => {
requestId = null
fn(...args)
}
requestId = null;
fn(...args);
};
const throttled = (...args) => {
if (requestId == null) {
requestId = raf(later(args))
requestId = raf(later(args));
}
}
};
throttled.cancel = () => raf.cancel(requestId)
throttled.cancel = () => raf.cancel(requestId);
return throttled
return throttled;
}
export function throttleByAnimationFrameDecorator () {
return function (target, key, descriptor) {
const fn = descriptor.value
let definingProperty = false
export function throttleByAnimationFrameDecorator() {
return function(target, key, descriptor) {
const fn = descriptor.value;
let definingProperty = false;
return {
configurable: true,
get () {
get() {
if (definingProperty || this === target.prototype || this.hasOwnProperty(key)) {
return fn
return fn;
}
const boundFn = throttleByAnimationFrame(fn.bind(this))
definingProperty = true
const boundFn = throttleByAnimationFrame(fn.bind(this));
definingProperty = true;
Object.defineProperty(this, key, {
value: boundFn,
configurable: true,
writable: true,
})
definingProperty = false
return boundFn
});
definingProperty = false;
return boundFn;
},
}
}
};
};
}

View File

@ -1,8 +1,8 @@
export default function triggerEvent (el, type) {
export default function triggerEvent(el, type) {
if ('createEvent' in document) {
// modern browsers, IE9+
const e = document.createEvent('HTMLEvents')
e.initEvent(type, false, true)
el.dispatchEvent(e)
const e = document.createEvent('HTMLEvents');
e.initEvent(type, false, true);
el.dispatchEvent(e);
}
}

View File

@ -1,16 +1,16 @@
import { filterEmpty, parseStyleText } from './props-util'
export function cloneVNode (vnode, deep) {
const componentOptions = vnode.componentOptions
const data = vnode.data
import { filterEmpty, parseStyleText } from './props-util';
export function cloneVNode(vnode, deep) {
const componentOptions = vnode.componentOptions;
const data = vnode.data;
let listeners = {}
let listeners = {};
if (componentOptions && componentOptions.listeners) {
listeners = { ...componentOptions.listeners }
listeners = { ...componentOptions.listeners };
}
let on = {}
let on = {};
if (data && data.on) {
on = { ...data.on }
on = { ...data.on };
}
const cloned = new vnode.constructor(
@ -21,49 +21,49 @@ export function cloneVNode (vnode, deep) {
vnode.elm,
vnode.context,
componentOptions ? { ...componentOptions, listeners } : componentOptions,
vnode.asyncFactory
)
cloned.ns = vnode.ns
cloned.isStatic = vnode.isStatic
cloned.key = vnode.key
cloned.isComment = vnode.isComment
cloned.fnContext = vnode.fnContext
cloned.fnOptions = vnode.fnOptions
cloned.fnScopeId = vnode.fnScopeId
cloned.isCloned = true
vnode.asyncFactory,
);
cloned.ns = vnode.ns;
cloned.isStatic = vnode.isStatic;
cloned.key = vnode.key;
cloned.isComment = vnode.isComment;
cloned.fnContext = vnode.fnContext;
cloned.fnOptions = vnode.fnOptions;
cloned.fnScopeId = vnode.fnScopeId;
cloned.isCloned = true;
if (deep) {
if (vnode.children) {
cloned.children = cloneVNodes(vnode.children, true)
cloned.children = cloneVNodes(vnode.children, true);
}
if (componentOptions && componentOptions.children) {
componentOptions.children = cloneVNodes(componentOptions.children, true)
componentOptions.children = cloneVNodes(componentOptions.children, true);
}
}
return cloned
return cloned;
}
export function cloneVNodes (vnodes, deep) {
const len = vnodes.length
const res = new Array(len)
export function cloneVNodes(vnodes, deep) {
const len = vnodes.length;
const res = new Array(len);
for (let i = 0; i < len; i++) {
res[i] = cloneVNode(vnodes[i], deep)
res[i] = cloneVNode(vnodes[i], deep);
}
return res
return res;
}
export function cloneElement (n, nodeProps = {}, deep) {
let ele = n
export function cloneElement(n, nodeProps = {}, deep) {
let ele = n;
if (Array.isArray(n)) {
ele = filterEmpty(n)[0]
ele = filterEmpty(n)[0];
}
if (!ele) {
return null
return null;
}
const node = cloneVNode(ele, deep)
const { props = {}, key, on = {}, children, directives = [] } = nodeProps
const data = node.data || {}
let cls = {}
let style = {}
const node = cloneVNode(ele, deep);
const { props = {}, key, on = {}, children, directives = [] } = nodeProps;
const data = node.data || {};
let cls = {};
let style = {};
const {
attrs = {},
ref,
@ -71,28 +71,32 @@ export function cloneElement (n, nodeProps = {}, deep) {
style: tempStyle = {},
class: tempCls = {},
scopedSlots = {},
} = nodeProps
} = nodeProps;
if (typeof data.style === 'string') {
style = parseStyleText(data.style)
style = parseStyleText(data.style);
} else {
style = { ...data.style, ...style }
style = { ...data.style, ...style };
}
if (typeof tempStyle === 'string') {
style = { ...style, ...parseStyleText(style) }
style = { ...style, ...parseStyleText(style) };
} else {
style = { ...style, ...tempStyle }
style = { ...style, ...tempStyle };
}
if (typeof data.class === 'string' && data.class.trim() !== '') {
data.class.split(' ').forEach(c => { cls[c.trim()] = true })
data.class.split(' ').forEach(c => {
cls[c.trim()] = true;
});
} else {
cls = { ...data.class, ...cls }
cls = { ...data.class, ...cls };
}
if (typeof tempCls === 'string' && tempCls.trim() !== '') {
tempCls.split(' ').forEach(c => { cls[c.trim()] = true })
tempCls.split(' ').forEach(c => {
cls[c.trim()] = true;
});
} else {
cls = { ...cls, ...tempCls }
cls = { ...cls, ...tempCls };
}
node.data = Object.assign({}, data, {
style,
@ -101,27 +105,26 @@ export function cloneElement (n, nodeProps = {}, deep) {
domProps: { ...data.domProps, ...domProps },
scopedSlots: { ...data.scopedSlots, ...scopedSlots },
directives: [...(data.directives || []), ...directives],
})
});
if (node.componentOptions) {
node.componentOptions.propsData = node.componentOptions.propsData || {}
node.componentOptions.listeners = node.componentOptions.listeners || {}
node.componentOptions.propsData = { ...node.componentOptions.propsData, ...props }
node.componentOptions.listeners = { ...node.componentOptions.listeners, ...on }
node.componentOptions.propsData = node.componentOptions.propsData || {};
node.componentOptions.listeners = node.componentOptions.listeners || {};
node.componentOptions.propsData = { ...node.componentOptions.propsData, ...props };
node.componentOptions.listeners = { ...node.componentOptions.listeners, ...on };
if (children) {
node.componentOptions.children = children
node.componentOptions.children = children;
}
} else {
node.data.on = { ...(node.data.on || {}), ...on }
node.data.on = { ...(node.data.on || {}), ...on };
}
if (key !== undefined) {
node.key = key
node.data.key = key
node.key = key;
node.data.key = key;
}
if (typeof ref === 'string') {
node.data.ref = ref
node.data.ref = ref;
}
return node
return node;
}

View File

@ -1,231 +1,236 @@
import isPlainObject from 'lodash/isPlainObject'
import { toType, getType, isFunction, validateType, isInteger, isArray, warn } from './utils'
import isPlainObject from 'lodash/isPlainObject';
import { toType, getType, isFunction, validateType, isInteger, isArray, warn } from './utils';
const VuePropTypes = {
get any () {
get any() {
return toType('any', {
type: null,
})
});
},
get func () {
get func() {
return toType('function', {
type: Function,
}).def(currentDefaults.func)
}).def(currentDefaults.func);
},
get bool () {
get bool() {
return toType('boolean', {
type: Boolean,
}).def(currentDefaults.bool)
}).def(currentDefaults.bool);
},
get string () {
get string() {
return toType('string', {
type: String,
}).def(currentDefaults.string)
}).def(currentDefaults.string);
},
get number () {
get number() {
return toType('number', {
type: Number,
}).def(currentDefaults.number)
}).def(currentDefaults.number);
},
get array () {
get array() {
return toType('array', {
type: Array,
}).def(currentDefaults.array)
}).def(currentDefaults.array);
},
get object () {
get object() {
return toType('object', {
type: Object,
}).def(currentDefaults.object)
}).def(currentDefaults.object);
},
get integer () {
get integer() {
return toType('integer', {
type: Number,
validator (value) {
return isInteger(value)
validator(value) {
return isInteger(value);
},
}).def(currentDefaults.integer)
}).def(currentDefaults.integer);
},
get symbol () {
get symbol() {
return toType('symbol', {
type: null,
validator (value) {
return typeof value === 'symbol'
validator(value) {
return typeof value === 'symbol';
},
})
});
},
custom (validatorFn, warnMsg = 'custom validation failed') {
custom(validatorFn, warnMsg = 'custom validation failed') {
if (typeof validatorFn !== 'function') {
throw new TypeError('[VueTypes error]: You must provide a function as argument')
throw new TypeError('[VueTypes error]: You must provide a function as argument');
}
return toType(validatorFn.name || '<<anonymous function>>', {
validator (...args) {
const valid = validatorFn(...args)
if (!valid) warn(`${this._vueTypes_name} - ${warnMsg}`)
return valid
validator(...args) {
const valid = validatorFn(...args);
if (!valid) warn(`${this._vueTypes_name} - ${warnMsg}`);
return valid;
},
})
});
},
oneOf (arr) {
oneOf(arr) {
if (!isArray(arr)) {
throw new TypeError('[VueTypes error]: You must provide an array as argument')
throw new TypeError('[VueTypes error]: You must provide an array as argument');
}
const msg = `oneOf - value should be one of "${arr.join('", "')}"`
const msg = `oneOf - value should be one of "${arr.join('", "')}"`;
const allowedTypes = arr.reduce((ret, v) => {
if (v !== null && v !== undefined) {
ret.indexOf(v.constructor) === -1 && ret.push(v.constructor)
ret.indexOf(v.constructor) === -1 && ret.push(v.constructor);
}
return ret
}, [])
return ret;
}, []);
return toType('oneOf', {
type: allowedTypes.length > 0 ? allowedTypes : null,
validator (value) {
const valid = arr.indexOf(value) !== -1
if (!valid) warn(msg)
return valid
validator(value) {
const valid = arr.indexOf(value) !== -1;
if (!valid) warn(msg);
return valid;
},
})
});
},
instanceOf (instanceConstructor) {
instanceOf(instanceConstructor) {
return toType('instanceOf', {
type: instanceConstructor,
})
});
},
oneOfType (arr) {
oneOfType(arr) {
if (!isArray(arr)) {
throw new TypeError('[VueTypes error]: You must provide an array as argument')
throw new TypeError('[VueTypes error]: You must provide an array as argument');
}
let hasCustomValidators = false
let hasCustomValidators = false;
const nativeChecks = arr.reduce((ret, type, i) => {
if (isPlainObject(type)) {
if (type._vueTypes_name === 'oneOf') {
return ret.concat(type.type || [])
return ret.concat(type.type || []);
}
if (type.type && !isFunction(type.validator)) {
if (isArray(type.type)) return ret.concat(type.type)
ret.push(type.type)
if (isArray(type.type)) return ret.concat(type.type);
ret.push(type.type);
} else if (isFunction(type.validator)) {
hasCustomValidators = true
hasCustomValidators = true;
}
return ret
return ret;
}
ret.push(type)
return ret
}, [])
ret.push(type);
return ret;
}, []);
if (!hasCustomValidators) {
// we got just native objects (ie: Array, Object)
// delegate to Vue native prop check
return toType('oneOfType', {
type: nativeChecks,
}).def(undefined)
}).def(undefined);
}
const typesStr = arr.map((type) => {
if (type && isArray(type.type)) {
return type.type.map(getType)
}
return getType(type)
}).reduce((ret, type) => ret.concat(isArray(type) ? type : [type]), []).join('", "')
return this.custom(function oneOfType (value) {
const valid = arr.some((type) => {
if (type._vueTypes_name === 'oneOf') {
return type.type ? validateType(type.type, value, true) : true
const typesStr = arr
.map(type => {
if (type && isArray(type.type)) {
return type.type.map(getType);
}
return validateType(type, value, true)
return getType(type);
})
if (!valid) warn(`oneOfType - value type should be one of "${typesStr}"`)
return valid
}).def(undefined)
.reduce((ret, type) => ret.concat(isArray(type) ? type : [type]), [])
.join('", "');
return this.custom(function oneOfType(value) {
const valid = arr.some(type => {
if (type._vueTypes_name === 'oneOf') {
return type.type ? validateType(type.type, value, true) : true;
}
return validateType(type, value, true);
});
if (!valid) warn(`oneOfType - value type should be one of "${typesStr}"`);
return valid;
}).def(undefined);
},
arrayOf (type) {
arrayOf(type) {
return toType('arrayOf', {
type: Array,
validator (values) {
const valid = values.every((value) => validateType(type, value))
if (!valid) warn(`arrayOf - value must be an array of "${getType(type)}"`)
return valid
validator(values) {
const valid = values.every(value => validateType(type, value));
if (!valid) warn(`arrayOf - value must be an array of "${getType(type)}"`);
return valid;
},
})
});
},
objectOf (type) {
objectOf(type) {
return toType('objectOf', {
type: Object,
validator (obj) {
const valid = Object.keys(obj).every((key) => validateType(type, obj[key]))
if (!valid) warn(`objectOf - value must be an object of "${getType(type)}"`)
return valid
validator(obj) {
const valid = Object.keys(obj).every(key => validateType(type, obj[key]));
if (!valid) warn(`objectOf - value must be an object of "${getType(type)}"`);
return valid;
},
})
});
},
shape (obj) {
const keys = Object.keys(obj)
const requiredKeys = keys.filter((key) => obj[key] && obj[key].required === true)
shape(obj) {
const keys = Object.keys(obj);
const requiredKeys = keys.filter(key => obj[key] && obj[key].required === true);
const type = toType('shape', {
type: Object,
validator (value) {
validator(value) {
if (!isPlainObject(value)) {
return false
return false;
}
const valueKeys = Object.keys(value)
const valueKeys = Object.keys(value);
// check for required keys (if any)
if (requiredKeys.length > 0 && requiredKeys.some((req) => valueKeys.indexOf(req) === -1)) {
warn(`shape - at least one of required properties "${requiredKeys.join('", "')}" is not present`)
return false
if (requiredKeys.length > 0 && requiredKeys.some(req => valueKeys.indexOf(req) === -1)) {
warn(
`shape - at least one of required properties "${requiredKeys.join(
'", "',
)}" is not present`,
);
return false;
}
return valueKeys.every((key) => {
return valueKeys.every(key => {
if (keys.indexOf(key) === -1) {
if (this._vueTypes_isLoose === true) return true
warn(`shape - object is missing "${key}" property`)
return false
if (this._vueTypes_isLoose === true) return true;
warn(`shape - object is missing "${key}" property`);
return false;
}
const type = obj[key]
return validateType(type, value[key])
})
const type = obj[key];
return validateType(type, value[key]);
});
},
})
});
Object.defineProperty(type, '_vueTypes_isLoose', {
enumerable: false,
writable: true,
value: false,
})
});
Object.defineProperty(type, 'loose', {
get () {
this._vueTypes_isLoose = true
return this
get() {
this._vueTypes_isLoose = true;
return this;
},
enumerable: false,
})
});
return type
return type;
},
}
};
const typeDefaults = () => ({
func: undefined,
@ -235,24 +240,24 @@ const typeDefaults = () => ({
array: undefined,
object: undefined,
integer: undefined,
})
});
let currentDefaults = typeDefaults()
let currentDefaults = typeDefaults();
Object.defineProperty(VuePropTypes, 'sensibleDefaults', {
enumerable: false,
set (value) {
set(value) {
if (value === false) {
currentDefaults = {}
currentDefaults = {};
} else if (value === true) {
currentDefaults = typeDefaults()
currentDefaults = typeDefaults();
} else if (isPlainObject(value)) {
currentDefaults = value
currentDefaults = value;
}
},
get () {
return currentDefaults
get() {
return currentDefaults;
},
})
});
export default VuePropTypes
export default VuePropTypes;

View File

@ -1,28 +1,28 @@
import isPlainObject from 'lodash/isPlainObject'
import isPlainObject from 'lodash/isPlainObject';
const ObjProto = Object.prototype
const toString = ObjProto.toString
export const hasOwn = ObjProto.hasOwnProperty
const ObjProto = Object.prototype;
const toString = ObjProto.toString;
export const hasOwn = ObjProto.hasOwnProperty;
const FN_MATCH_REGEXP = /^\s*function (\w+)/
const FN_MATCH_REGEXP = /^\s*function (\w+)/;
// https://github.com/vuejs/vue/blob/dev/src/core/util/props.js#L159
export const getType = (fn) => {
const type = (fn !== null && fn !== undefined) ? (fn.type ? fn.type : fn) : null
const match = type && type.toString().match(FN_MATCH_REGEXP)
return match && match[1]
}
export const getType = fn => {
const type = fn !== null && fn !== undefined ? (fn.type ? fn.type : fn) : null;
const match = type && type.toString().match(FN_MATCH_REGEXP);
return match && match[1];
};
export const getNativeType = (value) => {
if (value === null || value === undefined) return null
const match = value.constructor.toString().match(FN_MATCH_REGEXP)
return match && match[1]
}
export const getNativeType = value => {
if (value === null || value === undefined) return null;
const match = value.constructor.toString().match(FN_MATCH_REGEXP);
return match && match[1];
};
/**
* No-op function
*/
export const noop = () => {}
export const noop = () => {};
/**
* Checks for a own property in an object
@ -30,7 +30,7 @@ export const noop = () => {}
* @param {object} obj - Object
* @param {string} prop - Property to check
*/
export const has = (obj, prop) => hasOwn.call(obj, prop)
export const has = (obj, prop) => hasOwn.call(obj, prop);
/**
* Determines whether the passed value is an integer. Uses `Number.isInteger` if available
@ -39,9 +39,11 @@ export const has = (obj, prop) => hasOwn.call(obj, prop)
* @param {*} value - The value to be tested for being an integer.
* @returns {boolean}
*/
export const isInteger = Number.isInteger || function (value) {
return typeof value === 'number' && isFinite(value) && Math.floor(value) === value
}
export const isInteger =
Number.isInteger ||
function(value) {
return typeof value === 'number' && isFinite(value) && Math.floor(value) === value;
};
/**
* Determines whether the passed value is an Array.
@ -49,9 +51,11 @@ export const isInteger = Number.isInteger || function (value) {
* @param {*} value - The value to be tested for being an array.
* @returns {boolean}
*/
export const isArray = Array.isArray || function (value) {
return toString.call(value) === '[object Array]'
}
export const isArray =
Array.isArray ||
function(value) {
return toString.call(value) === '[object Array]';
};
/**
* Checks if a value is a function
@ -59,49 +63,52 @@ export const isArray = Array.isArray || function (value) {
* @param {any} value - Value to check
* @returns {boolean}
*/
export const isFunction = (value) => toString.call(value) === '[object Function]'
export const isFunction = value => toString.call(value) === '[object Function]';
/**
* Adds a `def` method to the object returning a new object with passed in argument as `default` property
*
* @param {object} type - Object to enhance
*/
export const withDefault = function (type) {
export const withDefault = function(type) {
Object.defineProperty(type, 'def', {
value (def) {
value(def) {
if (def === undefined && this.default === undefined) {
this.default = undefined
return this
this.default = undefined;
return this;
}
if (!isFunction(def) && !validateType(this, def)) {
warn(`${this._vueTypes_name} - invalid default value: "${def}"`, def)
return this
warn(`${this._vueTypes_name} - invalid default value: "${def}"`, def);
return this;
}
this.default = (isArray(def) || isPlainObject(def)) ? function () {
return def
} : def
this.default =
isArray(def) || isPlainObject(def)
? function() {
return def;
}
: def;
return this
return this;
},
enumerable: false,
writable: false,
})
}
});
};
/**
* Adds a `isRequired` getter returning a new object with `required: true` key-value
*
* @param {object} type - Object to enhance
*/
export const withRequired = function (type) {
export const withRequired = function(type) {
Object.defineProperty(type, 'isRequired', {
get () {
this.required = true
return this
get() {
this.required = true;
return this;
},
enumerable: false,
})
}
});
};
/**
* Adds `isRequired` and `def` modifiers to an object
@ -115,15 +122,15 @@ export const toType = (name, obj) => {
enumerable: false,
writable: false,
value: name,
})
withRequired(obj)
withDefault(obj)
});
withRequired(obj);
withDefault(obj);
if (isFunction(obj.validator)) {
obj.validator = obj.validator.bind(obj)
obj.validator = obj.validator.bind(obj);
}
return obj
}
return obj;
};
/**
* Validates a given value against a prop type object
@ -134,55 +141,60 @@ export const toType = (name, obj) => {
* @returns {boolean}
*/
export const validateType = (type, value, silent = false) => {
let typeToCheck = type
let valid = true
let expectedType
let typeToCheck = type;
let valid = true;
let expectedType;
if (!isPlainObject(type)) {
typeToCheck = { type }
typeToCheck = { type };
}
const namePrefix = typeToCheck._vueTypes_name ? (typeToCheck._vueTypes_name + ' - ') : ''
const namePrefix = typeToCheck._vueTypes_name ? typeToCheck._vueTypes_name + ' - ' : '';
if (hasOwn.call(typeToCheck, 'type') && typeToCheck.type !== null) {
if (isArray(typeToCheck.type)) {
valid = typeToCheck.type.some((type) => validateType(type, value, true))
expectedType = typeToCheck.type.map((type) => getType(type)).join(' or ')
valid = typeToCheck.type.some(type => validateType(type, value, true));
expectedType = typeToCheck.type.map(type => getType(type)).join(' or ');
} else {
expectedType = getType(typeToCheck)
expectedType = getType(typeToCheck);
if (expectedType === 'Array') {
valid = isArray(value)
valid = isArray(value);
} else if (expectedType === 'Object') {
valid = isPlainObject(value)
} else if (expectedType === 'String' || expectedType === 'Number' || expectedType === 'Boolean' || expectedType === 'Function') {
valid = getNativeType(value) === expectedType
valid = isPlainObject(value);
} else if (
expectedType === 'String' ||
expectedType === 'Number' ||
expectedType === 'Boolean' ||
expectedType === 'Function'
) {
valid = getNativeType(value) === expectedType;
} else {
valid = value instanceof typeToCheck.type
valid = value instanceof typeToCheck.type;
}
}
}
if (!valid) {
silent === false && warn(`${namePrefix}value "${value}" should be of type "${expectedType}"`)
return false
silent === false && warn(`${namePrefix}value "${value}" should be of type "${expectedType}"`);
return false;
}
if (hasOwn.call(typeToCheck, 'validator') && isFunction(typeToCheck.validator)) {
valid = typeToCheck.validator(value)
if (!valid && silent === false) warn(`${namePrefix}custom validation failed`)
return valid
valid = typeToCheck.validator(value);
if (!valid && silent === false) warn(`${namePrefix}custom validation failed`);
return valid;
}
return valid
}
return valid;
};
let warn = noop
let warn = noop;
if (process.env.NODE_ENV !== 'production') {
const hasConsole = typeof console !== 'undefined'
warn = (msg) => {
const hasConsole = typeof console !== 'undefined';
warn = msg => {
if (hasConsole) {
console.warn(`[VueTypes warn]: ${msg}`)
console.warn(`[VueTypes warn]: ${msg}`);
}
}
};
}
export { warn }
export { warn };

View File

@ -1,9 +1,9 @@
import warning from 'warning'
import warning from 'warning';
const warned = {}
const warned = {};
export default (valid, message, throwError) => {
if (!valid && !warned[message]) {
warning(false, message)
warned[message] = true
warning(false, message);
warned[message] = true;
}
}
};

View File

@ -1,161 +1,163 @@
import TransitionEvents from './css-animation/Event'
import raf from '../_util/raf'
let styleForPesudo
import TransitionEvents from './css-animation/Event';
import raf from '../_util/raf';
let styleForPesudo;
// Where el is the DOM element you'd like to test for visibility
function isHidden (element) {
function isHidden(element) {
if (process.env.NODE_ENV === 'test') {
return false
return false;
}
return !element || element.offsetParent === null
return !element || element.offsetParent === null;
}
export default {
name: 'Wave',
props: ['insertExtraNode'],
mounted () {
mounted() {
this.$nextTick(() => {
const node = this.$el
const node = this.$el;
if (node.nodeType !== 1) {
return
return;
}
this.instance = this.bindAnimationEvent(node)
})
this.instance = this.bindAnimationEvent(node);
});
},
beforeDestroy () {
beforeDestroy() {
if (this.instance) {
this.instance.cancel()
this.instance.cancel();
}
if (this.clickWaveTimeoutId) {
clearTimeout(this.clickWaveTimeoutId)
clearTimeout(this.clickWaveTimeoutId);
}
this.destroy = true
this.destroy = true;
},
methods: {
isNotGrey (color) {
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/)
isNotGrey(color) {
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/);
if (match && match[1] && match[2] && match[3]) {
return !(match[1] === match[2] && match[2] === match[3])
return !(match[1] === match[2] && match[2] === match[3]);
}
return true
return true;
},
onClick (node, waveColor) {
onClick(node, waveColor) {
if (!node || isHidden(node) || node.className.indexOf('-leave') >= 0) {
return
return;
}
this.removeExtraStyleNode()
const { insertExtraNode } = this.$props
this.extraNode = document.createElement('div')
const extraNode = this.extraNode
extraNode.className = 'ant-click-animating-node'
const attributeName = this.getAttributeName()
node.removeAttribute(attributeName)
node.setAttribute(attributeName, 'true')
this.removeExtraStyleNode();
const { insertExtraNode } = this.$props;
this.extraNode = document.createElement('div');
const extraNode = this.extraNode;
extraNode.className = 'ant-click-animating-node';
const attributeName = this.getAttributeName();
node.removeAttribute(attributeName);
node.setAttribute(attributeName, 'true');
// Not white or transparnt or grey
styleForPesudo = styleForPesudo || document.createElement('style')
if (waveColor &&
waveColor !== '#ffffff' &&
waveColor !== 'rgb(255, 255, 255)' &&
this.isNotGrey(waveColor) &&
!/rgba\(\d*, \d*, \d*, 0\)/.test(waveColor) && // any transparent rgba color
waveColor !== 'transparent') {
extraNode.style.borderColor = waveColor
styleForPesudo = styleForPesudo || document.createElement('style');
if (
waveColor &&
waveColor !== '#ffffff' &&
waveColor !== 'rgb(255, 255, 255)' &&
this.isNotGrey(waveColor) &&
!/rgba\(\d*, \d*, \d*, 0\)/.test(waveColor) && // any transparent rgba color
waveColor !== 'transparent'
) {
extraNode.style.borderColor = waveColor;
styleForPesudo.innerHTML =
`[ant-click-animating-without-extra-node]:after { border-color: ${waveColor}; }`
styleForPesudo.innerHTML = `[ant-click-animating-without-extra-node]:after { border-color: ${waveColor}; }`;
if (!document.body.contains(styleForPesudo)) {
document.body.appendChild(styleForPesudo)
document.body.appendChild(styleForPesudo);
}
}
if (insertExtraNode) {
node.appendChild(extraNode)
node.appendChild(extraNode);
}
TransitionEvents.addStartEventListener(node, this.onTransitionStart)
TransitionEvents.addEndEventListener(node, this.onTransitionEnd)
TransitionEvents.addStartEventListener(node, this.onTransitionStart);
TransitionEvents.addEndEventListener(node, this.onTransitionEnd);
},
bindAnimationEvent (node) {
if (!node ||
bindAnimationEvent(node) {
if (
!node ||
!node.getAttribute ||
node.getAttribute('disabled') ||
node.className.indexOf('disabled') >= 0) {
return
node.className.indexOf('disabled') >= 0
) {
return;
}
const onClick = (e) => {
const onClick = e => {
// Fix radio button click twice
if (e.target.tagName === 'INPUT' || isHidden(e.target)) {
return
return;
}
this.resetEffect(node)
this.resetEffect(node);
// Get wave color from target
const waveColor =
getComputedStyle(node).getPropertyValue('border-top-color') || // Firefox Compatible
getComputedStyle(node).getPropertyValue('border-color') ||
getComputedStyle(node).getPropertyValue('background-color')
this.clickWaveTimeoutId = window.setTimeout(() => this.onClick(node, waveColor), 0)
raf.cancel(this.animationStartId)
this.animationStart = true
getComputedStyle(node).getPropertyValue('background-color');
this.clickWaveTimeoutId = window.setTimeout(() => this.onClick(node, waveColor), 0);
raf.cancel(this.animationStartId);
this.animationStart = true;
// Render to trigger transition event cost 3 frames. Let's delay 10 frames to reset this.
this.animationStartId = raf(() => {
this.animationStart = false
}, 10)
}
node.addEventListener('click', onClick, true)
this.animationStart = false;
}, 10);
};
node.addEventListener('click', onClick, true);
return {
cancel: () => {
node.removeEventListener('click', onClick, true)
node.removeEventListener('click', onClick, true);
},
}
};
},
getAttributeName () {
const { insertExtraNode } = this.$props
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node'
getAttributeName() {
const { insertExtraNode } = this.$props;
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
},
resetEffect (node) {
resetEffect(node) {
if (!node || node === this.extraNode || !(node instanceof Element)) {
return
return;
}
const { insertExtraNode } = this.$props
const attributeName = this.getAttributeName()
node.removeAttribute(attributeName)
this.removeExtraStyleNode()
const { insertExtraNode } = this.$props;
const attributeName = this.getAttributeName();
node.removeAttribute(attributeName);
this.removeExtraStyleNode();
if (insertExtraNode && this.extraNode && node.contains(this.extraNode)) {
node.removeChild(this.extraNode)
node.removeChild(this.extraNode);
}
TransitionEvents.removeStartEventListener(node, this.onTransitionStart)
TransitionEvents.removeEndEventListener(node, this.onTransitionEnd)
TransitionEvents.removeStartEventListener(node, this.onTransitionStart);
TransitionEvents.removeEndEventListener(node, this.onTransitionEnd);
},
onTransitionStart (e) {
if (this.destroy) return
onTransitionStart(e) {
if (this.destroy) return;
const node = this.$el
const node = this.$el;
if (!e || e.target !== node) {
return
return;
}
if (!this.animationStart) {
this.resetEffect(node)
this.resetEffect(node);
}
},
onTransitionEnd (e) {
onTransitionEnd(e) {
if (!e || e.animationName !== 'fadeEffect') {
return
return;
}
this.resetEffect(e.target)
this.resetEffect(e.target);
},
removeExtraStyleNode () {
removeExtraStyleNode() {
if (styleForPesudo) {
styleForPesudo.innerHTML = ''
styleForPesudo.innerHTML = '';
}
},
},
render () {
return this.$slots.default && this.$slots.default[0]
render() {
return this.$slots.default && this.$slots.default[0];
},
}
};

View File

@ -1,3 +1,3 @@
import demoTest from '../../../tests/shared/demoTest'
import demoTest from '../../../tests/shared/demoTest';
demoTest('affix')
demoTest('affix');

View File

@ -1,116 +1,121 @@
import Affix from '..'
import Button from '../../button'
import { mount } from '@vue/test-utils'
const events = {}
import Affix from '..';
import Button from '../../button';
import { mount } from '@vue/test-utils';
const events = {};
const AffixMounter = {
props: ['offsetBottom', 'offsetTop'],
mounted () {
mounted() {
this.$refs.container.addEventListener = jest.fn().mockImplementation((event, cb) => {
events[event] = cb
})
events[event] = cb;
});
},
methods: {
getTarget () {
return this.$refs.container
getTarget() {
return this.$refs.container;
},
},
render () {
render() {
return (
<div
style={{
height: '100px',
overflowY: 'scroll',
}}
ref='container'
ref="container"
>
<div
className='background'
className="background"
style={{
paddingTop: '60px',
height: '300px',
}}
>
<Affix
target={() => this.$refs.container}
ref='affix'
{...{ props: this.$props }}
>
<Button type='primary' >
Fixed at the top of container
</Button>
<Affix target={() => this.$refs.container} ref="affix" {...{ props: this.$props }}>
<Button type="primary">Fixed at the top of container</Button>
</Affix>
</div>
</div>
)
);
},
}
};
describe('Affix Render', () => {
let wrapper
let wrapper;
beforeAll(() => {
document.body.innerHTML = ''
jest.useFakeTimers()
})
document.body.innerHTML = '';
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers()
})
const scrollTo = (top) => {
jest.useRealTimers();
});
const scrollTo = top => {
wrapper.vm.$refs.affix.$refs.fixedNode.parentNode.getBoundingClientRect = jest.fn(() => {
return {
bottom: 100, height: 28, left: 0, right: 0, top: 50 - top, width: 195,
}
})
wrapper.vm.$refs.container.scrollTop = top
bottom: 100,
height: 28,
left: 0,
right: 0,
top: 50 - top,
width: 195,
};
});
wrapper.vm.$refs.container.scrollTop = top;
events.scroll({
type: 'scroll',
})
jest.runAllTimers()
}
});
jest.runAllTimers();
};
it('Anchor render perfectly', () => {
wrapper = mount(AffixMounter, { attachToDocument: true })
jest.runAllTimers()
wrapper = mount(AffixMounter, { attachToDocument: true });
jest.runAllTimers();
scrollTo(0)
expect(wrapper.vm.$refs.affix.affixStyle).toBe(null)
scrollTo(0);
expect(wrapper.vm.$refs.affix.affixStyle).toBe(null);
scrollTo(100)
expect(wrapper.vm.$refs.affix.affixStyle).not.toBe(null)
scrollTo(100);
expect(wrapper.vm.$refs.affix.affixStyle).not.toBe(null);
scrollTo(0)
expect(wrapper.vm.$refs.affix.affixStyle).toBe(null)
})
scrollTo(0);
expect(wrapper.vm.$refs.affix.affixStyle).toBe(null);
});
it('support offsetBottom', () => {
wrapper = mount(AffixMounter, { attachToDocument: true, propsData: {
offsetBottom: 0,
}})
wrapper = mount(AffixMounter, {
attachToDocument: true,
propsData: {
offsetBottom: 0,
},
});
jest.runAllTimers()
jest.runAllTimers();
scrollTo(0)
expect(wrapper.vm.$refs.affix.affixStyle).not.toBe(null)
scrollTo(0);
expect(wrapper.vm.$refs.affix.affixStyle).not.toBe(null);
scrollTo(100)
expect(wrapper.vm.$refs.affix.affixStyle).toBe(null)
scrollTo(100);
expect(wrapper.vm.$refs.affix.affixStyle).toBe(null);
scrollTo(0)
expect(wrapper.vm.$refs.affix.affixStyle).not.toBe(null)
})
scrollTo(0);
expect(wrapper.vm.$refs.affix.affixStyle).not.toBe(null);
});
it('updatePosition when offsetTop changed', () => {
wrapper = mount(AffixMounter, { attachToDocument: true, propsData: {
offsetTop: 0,
}})
wrapper = mount(AffixMounter, {
attachToDocument: true,
propsData: {
offsetTop: 0,
},
});
jest.runAllTimers()
jest.runAllTimers();
scrollTo(100)
expect(wrapper.vm.$refs.affix.affixStyle.top).toBe('0px')
scrollTo(100);
expect(wrapper.vm.$refs.affix.affixStyle.top).toBe('0px');
wrapper.setProps({
offsetTop: 10,
})
jest.runAllTimers()
expect(wrapper.vm.$refs.affix.affixStyle.top).toBe('10px')
})
})
});
jest.runAllTimers();
expect(wrapper.vm.$refs.affix.affixStyle.top).toBe('10px');
});
});

View File

@ -1,9 +1,9 @@
<script>
import Basic from './basic'
import Onchange from './on-change'
import Target from './target'
import CN from '../index.zh-CN.md'
import US from '../index.en-US.md'
import Basic from './basic';
import Onchange from './on-change';
import Target from './target';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# Affix 固钉
将页面元素钉在可视范围
@ -17,7 +17,7 @@ const md = {
When user browses a long web page, some content need to stick to the viewport. This is common for menus and actions.
Please note that Affix should not cover other content on the page, especially when the size of the viewport is small.
## Examples `,
}
};
export default {
category: 'Components',
subtitle: '固钉',
@ -36,7 +36,7 @@ export default {
<US/>
</api>
</div>
)
);
},
}
};
</script>

View File

@ -1,42 +1,37 @@
import PropTypes from '../_util/vue-types';
import addEventListener from '../_util/Dom/addEventListener';
import classNames from 'classnames';
import shallowequal from 'shallowequal';
import omit from 'omit.js';
import getScroll from '../_util/getScroll';
import BaseMixin from '../_util/BaseMixin';
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
import PropTypes from '../_util/vue-types'
import addEventListener from '../_util/Dom/addEventListener'
import classNames from 'classnames'
import shallowequal from 'shallowequal'
import omit from 'omit.js'
import getScroll from '../_util/getScroll'
import BaseMixin from '../_util/BaseMixin'
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame'
function getTargetRect (target) {
return target !== window
? target.getBoundingClientRect()
: { top: 0, left: 0, bottom: 0 }
function getTargetRect(target) {
return target !== window ? target.getBoundingClientRect() : { top: 0, left: 0, bottom: 0 };
}
function getOffset (element, target) {
const elemRect = element.getBoundingClientRect()
const targetRect = getTargetRect(target)
function getOffset(element, target) {
const elemRect = element.getBoundingClientRect();
const targetRect = getTargetRect(target);
const scrollTop = getScroll(target, true)
const scrollLeft = getScroll(target, false)
const scrollTop = getScroll(target, true);
const scrollLeft = getScroll(target, false);
const docElem = window.document.body
const clientTop = docElem.clientTop || 0
const clientLeft = docElem.clientLeft || 0
const docElem = window.document.body;
const clientTop = docElem.clientTop || 0;
const clientLeft = docElem.clientLeft || 0;
return {
top: elemRect.top - targetRect.top +
scrollTop - clientTop,
left: elemRect.left - targetRect.left +
scrollLeft - clientLeft,
top: elemRect.top - targetRect.top + scrollTop - clientTop,
left: elemRect.left - targetRect.left + scrollLeft - clientLeft,
width: elemRect.width,
height: elemRect.height,
}
};
}
function getDefaultTarget () {
return typeof window !== 'undefined' ? window : null
function getDefaultTarget() {
return typeof window !== 'undefined' ? window : null;
}
// Affix
@ -53,222 +48,218 @@ const AffixProps = {
/** 设置 Affix 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 */
target: PropTypes.func,
prefixCls: PropTypes.string,
}
};
const Affix = {
name: 'AAffix',
props: AffixProps,
mixins: [BaseMixin],
data () {
this.events = [
'resize',
'scroll',
'touchstart',
'touchmove',
'touchend',
'pageshow',
'load',
]
this.eventHandlers = {}
data() {
this.events = ['resize', 'scroll', 'touchstart', 'touchmove', 'touchend', 'pageshow', 'load'];
this.eventHandlers = {};
return {
affixStyle: undefined,
placeholderStyle: undefined,
}
};
},
beforeMount () {
this.updatePosition = throttleByAnimationFrame(this.updatePosition)
beforeMount() {
this.updatePosition = throttleByAnimationFrame(this.updatePosition);
},
mounted () {
const target = this.target || getDefaultTarget
mounted() {
const target = this.target || getDefaultTarget;
// Wait for parent component ref has its value
this.timeout = setTimeout(() => {
this.setTargetEventListeners(target)
this.setTargetEventListeners(target);
// Mock Event object.
this.updatePosition({})
})
this.updatePosition({});
});
},
watch: {
target (val) {
this.clearEventListeners()
this.setTargetEventListeners(val)
target(val) {
this.clearEventListeners();
this.setTargetEventListeners(val);
// Mock Event object.
this.updatePosition({})
this.updatePosition({});
},
offsetTop (val) {
this.updatePosition({})
offsetTop(val) {
this.updatePosition({});
},
offsetBottom (val) {
this.updatePosition({})
offsetBottom(val) {
this.updatePosition({});
},
},
beforeDestroy () {
this.clearEventListeners()
clearTimeout(this.timeout)
this.updatePosition.cancel()
beforeDestroy() {
this.clearEventListeners();
clearTimeout(this.timeout);
this.updatePosition.cancel();
},
methods: {
setAffixStyle (e, affixStyle) {
const { target = getDefaultTarget } = this
const originalAffixStyle = this.affixStyle
const isWindow = target() === window
setAffixStyle(e, affixStyle) {
const { target = getDefaultTarget } = this;
const originalAffixStyle = this.affixStyle;
const isWindow = target() === window;
if (e.type === 'scroll' && originalAffixStyle && affixStyle && isWindow) {
return
return;
}
if (shallowequal(affixStyle, originalAffixStyle)) {
return
return;
}
this.setState({ affixStyle: affixStyle }, () => {
const affixed = !!this.affixStyle
if ((affixStyle && !originalAffixStyle) ||
(!affixStyle && originalAffixStyle)) {
this.$emit('change', affixed)
const affixed = !!this.affixStyle;
if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) {
this.$emit('change', affixed);
}
})
});
},
setPlaceholderStyle (placeholderStyle) {
const originalPlaceholderStyle = this.placeholderStyle
setPlaceholderStyle(placeholderStyle) {
const originalPlaceholderStyle = this.placeholderStyle;
if (shallowequal(placeholderStyle, originalPlaceholderStyle)) {
return
return;
}
this.setState({ placeholderStyle: placeholderStyle })
this.setState({ placeholderStyle: placeholderStyle });
},
syncPlaceholderStyle (e) {
const { affixStyle } = this
syncPlaceholderStyle(e) {
const { affixStyle } = this;
if (!affixStyle) {
return
return;
}
this.$refs.placeholderNode.style.cssText = ''
this.$refs.placeholderNode.style.cssText = '';
this.setAffixStyle(e, {
...affixStyle,
width: this.$refs.placeholderNode.offsetWidth + 'px',
})
});
this.setPlaceholderStyle({
width: this.$refs.placeholderNode.offsetWidth + 'px',
})
});
},
updatePosition (e) {
const { offsetBottom, offset, target = getDefaultTarget } = this
let { offsetTop } = this
const targetNode = target()
updatePosition(e) {
const { offsetBottom, offset, target = getDefaultTarget } = this;
let { offsetTop } = this;
const targetNode = target();
// Backwards support
// Fix: if offsetTop === 0, it will get undefined,
// if offsetBottom is type of number, offsetMode will be { top: false, ... }
offsetTop = typeof offsetTop === 'undefined' ? offset : offsetTop
const scrollTop = getScroll(targetNode, true)
const affixNode = this.$el
const elemOffset = getOffset(affixNode, targetNode)
offsetTop = typeof offsetTop === 'undefined' ? offset : offsetTop;
const scrollTop = getScroll(targetNode, true);
const affixNode = this.$el;
const elemOffset = getOffset(affixNode, targetNode);
const elemSize = {
width: this.$refs.fixedNode.offsetWidth,
height: this.$refs.fixedNode.offsetHeight,
}
};
const offsetMode = {
top: false,
bottom: false,
}
};
// Default to `offsetTop=0`.
if (typeof offsetTop !== 'number' && typeof offsetBottom !== 'number') {
offsetMode.top = true
offsetTop = 0
offsetMode.top = true;
offsetTop = 0;
} else {
offsetMode.top = typeof offsetTop === 'number'
offsetMode.bottom = typeof offsetBottom === 'number'
offsetMode.top = typeof offsetTop === 'number';
offsetMode.bottom = typeof offsetBottom === 'number';
}
const targetRect = getTargetRect(targetNode)
const targetInnerHeight =
targetNode.innerHeight || targetNode.clientHeight
const targetRect = getTargetRect(targetNode);
const targetInnerHeight = targetNode.innerHeight || targetNode.clientHeight;
if (scrollTop > elemOffset.top - offsetTop && offsetMode.top) {
// Fixed Top
const width = `${elemOffset.width}px`
const top = `${targetRect.top + offsetTop}px`
// Fixed Top
const width = `${elemOffset.width}px`;
const top = `${targetRect.top + offsetTop}px`;
this.setAffixStyle(e, {
position: 'fixed',
top,
left: `${targetRect.left + elemOffset.left}px`,
width,
})
});
this.setPlaceholderStyle({
width,
height: `${elemSize.height}px`,
})
});
} else if (
scrollTop < elemOffset.top + elemSize.height + offsetBottom - targetInnerHeight &&
offsetMode.bottom
) {
// Fixed Bottom
const targetBottomOffet = targetNode === window ? 0 : (window.innerHeight - targetRect.bottom)
const width = `${elemOffset.width}px`
// Fixed Bottom
const targetBottomOffet =
targetNode === window ? 0 : window.innerHeight - targetRect.bottom;
const width = `${elemOffset.width}px`;
this.setAffixStyle(e, {
position: 'fixed',
bottom: targetBottomOffet + offsetBottom + 'px',
left: targetRect.left + elemOffset.left + 'px',
width,
})
});
this.setPlaceholderStyle({
width,
height: elemOffset.height + 'px',
})
});
} else {
const { affixStyle } = this
if (e.type === 'resize' && affixStyle && affixStyle.position === 'fixed' && affixNode.offsetWidth) {
this.setAffixStyle(e, { ...affixStyle, width: affixNode.offsetWidth + 'px' })
const { affixStyle } = this;
if (
e.type === 'resize' &&
affixStyle &&
affixStyle.position === 'fixed' &&
affixNode.offsetWidth
) {
this.setAffixStyle(e, { ...affixStyle, width: affixNode.offsetWidth + 'px' });
} else {
this.setAffixStyle(e, null)
this.setAffixStyle(e, null);
}
this.setPlaceholderStyle(null)
this.setPlaceholderStyle(null);
}
if (e.type === 'resize') {
this.syncPlaceholderStyle(e)
this.syncPlaceholderStyle(e);
}
},
setTargetEventListeners (getTarget) {
const target = getTarget()
setTargetEventListeners(getTarget) {
const target = getTarget();
if (!target) {
return
return;
}
this.clearEventListeners()
this.clearEventListeners();
this.events.forEach(eventName => {
this.eventHandlers[eventName] = addEventListener(target, eventName, this.updatePosition)
})
this.eventHandlers[eventName] = addEventListener(target, eventName, this.updatePosition);
});
},
clearEventListeners () {
clearEventListeners() {
this.events.forEach(eventName => {
const handler = this.eventHandlers[eventName]
const handler = this.eventHandlers[eventName];
if (handler && handler.remove) {
handler.remove()
handler.remove();
}
})
});
},
},
render () {
const { prefixCls, affixStyle, placeholderStyle, $slots, $props } = this
render() {
const { prefixCls, affixStyle, placeholderStyle, $slots, $props } = this;
const className = classNames({
[prefixCls || 'ant-affix']: affixStyle,
})
});
const props = {
attrs: omit($props, ['prefixCls', 'offsetTop', 'offsetBottom', 'target']),
}
};
return (
<div {...props} style={placeholderStyle} ref='placeholderNode'>
<div class={className} ref='fixedNode' style={affixStyle}>
<div {...props} style={placeholderStyle} ref="placeholderNode">
<div class={className} ref="fixedNode" style={affixStyle}>
{$slots.default}
</div>
</div>
)
);
},
}
};
/* istanbul ignore next */
Affix.install = function (Vue) {
Vue.component(Affix.name, Affix)
}
Affix.install = function(Vue) {
Vue.component(Affix.name, Affix);
};
export default Affix
export default Affix;

View File

@ -1,3 +1,3 @@
import demoTest from '../../../tests/shared/demoTest'
import demoTest from '../../../tests/shared/demoTest';
demoTest('alert')
demoTest('alert');

View File

@ -1,69 +1,71 @@
import { mount } from '@vue/test-utils'
import Alert from '..'
import { mount } from '@vue/test-utils';
import Alert from '..';
describe('Alert', () => {
beforeAll(() => {
jest.useFakeTimers()
})
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers()
})
jest.useRealTimers();
});
it('could be closed', () => {
const onClose = jest.fn()
const afterClose = jest.fn()
const onClose = jest.fn();
const afterClose = jest.fn();
const wrapper = mount({
render () {
return <Alert
message='Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text'
type='warning'
closable
onClose={onClose}
afterClose={afterClose}
ref='alert'
/>
render() {
return (
<Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
closable
onClose={onClose}
afterClose={afterClose}
ref="alert"
/>
);
},
})
wrapper.find('.ant-alert-close-icon').trigger('click')
expect(onClose).toBeCalled()
jest.runAllTimers()
wrapper.vm.$refs.alert.animationEnd()
expect(afterClose).toBeCalled()
})
});
wrapper.find('.ant-alert-close-icon').trigger('click');
expect(onClose).toBeCalled();
jest.runAllTimers();
wrapper.vm.$refs.alert.animationEnd();
expect(afterClose).toBeCalled();
});
describe('data and aria props', () => {
it('sets data attributes on input', () => {
const wrapper = mount({
render () {
return <Alert data-test='test-id' data-id='12345' />
render() {
return <Alert data-test="test-id" data-id="12345" />;
},
})
const input = wrapper.find('.ant-alert').element
expect(input.getAttribute('data-test')).toBe('test-id')
expect(input.getAttribute('data-id')).toBe('12345')
})
});
const input = wrapper.find('.ant-alert').element;
expect(input.getAttribute('data-test')).toBe('test-id');
expect(input.getAttribute('data-id')).toBe('12345');
});
it('sets aria attributes on input', () => {
const wrapper = mount({
render () {
return <Alert aria-describedby='some-label' />
render() {
return <Alert aria-describedby="some-label" />;
},
})
});
const input = wrapper.find('.ant-alert').element
expect(input.getAttribute('aria-describedby')).toBe('some-label')
})
const input = wrapper.find('.ant-alert').element;
expect(input.getAttribute('aria-describedby')).toBe('some-label');
});
it('sets role attribute on input', () => {
const wrapper = mount({
render () {
return <Alert role='status' />
render() {
return <Alert role="status" />;
},
})
});
const input = wrapper.find('.ant-alert').element
expect(input.getAttribute('role')).toBe('status')
})
})
})
const input = wrapper.find('.ant-alert').element;
expect(input.getAttribute('role')).toBe('status');
});
});
});

View File

@ -1,15 +1,15 @@
<script>
import Banner from './banner'
import Basic from './basic'
import Closable from './closable'
import CloseText from './close-text'
import Description from './description'
import Icon from './icon'
import Style from './style'
import SmoothClosed from './smooth-closed'
import Banner from './banner';
import Basic from './basic';
import Closable from './closable';
import CloseText from './close-text';
import Description from './description';
import Icon from './icon';
import Style from './style';
import SmoothClosed from './smooth-closed';
import CN from '../index.zh-CN.md'
import US from '../index.en-US.md'
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# Alert 警告提示
警告提示展现需要关注的信息
@ -24,7 +24,7 @@ const md = {
- When you need a persistent static container which is closable by user actions.
## Examples
`,
}
};
export default {
category: 'Components',
subtitle: '警告提示',
@ -48,9 +48,9 @@ export default {
<US/>
</api>
</div>
)
);
},
}
};
</script>
<style>
#components-alert-demo .ant-alert {

View File

@ -1,12 +1,11 @@
import Icon from '../icon'
import classNames from 'classnames'
import BaseMixin from '../_util/BaseMixin'
import PropTypes from '../_util/vue-types'
import getTransitionProps from '../_util/getTransitionProps'
import { getComponentFromProp, isValidElement } from '../_util/props-util'
import { cloneElement } from '../_util/vnode'
function noop () { }
import Icon from '../icon';
import classNames from 'classnames';
import BaseMixin from '../_util/BaseMixin';
import PropTypes from '../_util/vue-types';
import getTransitionProps from '../_util/getTransitionProps';
import { getComponentFromProp, isValidElement } from '../_util/props-util';
import { cloneElement } from '../_util/vnode';
function noop() {}
export const AlertProps = {
/**
* Type of Alert styles, options:`success`, `info`, `warning`, `error`
@ -30,82 +29,82 @@ export const AlertProps = {
prefixCls: PropTypes.string,
banner: PropTypes.bool,
icon: PropTypes.any,
}
};
const Alert = {
props: AlertProps,
mixins: [BaseMixin],
name: 'AAlert',
data () {
data() {
return {
closing: true,
closed: false,
}
};
},
methods: {
handleClose (e) {
e.preventDefault()
const dom = this.$el
dom.style.height = `${dom.offsetHeight}px`
handleClose(e) {
e.preventDefault();
const dom = this.$el;
dom.style.height = `${dom.offsetHeight}px`;
// Magic code
// height
dom.style.height = `${dom.offsetHeight}px`
dom.style.height = `${dom.offsetHeight}px`;
this.setState({
closing: false,
})
this.$emit('close', e)
});
this.$emit('close', e);
},
animationEnd () {
animationEnd() {
this.setState({
closed: true,
closing: true,
})
this.afterClose()
});
this.afterClose();
},
},
render () {
const { prefixCls = 'ant-alert', banner, closing, closed } = this
let { closable, type, showIcon, iconType } = this
const closeText = getComponentFromProp(this, 'closeText')
const description = getComponentFromProp(this, 'description')
const message = getComponentFromProp(this, 'message')
const icon = getComponentFromProp(this, 'icon')
render() {
const { prefixCls = 'ant-alert', banner, closing, closed } = this;
let { closable, type, showIcon, iconType } = this;
const closeText = getComponentFromProp(this, 'closeText');
const description = getComponentFromProp(this, 'description');
const message = getComponentFromProp(this, 'message');
const icon = getComponentFromProp(this, 'icon');
// banner Icon
showIcon = banner && showIcon === undefined ? true : showIcon
showIcon = banner && showIcon === undefined ? true : showIcon;
// banner
type = banner && type === undefined ? 'warning' : type || 'info'
let iconTheme = 'filled'
type = banner && type === undefined ? 'warning' : type || 'info';
let iconTheme = 'filled';
// should we give a warning?
// warning(!iconType, `The property 'iconType' is deprecated. Use the property 'icon' instead.`);
if (!iconType) {
switch (type) {
case 'success':
iconType = 'check-circle'
break
iconType = 'check-circle';
break;
case 'info':
iconType = 'info-circle'
break
iconType = 'info-circle';
break;
case 'error':
iconType = 'close-circle'
break
iconType = 'close-circle';
break;
case 'warning':
iconType = 'exclamation-circle'
break
iconType = 'exclamation-circle';
break;
default:
iconType = 'default'
iconType = 'default';
}
// use outline icon in alert with description
if (description) {
iconTheme = 'outlined'
iconTheme = 'outlined';
}
}
// closeable when closeText is assigned
if (closeText) {
closable = true
closable = true;
}
const alertCls = classNames(prefixCls, {
@ -115,29 +114,27 @@ const Alert = {
[`${prefixCls}-no-icon`]: !showIcon,
[`${prefixCls}-banner`]: !!banner,
[`${prefixCls}-closable`]: closable,
})
});
const closeIcon = closable ? (
<a onClick={this.handleClose} class={`${prefixCls}-close-icon`}>
{closeText || <Icon type='close' />}
{closeText || <Icon type="close" />}
</a>
) : null
) : null;
const iconNode = icon && (
isValidElement(icon)
? cloneElement(
icon,
{
class: `${prefixCls}-icon`,
},
) : <span class={`${prefixCls}-icon`}>{icon}</span>) || (
<Icon class={`${prefixCls}-icon`} type={iconType} theme={iconTheme} />
)
const iconNode = (icon &&
(isValidElement(icon) ? (
cloneElement(icon, {
class: `${prefixCls}-icon`,
})
) : (
<span class={`${prefixCls}-icon`}>{icon}</span>
))) || <Icon class={`${prefixCls}-icon`} type={iconType} theme={iconTheme} />;
const transitionProps = getTransitionProps(`${prefixCls}-slide-up`, {
appear: false,
afterLeave: this.animationEnd,
})
});
return closed ? null : (
<transition {...transitionProps}>
<div v-show={closing} class={alertCls} data-show={closing}>
@ -147,14 +144,13 @@ const Alert = {
{closeIcon}
</div>
</transition>
)
);
},
}
};
/* istanbul ignore next */
Alert.install = function (Vue) {
Vue.component(Alert.name, Alert)
}
export default Alert
Alert.install = function(Vue) {
Vue.component(Alert.name, Alert);
};
export default Alert;

View File

@ -1,76 +1,78 @@
import PropTypes from '../_util/vue-types'
import classNames from 'classnames'
import addEventListener from '../_util/Dom/addEventListener'
import Affix from '../affix'
import getScroll from '../_util/getScroll'
import raf from 'raf'
import { initDefaultProps, getClass } from '../_util/props-util'
import BaseMixin from '../_util/BaseMixin'
import PropTypes from '../_util/vue-types';
import classNames from 'classnames';
import addEventListener from '../_util/Dom/addEventListener';
import Affix from '../affix';
import getScroll from '../_util/getScroll';
import raf from 'raf';
import { initDefaultProps, getClass } from '../_util/props-util';
import BaseMixin from '../_util/BaseMixin';
function getDefaultContainer () {
return window
function getDefaultContainer() {
return window;
}
function getOffsetTop (element, container) {
function getOffsetTop(element, container) {
if (!element) {
return 0
return 0;
}
if (!element.getClientRects().length) {
return 0
return 0;
}
const rect = element.getBoundingClientRect()
const rect = element.getBoundingClientRect();
if (rect.width || rect.height) {
if (container === window) {
container = element.ownerDocument.documentElement
return rect.top - container.clientTop
container = element.ownerDocument.documentElement;
return rect.top - container.clientTop;
}
return rect.top - container.getBoundingClientRect().top
return rect.top - container.getBoundingClientRect().top;
}
return rect.top
return rect.top;
}
function easeInOutCubic (t, b, c, d) {
const cc = c - b
t /= d / 2
function easeInOutCubic(t, b, c, d) {
const cc = c - b;
t /= d / 2;
if (t < 1) {
return cc / 2 * t * t * t + b
return (cc / 2) * t * t * t + b;
}
return cc / 2 * ((t -= 2) * t * t + 2) + b
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
}
const sharpMatcherRegx = /#([^#]+)$/
function scrollTo (href, offsetTop = 0, getContainer, callback = () => { }) {
const container = getContainer()
const scrollTop = getScroll(container, true)
const sharpLinkMatch = sharpMatcherRegx.exec(href)
if (!sharpLinkMatch) { return }
const targetElement = document.getElementById(sharpLinkMatch[1])
if (!targetElement) {
return
const sharpMatcherRegx = /#([^#]+)$/;
function scrollTo(href, offsetTop = 0, getContainer, callback = () => {}) {
const container = getContainer();
const scrollTop = getScroll(container, true);
const sharpLinkMatch = sharpMatcherRegx.exec(href);
if (!sharpLinkMatch) {
return;
}
const eleOffsetTop = getOffsetTop(targetElement, container)
const targetScrollTop = scrollTop + eleOffsetTop - offsetTop
const startTime = Date.now()
const targetElement = document.getElementById(sharpLinkMatch[1]);
if (!targetElement) {
return;
}
const eleOffsetTop = getOffsetTop(targetElement, container);
const targetScrollTop = scrollTop + eleOffsetTop - offsetTop;
const startTime = Date.now();
const frameFunc = () => {
const timestamp = Date.now()
const time = timestamp - startTime
const nextScrollTop = easeInOutCubic(time, scrollTop, targetScrollTop, 450)
const timestamp = Date.now();
const time = timestamp - startTime;
const nextScrollTop = easeInOutCubic(time, scrollTop, targetScrollTop, 450);
if (container === window) {
window.scrollTo(window.pageXOffset, nextScrollTop)
window.scrollTo(window.pageXOffset, nextScrollTop);
} else {
container.scrollTop = nextScrollTop
container.scrollTop = nextScrollTop;
}
if (time < 450) {
raf(frameFunc)
raf(frameFunc);
} else {
callback()
callback();
}
}
raf(frameFunc)
};
raf(frameFunc);
}
export const AnchorProps = {
@ -80,7 +82,7 @@ export const AnchorProps = {
affix: PropTypes.bool,
showInkInFixed: PropTypes.bool,
getContainer: PropTypes.func,
}
};
export default {
name: 'AAnchor',
@ -93,159 +95,152 @@ export default {
getContainer: getDefaultContainer,
}),
data () {
this.links = []
data() {
this.links = [];
return {
activeLink: null,
}
};
},
provide () {
provide() {
return {
antAnchor: {
registerLink: (link) => {
registerLink: link => {
if (!this.links.includes(link)) {
this.links.push(link)
this.links.push(link);
}
},
unregisterLink: (link) => {
const index = this.links.indexOf(link)
unregisterLink: link => {
const index = this.links.indexOf(link);
if (index !== -1) {
this.links.splice(index, 1)
this.links.splice(index, 1);
}
},
$data: this.$data,
scrollTo: this.handleScrollTo,
},
antAnchorContext: this,
}
};
},
mounted () {
mounted() {
this.$nextTick(() => {
const { getContainer } = this
this.scrollEvent = addEventListener(getContainer(), 'scroll', this.handleScroll)
this.handleScroll()
})
const { getContainer } = this;
this.scrollEvent = addEventListener(getContainer(), 'scroll', this.handleScroll);
this.handleScroll();
});
},
beforeDestroy () {
beforeDestroy() {
if (this.scrollEvent) {
this.scrollEvent.remove()
this.scrollEvent.remove();
}
},
updated () {
updated() {
this.$nextTick(() => {
this.updateInk()
})
this.updateInk();
});
},
methods: {
handleScroll () {
handleScroll() {
if (this.animating) {
return
return;
}
const { offsetTop, bounds } = this
const { offsetTop, bounds } = this;
this.setState({
activeLink: this.getCurrentAnchor(offsetTop, bounds),
})
});
},
handleScrollTo (link) {
const { offsetTop, getContainer } = this
this.animating = true
this.setState({ activeLink: link })
handleScrollTo(link) {
const { offsetTop, getContainer } = this;
this.animating = true;
this.setState({ activeLink: link });
scrollTo(link, offsetTop, getContainer, () => {
this.animating = false
})
this.animating = false;
});
},
getCurrentAnchor (offsetTop = 0, bounds = 5) {
const activeLink = ''
getCurrentAnchor(offsetTop = 0, bounds = 5) {
const activeLink = '';
if (typeof document === 'undefined') {
return activeLink
return activeLink;
}
const linkSections = []
const { getContainer } = this
const container = getContainer()
const linkSections = [];
const { getContainer } = this;
const container = getContainer();
this.links.forEach(link => {
const sharpLinkMatch = sharpMatcherRegx.exec(link.toString())
if (!sharpLinkMatch) { return }
const target = document.getElementById(sharpLinkMatch[1])
const sharpLinkMatch = sharpMatcherRegx.exec(link.toString());
if (!sharpLinkMatch) {
return;
}
const target = document.getElementById(sharpLinkMatch[1]);
if (target) {
const top = getOffsetTop(target, container)
const top = getOffsetTop(target, container);
if (top < offsetTop + bounds) {
linkSections.push({
link,
top,
})
});
}
}
})
});
if (linkSections.length) {
const maxSection = linkSections.reduce((prev, curr) => curr.top > prev.top ? curr : prev)
return maxSection.link
const maxSection = linkSections.reduce((prev, curr) => (curr.top > prev.top ? curr : prev));
return maxSection.link;
}
return ''
return '';
},
updateInk () {
updateInk() {
if (typeof document === 'undefined') {
return
return;
}
const { prefixCls } = this
const linkNode = this.$el.getElementsByClassName(`${prefixCls}-link-title-active`)[0]
const { prefixCls } = this;
const linkNode = this.$el.getElementsByClassName(`${prefixCls}-link-title-active`)[0];
if (linkNode) {
this.$refs.linkNode.style.top = `${(linkNode).offsetTop + linkNode.clientHeight / 2 - 4.5}px`
this.$refs.linkNode.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2 - 4.5}px`;
}
},
},
render () {
const {
prefixCls,
offsetTop,
affix,
showInkInFixed,
activeLink,
$slots,
getContainer,
} = this
render() {
const { prefixCls, offsetTop, affix, showInkInFixed, activeLink, $slots, getContainer } = this;
const inkClass = classNames(`${prefixCls}-ink-ball`, {
visible: activeLink,
})
});
const wrapperClass = classNames(getClass(this), `${prefixCls}-wrapper`)
const wrapperClass = classNames(getClass(this), `${prefixCls}-wrapper`);
const anchorClass = classNames(prefixCls, {
'fixed': !affix && !showInkInFixed,
})
fixed: !affix && !showInkInFixed,
});
const wrapperStyle = {
maxHeight: offsetTop ? `calc(100vh - ${offsetTop}px)` : '100vh',
// ...getStyle(this, true),
}
};
const anchorContent = (
<div
class={wrapperClass}
style={wrapperStyle}
>
<div class={wrapperClass} style={wrapperStyle}>
<div class={anchorClass}>
<div class={`${prefixCls}-ink`} >
<span class={inkClass} ref='linkNode' />
<div class={`${prefixCls}-ink`}>
<span class={inkClass} ref="linkNode" />
</div>
{$slots.default}
</div>
</div>
)
);
return !affix ? anchorContent : (
return !affix ? (
anchorContent
) : (
<Affix offsetTop={offsetTop} target={getContainer}>
{anchorContent}
</Affix>
)
);
},
}
};

View File

@ -1,12 +1,12 @@
import PropTypes from '../_util/vue-types'
import { initDefaultProps, getComponentFromProp } from '../_util/props-util'
import classNames from 'classnames'
import PropTypes from '../_util/vue-types';
import { initDefaultProps, getComponentFromProp } from '../_util/props-util';
import classNames from 'classnames';
export const AnchorLinkProps = {
prefixCls: PropTypes.string,
href: PropTypes.string,
title: PropTypes.any,
}
};
export default {
name: 'AAnchorLink',
@ -15,48 +15,44 @@ export default {
href: '#',
}),
inject: {
antAnchor: { default: {}},
antAnchorContext: { default: {}},
antAnchor: { default: {} },
antAnchorContext: { default: {} },
},
mounted () {
this.antAnchor.registerLink(this.href)
mounted() {
this.antAnchor.registerLink(this.href);
},
beforeDestroy () {
this.antAnchor.unregisterLink(this.href)
beforeDestroy() {
this.antAnchor.unregisterLink(this.href);
},
watch: {
href (val, oldVal) {
this.antAnchor.unregisterLink(oldVal)
this.antAnchor.registerLink(val)
href(val, oldVal) {
this.antAnchor.unregisterLink(oldVal);
this.antAnchor.registerLink(val);
},
},
methods: {
handleClick (e) {
this.antAnchor.scrollTo(this.href)
const { scrollTo } = this.antAnchor
const { href, title } = this.$props
handleClick(e) {
this.antAnchor.scrollTo(this.href);
const { scrollTo } = this.antAnchor;
const { href, title } = this.$props;
if (this.antAnchorContext.$emit) {
this.antAnchorContext.$emit('click', e, { title, href })
this.antAnchorContext.$emit('click', e, { title, href });
}
scrollTo(href)
scrollTo(href);
},
},
render () {
const {
prefixCls,
href,
$slots,
} = this
const title = getComponentFromProp(this, 'title')
const active = this.antAnchor.$data.activeLink === href
render() {
const { prefixCls, href, $slots } = this;
const title = getComponentFromProp(this, 'title');
const active = this.antAnchor.$data.activeLink === href;
const wrapperClassName = classNames(`${prefixCls}-link`, {
[`${prefixCls}-link-active`]: active,
})
});
const titleClassName = classNames(`${prefixCls}-link-title`, {
[`${prefixCls}-link-title-active`]: active,
})
});
return (
<div class={wrapperClassName}>
<a
@ -69,6 +65,6 @@ export default {
</a>
{$slots.default}
</div>
)
);
},
}
};

View File

@ -1,181 +1,201 @@
import { mount } from '@vue/test-utils'
import Vue from 'vue'
import { asyncExpect } from '@/tests/utils'
import Anchor from '..'
import { mount } from '@vue/test-utils';
import Vue from 'vue';
import { asyncExpect } from '@/tests/utils';
import Anchor from '..';
const { Link } = Anchor
const { Link } = Anchor;
describe('Anchor Render', () => {
it('Anchor render perfectly', (done) => {
const wrapper = mount({
render () {
return (
<Anchor ref='anchor'>
<Link href='#API' title='API' />
</Anchor>
)
},
}, { sync: false })
Vue.nextTick(() => {
wrapper.find('a[href="#API"]').trigger('click')
wrapper.vm.$refs.anchor.handleScroll()
setTimeout(() => {
expect(wrapper.vm.$refs.anchor.$data.activeLink).not.toBe(null)
done()
}, 1000)
})
})
it('Anchor render perfectly for complete href - click', (done) => {
const wrapper = mount({
render () {
return (
<Anchor ref='anchor'>
<Link href='http://www.example.com/#API' title='API' />
</Anchor>
)
},
}, { sync: false })
Vue.nextTick(() => {
wrapper.find('a[href="http://www.example.com/#API"]').trigger('click')
expect(wrapper.vm.$refs.anchor.$data.activeLink).toBe('http://www.example.com/#API')
done()
})
})
it('Anchor render perfectly for complete href - scroll', (done) => {
const wrapper = mount({
render () {
return (
<div>
<div id='API'>Hello</div>
<Anchor ref='anchor'>
<Link href='http://www.example.com/#API' title='API' />
</Anchor>
</div>
)
},
}, { sync: false, attachToDocument: true })
Vue.nextTick(() => {
wrapper.vm.$refs.anchor.handleScroll()
expect(wrapper.vm.$refs.anchor.$data.activeLink).toBe('http://www.example.com/#API')
done()
})
})
it('Anchor render perfectly for complete href - scrollTo', async () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo')
const wrapper = mount({
render () {
return (
<div>
<div id='API'>Hello</div>
<Anchor ref='anchor'>
<Link href='##API' title='API' />
</Anchor>
</div>
)
},
}, { sync: false, attachToDocument: true })
await asyncExpect(() => {
wrapper.vm.$refs.anchor.handleScrollTo('##API')
expect(wrapper.vm.$refs.anchor.$data.activeLink).toBe('##API')
expect(scrollToSpy).not.toHaveBeenCalled()
})
await asyncExpect(() => {
expect(scrollToSpy).toHaveBeenCalled()
}, 1000)
})
it('should remove listener when unmount', async () => {
const wrapper = mount({
render () {
return (
<Anchor ref='anchor'>
<Link href='#API' title='API' />
</Anchor>
)
},
}, { sync: false, attachToDocument: true })
await asyncExpect(() => {
const removeListenerSpy = jest.spyOn(wrapper.vm.$refs.anchor.scrollEvent, 'remove')
wrapper.destroy()
expect(removeListenerSpy).toHaveBeenCalled()
})
})
it('should unregister link when unmount children', async () => {
const wrapper = mount({
props: {
showLink: {
type: Boolean,
default: true,
},
},
render () {
return (
<Anchor ref='anchor'>
{this.showLink ? <Link href='#API' title='API' /> : null}
</Anchor>
)
},
}, { sync: false, attachToDocument: true })
await asyncExpect(() => {
expect(wrapper.vm.$refs.anchor.links).toEqual(['#API'])
wrapper.setProps({ showLink: false })
})
await asyncExpect(() => {
expect(wrapper.vm.$refs.anchor.links).toEqual([])
})
})
it('should update links when link href update', async () => {
const wrapper = mount({
props: ['href'],
render () {
return <Anchor ref='anchor'>
<Link href={this.href} title='API' />
</Anchor>
},
}, {
sync: false,
attachToDocument: true,
propsData: {
href: '#API',
}})
await asyncExpect(() => {
expect(wrapper.vm.$refs.anchor.links).toEqual(['#API'])
wrapper.setProps({ href: '#API_1' })
})
await asyncExpect(() => {
expect(wrapper.vm.$refs.anchor.links).toEqual(['#API_1'])
})
})
it('Anchor onClick event', () => {
let event
let link
const handleClick = (...arg) => ([event, link] = arg)
const href = '#API'
const title = 'API'
it('Anchor render perfectly', done => {
const wrapper = mount(
{
render () {
render() {
return (
<Anchor ref='anchorRef' onClick={handleClick}>
<Link href={href} title={title} />
<Anchor ref="anchor">
<Link href="#API" title="API" />
</Anchor>
)
);
},
}
)
},
{ sync: false },
);
Vue.nextTick(() => {
wrapper.find('a[href="#API"]').trigger('click');
wrapper.vm.$refs.anchor.handleScroll();
setTimeout(() => {
expect(wrapper.vm.$refs.anchor.$data.activeLink).not.toBe(null);
done();
}, 1000);
});
});
wrapper.find(`a[href="${href}"]`).trigger('click')
it('Anchor render perfectly for complete href - click', done => {
const wrapper = mount(
{
render() {
return (
<Anchor ref="anchor">
<Link href="http://www.example.com/#API" title="API" />
</Anchor>
);
},
},
{ sync: false },
);
Vue.nextTick(() => {
wrapper.find('a[href="http://www.example.com/#API"]').trigger('click');
expect(wrapper.vm.$refs.anchor.$data.activeLink).toBe('http://www.example.com/#API');
done();
});
});
wrapper.vm.$refs.anchorRef.handleScroll()
expect(event).not.toBe(undefined)
expect(link).toEqual({ href, title })
})
})
it('Anchor render perfectly for complete href - scroll', done => {
const wrapper = mount(
{
render() {
return (
<div>
<div id="API">Hello</div>
<Anchor ref="anchor">
<Link href="http://www.example.com/#API" title="API" />
</Anchor>
</div>
);
},
},
{ sync: false, attachToDocument: true },
);
Vue.nextTick(() => {
wrapper.vm.$refs.anchor.handleScroll();
expect(wrapper.vm.$refs.anchor.$data.activeLink).toBe('http://www.example.com/#API');
done();
});
});
it('Anchor render perfectly for complete href - scrollTo', async () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo');
const wrapper = mount(
{
render() {
return (
<div>
<div id="API">Hello</div>
<Anchor ref="anchor">
<Link href="##API" title="API" />
</Anchor>
</div>
);
},
},
{ sync: false, attachToDocument: true },
);
await asyncExpect(() => {
wrapper.vm.$refs.anchor.handleScrollTo('##API');
expect(wrapper.vm.$refs.anchor.$data.activeLink).toBe('##API');
expect(scrollToSpy).not.toHaveBeenCalled();
});
await asyncExpect(() => {
expect(scrollToSpy).toHaveBeenCalled();
}, 1000);
});
it('should remove listener when unmount', async () => {
const wrapper = mount(
{
render() {
return (
<Anchor ref="anchor">
<Link href="#API" title="API" />
</Anchor>
);
},
},
{ sync: false, attachToDocument: true },
);
await asyncExpect(() => {
const removeListenerSpy = jest.spyOn(wrapper.vm.$refs.anchor.scrollEvent, 'remove');
wrapper.destroy();
expect(removeListenerSpy).toHaveBeenCalled();
});
});
it('should unregister link when unmount children', async () => {
const wrapper = mount(
{
props: {
showLink: {
type: Boolean,
default: true,
},
},
render() {
return (
<Anchor ref="anchor">{this.showLink ? <Link href="#API" title="API" /> : null}</Anchor>
);
},
},
{ sync: false, attachToDocument: true },
);
await asyncExpect(() => {
expect(wrapper.vm.$refs.anchor.links).toEqual(['#API']);
wrapper.setProps({ showLink: false });
});
await asyncExpect(() => {
expect(wrapper.vm.$refs.anchor.links).toEqual([]);
});
});
it('should update links when link href update', async () => {
const wrapper = mount(
{
props: ['href'],
render() {
return (
<Anchor ref="anchor">
<Link href={this.href} title="API" />
</Anchor>
);
},
},
{
sync: false,
attachToDocument: true,
propsData: {
href: '#API',
},
},
);
await asyncExpect(() => {
expect(wrapper.vm.$refs.anchor.links).toEqual(['#API']);
wrapper.setProps({ href: '#API_1' });
});
await asyncExpect(() => {
expect(wrapper.vm.$refs.anchor.links).toEqual(['#API_1']);
});
});
it('Anchor onClick event', () => {
let event;
let link;
const handleClick = (...arg) => ([event, link] = arg);
const href = '#API';
const title = 'API';
const wrapper = mount({
render() {
return (
<Anchor ref="anchorRef" onClick={handleClick}>
<Link href={href} title={title} />
</Anchor>
);
},
});
wrapper.find(`a[href="${href}"]`).trigger('click');
wrapper.vm.$refs.anchorRef.handleScroll();
expect(event).not.toBe(undefined);
expect(link).toEqual({ href, title });
});
});

View File

@ -1,3 +1,3 @@
import demoTest from '../../../tests/shared/demoTest'
import demoTest from '../../../tests/shared/demoTest';
demoTest('anchor')
demoTest('anchor');

View File

@ -1,10 +1,10 @@
<script>
import Basic from './basic'
import Static from './static'
import OnClick from './onClick'
import Basic from './basic';
import Static from './static';
import OnClick from './onClick';
import CN from '../index.zh-CN.md'
import US from '../index.en-US.md'
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# Anchor 锚点
用于跳转到页面指定位置
@ -22,7 +22,7 @@ Hyperlinks to scroll on one page.
For displaying anchor hyperlinks on page and jumping between them.
## Examples
`,
}
};
export default {
category: 'Components',
subtitle: '锚点',
@ -42,9 +42,9 @@ export default {
<US/>
</api>
</div>
)
);
},
}
};
</script>
<style>
#components-anchor-demo .ant-affix {

View File

@ -1,15 +1,15 @@
import Anchor from './Anchor'
import AnchorLink from './AnchorLink'
import Anchor from './Anchor';
import AnchorLink from './AnchorLink';
export { AnchorProps } from './Anchor'
export { AnchorLinkProps } from './AnchorLink'
export { AnchorProps } from './Anchor';
export { AnchorLinkProps } from './AnchorLink';
Anchor.Link = AnchorLink
Anchor.Link = AnchorLink;
/* istanbul ignore next */
Anchor.install = function (Vue) {
Vue.component(Anchor.name, Anchor)
Vue.component(Anchor.Link.name, Anchor.Link)
}
Anchor.install = function(Vue) {
Vue.component(Anchor.name, Anchor);
Vue.component(Anchor.Link.name, Anchor.Link);
};
export default Anchor
export default Anchor;

View File

@ -1,15 +1,15 @@
import PropTypes from '../_util/vue-types'
import { cloneElement } from '../_util/vnode'
function chaining (...fns) {
return function (...args) { // eslint-disable-line
import PropTypes from '../_util/vue-types';
import { cloneElement } from '../_util/vnode';
function chaining(...fns) {
return function(...args) {
// eslint-disable-line
// eslint-disable-line
for (let i = 0; i < fns.length; i++) {
if (fns[i] && typeof fns[i] === 'function') {
fns[i].apply(this, args)
fns[i].apply(this, args);
}
}
}
};
}
export default {
props: {
@ -17,27 +17,26 @@ export default {
disabled: PropTypes.bool,
},
methods: {
focus () {
const ele = this.$refs.ele
ele.focus ? ele.focus() : this.$el.focus()
focus() {
const ele = this.$refs.ele;
ele.focus ? ele.focus() : this.$el.focus();
},
blur () {
const ele = this.$refs.ele
ele.blur ? ele.blur() : this.$el.blur()
blur() {
const ele = this.$refs.ele;
ele.blur ? ele.blur() : this.$el.blur();
},
},
render () {
const { $slots = {}, $listeners = {}, $props = {}, $attrs = {}} = this
const value = $props.value === undefined ? '' : $props.value
const children = $slots.default[0]
const { componentOptions = {}} = $slots.default[0]
const { listeners = {}} = componentOptions
const newEvent = { ...listeners }
render() {
const { $slots = {}, $listeners = {}, $props = {}, $attrs = {} } = this;
const value = $props.value === undefined ? '' : $props.value;
const children = $slots.default[0];
const { componentOptions = {} } = $slots.default[0];
const { listeners = {} } = componentOptions;
const newEvent = { ...listeners };
for (const [eventName, event] of Object.entries($listeners)) {
newEvent[eventName] = chaining(event, listeners[eventName])
newEvent[eventName] = chaining(event, listeners[eventName]);
}
return cloneElement(children, {
@ -48,7 +47,6 @@ export default {
on: newEvent,
attrs: { ...$attrs, value },
ref: 'ele',
})
});
},
}
};

View File

@ -1,3 +1,3 @@
import demoTest from '../../../tests/shared/demoTest'
import demoTest from '../../../tests/shared/demoTest';
demoTest('auto-complete')
demoTest('auto-complete');

View File

@ -1,35 +1,43 @@
import { mount } from '@vue/test-utils'
import Vue from 'vue'
import AutoComplete from '..'
import focusTest from '../../../tests/shared/focusTest'
import { mount } from '@vue/test-utils';
import Vue from 'vue';
import AutoComplete from '..';
import focusTest from '../../../tests/shared/focusTest';
describe('AutoComplete with Custom Input Element Render', () => {
focusTest(AutoComplete)
function $$ (className) {
return document.body.querySelectorAll(className)
focusTest(AutoComplete);
function $$(className) {
return document.body.querySelectorAll(className);
}
it('AutoComplete with custom Input render perfectly', (done) => {
const wrapper = mount({
render (h) {
return <AutoComplete ref='component' dataSource={['12345', '23456', '34567']}>
<input />
</AutoComplete>
},
}, { attachToDocument: true, sync: false })
expect(wrapper.findAll('input').length).toBe(1)
const input = wrapper.find('input')
input.element.value = '123'
input.trigger('input')
Vue.nextTick(() => {
mount({
render () {
return wrapper.find({ name: 'Trigger' }).vm.getComponent()
it('AutoComplete with custom Input render perfectly', done => {
const wrapper = mount(
{
render(h) {
return (
<AutoComplete ref="component" dataSource={['12345', '23456', '34567']}>
<input />
</AutoComplete>
);
},
}, { sync: false })
},
{ attachToDocument: true, sync: false },
);
expect(wrapper.findAll('input').length).toBe(1);
const input = wrapper.find('input');
input.element.value = '123';
input.trigger('input');
Vue.nextTick(() => {
mount(
{
render() {
return wrapper.find({ name: 'Trigger' }).vm.getComponent();
},
},
{ sync: false },
);
Vue.nextTick(() => {
expect($$('.ant-select-dropdown-menu-item').length).toBe(3)
done()
})
})
})
})
expect($$('.ant-select-dropdown-menu-item').length).toBe(3);
done();
});
});
});
});

View File

@ -1,13 +1,13 @@
<script>
import Basic from './basic'
import CertainCategory from './certain-category'
import Custom from './custom'
import NonCaseSensitive from './non-case-sensitive'
import Options from './options'
import UncertainCategory from './uncertain-category'
import Basic from './basic';
import CertainCategory from './certain-category';
import Custom from './custom';
import NonCaseSensitive from './non-case-sensitive';
import Options from './options';
import UncertainCategory from './uncertain-category';
import CN from '../index.zh-CN.md'
import US from '../index.en-US.md'
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# AutoComplete 自动完成
输入框自动完成功能
@ -20,7 +20,7 @@ const md = {
When there is a need for autocomplete functionality.
## Examples
`,
}
};
export default {
category: 'Components',
subtitle: '自动完成',
@ -43,8 +43,8 @@ export default {
<US/>
</api>
</div>
)
);
},
}
};
</script>

View File

@ -1,10 +1,14 @@
import { Option, OptGroup } from '../vc-select'
import Select, { AbstractSelectProps, SelectValue } from '../select'
import Input from '../input'
import InputElement from './InputElement'
import PropTypes from '../_util/vue-types'
import { getComponentFromProp, getOptionProps, filterEmpty, isValidElement } from '../_util/props-util'
import { Option, OptGroup } from '../vc-select';
import Select, { AbstractSelectProps, SelectValue } from '../select';
import Input from '../input';
import InputElement from './InputElement';
import PropTypes from '../_util/vue-types';
import {
getComponentFromProp,
getOptionProps,
filterEmpty,
isValidElement,
} from '../_util/props-util';
// const DataSourceItemObject = PropTypes.shape({
// value: String,
@ -29,7 +33,7 @@ const AutoCompleteProps = {
dropdownMatchSelectWidth: PropTypes.bool,
// onChange?: (value: SelectValue) => void;
// onSelect?: (value: SelectValue, option: Object) => any;
}
};
const AutoComplete = {
name: 'AAutoComplete',
@ -42,10 +46,7 @@ const AutoComplete = {
autoFocus: PropTypes.bool,
backfill: PropTypes.bool,
optionLabelProp: PropTypes.string.def('children'),
filterOption: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.func,
]).def(false),
filterOption: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]).def(false),
defaultActiveFirstOption: PropTypes.bool.def(true),
},
Option: { ...Option, name: 'AAutoCompleteOption' },
@ -55,62 +56,58 @@ const AutoComplete = {
event: 'change',
},
methods: {
getInputElement () {
const { $slots } = this
const children = filterEmpty($slots.default)
const element = children.length ? children[0] : <Input />
return (
<InputElement>{element}</InputElement>
)
getInputElement() {
const { $slots } = this;
const children = filterEmpty($slots.default);
const element = children.length ? children[0] : <Input />;
return <InputElement>{element}</InputElement>;
},
focus () {
focus() {
if (this.$refs.select) {
this.$refs.select.focus()
this.$refs.select.focus();
}
},
blur () {
blur() {
if (this.$refs.select) {
this.$refs.select.blur()
this.$refs.select.blur();
}
},
},
render () {
const {
size, prefixCls, optionLabelProp, dataSource, $slots, $listeners,
} = this
render() {
const { size, prefixCls, optionLabelProp, dataSource, $slots, $listeners } = this;
const cls = {
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-sm`]: size === 'small',
[`${prefixCls}-show-search`]: true,
[`${prefixCls}-auto-complete`]: true,
}
};
let options
const childArray = filterEmpty($slots.dataSource)
let options;
const childArray = filterEmpty($slots.dataSource);
if (childArray.length) {
options = childArray
options = childArray;
} else {
options = dataSource ? dataSource.map((item) => {
if (isValidElement(item)) {
return item
}
switch (typeof item) {
case 'string':
return <Option key={item}>{item}</Option>
case 'object':
return (
<Option key={item.value}>
{item.text}
</Option>
)
default:
throw new Error('AutoComplete[dataSource] only supports type `string[] | Object[]`.')
}
}) : []
options = dataSource
? dataSource.map(item => {
if (isValidElement(item)) {
return item;
}
switch (typeof item) {
case 'string':
return <Option key={item}>{item}</Option>;
case 'object':
return <Option key={item.value}>{item.text}</Option>;
default:
throw new Error(
'AutoComplete[dataSource] only supports type `string[] | Object[]`.',
);
}
})
: [];
}
const selectProps = {
props: {
@ -123,23 +120,16 @@ const AutoComplete = {
class: cls,
ref: 'select',
on: $listeners,
}
return (
<Select
{...selectProps}
>
{options}
</Select>
)
};
return <Select {...selectProps}>{options}</Select>;
},
}
};
/* istanbul ignore next */
AutoComplete.install = function (Vue) {
Vue.component(AutoComplete.name, AutoComplete)
Vue.component(AutoComplete.Option.name, AutoComplete.Option)
Vue.component(AutoComplete.OptGroup.name, AutoComplete.OptGroup)
}
export default AutoComplete
AutoComplete.install = function(Vue) {
Vue.component(AutoComplete.name, AutoComplete);
Vue.component(AutoComplete.Option.name, AutoComplete.Option);
Vue.component(AutoComplete.OptGroup.name, AutoComplete.OptGroup);
};
export default AutoComplete;

View File

@ -1,5 +1,4 @@
import Icon from '../icon'
import Icon from '../icon';
export default {
name: 'AAvatar',
@ -9,12 +8,12 @@ export default {
default: 'ant-avatar',
},
shape: {
validator: (val) => (['circle', 'square'].includes(val)),
validator: val => ['circle', 'square'].includes(val),
default: 'circle',
},
size: {
validator: (val) => {
return typeof val === 'number' || ['small', 'large', 'default'].includes(val)
validator: val => {
return typeof val === 'number' || ['small', 'large', 'default'].includes(val);
},
default: 'default',
},
@ -25,63 +24,63 @@ export default {
alt: String,
loadError: Function,
},
data () {
data() {
return {
isImgExist: true,
scale: 1,
}
};
},
mounted () {
this.prevChildren = this.$slots.default
this.prevState = { ...this.$data }
mounted() {
this.prevChildren = this.$slots.default;
this.prevState = { ...this.$data };
this.$nextTick(() => {
this.setScale()
})
this.setScale();
});
},
updated () {
if (this.preChildren !== this.$slots.default ||
updated() {
if (
this.preChildren !== this.$slots.default ||
(this.prevState.scale !== this.$data.scale && this.$data.scale === 1) ||
(this.prevState.isImgExist !== this.$data.isImgExist)) {
this.prevState.isImgExist !== this.$data.isImgExist
) {
this.$nextTick(() => {
this.setScale()
})
this.setScale();
});
}
this.preChildren = this.$slots.default
this.prevState = { ...this.$data }
this.preChildren = this.$slots.default;
this.prevState = { ...this.$data };
},
methods: {
setScale () {
const childrenNode = this.$refs.avatarChildren
setScale() {
const childrenNode = this.$refs.avatarChildren;
if (childrenNode) {
const childrenWidth = childrenNode.offsetWidth
const avatarWidth = this.$el.getBoundingClientRect().width
const childrenWidth = childrenNode.offsetWidth;
const avatarWidth = this.$el.getBoundingClientRect().width;
if (avatarWidth - 8 < childrenWidth) {
this.scale = (avatarWidth - 8) / childrenWidth
this.scale = (avatarWidth - 8) / childrenWidth;
} else {
this.scale = 1
this.scale = 1;
}
this.$forceUpdate()
this.$forceUpdate();
}
},
handleImgLoadError () {
const { loadError } = this.$props
const errorFlag = loadError ? loadError() : undefined
handleImgLoadError() {
const { loadError } = this.$props;
const errorFlag = loadError ? loadError() : undefined;
if (errorFlag !== false) {
this.isImgExist = false
this.isImgExist = false;
}
},
},
render () {
const {
prefixCls, shape, size, src, icon, alt, srcSet,
} = this.$props
render() {
const { prefixCls, shape, size, src, icon, alt, srcSet } = this.$props;
const { isImgExist, scale } = this.$data
const { isImgExist, scale } = this.$data;
const sizeCls = {
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-sm`]: size === 'small',
}
};
const classString = {
[prefixCls]: true,
@ -89,64 +88,57 @@ export default {
[`${prefixCls}-${shape}`]: shape,
[`${prefixCls}-image`]: src && isImgExist,
[`${prefixCls}-icon`]: icon,
}
};
const sizeStyle = typeof size === 'number' ? {
width: `${size}px`,
height: `${size}px`,
lineHeight: `${size}px`,
fontSize: icon ? `${size / 2}px` : '18px',
} : {}
const sizeStyle =
typeof size === 'number'
? {
width: `${size}px`,
height: `${size}px`,
lineHeight: `${size}px`,
fontSize: icon ? `${size / 2}px` : '18px',
}
: {};
let children = this.$slots.default
let children = this.$slots.default;
if (src && isImgExist) {
children = (
<img
src={src}
srcSet={srcSet}
onError={this.handleImgLoadError}
alt={alt}
/>
)
children = <img src={src} srcSet={srcSet} onError={this.handleImgLoadError} alt={alt} />;
} else if (icon) {
children = <Icon type={icon} />
children = <Icon type={icon} />;
} else {
const childrenNode = this.$refs.avatarChildren
const childrenNode = this.$refs.avatarChildren;
if (childrenNode || (scale !== 1 && childrenNode)) {
const transformString = `scale(${scale}) translateX(-50%)`
const transformString = `scale(${scale}) translateX(-50%)`;
const childrenStyle = {
msTransform: transformString,
WebkitTransform: transformString,
transform: transformString,
}
const sizeChildrenStyle = typeof size === 'number' ? {
lineHeight: `${size}px`,
} : {}
};
const sizeChildrenStyle =
typeof size === 'number'
? {
lineHeight: `${size}px`,
}
: {};
children = (
<span
class={`${prefixCls}-string`}
ref='avatarChildren'
ref="avatarChildren"
style={{ ...sizeChildrenStyle, ...childrenStyle }}
>
{children}
</span>
)
);
} else {
children = (
<span
class={`${prefixCls}-string`}
ref='avatarChildren'
>
<span class={`${prefixCls}-string`} ref="avatarChildren">
{children}
</span>
)
);
}
}
return (
<span {...{ on: this.$listeners, class: classString, style: sizeStyle }}>
{children}
</span>
)
<span {...{ on: this.$listeners, class: classString, style: sizeStyle }}>{children}</span>
);
},
}
};

View File

@ -1,6 +1,6 @@
import { mount } from '@vue/test-utils'
import { asyncExpect } from '@/tests/utils'
import Avatar from '..'
import { mount } from '@vue/test-utils';
import { asyncExpect } from '@/tests/utils';
import Avatar from '..';
describe('Avatar Render', () => {
it('Render long string correctly', () => {
@ -8,12 +8,12 @@ describe('Avatar Render', () => {
slots: {
default: 'TestString',
},
})
const children = wrapper.findAll('.ant-avatar-string')
expect(children.length).toBe(1)
})
});
const children = wrapper.findAll('.ant-avatar-string');
expect(children.length).toBe(1);
});
it('should render fallback string correctly', async () => {
global.document.body.innerHTML = ''
global.document.body.innerHTML = '';
const wrapper = mount(Avatar, {
slots: {
default: 'Fallback',
@ -21,53 +21,59 @@ describe('Avatar Render', () => {
propsData: {
src: 'http://error.url',
},
sync: false, attachToDocument: true,
})
wrapper.vm.setScale = jest.fn(() => { wrapper.setData({ scale: 0.5 }); wrapper.vm.$forceUpdate() })
sync: false,
attachToDocument: true,
});
wrapper.vm.setScale = jest.fn(() => {
wrapper.setData({ scale: 0.5 });
wrapper.vm.$forceUpdate();
});
await asyncExpect(() => {
wrapper.find('img').trigger('error')
}, 0)
wrapper.find('img').trigger('error');
}, 0);
await asyncExpect(() => {
const children = wrapper.findAll('.ant-avatar-string')
expect(children.length).toBe(1)
expect(children.at(0).text()).toBe('Fallback')
expect(wrapper.vm.setScale).toBeCalled()
})
const children = wrapper.findAll('.ant-avatar-string');
expect(children.length).toBe(1);
expect(children.at(0).text()).toBe('Fallback');
expect(wrapper.vm.setScale).toBeCalled();
});
await asyncExpect(() => {
expect(global.document.body.querySelector('.ant-avatar-string').style.transform).toContain('scale(0.5)')
global.document.body.innerHTML = ''
}, 0)
})
expect(global.document.body.querySelector('.ant-avatar-string').style.transform).toContain(
'scale(0.5)',
);
global.document.body.innerHTML = '';
}, 0);
});
it('should handle onError correctly', () => {
global.document.body.innerHTML = ''
const LOAD_FAILURE_SRC = 'http://error.url'
const LOAD_SUCCESS_SRC = 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png'
global.document.body.innerHTML = '';
const LOAD_FAILURE_SRC = 'http://error.url';
const LOAD_SUCCESS_SRC = 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png';
const Foo = {
data () {
data() {
return {
src: LOAD_FAILURE_SRC,
}
};
},
methods: {
handleImgError () {
this.src = LOAD_SUCCESS_SRC
return false
handleImgError() {
this.src = LOAD_SUCCESS_SRC;
return false;
},
},
render () {
const { src } = this
return <Avatar src={src} loadError={this.handleImgError} />
render() {
const { src } = this;
return <Avatar src={src} loadError={this.handleImgError} />;
},
}
};
const wrapper = mount(Foo, { attachToDocument: true })
const wrapper = mount(Foo, { attachToDocument: true });
// mock img load Error, since jsdom do not load resource by default
// https://github.com/jsdom/jsdom/issues/1816
wrapper.find('img').trigger('error')
wrapper.find('img').trigger('error');
expect(wrapper.find({ name: 'AAvatar' }).vm.isImgExist).toBe(true)
expect(global.document.body.querySelector('img').getAttribute('src')).toBe(LOAD_SUCCESS_SRC)
})
})
expect(wrapper.find({ name: 'AAvatar' }).vm.isImgExist).toBe(true);
expect(global.document.body.querySelector('img').getAttribute('src')).toBe(LOAD_SUCCESS_SRC);
});
});

View File

@ -1,3 +1,3 @@
import demoTest from '../../../tests/shared/demoTest'
import demoTest from '../../../tests/shared/demoTest';
demoTest('avatar')
demoTest('avatar');

View File

@ -1,10 +1,10 @@
<script>
import Basic from './basic'
import Badge from './badge'
import Type from './type'
import Dynamic from './dynamic'
import CN from '../index.zh-CN.md'
import US from '../index.en-US.md'
import Basic from './basic';
import Badge from './badge';
import Type from './type';
import Dynamic from './dynamic';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# Avatar头像
@ -17,7 +17,7 @@ const md = {
Avatars can be used to represent people or objects. It supports images, 'Icon's, or letters.
## Examples
`,
}
};
export default {
category: 'Components',
subtitle: '头像',
@ -43,7 +43,7 @@ export default {
<US/>
</api>
</div>
)
);
},
}
};
</script>

View File

@ -1,8 +1,8 @@
import Avatar from './Avatar'
import Avatar from './Avatar';
/* istanbul ignore next */
Avatar.install = function (Vue) {
Vue.component(Avatar.name, Avatar)
}
Avatar.install = function(Vue) {
Vue.component(Avatar.name, Avatar);
};
export default Avatar
export default Avatar;

View File

@ -1,3 +1,3 @@
import demoTest from '../../../tests/shared/demoTest'
import demoTest from '../../../tests/shared/demoTest';
demoTest('back-top')
demoTest('back-top');

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils'
import BackTop from '..'
import { mount } from '@vue/test-utils';
import BackTop from '..';
describe('BackTop', () => {
it('should scroll to top after click it', async () => {
@ -7,13 +7,13 @@ describe('BackTop', () => {
propsData: {
visibilityHeight: -1,
},
})
document.documentElement.scrollTop = 400
});
document.documentElement.scrollTop = 400;
// trigger scroll manually
wrapper.vm.handleScroll()
await new Promise(resolve => setTimeout(resolve, 0))
wrapper.find('.ant-back-top').trigger('click')
await new Promise(resolve => setTimeout(resolve, 1000))
expect(Math.abs(Math.round(document.documentElement.scrollTop))).toBe(0)
})
})
wrapper.vm.handleScroll();
await new Promise(resolve => setTimeout(resolve, 0));
wrapper.find('.ant-back-top').trigger('click');
await new Promise(resolve => setTimeout(resolve, 1000));
expect(Math.abs(Math.round(document.documentElement.scrollTop))).toBe(0);
});
});

View File

@ -1,8 +1,8 @@
<script>
import Basic from './basic'
import Custom from './custom'
import CN from '../index.zh-CN.md'
import US from '../index.en-US.md'
import Basic from './basic';
import Custom from './custom';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# BackTop 回到顶部
返回页面顶部的操作按钮
@ -17,7 +17,7 @@ const md = {
- When you need to go back to the top very frequently in order to view the contents.
## Examples
`,
}
};
export default {
category: 'Components',
type: 'Other',
@ -35,7 +35,7 @@ export default {
<US/>
</api>
</div>
)
);
},
}
};
</script>

View File

@ -1,22 +1,22 @@
import raf from 'raf'
import PropTypes from '../_util/vue-types'
import addEventListener from '../_util/Dom/addEventListener'
import getScroll from '../_util/getScroll'
import BaseMixin from '../_util/BaseMixin'
import getTransitionProps from '../_util/getTransitionProps'
import raf from 'raf';
import PropTypes from '../_util/vue-types';
import addEventListener from '../_util/Dom/addEventListener';
import getScroll from '../_util/getScroll';
import BaseMixin from '../_util/BaseMixin';
import getTransitionProps from '../_util/getTransitionProps';
const easeInOutCubic = (t, b, c, d) => {
const cc = c - b
t /= d / 2
const cc = c - b;
t /= d / 2;
if (t < 1) {
return cc / 2 * t * t * t + b
return (cc / 2) * t * t * t + b;
} else {
return cc / 2 * ((t -= 2) * t * t + 2) + b
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
}
}
};
function getDefaultTarget () {
return window
function getDefaultTarget() {
return window;
}
const BackTopProps = {
@ -24,7 +24,7 @@ const BackTopProps = {
// onClick?: React.MouseEventHandler<any>;
target: PropTypes.func,
prefixCls: PropTypes.string,
}
};
const BackTop = {
name: 'ABackTop',
@ -33,106 +33,100 @@ const BackTop = {
...BackTopProps,
visibilityHeight: PropTypes.number.def(400),
},
data () {
this.scrollEvent = null
data() {
this.scrollEvent = null;
return {
visible: false,
}
};
},
mounted () {
mounted() {
this.$nextTick(() => {
const getTarget = this.target || getDefaultTarget
this.scrollEvent = addEventListener(getTarget(), 'scroll', this.handleScroll)
this.handleScroll()
})
const getTarget = this.target || getDefaultTarget;
this.scrollEvent = addEventListener(getTarget(), 'scroll', this.handleScroll);
this.handleScroll();
});
},
beforeDestroy () {
beforeDestroy() {
if (this.scrollEvent) {
this.scrollEvent.remove()
this.scrollEvent.remove();
}
},
methods: {
getCurrentScrollTop () {
const getTarget = this.target || getDefaultTarget
const targetNode = getTarget()
getCurrentScrollTop() {
const getTarget = this.target || getDefaultTarget;
const targetNode = getTarget();
if (targetNode === window) {
return window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop
return window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
}
return targetNode.scrollTop
return targetNode.scrollTop;
},
scrollToTop (e) {
const scrollTop = this.getCurrentScrollTop()
const startTime = Date.now()
scrollToTop(e) {
const scrollTop = this.getCurrentScrollTop();
const startTime = Date.now();
const frameFunc = () => {
const timestamp = Date.now()
const time = timestamp - startTime
this.setScrollTop(easeInOutCubic(time, scrollTop, 0, 450))
const timestamp = Date.now();
const time = timestamp - startTime;
this.setScrollTop(easeInOutCubic(time, scrollTop, 0, 450));
if (time < 450) {
raf(frameFunc)
raf(frameFunc);
} else {
this.setScrollTop(0)
this.setScrollTop(0);
}
}
raf(frameFunc)
this.$emit('click', e)
};
raf(frameFunc);
this.$emit('click', e);
},
setScrollTop (value) {
const getTarget = this.target || getDefaultTarget
const targetNode = getTarget()
setScrollTop(value) {
const getTarget = this.target || getDefaultTarget;
const targetNode = getTarget();
if (targetNode === window) {
document.body.scrollTop = value
document.documentElement.scrollTop = value
document.body.scrollTop = value;
document.documentElement.scrollTop = value;
} else {
targetNode.scrollTop = value
targetNode.scrollTop = value;
}
},
handleScroll () {
const { visibilityHeight, target = getDefaultTarget } = this
const scrollTop = getScroll(target(), true)
handleScroll() {
const { visibilityHeight, target = getDefaultTarget } = this;
const scrollTop = getScroll(target(), true);
this.setState({
visible: scrollTop > visibilityHeight,
})
});
},
},
render () {
const { prefixCls = 'ant-back-top', $slots, $listeners } = this
render() {
const { prefixCls = 'ant-back-top', $slots, $listeners } = this;
const defaultElement = (
<div class={`${prefixCls}-content`}>
<div class={`${prefixCls}-icon`} />
</div>
)
);
const divProps = {
on: {
...$listeners,
click: this.scrollToTop,
},
class: prefixCls,
}
};
const backTopBtn = this.visible ? (
<div {...divProps}>
{$slots.default || defaultElement}
</div>
) : null
const transitionProps = getTransitionProps('fade')
return (
<transition {...transitionProps}>
{backTopBtn}
</transition>
)
<div {...divProps}>{$slots.default || defaultElement}</div>
) : null;
const transitionProps = getTransitionProps('fade');
return <transition {...transitionProps}>{backTopBtn}</transition>;
},
}
};
/* istanbul ignore next */
BackTop.install = function (Vue) {
Vue.component(BackTop.name, BackTop)
}
BackTop.install = function(Vue) {
Vue.component(BackTop.name, BackTop);
};
export default BackTop
export default BackTop;

View File

@ -1,11 +1,10 @@
import PropTypes from '../_util/vue-types'
import ScrollNumber from './ScrollNumber'
import classNames from 'classnames'
import { initDefaultProps, filterEmpty, getComponentFromProp } from '../_util/props-util'
import { cloneElement } from '../_util/vnode'
import getTransitionProps from '../_util/getTransitionProps'
import isNumeric from '../_util/isNumeric'
import PropTypes from '../_util/vue-types';
import ScrollNumber from './ScrollNumber';
import classNames from 'classnames';
import { initDefaultProps, filterEmpty, getComponentFromProp } from '../_util/props-util';
import { cloneElement } from '../_util/vnode';
import getTransitionProps from '../_util/getTransitionProps';
import isNumeric from '../_util/isNumeric';
export const BadgeProps = {
/** Number to show in badge */
@ -22,7 +21,7 @@ export const BadgeProps = {
offset: PropTypes.array,
numberStyle: PropTypes.object.def({}),
title: PropTypes.string,
}
};
export default {
name: 'ABadge',
@ -34,95 +33,94 @@ export default {
overflowCount: 99,
}),
methods: {
getBadgeClassName () {
const { prefixCls, status } = this.$props
const children = filterEmpty(this.$slots.default)
getBadgeClassName() {
const { prefixCls, status } = this.$props;
const children = filterEmpty(this.$slots.default);
return classNames(prefixCls, {
[`${prefixCls}-status`]: !!status,
[`${prefixCls}-not-a-wrapper`]: !children.length,
})
});
},
isZero () {
const numberedDispayCount = this.getNumberedDispayCount()
return numberedDispayCount === '0' || numberedDispayCount === 0
isZero() {
const numberedDispayCount = this.getNumberedDispayCount();
return numberedDispayCount === '0' || numberedDispayCount === 0;
},
isDot () {
const { dot, status } = this.$props
const isZero = this.isZero()
return (dot && !isZero) || status
isDot() {
const { dot, status } = this.$props;
const isZero = this.isZero();
return (dot && !isZero) || status;
},
isHidden () {
const { showZero } = this.$props
const displayCount = this.getDispayCount()
const isZero = this.isZero()
const isDot = this.isDot()
const isEmpty = displayCount === null || displayCount === undefined || displayCount === ''
return (isEmpty || (isZero && !showZero)) && !isDot
isHidden() {
const { showZero } = this.$props;
const displayCount = this.getDispayCount();
const isZero = this.isZero();
const isDot = this.isDot();
const isEmpty = displayCount === null || displayCount === undefined || displayCount === '';
return (isEmpty || (isZero && !showZero)) && !isDot;
},
getNumberedDispayCount () {
const { overflowCount } = this.$props
const count = this.badgeCount
const displayCount =
count > overflowCount ? `${overflowCount}+` : count
return displayCount
getNumberedDispayCount() {
const { overflowCount } = this.$props;
const count = this.badgeCount;
const displayCount = count > overflowCount ? `${overflowCount}+` : count;
return displayCount;
},
getDispayCount () {
const isDot = this.isDot()
getDispayCount() {
const isDot = this.isDot();
// dot mode don't need count
if (isDot) {
return ''
return '';
}
return this.getNumberedDispayCount()
return this.getNumberedDispayCount();
},
getScollNumberTitle () {
const { title } = this.$props
const count = this.badgeCount
getScollNumberTitle() {
const { title } = this.$props;
const count = this.badgeCount;
if (title) {
return title
return title;
}
return typeof count === 'string' || typeof count === 'number' ? count : undefined
return typeof count === 'string' || typeof count === 'number' ? count : undefined;
},
getStyleWithOffset () {
const { offset, numberStyle } = this.$props
getStyleWithOffset() {
const { offset, numberStyle } = this.$props;
return offset
? {
right: `${-parseInt(offset[0], 10)}px`,
marginTop: isNumeric(offset[1]) ? `${offset[1]}px` : offset[1],
...numberStyle,
}
: numberStyle
right: `${-parseInt(offset[0], 10)}px`,
marginTop: isNumeric(offset[1]) ? `${offset[1]}px` : offset[1],
...numberStyle,
}
: numberStyle;
},
renderStatusText () {
const { prefixCls, text } = this.$props
const hidden = this.isHidden()
return hidden || !text ? null : <span class={`${prefixCls}-status-text`}>{text}</span>
renderStatusText() {
const { prefixCls, text } = this.$props;
const hidden = this.isHidden();
return hidden || !text ? null : <span class={`${prefixCls}-status-text`}>{text}</span>;
},
renderDispayComponent () {
const count = this.badgeCount
const customNode = count
renderDispayComponent() {
const count = this.badgeCount;
const customNode = count;
if (!customNode || typeof customNode !== 'object') {
return undefined
return undefined;
}
return cloneElement(customNode, {
style: this.getStyleWithOffset(),
})
});
},
renderBadgeNumber () {
const { prefixCls, scrollNumberPrefixCls, status } = this.$props
const count = this.badgeCount
const displayCount = this.getDispayCount()
const isDot = this.isDot()
const hidden = this.isHidden()
renderBadgeNumber() {
const { prefixCls, scrollNumberPrefixCls, status } = this.$props;
const count = this.badgeCount;
const displayCount = this.getDispayCount();
const isDot = this.isDot();
const hidden = this.isHidden();
const scrollNumberCls = {
[`${prefixCls}-dot`]: isDot,
@ -130,7 +128,7 @@ export default {
[`${prefixCls}-multiple-words`]:
!isDot && count && count.toString && count.toString().length > 1,
[`${prefixCls}-status-${status}`]: !!status,
}
};
return hidden ? null : (
<ScrollNumber
@ -142,31 +140,26 @@ export default {
displayComponent={this.renderDispayComponent()} // <Badge status="success" count={<Icon type="xxx" />}></Badge>
title={this.getScollNumberTitle()}
style={this.getStyleWithOffset()}
key='scrollNumber'
key="scrollNumber"
/>
)
);
},
},
render () {
const {
prefixCls,
status,
text,
$slots,
} = this
const children = filterEmpty($slots.default)
let count = getComponentFromProp(this, 'count')
render() {
const { prefixCls, status, text, $slots } = this;
const children = filterEmpty($slots.default);
let count = getComponentFromProp(this, 'count');
if (Array.isArray(count)) {
count = count[0]
count = count[0];
}
this.badgeCount = count
const scrollNumber = this.renderBadgeNumber()
const statusText = this.renderStatusText()
this.badgeCount = count;
const scrollNumber = this.renderBadgeNumber();
const statusText = this.renderStatusText();
const statusCls = classNames({
[`${prefixCls}-status-dot`]: !!status,
[`${prefixCls}-status-${status}`]: !!status,
})
});
// <Badge status="success" />
if (!children.length && status) {
@ -179,18 +172,17 @@ export default {
<span class={statusCls} />
<span class={`${prefixCls}-status-text`}>{text}</span>
</span>
)
);
}
const transitionProps = getTransitionProps(children.length ? `${prefixCls}-zoom` : '')
const transitionProps = getTransitionProps(children.length ? `${prefixCls}-zoom` : '');
return (<span {...{ on: this.$listeners }} class={this.getBadgeClassName()}>
{children}
<transition {...transitionProps}>
{scrollNumber}
</transition>
{statusText}
</span>)
return (
<span {...{ on: this.$listeners }} class={this.getBadgeClassName()}>
{children}
<transition {...transitionProps}>{scrollNumber}</transition>
{statusText}
</span>
);
},
}
};

View File

@ -1,16 +1,18 @@
import classNames from 'classnames'
import PropTypes from '../_util/vue-types'
import BaseMixin from '../_util/BaseMixin'
import { getStyle } from '../_util/props-util'
import omit from 'omit.js'
import { cloneElement } from '../_util/vnode'
import classNames from 'classnames';
import PropTypes from '../_util/vue-types';
import BaseMixin from '../_util/BaseMixin';
import { getStyle } from '../_util/props-util';
import omit from 'omit.js';
import { cloneElement } from '../_util/vnode';
function getNumberArray (num) {
function getNumberArray(num) {
return num
? num.toString()
.split('')
.reverse()
.map(i => Number(i)) : []
? num
.toString()
.split('')
.reverse()
.map(i => Number(i))
: [];
}
const ScrollNumberProps = {
@ -20,108 +22,115 @@ const ScrollNumberProps = {
title: PropTypes.oneOfType([PropTypes.number, PropTypes.string, null]),
displayComponent: PropTypes.any,
className: PropTypes.object,
}
};
export default {
mixins: [BaseMixin],
props: ScrollNumberProps,
data () {
data() {
return {
animateStarted: true,
sCount: this.count,
}
};
},
watch: {
count (val) {
count(val) {
if (this.sCount !== val) {
this.lastCount = this.sCount
this.lastCount = this.sCount;
//
this.setState({
animateStarted: true,
}, () => {
//
//
setTimeout(() => {
this.setState({
animateStarted: false,
sCount: val,
}, () => {
this.$emit('animated')
})
}, 5)
})
this.setState(
{
animateStarted: true,
},
() => {
//
//
setTimeout(() => {
this.setState(
{
animateStarted: false,
sCount: val,
},
() => {
this.$emit('animated');
},
);
}, 5);
},
);
}
},
},
methods: {
getPositionByNum (num, i) {
getPositionByNum(num, i) {
if (this.animateStarted) {
return 10 + num
return 10 + num;
}
const currentDigit = getNumberArray(this.sCount)[i]
const lastDigit = getNumberArray(this.lastCount)[i]
const currentDigit = getNumberArray(this.sCount)[i];
const lastDigit = getNumberArray(this.lastCount)[i];
//
if (this.sCount > this.lastCount) {
if (currentDigit >= lastDigit) {
return 10 + num
return 10 + num;
}
return 20 + num
return 20 + num;
}
if (currentDigit <= lastDigit) {
return 10 + num
return 10 + num;
}
return num
return num;
},
renderNumberList (position) {
const childrenToReturn = []
renderNumberList(position) {
const childrenToReturn = [];
for (let i = 0; i < 30; i++) {
const currentClassName = (position === i) ? 'current' : ''
childrenToReturn.push(<p key={i.toString()} class={currentClassName}>{i % 10}</p>)
const currentClassName = position === i ? 'current' : '';
childrenToReturn.push(
<p key={i.toString()} class={currentClassName}>
{i % 10}
</p>,
);
}
return childrenToReturn
return childrenToReturn;
},
renderCurrentNumber (num, i) {
const position = this.getPositionByNum(num, i)
const removeTransition = this.animateStarted || getNumberArray(this.lastCount)[i] === undefined
renderCurrentNumber(num, i) {
const position = this.getPositionByNum(num, i);
const removeTransition =
this.animateStarted || getNumberArray(this.lastCount)[i] === undefined;
const style = {
transition: removeTransition ? 'none' : undefined,
msTransform: `translateY(${-position * 100}%)`,
WebkitTransform: `translateY(${-position * 100}%)`,
transform: `translateY(${-position * 100}%)`,
}
};
return (
<span class={`${this.prefixCls}-only`} style={style} key={i}>
{this.renderNumberList(position)}
</span>
)
);
},
renderNumberElement () {
const { sCount } = this
renderNumberElement() {
const { sCount } = this;
if (!sCount || isNaN(sCount)) {
return sCount
return sCount;
}
return getNumberArray(sCount)
.map((num, i) => this.renderCurrentNumber(num, i)).reverse()
.map((num, i) => this.renderCurrentNumber(num, i))
.reverse();
},
},
render () {
const { prefixCls, title, component: Tag = 'sup', displayComponent, className } = this
render() {
const { prefixCls, title, component: Tag = 'sup', displayComponent, className } = this;
if (displayComponent) {
return cloneElement(displayComponent, {
class: `${prefixCls}-custom-component`,
})
});
}
const style = getStyle(this, true)
const style = getStyle(this, true);
// fix https://fb.me/react-unknown-prop
const restProps = omit(this.$props, [
'count',
'component',
'prefixCls',
'displayComponent',
])
const restProps = omit(this.$props, ['count', 'component', 'prefixCls', 'displayComponent']);
const newProps = {
props: {
...restProps,
@ -131,19 +140,14 @@ export default {
},
style,
class: classNames(prefixCls, className),
}
};
// allow specify the border
// mock border-color by box-shadow for compatible with old usage:
// <Badge count={4} style={{ backgroundColor: '#fff', color: '#999', borderColor: '#d9d9d9' }} />
if (style && style.borderColor) {
newProps.style.boxShadow = `0 0 0 1px ${style.borderColor} inset`
newProps.style.boxShadow = `0 0 0 1px ${style.borderColor} inset`;
}
return (
<Tag {...newProps} >
{ this.renderNumberElement()}
</Tag>
)
return <Tag {...newProps}>{this.renderNumberElement()}</Tag>;
},
}
};

View File

@ -1,3 +1,3 @@
import demoTest from '../../../tests/shared/demoTest'
import demoTest from '../../../tests/shared/demoTest';
demoTest('badge')
demoTest('badge');

View File

@ -1,33 +1,35 @@
import { mount } from '@vue/test-utils'
import Badge from '../index'
import { mount } from '@vue/test-utils';
import Badge from '../index';
import { asyncExpect } from '@/tests/utils'
import { asyncExpect } from '@/tests/utils';
describe('Badge', () => {
it('badge dot not scaling count > 9', () => {
const badge = mount({
render () {
return <Badge count={10} dot />
render() {
return <Badge count={10} dot />;
},
})
expect(badge.findAll('.ant-card-multiple-words').length).toBe(0)
})
});
expect(badge.findAll('.ant-card-multiple-words').length).toBe(0);
});
it('badge dot not showing count == 0', () => {
const badge = mount({
render () {
return <Badge count={0} dot />
render() {
return <Badge count={0} dot />;
},
})
expect(badge.findAll('.ant-badge-dot').length).toBe(0)
})
});
expect(badge.findAll('.ant-badge-dot').length).toBe(0);
});
it('should have an overriden title attribute', () => {
const badge = mount({
render () {
return <Badge count={10} title='Custom title' />
render() {
return <Badge count={10} title="Custom title" />;
},
})
expect(badge.find('.ant-scroll-number').element.attributes.getNamedItem('title').value).toEqual('Custom title')
})
});
expect(badge.find('.ant-scroll-number').element.attributes.getNamedItem('title').value).toEqual(
'Custom title',
);
});
// https://github.com/ant-design/ant-design/issues/10626
// it('should be composable with Tooltip', async () => {
@ -51,52 +53,57 @@ describe('Badge', () => {
count: 9,
},
sync: false,
})
});
await asyncExpect(() => {
wrapper.setProps({ count: 10 })
}, 100)
wrapper.setProps({ count: 10 });
}, 100);
await asyncExpect(() => {
expect(wrapper.html()).toMatchSnapshot()
wrapper.setProps({ count: 11 })
}, 100)
expect(wrapper.html()).toMatchSnapshot();
wrapper.setProps({ count: 11 });
}, 100);
await asyncExpect(() => {
expect(wrapper.html()).toMatchSnapshot()
wrapper.setProps({ count: 11 })
}, 100)
expect(wrapper.html()).toMatchSnapshot();
wrapper.setProps({ count: 11 });
}, 100);
await asyncExpect(() => {
expect(wrapper.html()).toMatchSnapshot()
wrapper.setProps({ count: 10 })
}, 100)
expect(wrapper.html()).toMatchSnapshot();
wrapper.setProps({ count: 10 });
}, 100);
await asyncExpect(() => {
expect(wrapper.html()).toMatchSnapshot()
wrapper.setProps({ count: 9 })
}, 100)
expect(wrapper.html()).toMatchSnapshot();
wrapper.setProps({ count: 9 });
}, 100);
await asyncExpect(() => {
expect(wrapper.html()).toMatchSnapshot()
}, 100)
})
expect(wrapper.html()).toMatchSnapshot();
}, 100);
});
it('should be compatible with borderColor style', () => {
const wrapper = mount({
render () {
return <Badge count={4} style={{ backgroundColor: '#fff', color: '#999', borderColor: '#d9d9d9' }} />
render() {
return (
<Badge
count={4}
style={{ backgroundColor: '#fff', color: '#999', borderColor: '#d9d9d9' }}
/>
);
},
})
});
expect(wrapper.html()).toMatchSnapshot()
})
expect(wrapper.html()).toMatchSnapshot();
});
// https://github.com/ant-design/ant-design/issues/13694
it('should support offset when count is a ReactNode', () => {
const wrapper = mount({
render () {
render() {
return (
<Badge count={<span class='custom' style={{ color: '#f5222d' }} />} offset={[10, 20]}>
<a href='#' class='head-example' />
<Badge count={<span class="custom" style={{ color: '#f5222d' }} />} offset={[10, 20]}>
<a href="#" class="head-example" />
</Badge>
)
);
},
})
expect(wrapper.html()).toMatchSnapshot()
})
})
});
expect(wrapper.html()).toMatchSnapshot();
});
});

View File

@ -1,14 +1,14 @@
<script>
import Basic from './basic.md'
import NoWapper from './no-wrapper'
import Dot from './dot'
import Change from './change'
import Overflow from './overflow'
import Status from './status'
import Title from './title'
import Basic from './basic.md';
import NoWapper from './no-wrapper';
import Dot from './dot';
import Change from './change';
import Overflow from './overflow';
import Status from './status';
import Title from './title';
import CN from './../index.zh-CN.md'
import US from './../index.en_US.md'
import CN from './../index.zh-CN.md';
import US from './../index.en_US.md';
const md = {
cn: `# Badge徽标数
@ -23,7 +23,7 @@
Badge normally appears in proximity to notifications or user avatars with eye-catching appeal, typically displaying unread messages count.
## Examples
`,
}
};
export default {
category: 'Components',
@ -47,9 +47,9 @@
<US />
</api>
</div>
)
);
},
}
};
</script>
<style>

View File

@ -1,9 +1,8 @@
import Badge from './Badge'
import Badge from './Badge';
/* istanbul ignore next */
Badge.install = function (Vue) {
Vue.component(Badge.name, Badge)
}
export default Badge
Badge.install = function(Vue) {
Vue.component(Badge.name, Badge);
};
export default Badge;

View File

@ -1,14 +1,13 @@
import PropTypes from '../_util/vue-types'
import { cloneElement } from '../_util/vnode'
import { filterEmpty, getComponentFromProp, getSlotOptions } from '../_util/props-util'
import warning from '../_util/warning'
import BreadcrumbItem from './BreadcrumbItem'
import PropTypes from '../_util/vue-types';
import { cloneElement } from '../_util/vnode';
import { filterEmpty, getComponentFromProp, getSlotOptions } from '../_util/props-util';
import warning from '../_util/warning';
import BreadcrumbItem from './BreadcrumbItem';
const Route = PropTypes.shape({
path: PropTypes.string,
breadcrumbName: PropTypes.string,
}).loose
}).loose;
const BreadcrumbProps = {
prefixCls: PropTypes.string.def('ant-breadcrumb'),
@ -16,75 +15,65 @@ const BreadcrumbProps = {
params: PropTypes.any,
separator: PropTypes.any,
itemRender: PropTypes.func,
}
};
function getBreadcrumbName (route, params) {
function getBreadcrumbName(route, params) {
if (!route.breadcrumbName) {
return null
return null;
}
const paramsKeys = Object.keys(params).join('|')
const paramsKeys = Object.keys(params).join('|');
const name = route.breadcrumbName.replace(
new RegExp(`:(${paramsKeys})`, 'g'),
(replacement, key) => params[key] || replacement,
)
return name
);
return name;
}
export default {
name: 'ABreadcrumb',
props: BreadcrumbProps,
methods: {
defaultItemRender ({ route, params, routes, paths }) {
const isLastItem = routes.indexOf(route) === routes.length - 1
const name = getBreadcrumbName(route, params)
return isLastItem
? <span>{name}</span>
: <a href={`#/${paths.join('/')}`}>{name}</a>
defaultItemRender({ route, params, routes, paths }) {
const isLastItem = routes.indexOf(route) === routes.length - 1;
const name = getBreadcrumbName(route, params);
return isLastItem ? <span>{name}</span> : <a href={`#/${paths.join('/')}`}>{name}</a>;
},
},
render () {
let crumbs
const {
prefixCls, routes, params = {},
$slots, $scopedSlots,
} = this
const children = filterEmpty($slots.default)
const separator = getComponentFromProp(this, 'separator')
render() {
let crumbs;
const { prefixCls, routes, params = {}, $slots, $scopedSlots } = this;
const children = filterEmpty($slots.default);
const separator = getComponentFromProp(this, 'separator');
if (routes && routes.length > 0) {
const paths = []
const itemRender = this.itemRender || $scopedSlots.itemRender || this.defaultItemRender
crumbs = routes.map((route) => {
route.path = route.path || ''
let path = route.path.replace(/^\//, '')
const paths = [];
const itemRender = this.itemRender || $scopedSlots.itemRender || this.defaultItemRender;
crumbs = routes.map(route => {
route.path = route.path || '';
let path = route.path.replace(/^\//, '');
Object.keys(params).forEach(key => {
path = path.replace(`:${key}`, params[key])
})
path = path.replace(`:${key}`, params[key]);
});
if (path) {
paths.push(path)
paths.push(path);
}
return (
<BreadcrumbItem separator={separator} key={route.breadcrumbName || path}>
{itemRender({ route, params, routes, paths })}
</BreadcrumbItem>
)
})
);
});
} else if (children.length) {
crumbs = children.map((element, index) => {
warning(
getSlotOptions(element).__ANT_BREADCRUMB_ITEM,
'Breadcrumb only accepts Breadcrumb.Item as it\'s children',
)
"Breadcrumb only accepts Breadcrumb.Item as it's children",
);
return cloneElement(element, {
props: { separator },
key: index,
})
})
});
});
}
return (
<div class={prefixCls}>
{crumbs}
</div>
)
return <div class={prefixCls}>{crumbs}</div>;
},
}
};

View File

@ -1,6 +1,5 @@
import PropTypes from '../_util/vue-types'
import { hasProp, getComponentFromProp } from '../_util/props-util'
import PropTypes from '../_util/vue-types';
import { hasProp, getComponentFromProp } from '../_util/props-util';
export default {
name: 'ABreadcrumbItem',
@ -10,24 +9,25 @@ export default {
href: PropTypes.string,
separator: PropTypes.any,
},
render () {
const { prefixCls, $slots } = this
const children = $slots.default
let link
render() {
const { prefixCls, $slots } = this;
const children = $slots.default;
let link;
if (hasProp(this, 'href')) {
link = <a class={`${prefixCls}-link`}>{children}</a>
link = <a class={`${prefixCls}-link`}>{children}</a>;
} else {
link = <span class={`${prefixCls}-link`}>{children}</span>
link = <span class={`${prefixCls}-link`}>{children}</span>;
}
if (children) {
return (
<span>
{link}
<span class={`${prefixCls}-separator`}>{getComponentFromProp(this, 'separator') || '/'}</span>
<span class={`${prefixCls}-separator`}>
{getComponentFromProp(this, 'separator') || '/'}
</span>
</span>
)
);
}
return null
return null;
},
}
};

View File

@ -1,70 +1,64 @@
import { mount } from '@vue/test-utils'
import Breadcrumb from '../index'
import { mount } from '@vue/test-utils';
import Breadcrumb from '../index';
describe('Breadcrumb', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
afterEach(() => {
errorSpy.mockReset()
})
errorSpy.mockReset();
});
afterAll(() => {
errorSpy.mockRestore()
})
errorSpy.mockRestore();
});
// // https://github.com/airbnb/enzyme/issues/875
it('warns on non-Breadcrumb.Item children', () => {
mount(
{
render () {
return (
<Breadcrumb>
<div>foo</div>
</Breadcrumb>
)
},
}
)
expect(errorSpy.mock.calls).toHaveLength(1)
mount({
render() {
return (
<Breadcrumb>
<div>foo</div>
</Breadcrumb>
);
},
});
expect(errorSpy.mock.calls).toHaveLength(1);
expect(errorSpy.mock.calls[0][0]).toMatch(
'Breadcrumb only accepts Breadcrumb.Item as it\'s children'
)
})
"Breadcrumb only accepts Breadcrumb.Item as it's children",
);
});
// https:// github.com/ant-design/ant-design/issues/5015
it('should allow Breadcrumb.Item is null or undefined', () => {
const wrapper = mount(
{
render () {
return (
<Breadcrumb>
{null}
<Breadcrumb.Item>Home</Breadcrumb.Item>
{undefined}
</Breadcrumb>
)
},
}
)
expect(errorSpy).not.toHaveBeenCalled()
expect(wrapper.html()).toMatchSnapshot()
})
const wrapper = mount({
render() {
return (
<Breadcrumb>
{null}
<Breadcrumb.Item>Home</Breadcrumb.Item>
{undefined}
</Breadcrumb>
);
},
});
expect(errorSpy).not.toHaveBeenCalled();
expect(wrapper.html()).toMatchSnapshot();
});
// https://github.com/ant-design/ant-design/issues/5542
it('should not display Breadcrumb Item when its children is falsy', () => {
const wrapper = mount(
{
render () {
return (
<Breadcrumb>
<Breadcrumb.Item />
<Breadcrumb.Item>xxx</Breadcrumb.Item>
<Breadcrumb.Item>yyy</Breadcrumb.Item>
</Breadcrumb>
)
},
}
)
expect(wrapper.html()).toMatchSnapshot()
})
})
const wrapper = mount({
render() {
return (
<Breadcrumb>
<Breadcrumb.Item />
<Breadcrumb.Item>xxx</Breadcrumb.Item>
<Breadcrumb.Item>yyy</Breadcrumb.Item>
</Breadcrumb>
);
},
});
expect(wrapper.html()).toMatchSnapshot();
});
});

View File

@ -1,3 +1,3 @@
import demoTest from '../../../tests/shared/demoTest'
import demoTest from '../../../tests/shared/demoTest';
demoTest('breadcrumb', { skip: ['router.md'] })
demoTest('breadcrumb', { skip: ['router.md'] });

View File

@ -1,11 +1,11 @@
<script>
import Basic from './basic.md'
import WithIcon from './withIcon.md'
import Separator from './separator.md'
import Router from './router'
import Basic from './basic.md';
import WithIcon from './withIcon.md';
import Separator from './separator.md';
import Router from './router';
import US from './../index.en-US.md'
import CN from './../index.zh-CN.md'
import US from './../index.en-US.md';
import CN from './../index.zh-CN.md';
const md = {
cn: `# Breadcrumb面包屑
@ -25,7 +25,7 @@
- When the application has multi-layer architecture.
## Examples
`,
}
};
export default {
category: 'Components',
@ -46,7 +46,7 @@
<US />
</api>
</div>
)
);
},
}
};
</script>

View File

@ -1,13 +1,12 @@
import Breadcrumb from './Breadcrumb'
import BreadcrumbItem from './BreadcrumbItem'
import Breadcrumb from './Breadcrumb';
import BreadcrumbItem from './BreadcrumbItem';
Breadcrumb.Item = BreadcrumbItem
Breadcrumb.Item = BreadcrumbItem;
/* istanbul ignore next */
Breadcrumb.install = function (Vue) {
Vue.component(Breadcrumb.name, Breadcrumb)
Vue.component(BreadcrumbItem.name, BreadcrumbItem)
}
export default Breadcrumb
Breadcrumb.install = function(Vue) {
Vue.component(Breadcrumb.name, Breadcrumb);
Vue.component(BreadcrumbItem.name, BreadcrumbItem);
};
export default Breadcrumb;

View File

@ -1,3 +1,3 @@
import demoTest from '../../../tests/shared/demoTest'
import demoTest from '../../../tests/shared/demoTest';
demoTest('button')
demoTest('button');

View File

@ -1,187 +1,200 @@
import Button from '../index'
import Icon from '../../icon'
import { mount } from '@vue/test-utils'
import Vue from 'vue'
import { asyncExpect } from '@/tests/utils'
import Button from '../index';
import Icon from '../../icon';
import { mount } from '@vue/test-utils';
import Vue from 'vue';
import { asyncExpect } from '@/tests/utils';
describe('Button', () => {
it('renders correctly', () => {
const wrapper = mount({
render () {
return <Button>Follow</Button>
render() {
return <Button>Follow</Button>;
},
})
expect(wrapper.html()).toMatchSnapshot()
})
});
expect(wrapper.html()).toMatchSnapshot();
});
it('create primary button', () => {
const wrapper = mount({
render (h) {
return <Button type='primary'>按钮</Button>
render(h) {
return <Button type="primary">按钮</Button>;
},
})
expect(wrapper.contains('.ant-btn-primary')).toBe(true)
})
});
expect(wrapper.contains('.ant-btn-primary')).toBe(true);
});
it('renders Chinese characters correctly', (done) => {
const wrapper = mount(
{
render (h) {
return <Button>按钮</Button>
},
}
)
expect(wrapper.text()).toBe('按 钮')
it('renders Chinese characters correctly', done => {
const wrapper = mount({
render(h) {
return <Button>按钮</Button>;
},
});
expect(wrapper.text()).toBe('按 钮');
const wrapper1 = mount(
{
render (h) {
return <Button icon='search'>按钮</Button>
},
}
)
const wrapper1 = mount({
render(h) {
return <Button icon="search">按钮</Button>;
},
});
expect(wrapper1.html()).toMatchSnapshot()
expect(wrapper1.html()).toMatchSnapshot();
const wrapper2 = mount(
{
render (h) {
return <Button><Icon type='search' />按钮</Button>
},
}
)
expect(wrapper2.html()).toMatchSnapshot()
const wrapper2 = mount({
render(h) {
return (
<Button>
<Icon type="search" />
按钮
</Button>
);
},
});
expect(wrapper2.html()).toMatchSnapshot();
// should not insert space when there is icon
const wrapper3 = mount(
{
render (h) {
return <Button icon='search'>按钮</Button>
},
}
)
expect(wrapper3.html()).toMatchSnapshot()
const wrapper3 = mount({
render(h) {
return <Button icon="search">按钮</Button>;
},
});
expect(wrapper3.html()).toMatchSnapshot();
// should not insert space when there is icon while loading
const wrapper4 = mount(
{
render (h) {
return <Button icon='search' loading>按钮</Button>
},
}
)
expect(wrapper4.html()).toMatchSnapshot()
const wrapper4 = mount({
render(h) {
return (
<Button icon="search" loading>
按钮
</Button>
);
},
});
expect(wrapper4.html()).toMatchSnapshot();
// should insert space while loading
const wrapper5 = mount(
{
render (h) {
return <Button loading>按钮</Button>
},
}
)
expect(wrapper5.html()).toMatchSnapshot()
const wrapper6 = mount(
{
render (h) {
return <Button><span>按钮</span></Button>
},
}
)
const wrapper5 = mount({
render(h) {
return <Button loading>按钮</Button>;
},
});
expect(wrapper5.html()).toMatchSnapshot();
const wrapper6 = mount({
render(h) {
return (
<Button>
<span>按钮</span>
</Button>
);
},
});
Vue.nextTick(() => {
expect(wrapper6.find('.ant-btn').contains('.ant-btn-two-chinese-chars')).toBe(true)
done()
})
})
expect(wrapper6.find('.ant-btn').contains('.ant-btn-two-chinese-chars')).toBe(true);
done();
});
});
it('should change loading state instantly by default', async () => {
const DefaultButton = {
data () {
data() {
return {
loading: false,
}
};
},
methods: {
enterLoading () {
this.loading = true
enterLoading() {
this.loading = true;
},
},
render () {
return <Button loading={this.loading} onClick={this.enterLoading}>Button</Button>
render() {
return (
<Button loading={this.loading} onClick={this.enterLoading}>
Button
</Button>
);
},
}
const wrapper = mount(DefaultButton, { sync: false })
};
const wrapper = mount(DefaultButton, { sync: false });
await asyncExpect(() => {
wrapper.trigger('click')
})
wrapper.trigger('click');
});
await asyncExpect(() => {
expect(wrapper.findAll('.ant-btn-loading').length).toBe(1)
})
})
expect(wrapper.findAll('.ant-btn-loading').length).toBe(1);
});
});
it('should change loading state with delay', async () => {
const DefaultButton = {
data () {
data() {
return {
loading: false,
}
};
},
methods: {
enterLoading () {
this.loading = { delay: 1000 }
enterLoading() {
this.loading = { delay: 1000 };
},
},
render () {
return <Button loading={this.loading} onClick={this.enterLoading}>Button</Button>
render() {
return (
<Button loading={this.loading} onClick={this.enterLoading}>
Button
</Button>
);
},
}
const wrapper = mount(DefaultButton, { sync: false })
};
const wrapper = mount(DefaultButton, { sync: false });
await asyncExpect(() => {
wrapper.trigger('click')
})
wrapper.trigger('click');
});
await asyncExpect(() => {
expect(wrapper.contains('.ant-btn-loading')).toBe(false)
})
})
expect(wrapper.contains('.ant-btn-loading')).toBe(false);
});
});
it('should support link button', () => {
const wrapper = mount({
render (h) {
return <Button target='_blank' href='http://ant.design'>link button</Button>
render(h) {
return (
<Button target="_blank" href="http://ant.design">
link button
</Button>
);
},
})
expect(wrapper.html()).toMatchSnapshot()
})
});
expect(wrapper.html()).toMatchSnapshot();
});
it('fixbug renders {0} , 0 and {false}', () => {
const wrapper = mount({
render (h) {
return <Button>{0}</Button>
render(h) {
return <Button>{0}</Button>;
},
})
expect(wrapper.html()).toMatchSnapshot()
});
expect(wrapper.html()).toMatchSnapshot();
const wrapper1 = mount({
render (h) {
return <Button>0</Button>
render(h) {
return <Button>0</Button>;
},
})
expect(wrapper1.html()).toMatchSnapshot()
});
expect(wrapper1.html()).toMatchSnapshot();
const wrapper2 = mount({
render (h) {
return <Button>{false}</Button>
render(h) {
return <Button>{false}</Button>;
},
})
expect(wrapper2.html()).toMatchSnapshot()
})
});
expect(wrapper2.html()).toMatchSnapshot();
});
it('should not render as link button when href is undefined', async () => {
const wrapper = mount({
render () {
render() {
return (
<Button type='primary' href={undefined}>button</Button>
)
<Button type="primary" href={undefined}>
button
</Button>
);
},
})
expect(wrapper.html()).toMatchSnapshot()
})
})
});
expect(wrapper.html()).toMatchSnapshot();
});
});

Some files were not shown because too many files have changed in this diff Show More