mirror of
https://gitee.com/ant-design-vue/ant-design-vue.git
synced 2024-12-01 19:48:38 +08:00
style: lint add semi and prettier
This commit is contained in:
parent
b4d9bfd8d9
commit
ffb2a593b1
@ -3,3 +3,7 @@ node_modules/
|
||||
**/style/
|
||||
*.html
|
||||
/components/test/*
|
||||
es/
|
||||
lib/
|
||||
site-dist/
|
||||
dist/
|
||||
|
@ -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
9
.prettierignore
Normal file
@ -0,0 +1,9 @@
|
||||
**/*.md
|
||||
**/*.svg
|
||||
**/*.ejs
|
||||
**/*.html
|
||||
package.json
|
||||
es/**
|
||||
lib/**
|
||||
site-dist/**
|
||||
dist/**
|
19
.prettierrc
Normal file
19
.prettierrc
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ".prettierrc",
|
||||
"options": {
|
||||
"parser": "json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ".stylelintrc",
|
||||
"options": {
|
||||
"parser": "json"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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')],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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'],
|
||||
}),
|
||||
],
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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');
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -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,
|
||||
})
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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];
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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,
|
||||
})
|
||||
});
|
||||
|
@ -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];
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 };
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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];
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -1,3 +1,3 @@
|
||||
import demoTest from '../../../tests/shared/demoTest'
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('affix')
|
||||
demoTest('affix');
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -1,3 +1,3 @@
|
||||
import demoTest from '../../../tests/shared/demoTest'
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('alert')
|
||||
demoTest('alert');
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
)
|
||||
);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -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>
|
||||
)
|
||||
);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
@ -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 });
|
||||
});
|
||||
});
|
||||
|
@ -1,3 +1,3 @@
|
||||
import demoTest from '../../../tests/shared/demoTest'
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('anchor')
|
||||
demoTest('anchor');
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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',
|
||||
})
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -1,3 +1,3 @@
|
||||
import demoTest from '../../../tests/shared/demoTest'
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('auto-complete')
|
||||
demoTest('auto-complete');
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -1,3 +1,3 @@
|
||||
import demoTest from '../../../tests/shared/demoTest'
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('avatar')
|
||||
demoTest('avatar');
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -1,3 +1,3 @@
|
||||
import demoTest from '../../../tests/shared/demoTest'
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('back-top')
|
||||
demoTest('back-top');
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -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>;
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -1,3 +1,3 @@
|
||||
import demoTest from '../../../tests/shared/demoTest'
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('badge')
|
||||
demoTest('badge');
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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>;
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -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;
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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'] });
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -1,3 +1,3 @@
|
||||
import demoTest from '../../../tests/shared/demoTest'
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('button')
|
||||
demoTest('button');
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user