├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .postcssrc.js ├── .travis.yml ├── LICENCE.md ├── README.md ├── _config.yml ├── build ├── build.js ├── check-versions.js ├── dev-client.js ├── dev-server.js ├── utils.js ├── vue-loader.conf.js ├── webpack.base.conf.js ├── webpack.dev.conf.js ├── webpack.prod.conf.js └── webpack.test.conf.js ├── config ├── dev.env.js ├── index.js ├── prod.env.js └── test.env.js ├── dev-example ├── App.vue ├── FormGeneratorRoute.vue ├── TestRoute.vue ├── WizardRoute.vue ├── loader.css └── main.js ├── dist ├── [object Object] ├── example │ └── index.html ├── vue-form-wizard.js └── vue-form-wizard.min.css ├── docs ├── .nojekyll ├── README.md └── index.html ├── index.html ├── package-lock.json ├── package.json ├── src ├── assets │ ├── form-wizard │ │ ├── _buttons.scss │ │ ├── _mixins.scss │ │ ├── _navs-pagination.scss │ │ ├── _variables.scss │ │ ├── _wizard-card.scss │ │ ├── bs_button.scss │ │ ├── bs_nav_pills.scss │ │ ├── bs_progress_bar.scss │ │ ├── bs_variables.scss │ │ └── mixins │ │ │ ├── _buttons.scss │ │ │ ├── _transparency.scss │ │ │ ├── _vendor-prefixes.scss │ │ │ └── _wizard-size.scss │ └── wizard.scss ├── components │ ├── FormWizard.vue │ ├── TabContent.vue │ ├── WizardButton.vue │ ├── WizardStep.vue │ └── helpers.js └── index.js ├── static ├── .gitkeep ├── fonts │ ├── themify.eot │ ├── themify.svg │ ├── themify.ttf │ └── themify.woff └── themify-icons.css ├── test ├── e2e │ ├── custom-assertions │ │ └── elementCount.js │ ├── nightwatch.conf.js │ ├── runner.js │ └── specs │ │ └── test.js └── unit │ ├── .eslintrc │ ├── index.js │ ├── karma.conf.js │ └── specs │ └── FormWizard.spec.js ├── types ├── FormWizard.ts ├── TabContent.ts ├── WizardStep.ts └── index.d.ts ├── webpack.build.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { "modules": false }], 4 | "stage-2" 5 | ], 6 | "plugins": ["transform-runtime"], 7 | "comments": false, 8 | "env": { 9 | "test": { 10 | "presets": ["env", "stage-2"], 11 | "plugins": [ "istanbul" ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | config/*.js 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // http://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | sourceType: 'module' 8 | }, 9 | env: { 10 | browser: true, 11 | }, 12 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style 13 | extends: 'standard', 14 | // required to lint *.vue files 15 | plugins: [ 16 | 'html' 17 | ], 18 | // add your custom rules here 19 | 'rules': { 20 | // allow paren-less arrow functions 21 | 'arrow-parens': 0, 22 | // allow async-await 23 | 'generator-star-spacing': 0, 24 | // allow debugger during development 25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | *.scss linguist-language=Vue 3 | 4 | # Standard to msysgit 5 | *.doc diff=astextplain 6 | *.DOC diff=astextplain 7 | *.docx diff=astextplain 8 | *.DOCX diff=astextplain 9 | *.dot diff=astextplain 10 | *.DOT diff=astextplain 11 | *.pdf diff=astextplain 12 | *.PDF diff=astextplain 13 | *.rtf diff=astextplain 14 | *.RTF diff=astextplain 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | test/unit/coverage 7 | test/e2e/reports 8 | selenium-debug.log 9 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserlist" field in package.json 6 | "autoprefixer": {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | #cache: 3 | # directories: 4 | # - node_modules 5 | node_js: 6 | - "8" 7 | after_success: 8 | - npm run coverall 9 | -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Cristi Jora 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

Vue form wizard

3 | 4 |

5 | 6 | 7 | Coverage Status 8 | 9 | 10 | 11 | 12 |

13 | 14 | A dynamic wizard to split your forms easier 15 | 16 | Vue-form-wizard is a vue based component with no external depenendcies which simplifies tab wizard management. 17 | 18 | ## Demos: 19 | Basic [demo](https://jsfiddle.net/bt5dhqtf/97/) 20 | 21 | Other demos: 22 | * [Squared steps](https://jsfiddle.net/bt5dhqtf/98/) 23 | * [Tabbed steps](https://jsfiddle.net/bt5dhqtf/99/) 24 | * [Sizing](https://jsfiddle.net/CristiJ/bt5dhqtf/924/) 25 | * [Step index](https://jsfiddle.net/bt5dhqtf/100/) Start at any step. Note: start-index is zero-based and the count starts at 0 26 | * [Custom button and title text](https://jsfiddle.net/bt5dhqtf/101/) 27 | * [Custom title slot](https://jsfiddle.net/bt5dhqtf/102/) 28 | * [Customized buttons with slots](https://jsfiddle.net/bt5dhqtf/103/) Replace stuff you don't like 29 | * [Call a function before tab switch](https://jsfiddle.net/bt5dhqtf/105/) 30 | * [Complete form example](https://jsfiddle.net/CristiJ/bt5dhqtf/286/) integrated with [vue-form-generator](https://github.com/icebob/vue-form-generator) 31 | * [Element UI form integration](https://codesandbox.io/s/l9mz5zn7yz) 32 | * [Vuelidate integration](https://jsfiddle.net/CristiJ/bt5dhqtf/1119/) 33 | * [Dynamic components for tabs](https://jsfiddle.net/bt5dhqtf/973/) 34 | * [Vue router integration](https://jsfiddle.net/bt5dhqtf/267/) You can place a `router-view` inside the wizard and have a separate page per tab. A `route` prop must be passed to the tabs you want to handle certain tabs 35 | * [Async validation with error message](https://jsfiddle.net/CristiJ/bt5dhqtf/298/) `before-change` `beforeChange(): boolean | Promise` can accept a promise that resolves with a boolean. Resolving with a truthy value, will trigger the navigation to next step. Rejecting with a message, will set an internal message that can be handled and displayed if needed. 36 | * [Animations with animate.css](https://jsfiddle.net/bt5dhqtf/1478/) 37 | * [Fancy step animations with lottie](https://codesandbox.io/s/3y0voo2oxp) 38 | 39 | In browser [playground](https://cristijora.github.io/vue-form-wizard/#/?id=playground) 40 | 41 | # [Documentation](https://binarcode.github.io/vue-form-wizard/#/) 42 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | require('./check-versions')() 2 | 3 | process.env.NODE_ENV = 'production' 4 | 5 | var ora = require('ora') 6 | var rm = require('rimraf') 7 | var path = require('path') 8 | var chalk = require('chalk') 9 | var webpack = require('webpack') 10 | var config = require('../config') 11 | var webpackConfig = require('./webpack.prod.conf') 12 | 13 | var spinner = ora('building for production...') 14 | spinner.start() 15 | 16 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 17 | if (err) throw err 18 | webpack(webpackConfig, function (err, stats) { 19 | spinner.stop() 20 | if (err) throw err 21 | process.stdout.write(stats.toString({ 22 | colors: true, 23 | modules: false, 24 | children: false, 25 | chunks: false, 26 | chunkModules: false 27 | }) + '\n\n') 28 | 29 | console.log(chalk.cyan(' Build complete.\n')) 30 | console.log(chalk.yellow( 31 | ' Tip: built files are meant to be served over an HTTP server.\n' + 32 | ' Opening index.html over file:// won\'t work.\n' 33 | )) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /build/check-versions.js: -------------------------------------------------------------------------------- 1 | var chalk = require('chalk') 2 | var semver = require('semver') 3 | var packageConfig = require('../package.json') 4 | var shell = require('shelljs') 5 | function exec (cmd) { 6 | return require('child_process').execSync(cmd).toString().trim() 7 | } 8 | 9 | var versionRequirements = [ 10 | { 11 | name: 'node', 12 | currentVersion: semver.clean(process.version), 13 | versionRequirement: packageConfig.engines.node 14 | }, 15 | ] 16 | 17 | if (shell.which('npm')) { 18 | versionRequirements.push({ 19 | name: 'npm', 20 | currentVersion: exec('npm --version'), 21 | versionRequirement: packageConfig.engines.npm 22 | }) 23 | } 24 | 25 | module.exports = function () { 26 | var warnings = [] 27 | for (var i = 0; i < versionRequirements.length; i++) { 28 | var mod = versionRequirements[i] 29 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 30 | warnings.push(mod.name + ': ' + 31 | chalk.red(mod.currentVersion) + ' should be ' + 32 | chalk.green(mod.versionRequirement) 33 | ) 34 | } 35 | } 36 | 37 | if (warnings.length) { 38 | console.log('') 39 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 40 | console.log() 41 | for (var i = 0; i < warnings.length; i++) { 42 | var warning = warnings[i] 43 | console.log(' ' + warning) 44 | } 45 | console.log() 46 | process.exit(1) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /build/dev-client.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | require('eventsource-polyfill') 3 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 4 | 5 | hotClient.subscribe(function (event) { 6 | if (event.action === 'reload') { 7 | window.location.reload() 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /build/dev-server.js: -------------------------------------------------------------------------------- 1 | require('./check-versions')() 2 | 3 | var config = require('../config') 4 | if (!process.env.NODE_ENV) { 5 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) 6 | } 7 | 8 | var opn = require('opn') 9 | var path = require('path') 10 | var express = require('express') 11 | var webpack = require('webpack') 12 | var proxyMiddleware = require('http-proxy-middleware') 13 | var webpackConfig = process.env.NODE_ENV === 'testing' 14 | ? require('./webpack.prod.conf') 15 | : require('./webpack.dev.conf') 16 | 17 | // default port where dev server listens for incoming traffic 18 | var port = process.env.PORT || config.dev.port 19 | // automatically open browser, if not set will be false 20 | var autoOpenBrowser = !!config.dev.autoOpenBrowser 21 | // Define HTTP proxies to your custom API backend 22 | // https://github.com/chimurai/http-proxy-middleware 23 | var proxyTable = config.dev.proxyTable 24 | 25 | var app = express() 26 | var compiler = webpack(webpackConfig) 27 | 28 | var devMiddleware = require('webpack-dev-middleware')(compiler, { 29 | publicPath: webpackConfig.output.publicPath, 30 | quiet: true 31 | }) 32 | 33 | var hotMiddleware = require('webpack-hot-middleware')(compiler, { 34 | log: () => {} 35 | }) 36 | // force page reload when html-webpack-plugin template changes 37 | compiler.plugin('compilation', function (compilation) { 38 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { 39 | hotMiddleware.publish({ action: 'reload' }) 40 | cb() 41 | }) 42 | }) 43 | 44 | // proxy api requests 45 | Object.keys(proxyTable).forEach(function (context) { 46 | var options = proxyTable[context] 47 | if (typeof options === 'string') { 48 | options = { target: options } 49 | } 50 | app.use(proxyMiddleware(options.filter || context, options)) 51 | }) 52 | 53 | // handle fallback for HTML5 history API 54 | app.use(require('connect-history-api-fallback')()) 55 | 56 | // serve webpack bundle output 57 | app.use(devMiddleware) 58 | 59 | // enable hot-reload and state-preserving 60 | // compilation error display 61 | app.use(hotMiddleware) 62 | 63 | // serve pure static assets 64 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) 65 | app.use(staticPath, express.static('./static')) 66 | 67 | var uri = 'http://localhost:' + port 68 | 69 | var _resolve 70 | var readyPromise = new Promise(resolve => { 71 | _resolve = resolve 72 | }) 73 | 74 | console.log('> Starting dev server...') 75 | devMiddleware.waitUntilValid(() => { 76 | console.log('> Listening at ' + uri + '\n') 77 | // when env is testing, don't need open it 78 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { 79 | opn(uri) 80 | } 81 | _resolve() 82 | }) 83 | 84 | var server = app.listen(port) 85 | 86 | module.exports = { 87 | ready: readyPromise, 88 | close: () => { 89 | server.close() 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | 5 | exports.assetsPath = function (_path) { 6 | var assetsSubDirectory = process.env.NODE_ENV === 'production' 7 | ? config.build.assetsSubDirectory 8 | : config.dev.assetsSubDirectory 9 | return path.posix.join(assetsSubDirectory, _path) 10 | } 11 | 12 | exports.cssLoaders = function (options) { 13 | options = options || {} 14 | 15 | var cssLoader = { 16 | loader: 'css-loader', 17 | options: { 18 | minimize: process.env.NODE_ENV === 'production', 19 | sourceMap: options.sourceMap 20 | } 21 | } 22 | 23 | // generate loader string to be used with extract text plugin 24 | function generateLoaders (loader, loaderOptions) { 25 | var loaders = [cssLoader] 26 | if (loader) { 27 | loaders.push({ 28 | loader: loader + '-loader', 29 | options: Object.assign({}, loaderOptions, { 30 | sourceMap: options.sourceMap 31 | }) 32 | }) 33 | } 34 | 35 | // Extract CSS when that option is specified 36 | // (which is the case during production build) 37 | if (options.extract) { 38 | return ExtractTextPlugin.extract({ 39 | use: loaders, 40 | fallback: 'vue-style-loader' 41 | }) 42 | } else { 43 | return ['vue-style-loader'].concat(loaders) 44 | } 45 | } 46 | 47 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 48 | return { 49 | css: generateLoaders(), 50 | postcss: generateLoaders(), 51 | less: generateLoaders('less'), 52 | sass: generateLoaders('sass', { indentedSyntax: true }), 53 | scss: generateLoaders('sass'), 54 | stylus: generateLoaders('stylus'), 55 | styl: generateLoaders('stylus') 56 | } 57 | } 58 | 59 | // Generate loaders for standalone style files (outside of .vue) 60 | exports.styleLoaders = function (options) { 61 | var output = [] 62 | var loaders = exports.cssLoaders(options) 63 | for (var extension in loaders) { 64 | var loader = loaders[extension] 65 | output.push({ 66 | test: new RegExp('\\.' + extension + '$'), 67 | use: loader 68 | }) 69 | } 70 | return output 71 | } 72 | -------------------------------------------------------------------------------- /build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils') 2 | var config = require('../config') 3 | var isProduction = process.env.NODE_ENV === 'production' 4 | 5 | module.exports = { 6 | loaders: utils.cssLoaders({ 7 | sourceMap: isProduction 8 | ? config.build.productionSourceMap 9 | : config.dev.cssSourceMap, 10 | extract: isProduction 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var utils = require('./utils') 3 | var config = require('../config') 4 | var vueLoaderConfig = require('./vue-loader.conf') 5 | 6 | function resolve (dir) { 7 | return path.join(__dirname, '..', dir) 8 | } 9 | 10 | module.exports = { 11 | entry: { 12 | app: './dev-example/main.js' 13 | }, 14 | output: { 15 | path: config.build.assetsRoot, 16 | filename: '[name].js', 17 | publicPath: process.env.NODE_ENV === 'production' 18 | ? config.build.assetsPublicPath 19 | : config.dev.assetsPublicPath 20 | }, 21 | resolve: { 22 | extensions: ['.js', '.vue', '.json'], 23 | alias: { 24 | 'vue$': 'vue/dist/vue.esm.js', 25 | '@': resolve('src') 26 | } 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(js|vue)$/, 32 | loader: 'eslint-loader', 33 | enforce: 'pre', 34 | include: [resolve('src'), resolve('test')], 35 | options: { 36 | formatter: require('eslint-friendly-formatter') 37 | } 38 | }, 39 | { 40 | test: /\.vue$/, 41 | loader: 'vue-loader', 42 | options: vueLoaderConfig 43 | }, 44 | { 45 | test: /\.js$/, 46 | loader: 'babel-loader', 47 | include: [resolve('src'), resolve('test')] 48 | }, 49 | { 50 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 51 | loader: 'url-loader', 52 | options: { 53 | limit: 10000, 54 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 55 | } 56 | }, 57 | { 58 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 59 | loader: 'url-loader', 60 | options: { 61 | limit: 10000, 62 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 63 | } 64 | } 65 | ] 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils') 2 | var webpack = require('webpack') 3 | var config = require('../config') 4 | var merge = require('webpack-merge') 5 | var baseWebpackConfig = require('./webpack.base.conf') 6 | var HtmlWebpackPlugin = require('html-webpack-plugin') 7 | var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 8 | 9 | // add hot-reload related code to entry chunks 10 | Object.keys(baseWebpackConfig.entry).forEach(function (name) { 11 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) 12 | }) 13 | 14 | module.exports = merge(baseWebpackConfig, { 15 | module: { 16 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) 17 | }, 18 | // cheap-module-eval-source-map is faster for development 19 | devtool: '#cheap-module-eval-source-map', 20 | plugins: [ 21 | new webpack.DefinePlugin({ 22 | 'process.env': config.dev.env 23 | }), 24 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 25 | new webpack.HotModuleReplacementPlugin(), 26 | new webpack.NoEmitOnErrorsPlugin(), 27 | // https://github.com/ampedandwired/html-webpack-plugin 28 | new HtmlWebpackPlugin({ 29 | filename: 'index.html', 30 | template: 'index.html', 31 | inject: true 32 | }), 33 | new FriendlyErrorsPlugin() 34 | ] 35 | }) 36 | -------------------------------------------------------------------------------- /build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var utils = require('./utils') 3 | var webpack = require('webpack') 4 | var config = require('../config') 5 | var merge = require('webpack-merge') 6 | var baseWebpackConfig = require('./webpack.base.conf') 7 | var CopyWebpackPlugin = require('copy-webpack-plugin') 8 | var HtmlWebpackPlugin = require('html-webpack-plugin') 9 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 10 | var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 11 | 12 | var env = process.env.NODE_ENV === 'testing' 13 | ? require('../config/test.env') 14 | : config.build.env 15 | 16 | var webpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ 19 | sourceMap: config.build.productionSourceMap, 20 | extract: true 21 | }) 22 | }, 23 | devtool: config.build.productionSourceMap ? '#source-map' : false, 24 | output: { 25 | path: config.build.assetsRoot, 26 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 27 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 28 | }, 29 | plugins: [ 30 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 31 | new webpack.DefinePlugin({ 32 | 'process.env': env 33 | }), 34 | new webpack.optimize.UglifyJsPlugin({ 35 | compress: { 36 | warnings: false 37 | }, 38 | sourceMap: true 39 | }), 40 | // extract css into its own file 41 | new ExtractTextPlugin({ 42 | filename: utils.assetsPath('css/[name].[contenthash].css') 43 | }), 44 | // Compress extracted CSS. We are using this plugin so that possible 45 | // duplicated CSS from different components can be deduped. 46 | new OptimizeCSSPlugin({ 47 | cssProcessorOptions: { 48 | safe: true 49 | } 50 | }), 51 | // generate dist index.html with correct asset hash for caching. 52 | // you can customize output by editing /index.html 53 | // see https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: process.env.NODE_ENV === 'testing' 56 | ? 'index.html' 57 | : config.build.index, 58 | template: 'index.html', 59 | inject: true, 60 | minify: { 61 | removeComments: true, 62 | collapseWhitespace: true, 63 | removeAttributeQuotes: true 64 | // more options: 65 | // https://github.com/kangax/html-minifier#options-quick-reference 66 | }, 67 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 68 | chunksSortMode: 'dependency' 69 | }), 70 | // split vendor js into its own file 71 | new webpack.optimize.CommonsChunkPlugin({ 72 | name: 'vendor', 73 | minChunks: function (module, count) { 74 | // any required modules inside node_modules are extracted to vendor 75 | return ( 76 | module.resource && 77 | /\.js$/.test(module.resource) && 78 | module.resource.indexOf( 79 | path.join(__dirname, '../node_modules') 80 | ) === 0 81 | ) 82 | } 83 | }), 84 | // extract webpack runtime and module manifest to its own file in order to 85 | // prevent vendor hash from being updated whenever app bundle is updated 86 | new webpack.optimize.CommonsChunkPlugin({ 87 | name: 'manifest', 88 | chunks: ['vendor'] 89 | }), 90 | // copy custom static assets 91 | new CopyWebpackPlugin([ 92 | { 93 | from: path.resolve(__dirname, '../static'), 94 | to: config.build.assetsSubDirectory, 95 | ignore: ['.*'] 96 | } 97 | ]) 98 | ] 99 | }) 100 | 101 | if (config.build.productionGzip) { 102 | var CompressionWebpackPlugin = require('compression-webpack-plugin') 103 | 104 | webpackConfig.plugins.push( 105 | new CompressionWebpackPlugin({ 106 | asset: '[path].gz[query]', 107 | algorithm: 'gzip', 108 | test: new RegExp( 109 | '\\.(' + 110 | config.build.productionGzipExtensions.join('|') + 111 | ')$' 112 | ), 113 | threshold: 10240, 114 | minRatio: 0.8 115 | }) 116 | ) 117 | } 118 | 119 | if (config.build.bundleAnalyzerReport) { 120 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 121 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 122 | } 123 | 124 | module.exports = webpackConfig 125 | -------------------------------------------------------------------------------- /build/webpack.test.conf.js: -------------------------------------------------------------------------------- 1 | // This is the webpack config used for unit tests. 2 | 3 | var utils = require('./utils') 4 | var webpack = require('webpack') 5 | var merge = require('webpack-merge') 6 | var baseConfig = require('./webpack.base.conf') 7 | 8 | var webpackConfig = merge(baseConfig, { 9 | // use inline sourcemap for karma-sourcemap-loader 10 | module: { 11 | rules: utils.styleLoaders() 12 | }, 13 | devtool: '#inline-source-map', 14 | resolveLoader: { 15 | alias: { 16 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option 17 | // see discussion at https://github.com/vuejs/vue-loader/issues/724 18 | 'scss-loader': 'sass-loader' 19 | } 20 | }, 21 | plugins: [ 22 | new webpack.DefinePlugin({ 23 | 'process.env': require('../config/test.env') 24 | }) 25 | ] 26 | }) 27 | 28 | // no need for app entry during tests 29 | delete webpackConfig.entry 30 | 31 | module.exports = webpackConfig 32 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: '/', 11 | productionSourceMap: true, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'], 18 | // Run the build command with an extra argument to 19 | // View the bundle analyzer report after build finishes: 20 | // `npm run build --report` 21 | // Set to `true` or `false` to always turn it on or off 22 | bundleAnalyzerReport: process.env.npm_config_report 23 | }, 24 | dev: { 25 | env: require('./dev.env'), 26 | port: 8080, 27 | autoOpenBrowser: true, 28 | assetsSubDirectory: 'static', 29 | assetsPublicPath: '/', 30 | proxyTable: {}, 31 | // CSS Sourcemaps off by default because relative paths are "buggy" 32 | // with this option, according to the CSS-Loader README 33 | // (https://github.com/webpack/css-loader#sourcemaps) 34 | // In our experience, they generally work as expected, 35 | // just be aware of this issue when enabling this option. 36 | cssSourceMap: false 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var devEnv = require('./dev.env') 3 | 4 | module.exports = merge(devEnv, { 5 | NODE_ENV: '"testing"' 6 | }) 7 | -------------------------------------------------------------------------------- /dev-example/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 14 | 46 | -------------------------------------------------------------------------------- /dev-example/FormGeneratorRoute.vue: -------------------------------------------------------------------------------- 1 | 31 | 161 | 163 | -------------------------------------------------------------------------------- /dev-example/TestRoute.vue: -------------------------------------------------------------------------------- 1 | 7 | 10 | 12 | -------------------------------------------------------------------------------- /dev-example/WizardRoute.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 64 | 67 | 103 | -------------------------------------------------------------------------------- /dev-example/loader.css: -------------------------------------------------------------------------------- 1 | .loader, 2 | .loader:after { 3 | border-radius: 50%; 4 | width: 10em; 5 | height: 10em; 6 | } 7 | .loader { 8 | margin: 60px auto; 9 | font-size: 10px; 10 | position: relative; 11 | text-indent: -9999em; 12 | border-top: 1.1em solid rgba(255, 255, 255, 0.2); 13 | border-right: 1.1em solid rgba(255, 255, 255, 0.2); 14 | border-bottom: 1.1em solid rgba(255, 255, 255, 0.2); 15 | border-left: 1.1em solid #e74c3c; 16 | -webkit-transform: translateZ(0); 17 | -ms-transform: translateZ(0); 18 | transform: translateZ(0); 19 | -webkit-animation: load8 1.1s infinite linear; 20 | animation: load8 1.1s infinite linear; 21 | } 22 | @-webkit-keyframes load8 { 23 | 0% { 24 | -webkit-transform: rotate(0deg); 25 | transform: rotate(0deg); 26 | } 27 | 100% { 28 | -webkit-transform: rotate(360deg); 29 | transform: rotate(360deg); 30 | } 31 | } 32 | @keyframes load8 { 33 | 0% { 34 | -webkit-transform: rotate(0deg); 35 | transform: rotate(0deg); 36 | } 37 | 100% { 38 | -webkit-transform: rotate(360deg); 39 | transform: rotate(360deg); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /dev-example/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import VueRouter from 'vue-router' 5 | import App from './App.vue' 6 | import FormWizard from '../src/index' 7 | import WizardRoute from './WizardRoute.vue' 8 | import FormGeneratorRoute from './FormGeneratorRoute.vue' 9 | import TestRoute from './TestRoute.vue' 10 | 11 | const First = {template: '
First page
'} 12 | const Second = {template: '
Second page
'} 13 | const Third = {template: '
Third page
'} 14 | 15 | const router = new VueRouter({ 16 | mode: 'history', 17 | routes: [ 18 | { 19 | path: '/', component: WizardRoute, 20 | children: [ 21 | {path: '/first', component: First}, 22 | {path: '/second', component: Second}, 23 | {path: '/third', component: Third} 24 | ] 25 | }, 26 | {path: '/test', component: TestRoute}, 27 | {path: '/form-generator', component: FormGeneratorRoute}, 28 | 29 | ] 30 | }) 31 | Vue.use(VueRouter) 32 | Vue.use(FormWizard) 33 | 34 | Vue.config.productionTip = false 35 | 36 | /* eslint-disable no-new */ 37 | new Vue({ 38 | router, 39 | el: '#app', 40 | render: h => h(App) 41 | }) 42 | -------------------------------------------------------------------------------- /dist/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | webpack-vue-clean 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 16 | My first tab content 17 | 18 | 20 | My second tab content 21 | 22 | 24 | Yuhuuu! This seems pretty damn simple 25 | 26 | 27 |
28 |
29 | 30 | 31 | 32 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /dist/vue-form-wizard.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.VueFormWizard=e():t.VueFormWizard=e()}("undefined"!=typeof self?self:this,function(){return function(t){function e(n){if(a[n])return a[n].exports;var i=a[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,e),i.l=!0,i.exports}var a={};return e.m=t,e.c=a,e.d=function(t,a,n){e.o(t,a)||Object.defineProperty(t,a,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var a=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(a,"a",a),a},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=7)}([function(t,e){t.exports=function(t,e,a,n,i,r){var s,o=t=t||{},c=typeof t.default;"object"!==c&&"function"!==c||(s=t,o=t.default);var u="function"==typeof o?o.options:o;e&&(u.render=e.render,u.staticRenderFns=e.staticRenderFns,u._compiled=!0),a&&(u.functional=!0),i&&(u._scopeId=i);var l;if(r?(l=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),n&&n.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(r)},u._ssrRegister=l):n&&(l=n),l){var d=u.functional,b=d?u.render:u.beforeCreate;d?(u._injectStyles=l,u.render=function(t,e){return l.call(e),b(t,e)}):u.beforeCreate=b?[].concat(b,l):[l]}return{esModule:s,exports:o,options:u}}},function(t,e,a){"use strict";var n=a(2),i=a(4),r=a(14);e.a={name:"form-wizard",components:{WizardButton:n.a,WizardStep:i.a},props:{id:{type:String,default:"fw_"+(new Date).valueOf()},title:{type:String,default:"Awesome Wizard"},subtitle:{type:String,default:"Split a complicated flow in multiple steps"},nextButtonText:{type:String,default:"Next"},backButtonText:{type:String,default:"Back"},finishButtonText:{type:String,default:"Finish"},hideButtons:{type:Boolean,default:!1},validateOnBack:Boolean,color:{type:String,default:"#e74c3c"},errorColor:{type:String,default:"#8b0000"},shape:{type:String,default:"circle"},layout:{type:String,default:"horizontal"},stepsClasses:{type:[String,Array],default:""},stepSize:{type:String,default:"md",validator:function(t){return-1!==["xs","sm","md","lg"].indexOf(t)}},transition:{type:String,default:""},startIndex:{type:Number,default:0,validator:function(t){return t>=0}}},provide:function(){return{addTab:this.addTab,removeTab:this.removeTab}},data:function(){return{activeTabIndex:0,currentPercentage:0,maxStep:0,loading:!1,tabs:[]}},computed:{slotProps:function(){return{nextTab:this.nextTab,prevTab:this.prevTab,activeTabIndex:this.activeTabIndex,isLastStep:this.isLastStep,fillButtonStyle:this.fillButtonStyle}},tabCount:function(){return this.tabs.length},isLastStep:function(){return this.activeTabIndex===this.tabCount-1},isVertical:function(){return"vertical"===this.layout},displayPrevButton:function(){return 0!==this.activeTabIndex},stepPercentage:function(){return 1/(2*this.tabCount)*100},progressBarStyle:function(){return{backgroundColor:this.color,width:this.progress+"%",color:this.color}},fillButtonStyle:function(){return{backgroundColor:this.color,borderColor:this.color,color:"white"}},progress:function(){return this.activeTabIndex>0?this.stepPercentage*(2*this.activeTabIndex+1):this.stepPercentage}},methods:{emitTabChange:function(t,e){this.$emit("on-change",t,e),this.$emit("update:startIndex",e)},addTab:function(t){var e=this.$slots.default.indexOf(t.$vnode);t.tabId=""+t.title.replace(/ /g,"")+e,this.tabs.splice(e,0,t),e-1&&(a===this.activeTabIndex&&(this.maxStep=this.activeTabIndex-1,this.changeTab(this.activeTabIndex,this.activeTabIndex-1)),athis.activeTabIndex;if(t<=this.maxStep){var n=function n(){a&&t-e.activeTabIndex>1?(e.changeTab(e.activeTabIndex,e.activeTabIndex+1),e.beforeTabChange(e.activeTabIndex,n)):(e.changeTab(e.activeTabIndex,t),e.afterTabChange(e.activeTabIndex))};a?this.beforeTabChange(this.activeTabIndex,n):(this.setValidationError(null),n())}return t<=this.maxStep},nextTab:function(){var t=this,e=function(){t.activeTabIndex0&&(t.setValidationError(null),t.changeTab(t.activeTabIndex,t.activeTabIndex-1))};this.validateOnBack?this.beforeTabChange(this.activeTabIndex,e):e()},focusNextTab:function(){var t=Object(r.b)(this.tabs);if(-1!==t&&t0){var e=this.tabs[t-1].tabId;Object(r.a)(e)}},setLoading:function(t){this.loading=t,this.$emit("on-loading",t)},setValidationError:function(t){this.tabs[this.activeTabIndex].validationError=t,this.$emit("on-error",t)},validateBeforeChange:function(t,e){var a=this;if(this.setValidationError(null),Object(r.c)(t))this.setLoading(!0),t.then(function(t){a.setLoading(!1);var n=!0===t;a.executeBeforeChange(n,e)}).catch(function(t){a.setLoading(!1),a.setValidationError(t)});else{var n=!0===t;this.executeBeforeChange(n,e)}},executeBeforeChange:function(t,e){this.$emit("on-validate",t,this.activeTabIndex),t?e():this.tabs[this.activeTabIndex].validationError="error"},beforeTabChange:function(t,e){if(!this.loading){var a=this.tabs[t];if(a&&void 0!==a.beforeChange){var n=a.beforeChange();this.validateBeforeChange(n,e)}else e()}},afterTabChange:function(t){if(!this.loading){var e=this.tabs[t];e&&void 0!==e.afterChange&&e.afterChange()}},changeTab:function(t,e){var a=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],n=this.tabs[t],i=this.tabs[e];return n&&(n.active=!1),i&&(i.active=!0),a&&this.activeTabIndex!==e&&this.emitTabChange(t,e),this.activeTabIndex=e,this.activateTabAndCheckStep(this.activeTabIndex),!0},tryChangeRoute:function(t){this.$router&&t.route&&this.$router.push(t.route)},checkRouteChange:function(t){var e=-1,a=this.tabs.find(function(a,n){var i=a.route===t;return i&&(e=n),i});if(a&&!a.active){var n=e>this.activeTabIndex;this.navigateToTab(e,n)}},deactivateTabs:function(){this.tabs.forEach(function(t){t.active=!1})},activateTab:function(t){this.deactivateTabs();var e=this.tabs[t];e&&(e.active=!0,e.checked=!0,this.tryChangeRoute(e))},activateTabAndCheckStep:function(t){this.activateTab(t),t>this.maxStep&&(this.maxStep=t),this.activeTabIndex=t},initializeTabs:function(){this.tabs.length>0&&0===this.startIndex&&this.activateTab(this.activeTabIndex),this.startIndex0&&void 0!==arguments[0]?arguments[0]:[],e=n();return t.findIndex(function(t){return t.tabId===e})}function r(t){document.getElementById(t).focus()}function s(t){return t.then&&"function"==typeof t.then}e.b=i,e.a=r,e.c=s},function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"vue-form-wizard",class:[t.stepSize,{vertical:t.isVertical}],attrs:{id:t.id?t.id:""},on:{keyup:[function(e){return"button"in e||!t._k(e.keyCode,"right",39,e.key)?"button"in e&&2!==e.button?null:void t.focusNextTab(e):null},function(e){return"button"in e||!t._k(e.keyCode,"left",37,e.key)?"button"in e&&0!==e.button?null:void t.focusPrevTab(e):null}]}},[t.$slots.title?a("div",{staticClass:"wizard-header"},[t._t("title",[a("h4",{staticClass:"wizard-title"},[t._v(t._s(t.title))]),t._v(" "),a("p",{staticClass:"category"},[t._v(t._s(t.subtitle))])])],2):t._e(),t._v(" "),a("div",{staticClass:"wizard-navigation"},[t.isVertical?t._e():a("div",{staticClass:"wizard-progress-with-circle"},[a("div",{staticClass:"wizard-progress-bar",style:t.progressBarStyle})]),t._v(" "),a("ul",{staticClass:"wizard-nav wizard-nav-pills",class:t.stepsClasses,attrs:{role:"tablist"}},[t._l(t.tabs,function(e,n){return t._t("step",[a("wizard-step",{attrs:{tab:e,"step-size":t.stepSize,transition:t.transition,index:n},nativeOn:{click:function(e){t.navigateToTab(n)},keyup:function(e){if(!("button"in e)&&t._k(e.keyCode,"enter",13,e.key))return null;t.navigateToTab(n)}}})],{tab:e,index:n,navigateToTab:t.navigateToTab,stepSize:t.stepSize,transition:t.transition})})],2),t._v(" "),a("div",{staticClass:"wizard-tab-content"},[t._t("default",null,null,t.slotProps)],2)]),t._v(" "),t.hideButtons?t._e():a("div",{staticClass:"wizard-card-footer clearfix"},[t._t("footer",[a("div",{staticClass:"wizard-footer-left"},[t.displayPrevButton?a("span",{attrs:{role:"button",tabindex:"0"},on:{click:t.prevTab,keyup:function(e){if(!("button"in e)&&t._k(e.keyCode,"enter",13,e.key))return null;t.prevTab(e)}}},[t._t("prev",[a("wizard-button",{style:t.fillButtonStyle,attrs:{disabled:t.loading}},[t._v("\n "+t._s(t.backButtonText)+"\n ")])],null,t.slotProps)],2):t._e(),t._v(" "),t._t("custom-buttons-left",null,null,t.slotProps)],2),t._v(" "),a("div",{staticClass:"wizard-footer-right"},[t._t("custom-buttons-right",null,null,t.slotProps),t._v(" "),t.isLastStep?a("span",{attrs:{role:"button",tabindex:"0"},on:{click:t.nextTab,keyup:function(e){if(!("button"in e)&&t._k(e.keyCode,"enter",13,e.key))return null;t.nextTab(e)}}},[t._t("finish",[a("wizard-button",{style:t.fillButtonStyle},[t._v("\n "+t._s(t.finishButtonText)+"\n ")])],null,t.slotProps)],2):a("span",{attrs:{role:"button",tabindex:"0"},on:{click:t.nextTab,keyup:function(e){if(!("button"in e)&&t._k(e.keyCode,"enter",13,e.key))return null;t.nextTab(e)}}},[t._t("next",[a("wizard-button",{style:t.fillButtonStyle,attrs:{disabled:t.loading}},[t._v("\n "+t._s(t.nextButtonText)+"\n ")])],null,t.slotProps)],2)],2)],null,t.slotProps)],2)])},i=[],r={render:n,staticRenderFns:i};e.a=r},function(t,e,a){"use strict";var n=a(6),i=a(17),r=a(0),s=r(n.a,i.a,!1,null,null,null);e.a=s.exports},function(t,e,a){"use strict";var n=function(){var t=this,e=t.$createElement;return(t._self._c||e)("div",{directives:[{name:"show",rawName:"v-show",value:t.active,expression:"active"}],staticClass:"wizard-tab-container",attrs:{role:"tabpanel",id:t.tabId,"aria-hidden":!t.active,"aria-labelledby":"step-"+t.tabId}},[t._t("default",null,{active:t.active})],2)},i=[],r={render:n,staticRenderFns:i};e.a=r}])}); -------------------------------------------------------------------------------- /dist/vue-form-wizard.min.css: -------------------------------------------------------------------------------- 1 | .vue-form-wizard .wizard-btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857;border-radius:4px}.vue-form-wizard .wizard-btn.disabled,.vue-form-wizard .wizard-btn[disabled],fieldset[disabled] .vue-form-wizard .wizard-btn{cursor:not-allowed;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.vue-form-wizard *{-webkit-box-sizing:border-box;box-sizing:border-box}.vue-form-wizard a{text-decoration:none}.vue-form-wizard .wizard-nav{margin-bottom:0;padding-left:0;list-style:none}.vue-form-wizard .wizard-nav>li{position:relative;display:block}.vue-form-wizard .wizard-nav>li>a{position:relative;display:block;padding:10px 15px}.vue-form-wizard .wizard-nav>li>a:focus,.vue-form-wizard .wizard-nav>li>a:hover{text-decoration:none;background-color:#eee}.vue-form-wizard .wizard-nav>li.disabled>a{color:#777}.vue-form-wizard .wizard-nav>li.disabled>a:focus,.vue-form-wizard .wizard-nav>li.disabled>a:hover{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.vue-form-wizard .wizard-progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;transition:width .6s ease}.vue-form-wizard .navbar .navbar-nav>li>a.wizard-btn,.vue-form-wizard .wizard-btn{-webkit-box-sizing:border-box;box-sizing:border-box;border-width:2px;background-color:transparent;font-size:14px;font-weight:600;padding:6px 12px;min-width:140px}.vue-form-wizard .navbar .navbar-nav>li>a.wizard-btn:focus,.vue-form-wizard .navbar .navbar-nav>li>a.wizard-btn:hover,.vue-form-wizard .wizard-btn:focus,.vue-form-wizard .wizard-btn:hover{outline:0!important}.vue-form-wizard .wizard-nav-pills{margin-top:0;position:relative;text-align:center;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.vue-form-wizard .wizard-nav-pills a,.vue-form-wizard .wizard-nav-pills li{-webkit-box-flex:1;-ms-flex:1;flex:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-positive:1;flex-grow:1}.vue-form-wizard .wizard-nav-pills>li>a,.vue-form-wizard .wizard-nav-pills a{display:-webkit-box;display:-ms-flexbox;display:flex}.vue-form-wizard .wizard-nav-pills>li>a{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding:0;margin:0 auto;color:rgba(0,0,0,.2);position:relative;top:3px}.vue-form-wizard .wizard-nav-pills>li>a:focus,.vue-form-wizard .wizard-nav-pills>li>a:hover{background-color:transparent;color:rgba(0,0,0,.2);outline:0!important}.vue-form-wizard .wizard-nav-pills>li>a.disabled{pointer-events:none;cursor:default}.vue-form-wizard .wizard-nav-pills>li.active>a,.vue-form-wizard .wizard-nav-pills>li.active>a:focus,.vue-form-wizard .wizard-nav-pills>li.active>a:hover{background-color:transparent;-webkit-transition:font-size .2s linear;transition:font-size .2s linear}.vue-form-wizard .wizard-nav-pills>li.active>a .wizard-icon,.vue-form-wizard .wizard-nav-pills>li.active>a:focus .wizard-icon,.vue-form-wizard .wizard-nav-pills>li.active>a:hover .wizard-icon{color:#fff;font-size:24px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-transition:all .2s linear;transition:all .2s linear}.vue-form-wizard{padding-bottom:20px}.vue-form-wizard .is_error{border-color:#c84513!important}.vue-form-wizard .is_error .icon-container{background:#c84513!important}.vue-form-wizard.xs .wizard-icon-circle{width:40px;height:40px;font-size:16px}.vue-form-wizard.xs .wizard-icon-circle.tab_shape{height:25px}.vue-form-wizard.xs .wizard-nav-pills>li.active>a .wizard-icon{font-size:16px}.vue-form-wizard.xs .wizard-navigation .wizard-progress-with-circle{position:relative;top:25px;height:4px}.vue-form-wizard.sm .wizard-icon-circle{width:50px;height:50px;font-size:20px}.vue-form-wizard.sm .wizard-icon-circle.tab_shape{height:30px}.vue-form-wizard.sm .wizard-nav-pills>li.active>a .wizard-icon{font-size:20px}.vue-form-wizard.sm .wizard-navigation .wizard-progress-with-circle{position:relative;top:30px;height:4px}.vue-form-wizard.md .wizard-icon-circle{width:70px;height:70px;font-size:24px}.vue-form-wizard.md .wizard-icon-circle.tab_shape{height:40px}.vue-form-wizard.md .wizard-nav-pills>li.active>a .wizard-icon{font-size:24px}.vue-form-wizard.md .wizard-navigation .wizard-progress-with-circle{position:relative;top:40px;height:4px}.vue-form-wizard.lg .wizard-icon-circle{width:90px;height:90px;font-size:28px}.vue-form-wizard.lg .wizard-icon-circle.tab_shape{height:50px}.vue-form-wizard.lg .wizard-nav-pills>li.active>a .wizard-icon{font-size:28px}.vue-form-wizard.lg .wizard-navigation .wizard-progress-with-circle{position:relative;top:50px;height:4px}.vue-form-wizard .wizard-icon-circle{font-size:18px;border:3px solid #f3f2ee;border-radius:50%;font-weight:600;width:70px;height:70px;background-color:#fff;position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-ms-flex-line-pack:center;align-content:center}.vue-form-wizard .wizard-icon-circle.square_shape{border-radius:0}.vue-form-wizard .wizard-icon-circle.tab_shape{width:100%;min-width:100px;height:40px;border:none;background-color:#f3f2ee;border-radius:0}.vue-form-wizard .wizard-icon-circle .wizard-icon-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-flex:1;-ms-flex:1;flex:1;border-radius:50%;margin:-3px}.vue-form-wizard .wizard-icon-circle .wizard-icon-container.square_shape{border-radius:0}.vue-form-wizard .wizard-icon-circle .wizard-icon-container.tab_shape{border-radius:0;margin:0}.vue-form-wizard .wizard-icon-circle .wizard-icon{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.vue-form-wizard .wizard-tab-content{min-height:100px;padding:30px 20px 10px}.vue-form-wizard .wizard-header{padding:15px;position:relative;border-radius:3px 3px 0 0;text-align:center}.vue-form-wizard .wizard-title{color:#252422;font-weight:300;margin:0;text-align:center}.vue-form-wizard .category{font-size:14px;font-weight:400;color:#9a9a9a;margin-bottom:0;text-align:center}.vue-form-wizard .wizard-navigation .wizard-progress-with-circle{position:relative;top:40px;height:4px}.vue-form-wizard .wizard-navigation .wizard-progress-with-circle .wizard-progress-bar{-webkit-box-shadow:none;box-shadow:none;-webkit-transition:width .3s ease;transition:width .3s ease}.vue-form-wizard .clearfix:after{content:"";clear:both;display:table}.vue-form-wizard .wizard-card-footer{padding:0 20px}.vue-form-wizard .wizard-card-footer .wizard-footer-left{float:left}.vue-form-wizard .wizard-card-footer .wizard-footer-right{float:right}@media screen and (max-width:350px){.vue-form-wizard .wizard-card-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.vue-form-wizard .wizard-card-footer .wizard-footer-left,.vue-form-wizard .wizard-card-footer .wizard-footer-right{float:none;-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.vue-form-wizard .wizard-card-footer .wizard-footer-right button{margin-top:10px}}.vue-form-wizard.vertical .wizard-card-footer{display:block}.vue-form-wizard.vertical .wizard-nav-pills{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.vue-form-wizard.vertical .wizard-navigation{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.vue-form-wizard.vertical .wizard-card-footer{padding-top:30px} -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BinarCode/vue-form-wizard/4ba261ade8a1c284681e2d9af3c1c838daa6c246/docs/.nojekyll -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | A dynamic form wizard to split your forms easier 2 | 3 | Vue-form-wizard is a vue based component with **no external depenendcies** which simplifies tab wizard management and allows you to focus on the functional part of your app rather than 4 | wasting time on details. Just forget about id's, external scripts and jQuery dependencies 5 | 6 | 7 | # Usage 8 | 9 | ## NPM 10 | `npm install vue-form-wizard` 11 | 12 | ## Direct script include 13 | Download the css and js files from `dist` folder or reference them directly from github (check jsfiddle links) 14 | ```html 15 | 16 | 17 | ``` 18 | ## Component registration 19 | ```js 20 | //global registration 21 | import VueFormWizard from 'vue-form-wizard' 22 | import 'vue-form-wizard/dist/vue-form-wizard.min.css' 23 | Vue.use(VueFormWizard) 24 | 25 | //local registration 26 | import {FormWizard, TabContent} from 'vue-form-wizard' 27 | import 'vue-form-wizard/dist/vue-form-wizard.min.css' 28 | //component code 29 | components: { 30 | FormWizard, 31 | TabContent 32 | } 33 | ``` 34 | ## Template usage 35 | 36 | ```html 37 | 38 | 39 | My first tab content 40 | 41 | 42 | My second tab content 43 | 44 | 45 | Yuhuuu! This seems pretty damn simple 46 | 47 | 48 | ``` 49 | ## Compatibility 50 | 51 | Vue-form-wizard works with Vue > 2.2, but the examples in the docs follow the latest Vue > 2.5 changes especially on the `slot` parts. 52 | If you use vue < 2.5.x, use `scope="props"` instead of `slot-scope="props"` for scoped slots. See [Vue 2.5 simplified scoped slots](https://gist.github.com/yyx990803/9bdff05e5468a60ced06c29c39114c6b#simplified-scoped-slots-usage) 53 | 54 | 55 | ## Props 56 | ### Form Wizard props 57 | ```js 58 | props: { 59 | title: { 60 | type: String, 61 | default: 'Awesome Wizard' 62 | }, 63 | subtitle: { 64 | type: String, 65 | default: 'Split a complicated flow in multiple steps' 66 | }, 67 | nextButtonText: { 68 | type: String, 69 | default: 'Next' 70 | }, 71 | backButtonText: { 72 | type: String, 73 | default: 'Back' 74 | }, 75 | finishButtonText: { 76 | type: String, 77 | default: 'Finish' 78 | }, 79 | stepSize: { 80 | type: String, 81 | default: 'md', 82 | validator: (value) => { 83 | let acceptedValues = ['xs', 'sm', 'md', 'lg'] 84 | return acceptedValues.indexOf(value) !== -1 85 | } 86 | }, 87 | /*** 88 | * Sets validation (on/off) for back button. By default back button ignores validation 89 | */ 90 | validateOnBack: Boolean, 91 | /*** 92 | * Applies to text, border and circle 93 | */ 94 | color: { 95 | type: String, 96 | default: '#e74c3c' //circle, border and text color 97 | }, 98 | /*** 99 | * Is set to current step and text when beforeChange function fails 100 | */ 101 | errorColor: { 102 | type: String, 103 | default: '#8b0000' 104 | }, 105 | /** 106 | * Can take one of the following values: 'circle|square|tab` 107 | */ 108 | shape: { 109 | type: String, 110 | default: 'circle' 111 | }, 112 | /** 113 | * name of the transition when transition between steps 114 | */ 115 | transition: { 116 | type: String, 117 | default: '' //name of the transition when transition between steps 118 | }, 119 | /*** 120 | * Index of the initial tab to display 121 | */ 122 | startIndex: { 123 | type: Number, 124 | default: 0 125 | } 126 | } 127 | ``` 128 | 129 | ### Tab content props 130 | ```js 131 | props: { 132 | title: { 133 | type: String, 134 | default: '' 135 | }, 136 | /*** 137 | * Icon name for the upper circle corresponding to the tab 138 | * Supports themify icons only for now. 139 | */ 140 | icon: { 141 | type: String, 142 | default: '' 143 | }, 144 | /*** 145 | * Only render the content when the tab is active 146 | */ 147 | lazy: { 148 | type: Boolean, 149 | default: false 150 | }, 151 | /*** 152 | * Function to execute before tab switch. Return value must be boolean 153 | * If the return result is false, tab switch is restricted 154 | */ 155 | beforeChange: { 156 | type: Function 157 | } 158 | } 159 | ``` 160 | ### Events 161 | Vue-form-wizard emits certain events when certain actions happen inside the component. The events can be noticed in some of the demos and especially in the [async validation demo](https://jsfiddle.net/bt5dhqtf/272/) 162 | * **on-complete** Called when the finish button is clicked and the `before-change` for the last step (if present) was executed. No params are sent together with this event. `this.$emit('on-complete')` 163 | * **on-loading** Called whenever an async `before-change` is executed. This event is emitted before executing `before-change` and after finishing execution of `before-change` method. `on-loading` is emitted together with a Boolean value. `this.$emit('on-loading', value)` 164 | * **on-validate** Called whenever the execution of a `before-change` method is completed. The event sends along a Boolean which represents the validation result as well as an int with te tab index. `this.$emit('on-validate', validationResult, this.activeTabIndex)` 165 | * **on-error** Called when `before-change` is a promised and is rejected with a message. The message is passed along `this.$emit('on-error', error)` See async validation fiddle 166 | * **on-change** Called upon step changes. Has prevIndex and nextIndes as params. `this.$emit('on-change', prevIndex, nextIndex)` 167 | 168 | ## Slots 169 | * **Default** - Used for tab-contents 170 | * **title** - Upper title section including sub-title 171 | * **prev** - Previous button content (no need to worry about handling the button functionality) 172 | * **next** - Next button content 173 | * **finish** - Finish button content 174 | * **custom-buttons-left** - Appears on right of "Back" button 175 | * **custom-buttons-right** - Appears on the left of "Next/Finish" button 176 | 177 | ## Methods 178 | By using [refs](https://vuejs.org/v2/api/#ref) on the `form-wizard` component, you can access some internal wizard properties and methods. 179 | Some of them are intended for internal usage while others can be used for general purpose operations. 180 | 181 | * **reset** - will reset the wizard to the initial state 182 | * **activateAll** - will activate all steps as if the user went through all 183 | * **nextTab** - navigates to the next tab. The same method is used when clicking next button 184 | * **prevTab** - navigates to the prev tab. The same method is used when clicking prev button 185 | * **changeTab(oldIndex, newIndex)** - Navigates from one tab to another. Note that this method does not trigger validation methods. Use it with caution! 186 | 187 | !> It's advised to use only the methods above, since the methods which are not listed here are internal and might change or get removed over time. 188 | 189 | ## Scoped slots 190 | 191 | Form-wizard exposes multiple scoped slots which can be used to customize some parts of the wizard. Usage example and implementation details are presented in [0.6.2 release](https://github.com/cristijora/vue-form-wizard/releases/tag/v0.6.2) 192 | 193 | Since [0.6.4](https://github.com/cristijora/vue-form-wizard/releases/tag/v0.6.4), button slots can be also used as scoped slots and have the following methods/properties exposed 194 | 195 | * **nextTab** // will go to the next tab/step when called 196 | * **prevTab** //will got to the prev tab/step when called 197 | * **activeTabIndex** // current active tab index 198 | * **isLastStep** // boolean to tell whether it's the last step or not 199 | * **fillButtonStyle** // object with styles for wizard-buttons (contains background and color passed through wizard props) 200 | 201 | These properties apply to the following slots: 202 | 203 | * **prev** - Previous button content (no need to worry about handling the button functionality) 204 | * **next** - Next button content 205 | * **finish** - Finish button content 206 | * **custom-buttons-left** - Appears on right of "Back" button 207 | * **custom-buttons-right** - Appears on the left of "Next/Finish" button 208 | 209 | ### Footer slot 210 | The footer slot would be usually used to replace the whole content of your footer. By default it contains the wizard buttons (back, next, finish). 211 | When using this slot, those buttons are replaced with your own content. You can achieve the same default wizard functionality and event tweak it with the help of the exposed methods/properties from slot `props` 212 | 213 | Note that using this slot, means that you have to handle some of the wizard logic through the exposed methods/properties defined above and your template might get more verbose. 214 | If you need very fine customizations and more control over the wizard button actions, 215 | then you could use this slot. Otherwise, you could stick with the buttons slots as they can be used as scoped slots as well. 216 | One potential usage can be that you want to have a different button when completing the wizard. Maybe you want to position it in the center, give it a different color and click event 217 | 218 | ```html 219 | 229 | ``` 230 | This is just one example. You can add more buttons, hide or display conditionally based on the exposed properties. 231 | Working fiddle for the [example above](https://jsfiddle.net/bt5dhqtf/717/) 232 | 233 | ### Step slot 234 | This slot can be used to disable the click event on the step or to customize the UI of each step 235 | One possible usage: 236 | ```html 237 | 243 | 244 | ``` 245 | #### Exposed props for the `step` slot 246 | - tab (the tab object which contains the tab-content component corresponding to the step) This object contains several fields such as `active, checked, shape, color` and so on. You can check how these are used [here](https://github.com/cristijora/vue-form-wizard/blob/master/src/components/WizardStep.vue): 247 | - index (The index of the step) 248 | - transition (Transition prop passed from form-wizard) 249 | 250 | [Fiddle example](https://jsfiddle.net/bt5dhqtf/705/) You can notice that steps are not longer clickable. 251 | # Demos 252 | Basic [demo](https://jsfiddle.net/bt5dhqtf/97/) 253 | 254 | Other demos: 255 | * [Squared steps](https://jsfiddle.net/bt5dhqtf/98/) 256 | * [Tabbed steps](https://jsfiddle.net/bt5dhqtf/99/) 257 | * [Step index](https://jsfiddle.net/bt5dhqtf/100/) Start at any step. Note: start-index is zero-based and the count starts at 0 258 | * [Custom button and title text](https://jsfiddle.net/bt5dhqtf/101/) 259 | * [Custom title slot](https://jsfiddle.net/bt5dhqtf/102/) 260 | * [Call a function before tab switch](https://jsfiddle.net/bt5dhqtf/105/) 261 | * [Complete form example](https://jsfiddle.net/CristiJ/bt5dhqtf/286/) integrated with [vue-form-generator](https://github.com/icebob/vue-form-generator) 262 | * [Element UI form integration](https://jsfiddle.net/bt5dhqtf/409/) 263 | * [Vuelidate integration](https://jsfiddle.net/CristiJ/bt5dhqtf/1119/) 264 | * [Dynamic components for tabs](https://jsfiddle.net/bt5dhqtf/973/) 265 | * [Vue router integration](https://jsfiddle.net/bt5dhqtf/267/) You can place a `router-view` inside the wizard and have a separate page per tab. A `route` prop must be passed to the tabs you want to handle certain tabs 266 | * [Async validation with error message](https://jsfiddle.net/CristiJ/bt5dhqtf/298/) `before-change` prop can accept a promise that is resolved with `true` which will execute the promise before switching to another step/tab (NOTE: This feature is not present in the npm package yet) 267 | * [Customized buttons with scoped slot](https://jsfiddle.net/bt5dhqtf/717/) 268 | 269 | # Playground 270 | 271 | ## Simple 272 | 273 | 274 | 303 | 304 | 305 | 306 | ## Square steps 307 | 308 | 309 | 310 | 341 | 342 | 343 | 344 | ## Tabbed steps 345 | 346 | 347 | 348 | 379 | 380 | 381 | 382 | ## Start from a specific index 383 | 384 | 385 | 386 | 417 | 418 | 419 | 420 | ## Custom button and title text 421 | 422 | 423 | 424 | 460 | 461 | 462 | ## Customize buttons with footer slot 463 | 464 | 465 | 466 | 509 | 510 | 511 | ## Call a function before tab switch 512 | 513 | 514 | 515 | 550 | 551 | 552 | ## Async validation 553 | 554 | 555 | 556 | 633 | 634 | 635 | 636 | 637 | ## Element UI integration 638 | 639 | 640 | 641 | 715 | 716 | 717 | ## Contribution 718 | 1. Fork the repo 719 | 2. run `npm install` 720 | 3. `npm run dev` for launching the dev example 721 | 4. After making your changes make sure to pull the changes from the source repo to avoid conflicts 722 | 5. `npm run build` to generate the new js and css bundles 723 | 6. Commit your changes + the js and css bundles so it's easy to test them right away in fiddles, codepen etc 724 | 7. Open a Pull Request. For more information refer to [github forking workflow](https://gist.github.com/Chaser324/ce0505fbed06b947d962) 725 | 726 | 727 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-form-wizard - A vue based tab/form wizard 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 49 | 50 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | webpack-vue-clean 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-form-wizard", 3 | "version": "0.8.4", 4 | "description": "A vue based tab/form wizard", 5 | "main": "dist/vue-form-wizard.js", 6 | "files": [ 7 | "dist", 8 | "types", 9 | "src" 10 | ], 11 | "typings": "types/index.d.ts", 12 | "homepage": "https://github.com/cristijora/vue-form-wizard", 13 | "scripts": { 14 | "dev": "node build/dev-server.js", 15 | "start": "node build/dev-server.js", 16 | "build": "cross-env FULL_BUNDLE=false webpack -p --progress --config webpack.build.config.js", 17 | "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", 18 | "unit:watch": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --watch", 19 | "e2e": "node test/e2e/runner.js", 20 | "test": "npm run unit", 21 | "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs", 22 | "coverall": "cat ./test/unit/coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" 23 | }, 24 | "keywords": [ 25 | "vue", 26 | "vuejs", 27 | "form", 28 | "wizard", 29 | "tab" 30 | ], 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/cristijora/vue-form-wizard" 34 | }, 35 | "devDependencies": { 36 | "autoprefixer": "^7.2.2", 37 | "babel-core": "^6.22.1", 38 | "babel-eslint": "^8.0.3", 39 | "babel-loader": "^7.1.2", 40 | "babel-plugin-istanbul": "^4.1.5", 41 | "babel-plugin-transform-runtime": "^6.22.0", 42 | "babel-preset-env": "^1.6.1", 43 | "babel-preset-stage-2": "^6.22.0", 44 | "babel-register": "^6.22.0", 45 | "bootstrap": "^3.3.7", 46 | "chai": "^4.1.2", 47 | "chalk": "^1.1.3", 48 | "chromedriver": "^2.35.0", 49 | "connect-history-api-fallback": "^1.5.0", 50 | "copy-webpack-plugin": "^4.3.1", 51 | "coveralls": "^3.0.0", 52 | "cross-env": "^5.1.1", 53 | "cross-spawn": "^6.0.3", 54 | "css-loader": "^0.28.8", 55 | "eslint": "^3.14.1", 56 | "eslint-config-standard": "^6.2.1", 57 | "eslint-friendly-formatter": "^3.0.0", 58 | "eslint-loader": "^1.6.1", 59 | "eslint-plugin-html": "^2.0.0", 60 | "eslint-plugin-promise": "^3.6.0", 61 | "eslint-plugin-standard": "^3.0.1", 62 | "eventsource-polyfill": "^0.9.6", 63 | "express": "^4.14.1", 64 | "extract-text-webpack-plugin": "^3.0.2", 65 | "file-loader": "^1.1.6", 66 | "friendly-errors-webpack-plugin": "^1.1.3", 67 | "html-webpack-plugin": "^2.28.0", 68 | "http-proxy-middleware": "^0.17.3", 69 | "inject-loader": "^3.0.1", 70 | "karma": "^1.4.1", 71 | "karma-coverage": "^1.1.1", 72 | "karma-mocha": "^1.3.0", 73 | "karma-phantomjs-launcher": "^1.0.2", 74 | "karma-phantomjs-shim": "^1.5.0", 75 | "karma-polyfill": "^1.0.0", 76 | "karma-sinon-chai": "^1.3.3", 77 | "karma-sourcemap-loader": "^0.3.7", 78 | "karma-spec-reporter": "0.0.32", 79 | "karma-webpack": "^2.0.9", 80 | "lolex": "^2.3.1", 81 | "mocha": "^4.0.1", 82 | "nightwatch": "^0.9.19", 83 | "node-sass": "^4.7.2", 84 | "opn": "^5.2.0", 85 | "optimize-css-assets-webpack-plugin": "^3.2.0", 86 | "ora": "^1.1.0", 87 | "phantomjs-polyfill": "^0.0.2", 88 | "phantomjs-polyfill-find": "^0.0.1", 89 | "phantomjs-polyfill-find-index": "^1.0.1", 90 | "phantomjs-prebuilt": "^2.1.16", 91 | "rimraf": "^2.6.0", 92 | "sass-loader": "^6.0.3", 93 | "selenium-server": "^3.8.1", 94 | "semver": "^5.3.0", 95 | "shelljs": "^0.8.0", 96 | "sinon": "^4.2.2", 97 | "sinon-chai": "^2.14.0", 98 | "stats-webpack-plugin": "^0.6.0", 99 | "url-loader": "^0.6.2", 100 | "vue": "^2.5.9", 101 | "vue-form-generator": "^2.2.0", 102 | "vue-loader": "^13.6.2", 103 | "vue-router": "^3.0.1", 104 | "vue-style-loader": "^3.0.3", 105 | "vue-template-compiler": "^2.5.9", 106 | "vue-test-utils": "^1.0.0-beta.1", 107 | "webpack": "^3.10.0", 108 | "webpack-bundle-analyzer": "^2.9.1", 109 | "webpack-dev-middleware": "^2.0.4", 110 | "webpack-hot-middleware": "^2.21.0", 111 | "webpack-merge": "^4.1.1" 112 | }, 113 | "engines": { 114 | "node": ">= 4.0.0", 115 | "npm": ">= 3.0.0" 116 | }, 117 | "browserslist": [ 118 | "> 1%", 119 | "last 2 versions", 120 | "not ie <= 8" 121 | ], 122 | "dependencies": {} 123 | } 124 | -------------------------------------------------------------------------------- /src/assets/form-wizard/_buttons.scss: -------------------------------------------------------------------------------- 1 | .vue-form-wizard .wizard-btn, 2 | .vue-form-wizard .navbar .navbar-nav > li > a.wizard-btn { 3 | box-sizing: border-box; 4 | border-width: $border-thick; 5 | background-color: $transparent-bg; 6 | font-size: $font-size-base; 7 | font-weight: $font-weight-bold; 8 | padding: $padding-base-vertical $padding-base-horizontal; 9 | min-width: 140px; 10 | &:hover, 11 | &:focus { 12 | outline: 0 !important; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/assets/form-wizard/_mixins.scss: -------------------------------------------------------------------------------- 1 | @import "mixins/buttons"; 2 | @import "mixins/vendor-prefixes"; 3 | @import "mixins/transparency"; 4 | -------------------------------------------------------------------------------- /src/assets/form-wizard/_navs-pagination.scss: -------------------------------------------------------------------------------- 1 | .vue-form-wizard .wizard-nav-pills { 2 | margin-top:0; 3 | position: relative; 4 | text-align: center; 5 | display:flex; 6 | flex-wrap:wrap; 7 | li,a{ 8 | flex:1; 9 | align-items: center; 10 | flex-wrap:wrap; 11 | flex-grow: 1; 12 | } 13 | a{ 14 | display:flex; 15 | } 16 | > li > a { 17 | display:flex; 18 | flex-direction: column; 19 | padding: 0; 20 | margin: 0 auto; 21 | color: $form-placeholder-color; 22 | position: relative; 23 | top: 3px; 24 | &:hover, 25 | &:focus{ 26 | background-color: transparent; 27 | color: $form-placeholder-color; 28 | outline: 0 !important; 29 | } 30 | &.disabled { 31 | pointer-events: none; 32 | cursor: default; 33 | } 34 | } 35 | 36 | > li.active > a, 37 | > li.active > a:hover, 38 | > li.active > a:focus { 39 | background-color: transparent; 40 | @include transition-font-size(.2s, linear); 41 | 42 | 43 | .wizard-icon{ 44 | color: $white-color; 45 | font-size: 24px; 46 | display:flex; 47 | align-items:center; 48 | justify-content: center; 49 | @include transition(.2s, linear); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/assets/form-wizard/_variables.scss: -------------------------------------------------------------------------------- 1 | $gray-input-bg: #F3F2EE !default; 2 | $danger-color: #EB5E28 !default; 3 | $white-color: #FFFFFF !default; 4 | 5 | $form-placeholder-color: rgba(0,0,0,.2) !default; 6 | 7 | $card-black-color: #252422 !default; 8 | 9 | $border-thick: 2px !default; 10 | $transparent-bg: transparent !default; 11 | $default-color: #66615B !default; 12 | $default-states-color: darken($default-color,11%) !default; 13 | $fill-font-color: rgba(255, 255, 255, 0.85); 14 | 15 | $fast-transition-time: 150ms !default; 16 | 17 | $danger-states-color: darken($danger-color,11%) !default; 18 | 19 | $font-weight-light: 300 !default; 20 | $font-weight-normal: 400 !default; 21 | $font-weight-bold: 600 !default; 22 | -------------------------------------------------------------------------------- /src/assets/form-wizard/_wizard-card.scss: -------------------------------------------------------------------------------- 1 | @import "mixins/wizard-size"; 2 | @import "variables"; 3 | 4 | .vue-form-wizard { 5 | padding-bottom: 20px; 6 | .is_error { 7 | border-color: $danger-states-color !important; 8 | .icon-container { 9 | background: $danger-states-color !important; 10 | } 11 | } 12 | @include wizard-size('xs', 40px, 16px); 13 | @include wizard-size('sm', 50px, 20px); 14 | @include wizard-size('md', 70px, 24px); 15 | @include wizard-size('lg', 90px, 28px); 16 | 17 | .wizard-icon-circle { 18 | font-size: 18px; 19 | border: 3px solid $gray-input-bg; 20 | border-radius: 50%; 21 | font-weight: $font-weight-bold; 22 | width: 70px; 23 | height: 70px; 24 | background-color: $white-color; 25 | position: relative; 26 | display: flex; 27 | justify-content: center; 28 | align-content: center; 29 | &.square_shape { 30 | border-radius: 0; 31 | } 32 | &.tab_shape { 33 | width: 100%; 34 | min-width: 100px; 35 | height: 40px; 36 | border: none; 37 | background-color: $gray-input-bg; 38 | border-radius: 0; 39 | } 40 | .wizard-icon-container { 41 | display: flex; 42 | justify-content: center; 43 | flex: 1; 44 | border-radius: 50%; 45 | margin: -3px; 46 | &.square_shape { 47 | border-radius: 0; 48 | } 49 | &.tab_shape { 50 | border-radius: 0; 51 | margin: 0; 52 | } 53 | } 54 | .wizard-icon { 55 | display: flex; 56 | align-items: center; 57 | justify-content: center; 58 | } 59 | } 60 | 61 | .wizard-tab-content { 62 | min-height: 100px; 63 | padding: 30px 20px 10px; 64 | } 65 | 66 | .wizard-header { 67 | padding: 15px 15px 15px 15px; 68 | position: relative; 69 | border-radius: 3px 3px 0 0; 70 | text-align: center; 71 | } 72 | 73 | .wizard-title { 74 | color: $card-black-color; 75 | font-weight: $font-weight-light; 76 | margin: 0; 77 | text-align: center; 78 | } 79 | 80 | .category { 81 | font-size: 14px; 82 | font-weight: 400; 83 | color: #9A9A9A; 84 | margin-bottom: 0px; 85 | text-align: center; 86 | } 87 | 88 | .wizard-navigation { 89 | .wizard-progress-with-circle { 90 | position: relative; 91 | top: 40px; 92 | height: 4px; 93 | 94 | .wizard-progress-bar { 95 | box-shadow: none; 96 | -webkit-transition: width .3s ease; 97 | -o-transition: width .3s ease; 98 | transition: width .3s ease; 99 | } 100 | } 101 | } 102 | .clearfix::after { 103 | content: ""; 104 | clear: both; 105 | display: table; 106 | } 107 | .wizard-card-footer { 108 | padding: 0 20px; 109 | .wizard-footer-left { 110 | float: left; 111 | } 112 | .wizard-footer-right { 113 | float: right; 114 | } 115 | } 116 | @media screen and (max-width: 350px) { 117 | .wizard-card-footer { 118 | display:flex; 119 | justify-content: center; 120 | flex-direction: column; 121 | .wizard-footer-left, 122 | .wizard-footer-right { 123 | float: none; 124 | flex:1; 125 | display: flex; 126 | justify-content:center; 127 | } 128 | .wizard-footer-right button{ 129 | margin-top:10px; 130 | } 131 | } 132 | } 133 | 134 | &.vertical { 135 | .wizard-card-footer { 136 | display: block; 137 | } 138 | .wizard-nav-pills { 139 | flex-direction: column; 140 | } 141 | .wizard-navigation { 142 | display: flex; 143 | flex-direction: row; 144 | } 145 | .wizard-card-footer { 146 | padding-top: 30px; 147 | 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/assets/form-wizard/bs_button.scss: -------------------------------------------------------------------------------- 1 | @import "bs_variables"; 2 | @import "mixins"; 3 | 4 | .vue-form-wizard .wizard-btn { 5 | display: inline-block; 6 | margin-bottom: 0; // For input.btn 7 | font-weight: $btn-font-weight; 8 | text-align: center; 9 | vertical-align: middle; 10 | touch-action: manipulation; 11 | cursor: pointer; 12 | background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 13 | border: 1px solid transparent; 14 | white-space: nowrap; 15 | @include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base); 16 | //@include user-select(none); 17 | 18 | &.disabled, 19 | &[disabled], 20 | fieldset[disabled] & { 21 | cursor: $cursor-disabled; 22 | @include opacity(.65); 23 | @include box-shadow(none); 24 | } 25 | 26 | } 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/assets/form-wizard/bs_nav_pills.scss: -------------------------------------------------------------------------------- 1 | @import "bs_variables"; 2 | 3 | .vue-form-wizard *{ 4 | box-sizing: border-box; 5 | } 6 | .vue-form-wizard a{ 7 | text-decoration: none; 8 | } 9 | .vue-form-wizard .wizard-nav { 10 | margin-bottom: 0; 11 | padding-left: 0; // Override default ul/ol 12 | list-style: none; 13 | //@include clearfix; 14 | 15 | > li { 16 | position: relative; 17 | display: block; 18 | 19 | > a { 20 | position: relative; 21 | display: block; 22 | padding: $nav-link-padding; 23 | &:hover, 24 | &:focus { 25 | text-decoration: none; 26 | background-color: $nav-link-hover-bg; 27 | } 28 | } 29 | 30 | // Disabled state sets text to gray and nukes hover/tab effects 31 | &.disabled > a { 32 | color: $nav-disabled-link-color; 33 | 34 | &:hover, 35 | &:focus { 36 | color: $nav-disabled-link-hover-color; 37 | text-decoration: none; 38 | background-color: transparent; 39 | cursor: $cursor-disabled; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/assets/form-wizard/bs_progress_bar.scss: -------------------------------------------------------------------------------- 1 | @import "bs_variables"; 2 | @import "mixins"; 3 | @mixin transition($transition...) { 4 | -webkit-transition: $transition; 5 | -o-transition: $transition; 6 | transition: $transition; 7 | } 8 | 9 | // Bar of progress 10 | .vue-form-wizard .wizard-progress-bar { 11 | float: left; 12 | width: 0%; 13 | height: 100%; 14 | font-size: $font-size-small; 15 | line-height: $line-height-computed; 16 | color: $progress-bar-color; 17 | text-align: center; 18 | background-color: $progress-bar-bg; 19 | @include box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); 20 | @include transition(width .6s ease); 21 | } 22 | -------------------------------------------------------------------------------- /src/assets/form-wizard/bs_variables.scss: -------------------------------------------------------------------------------- 1 | $bootstrap-sass-asset-helper: false !default; 2 | // 3 | // Variables 4 | // -------------------------------------------------- 5 | 6 | 7 | //== Colors 8 | // 9 | //## Gray and brand colors for use across Bootstrap. 10 | 11 | $gray-base: #000 !default; 12 | $gray-darker: lighten($gray-base, 13.5%) !default; // #222 13 | $gray-dark: lighten($gray-base, 20%) !default; // #333 14 | $gray: lighten($gray-base, 33.5%) !default; // #555 15 | $gray-light: lighten($gray-base, 46.7%) !default; // #777 16 | $gray-lighter: lighten($gray-base, 93.5%) !default; // #eee 17 | 18 | $brand-primary: darken(#428bca, 6.5%) !default; // #337ab7 19 | $brand-success: #5cb85c !default; 20 | $brand-info: #5bc0de !default; 21 | $brand-warning: #f0ad4e !default; 22 | $brand-danger: #d9534f !default; 23 | 24 | 25 | //== Scaffolding 26 | // 27 | //## Settings for some of the most global styles. 28 | 29 | //** Background color for ``. 30 | $body-bg: #fff !default; 31 | //** Global text color on ``. 32 | $text-color: $gray-dark !default; 33 | 34 | //** Global textual link color. 35 | $link-color: $brand-primary !default; 36 | //** Link hover color set via `darken()` function. 37 | $link-hover-color: darken($link-color, 15%) !default; 38 | //** Link hover decoration. 39 | $link-hover-decoration: underline !default; 40 | 41 | 42 | //== Typography 43 | // 44 | //## Font, line-height, and color for body text, headings, and more. 45 | 46 | $font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default; 47 | $font-family-serif: Georgia, "Times New Roman", Times, serif !default; 48 | //** Default monospace fonts for ``, ``, and `
`.
 49 | $font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace !default;
 50 | $font-family-base:        $font-family-sans-serif !default;
 51 | 
 52 | $font-size-base:          14px !default;
 53 | $font-size-large:         ceil(($font-size-base * 1.25)) !default; // ~18px
 54 | $font-size-small:         ceil(($font-size-base * 0.85)) !default; // ~12px
 55 | 
 56 | $font-size-h1:            floor(($font-size-base * 2.6)) !default; // ~36px
 57 | $font-size-h2:            floor(($font-size-base * 2.15)) !default; // ~30px
 58 | $font-size-h3:            ceil(($font-size-base * 1.7)) !default; // ~24px
 59 | $font-size-h4:            ceil(($font-size-base * 1.25)) !default; // ~18px
 60 | $font-size-h5:            $font-size-base !default;
 61 | $font-size-h6:            ceil(($font-size-base * 0.85)) !default; // ~12px
 62 | 
 63 | //** Unit-less `line-height` for use in components like buttons.
 64 | $line-height-base:        1.428571429 !default; // 20/14
 65 | //** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
 66 | $line-height-computed:    floor(($font-size-base * $line-height-base)) !default; // ~20px
 67 | 
 68 | //** By default, this inherits from the ``.
 69 | $headings-font-family:    inherit !default;
 70 | $headings-font-weight:    500 !default;
 71 | $headings-line-height:    1.1 !default;
 72 | $headings-color:          inherit !default;
 73 | 
 74 | 
 75 | //== Iconography
 76 | //
 77 | //## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
 78 | 
 79 | //** Load fonts from this directory.
 80 | 
 81 | // [converter] If $bootstrap-sass-asset-helper if used, provide path relative to the assets load path.
 82 | // [converter] This is because some asset helpers, such as Sprockets, do not work with file-relative paths.
 83 | $icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/") !default;
 84 | 
 85 | //** File name for all font files.
 86 | $icon-font-name:          "glyphicons-halflings-regular" !default;
 87 | //** Element ID within SVG icon file.
 88 | $icon-font-svg-id:        "glyphicons_halflingsregular" !default;
 89 | 
 90 | 
 91 | //== Components
 92 | //
 93 | //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
 94 | 
 95 | $padding-base-vertical:     6px !default;
 96 | $padding-base-horizontal:   12px !default;
 97 | 
 98 | $padding-large-vertical:    10px !default;
 99 | $padding-large-horizontal:  16px !default;
100 | 
101 | $padding-small-vertical:    5px !default;
102 | $padding-small-horizontal:  10px !default;
103 | 
104 | $padding-xs-vertical:       1px !default;
105 | $padding-xs-horizontal:     5px !default;
106 | 
107 | $line-height-large:         1.3333333 !default; // extra decimals for Win 8.1 Chrome
108 | $line-height-small:         1.5 !default;
109 | 
110 | $border-radius-base:        4px !default;
111 | $border-radius-large:       6px !default;
112 | $border-radius-small:       3px !default;
113 | 
114 | //** Global color for active items (e.g., navs or dropdowns).
115 | $component-active-color:    #fff !default;
116 | //** Global background color for active items (e.g., navs or dropdowns).
117 | $component-active-bg:       $brand-primary !default;
118 | 
119 | //** Width of the `border` for generating carets that indicate dropdowns.
120 | $caret-width-base:          4px !default;
121 | //** Carets increase slightly in size for larger components.
122 | $caret-width-large:         5px !default;
123 | 
124 | 
125 | //== Tables
126 | //
127 | //## Customizes the `.table` component with basic values, each used across all table variations.
128 | 
129 | //** Padding for ``s and ``s.
130 | $table-cell-padding:            8px !default;
131 | //** Padding for cells in `.table-condensed`.
132 | $table-condensed-cell-padding:  5px !default;
133 | 
134 | //** Default background color used for all tables.
135 | $table-bg:                      transparent !default;
136 | //** Background color used for `.table-striped`.
137 | $table-bg-accent:               #f9f9f9 !default;
138 | //** Background color used for `.table-hover`.
139 | $table-bg-hover:                #f5f5f5 !default;
140 | $table-bg-active:               $table-bg-hover !default;
141 | 
142 | //** Border color for table and cell borders.
143 | $table-border-color:            #ddd !default;
144 | 
145 | 
146 | //== Buttons
147 | //
148 | //## For each of Bootstrap's buttons, define text, background and border color.
149 | 
150 | $btn-font-weight:                normal !default;
151 | 
152 | $btn-default-color:              #333 !default;
153 | $btn-default-bg:                 #fff !default;
154 | $btn-default-border:             #ccc !default;
155 | 
156 | $btn-primary-color:              #fff !default;
157 | $btn-primary-bg:                 $brand-primary !default;
158 | $btn-primary-border:             darken($btn-primary-bg, 5%) !default;
159 | 
160 | $btn-success-color:              #fff !default;
161 | $btn-success-bg:                 $brand-success !default;
162 | $btn-success-border:             darken($btn-success-bg, 5%) !default;
163 | 
164 | $btn-info-color:                 #fff !default;
165 | $btn-info-bg:                    $brand-info !default;
166 | $btn-info-border:                darken($btn-info-bg, 5%) !default;
167 | 
168 | $btn-warning-color:              #fff !default;
169 | $btn-warning-bg:                 $brand-warning !default;
170 | $btn-warning-border:             darken($btn-warning-bg, 5%) !default;
171 | 
172 | $btn-danger-color:               #fff !default;
173 | $btn-danger-bg:                  $brand-danger !default;
174 | $btn-danger-border:              darken($btn-danger-bg, 5%) !default;
175 | 
176 | $btn-link-disabled-color:        $gray-light !default;
177 | 
178 | // Allows for customizing button radius independently from global border radius
179 | $btn-border-radius-base:         $border-radius-base !default;
180 | $btn-border-radius-large:        $border-radius-large !default;
181 | $btn-border-radius-small:        $border-radius-small !default;
182 | 
183 | 
184 | //== Forms
185 | //
186 | //##
187 | 
188 | //** `` background color
189 | $input-bg:                       #fff !default;
190 | //** `` background color
191 | $input-bg-disabled:              $gray-lighter !default;
192 | 
193 | //** Text color for ``s
194 | $input-color:                    $gray !default;
195 | //** `` border color
196 | $input-border:                   #ccc !default;
197 | 
198 | // TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4
199 | //** Default `.form-control` border radius
200 | // This has no effect on ``s in CSS.
201 | $input-border-radius:            $border-radius-base !default;
202 | //** Large `.form-control` border radius
203 | $input-border-radius-large:      $border-radius-large !default;
204 | //** Small `.form-control` border radius
205 | $input-border-radius-small:      $border-radius-small !default;
206 | 
207 | //** Border color for inputs on focus
208 | $input-border-focus:             #66afe9 !default;
209 | 
210 | //** Placeholder text color
211 | $input-color-placeholder:        #999 !default;
212 | 
213 | //** Default `.form-control` height
214 | $input-height-base:              ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
215 | //** Large `.form-control` height
216 | $input-height-large:             (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default;
217 | //** Small `.form-control` height
218 | $input-height-small:             (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default;
219 | 
220 | //** `.form-group` margin
221 | $form-group-margin-bottom:       15px !default;
222 | 
223 | $legend-color:                   $gray-dark !default;
224 | $legend-border-color:            #e5e5e5 !default;
225 | 
226 | //** Background color for textual input addons
227 | $input-group-addon-bg:           $gray-lighter !default;
228 | //** Border color for textual input addons
229 | $input-group-addon-border-color: $input-border !default;
230 | 
231 | //** Disabled cursor for form controls and buttons.
232 | $cursor-disabled:                not-allowed !default;
233 | 
234 | 
235 | //== Dropdowns
236 | //
237 | //## Dropdown menu container and contents.
238 | 
239 | //** Background for the dropdown menu.
240 | $dropdown-bg:                    #fff !default;
241 | //** Dropdown menu `border-color`.
242 | $dropdown-border:                rgba(0,0,0,.15) !default;
243 | //** Dropdown menu `border-color` **for IE8**.
244 | $dropdown-fallback-border:       #ccc !default;
245 | //** Divider color for between dropdown items.
246 | $dropdown-divider-bg:            #e5e5e5 !default;
247 | 
248 | //** Dropdown link text color.
249 | $dropdown-link-color:            $gray-dark !default;
250 | //** Hover color for dropdown links.
251 | $dropdown-link-hover-color:      darken($gray-dark, 5%) !default;
252 | //** Hover background for dropdown links.
253 | $dropdown-link-hover-bg:         #f5f5f5 !default;
254 | 
255 | //** Active dropdown menu item text color.
256 | $dropdown-link-active-color:     $component-active-color !default;
257 | //** Active dropdown menu item background color.
258 | $dropdown-link-active-bg:        $component-active-bg !default;
259 | 
260 | //** Disabled dropdown menu item background color.
261 | $dropdown-link-disabled-color:   $gray-light !default;
262 | 
263 | //** Text color for headers within dropdown menus.
264 | $dropdown-header-color:          $gray-light !default;
265 | 
266 | //** Deprecated `$dropdown-caret-color` as of v3.1.0
267 | $dropdown-caret-color:           #000 !default;
268 | 
269 | 
270 | //-- Z-index master list
271 | //
272 | // Warning: Avoid customizing these values. They're used for a bird's eye view
273 | // of components dependent on the z-axis and are designed to all work together.
274 | //
275 | // Note: These variables are not generated into the Customizer.
276 | 
277 | $zindex-navbar:            1000 !default;
278 | $zindex-dropdown:          1000 !default;
279 | $zindex-popover:           1060 !default;
280 | $zindex-tooltip:           1070 !default;
281 | $zindex-navbar-fixed:      1030 !default;
282 | $zindex-modal-background:  1040 !default;
283 | $zindex-modal:             1050 !default;
284 | 
285 | 
286 | //== Media queries breakpoints
287 | //
288 | //## Define the breakpoints at which your layout will change, adapting to different screen sizes.
289 | 
290 | // Extra small screen / phone
291 | //** Deprecated `$screen-xs` as of v3.0.1
292 | $screen-xs:                  480px !default;
293 | //** Deprecated `$screen-xs-min` as of v3.2.0
294 | $screen-xs-min:              $screen-xs !default;
295 | //** Deprecated `$screen-phone` as of v3.0.1
296 | $screen-phone:               $screen-xs-min !default;
297 | 
298 | // Small screen / tablet
299 | //** Deprecated `$screen-sm` as of v3.0.1
300 | $screen-sm:                  768px !default;
301 | $screen-sm-min:              $screen-sm !default;
302 | //** Deprecated `$screen-tablet` as of v3.0.1
303 | $screen-tablet:              $screen-sm-min !default;
304 | 
305 | // Medium screen / desktop
306 | //** Deprecated `$screen-md` as of v3.0.1
307 | $screen-md:                  992px !default;
308 | $screen-md-min:              $screen-md !default;
309 | //** Deprecated `$screen-desktop` as of v3.0.1
310 | $screen-desktop:             $screen-md-min !default;
311 | 
312 | // Large screen / wide desktop
313 | //** Deprecated `$screen-lg` as of v3.0.1
314 | $screen-lg:                  1200px !default;
315 | $screen-lg-min:              $screen-lg !default;
316 | //** Deprecated `$screen-lg-desktop` as of v3.0.1
317 | $screen-lg-desktop:          $screen-lg-min !default;
318 | 
319 | // So media queries don't overlap when required, provide a maximum
320 | $screen-xs-max:              ($screen-sm-min - 1) !default;
321 | $screen-sm-max:              ($screen-md-min - 1) !default;
322 | $screen-md-max:              ($screen-lg-min - 1) !default;
323 | 
324 | 
325 | //== Grid system
326 | //
327 | //## Define your custom responsive grid.
328 | 
329 | //** Number of columns in the grid.
330 | $grid-columns:              12 !default;
331 | //** Padding between columns. Gets divided in half for the left and right.
332 | $grid-gutter-width:         30px !default;
333 | // Navbar collapse
334 | //** Point at which the navbar becomes uncollapsed.
335 | $grid-float-breakpoint:     $screen-sm-min !default;
336 | //** Point at which the navbar begins collapsing.
337 | $grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default;
338 | 
339 | 
340 | //== Container sizes
341 | //
342 | //## Define the maximum width of `.container` for different screen sizes.
343 | 
344 | // Small screen / tablet
345 | $container-tablet:             (720px + $grid-gutter-width) !default;
346 | //** For `$screen-sm-min` and up.
347 | $container-sm:                 $container-tablet !default;
348 | 
349 | // Medium screen / desktop
350 | $container-desktop:            (940px + $grid-gutter-width) !default;
351 | //** For `$screen-md-min` and up.
352 | $container-md:                 $container-desktop !default;
353 | 
354 | // Large screen / wide desktop
355 | $container-large-desktop:      (1140px + $grid-gutter-width) !default;
356 | //** For `$screen-lg-min` and up.
357 | $container-lg:                 $container-large-desktop !default;
358 | 
359 | 
360 | //== Navbar
361 | //
362 | //##
363 | 
364 | // Basics of a navbar
365 | $navbar-height:                    50px !default;
366 | $navbar-margin-bottom:             $line-height-computed !default;
367 | $navbar-border-radius:             $border-radius-base !default;
368 | $navbar-padding-horizontal:        floor(($grid-gutter-width / 2)) !default;
369 | $navbar-padding-vertical:          (($navbar-height - $line-height-computed) / 2) !default;
370 | $navbar-collapse-max-height:       340px !default;
371 | 
372 | $navbar-default-color:             #777 !default;
373 | $navbar-default-bg:                #f8f8f8 !default;
374 | $navbar-default-border:            darken($navbar-default-bg, 6.5%) !default;
375 | 
376 | // Navbar links
377 | $navbar-default-link-color:                #777 !default;
378 | $navbar-default-link-hover-color:          #333 !default;
379 | $navbar-default-link-hover-bg:             transparent !default;
380 | $navbar-default-link-active-color:         #555 !default;
381 | $navbar-default-link-active-bg:            darken($navbar-default-bg, 6.5%) !default;
382 | $navbar-default-link-disabled-color:       #ccc !default;
383 | $navbar-default-link-disabled-bg:          transparent !default;
384 | 
385 | // Navbar brand label
386 | $navbar-default-brand-color:               $navbar-default-link-color !default;
387 | $navbar-default-brand-hover-color:         darken($navbar-default-brand-color, 10%) !default;
388 | $navbar-default-brand-hover-bg:            transparent !default;
389 | 
390 | // Navbar toggle
391 | $navbar-default-toggle-hover-bg:           #ddd !default;
392 | $navbar-default-toggle-icon-bar-bg:        #888 !default;
393 | $navbar-default-toggle-border-color:       #ddd !default;
394 | 
395 | 
396 | //=== Inverted navbar
397 | // Reset inverted navbar basics
398 | $navbar-inverse-color:                      lighten($gray-light, 15%) !default;
399 | $navbar-inverse-bg:                         #222 !default;
400 | $navbar-inverse-border:                     darken($navbar-inverse-bg, 10%) !default;
401 | 
402 | // Inverted navbar links
403 | $navbar-inverse-link-color:                 lighten($gray-light, 15%) !default;
404 | $navbar-inverse-link-hover-color:           #fff !default;
405 | $navbar-inverse-link-hover-bg:              transparent !default;
406 | $navbar-inverse-link-active-color:          $navbar-inverse-link-hover-color !default;
407 | $navbar-inverse-link-active-bg:             darken($navbar-inverse-bg, 10%) !default;
408 | $navbar-inverse-link-disabled-color:        #444 !default;
409 | $navbar-inverse-link-disabled-bg:           transparent !default;
410 | 
411 | // Inverted navbar brand label
412 | $navbar-inverse-brand-color:                $navbar-inverse-link-color !default;
413 | $navbar-inverse-brand-hover-color:          #fff !default;
414 | $navbar-inverse-brand-hover-bg:             transparent !default;
415 | 
416 | // Inverted navbar toggle
417 | $navbar-inverse-toggle-hover-bg:            #333 !default;
418 | $navbar-inverse-toggle-icon-bar-bg:         #fff !default;
419 | $navbar-inverse-toggle-border-color:        #333 !default;
420 | 
421 | 
422 | //== Navs
423 | //
424 | //##
425 | 
426 | //=== Shared nav styles
427 | $nav-link-padding:                          10px 15px !default;
428 | $nav-link-hover-bg:                         $gray-lighter !default;
429 | 
430 | $nav-disabled-link-color:                   $gray-light !default;
431 | $nav-disabled-link-hover-color:             $gray-light !default;
432 | 
433 | //== Tabs
434 | $nav-tabs-border-color:                     #ddd !default;
435 | 
436 | $nav-tabs-link-hover-border-color:          $gray-lighter !default;
437 | 
438 | $nav-tabs-active-link-hover-bg:             $body-bg !default;
439 | $nav-tabs-active-link-hover-color:          $gray !default;
440 | $nav-tabs-active-link-hover-border-color:   #ddd !default;
441 | 
442 | $nav-tabs-justified-link-border-color:            #ddd !default;
443 | $nav-tabs-justified-active-link-border-color:     $body-bg !default;
444 | 
445 | //== Pills
446 | $nav-pills-border-radius:                   $border-radius-base !default;
447 | $nav-pills-active-link-hover-bg:            $component-active-bg !default;
448 | $nav-pills-active-link-hover-color:         $component-active-color !default;
449 | 
450 | 
451 | //== Pagination
452 | //
453 | //##
454 | 
455 | $pagination-color:                     $link-color !default;
456 | $pagination-bg:                        #fff !default;
457 | $pagination-border:                    #ddd !default;
458 | 
459 | $pagination-hover-color:               $link-hover-color !default;
460 | $pagination-hover-bg:                  $gray-lighter !default;
461 | $pagination-hover-border:              #ddd !default;
462 | 
463 | $pagination-active-color:              #fff !default;
464 | $pagination-active-bg:                 $brand-primary !default;
465 | $pagination-active-border:             $brand-primary !default;
466 | 
467 | $pagination-disabled-color:            $gray-light !default;
468 | $pagination-disabled-bg:               #fff !default;
469 | $pagination-disabled-border:           #ddd !default;
470 | 
471 | 
472 | //== Pager
473 | //
474 | //##
475 | 
476 | $pager-bg:                             $pagination-bg !default;
477 | $pager-border:                         $pagination-border !default;
478 | $pager-border-radius:                  15px !default;
479 | 
480 | $pager-hover-bg:                       $pagination-hover-bg !default;
481 | 
482 | $pager-active-bg:                      $pagination-active-bg !default;
483 | $pager-active-color:                   $pagination-active-color !default;
484 | 
485 | $pager-disabled-color:                 $pagination-disabled-color !default;
486 | 
487 | 
488 | //== Jumbotron
489 | //
490 | //##
491 | 
492 | $jumbotron-padding:              30px !default;
493 | $jumbotron-color:                inherit !default;
494 | $jumbotron-bg:                   $gray-lighter !default;
495 | $jumbotron-heading-color:        inherit !default;
496 | $jumbotron-font-size:            ceil(($font-size-base * 1.5)) !default;
497 | $jumbotron-heading-font-size:    ceil(($font-size-base * 4.5)) !default;
498 | 
499 | 
500 | //== Form states and alerts
501 | //
502 | //## Define colors for form feedback states and, by default, alerts.
503 | 
504 | $state-success-text:             #3c763d !default;
505 | $state-success-bg:               #dff0d8 !default;
506 | $state-success-border:           darken(adjust-hue($state-success-bg, -10), 5%) !default;
507 | 
508 | $state-info-text:                #31708f !default;
509 | $state-info-bg:                  #d9edf7 !default;
510 | $state-info-border:              darken(adjust-hue($state-info-bg, -10), 7%) !default;
511 | 
512 | $state-warning-text:             #8a6d3b !default;
513 | $state-warning-bg:               #fcf8e3 !default;
514 | $state-warning-border:           darken(adjust-hue($state-warning-bg, -10), 5%) !default;
515 | 
516 | $state-danger-text:              #a94442 !default;
517 | $state-danger-bg:                #f2dede !default;
518 | $state-danger-border:            darken(adjust-hue($state-danger-bg, -10), 5%) !default;
519 | 
520 | 
521 | //== Tooltips
522 | //
523 | //##
524 | 
525 | //** Tooltip max width
526 | $tooltip-max-width:           200px !default;
527 | //** Tooltip text color
528 | $tooltip-color:               #fff !default;
529 | //** Tooltip background color
530 | $tooltip-bg:                  #000 !default;
531 | $tooltip-opacity:             .9 !default;
532 | 
533 | //** Tooltip arrow width
534 | $tooltip-arrow-width:         5px !default;
535 | //** Tooltip arrow color
536 | $tooltip-arrow-color:         $tooltip-bg !default;
537 | 
538 | 
539 | //== Popovers
540 | //
541 | //##
542 | 
543 | //** Popover body background color
544 | $popover-bg:                          #fff !default;
545 | //** Popover maximum width
546 | $popover-max-width:                   276px !default;
547 | //** Popover border color
548 | $popover-border-color:                rgba(0,0,0,.2) !default;
549 | //** Popover fallback border color
550 | $popover-fallback-border-color:       #ccc !default;
551 | 
552 | //** Popover title background color
553 | $popover-title-bg:                    darken($popover-bg, 3%) !default;
554 | 
555 | //** Popover arrow width
556 | $popover-arrow-width:                 10px !default;
557 | //** Popover arrow color
558 | $popover-arrow-color:                 $popover-bg !default;
559 | 
560 | //** Popover outer arrow width
561 | $popover-arrow-outer-width:           ($popover-arrow-width + 1) !default;
562 | //** Popover outer arrow color
563 | $popover-arrow-outer-color:           fade_in($popover-border-color, 0.05) !default;
564 | //** Popover outer arrow fallback color
565 | $popover-arrow-outer-fallback-color:  darken($popover-fallback-border-color, 20%) !default;
566 | 
567 | 
568 | //== Labels
569 | //
570 | //##
571 | 
572 | //** Default label background color
573 | $label-default-bg:            $gray-light !default;
574 | //** Primary label background color
575 | $label-primary-bg:            $brand-primary !default;
576 | //** Success label background color
577 | $label-success-bg:            $brand-success !default;
578 | //** Info label background color
579 | $label-info-bg:               $brand-info !default;
580 | //** Warning label background color
581 | $label-warning-bg:            $brand-warning !default;
582 | //** Danger label background color
583 | $label-danger-bg:             $brand-danger !default;
584 | 
585 | //** Default label text color
586 | $label-color:                 #fff !default;
587 | //** Default text color of a linked label
588 | $label-link-hover-color:      #fff !default;
589 | 
590 | 
591 | //== Modals
592 | //
593 | //##
594 | 
595 | //** Padding applied to the modal body
596 | $modal-inner-padding:         15px !default;
597 | 
598 | //** Padding applied to the modal title
599 | $modal-title-padding:         15px !default;
600 | //** Modal title line-height
601 | $modal-title-line-height:     $line-height-base !default;
602 | 
603 | //** Background color of modal content area
604 | $modal-content-bg:                             #fff !default;
605 | //** Modal content border color
606 | $modal-content-border-color:                   rgba(0,0,0,.2) !default;
607 | //** Modal content border color **for IE8**
608 | $modal-content-fallback-border-color:          #999 !default;
609 | 
610 | //** Modal backdrop background color
611 | $modal-backdrop-bg:           #000 !default;
612 | //** Modal backdrop opacity
613 | $modal-backdrop-opacity:      .5 !default;
614 | //** Modal header border color
615 | $modal-header-border-color:   #e5e5e5 !default;
616 | //** Modal footer border color
617 | $modal-footer-border-color:   $modal-header-border-color !default;
618 | 
619 | $modal-lg:                    900px !default;
620 | $modal-md:                    600px !default;
621 | $modal-sm:                    300px !default;
622 | 
623 | 
624 | //== Alerts
625 | //
626 | //## Define alert colors, border radius, and padding.
627 | 
628 | $alert-padding:               15px !default;
629 | $alert-border-radius:         $border-radius-base !default;
630 | $alert-link-font-weight:      bold !default;
631 | 
632 | $alert-success-bg:            $state-success-bg !default;
633 | $alert-success-text:          $state-success-text !default;
634 | $alert-success-border:        $state-success-border !default;
635 | 
636 | $alert-info-bg:               $state-info-bg !default;
637 | $alert-info-text:             $state-info-text !default;
638 | $alert-info-border:           $state-info-border !default;
639 | 
640 | $alert-warning-bg:            $state-warning-bg !default;
641 | $alert-warning-text:          $state-warning-text !default;
642 | $alert-warning-border:        $state-warning-border !default;
643 | 
644 | $alert-danger-bg:             $state-danger-bg !default;
645 | $alert-danger-text:           $state-danger-text !default;
646 | $alert-danger-border:         $state-danger-border !default;
647 | 
648 | 
649 | //== Progress bars
650 | //
651 | //##
652 | 
653 | //** Background color of the whole progress component
654 | $progress-bg:                 #f5f5f5 !default;
655 | //** Progress bar text color
656 | $progress-bar-color:          #fff !default;
657 | //** Variable for setting rounded corners on progress bar.
658 | $progress-border-radius:      $border-radius-base !default;
659 | 
660 | //** Default progress bar color
661 | $progress-bar-bg:             $brand-primary !default;
662 | //** Success progress bar color
663 | $progress-bar-success-bg:     $brand-success !default;
664 | //** Warning progress bar color
665 | $progress-bar-warning-bg:     $brand-warning !default;
666 | //** Danger progress bar color
667 | $progress-bar-danger-bg:      $brand-danger !default;
668 | //** Info progress bar color
669 | $progress-bar-info-bg:        $brand-info !default;
670 | 
671 | 
672 | //== List group
673 | //
674 | //##
675 | 
676 | //** Background color on `.list-group-item`
677 | $list-group-bg:                 #fff !default;
678 | //** `.list-group-item` border color
679 | $list-group-border:             #ddd !default;
680 | //** List group border radius
681 | $list-group-border-radius:      $border-radius-base !default;
682 | 
683 | //** Background color of single list items on hover
684 | $list-group-hover-bg:           #f5f5f5 !default;
685 | //** Text color of active list items
686 | $list-group-active-color:       $component-active-color !default;
687 | //** Background color of active list items
688 | $list-group-active-bg:          $component-active-bg !default;
689 | //** Border color of active list elements
690 | $list-group-active-border:      $list-group-active-bg !default;
691 | //** Text color for content within active list items
692 | $list-group-active-text-color:  lighten($list-group-active-bg, 40%) !default;
693 | 
694 | //** Text color of disabled list items
695 | $list-group-disabled-color:      $gray-light !default;
696 | //** Background color of disabled list items
697 | $list-group-disabled-bg:         $gray-lighter !default;
698 | //** Text color for content within disabled list items
699 | $list-group-disabled-text-color: $list-group-disabled-color !default;
700 | 
701 | $list-group-link-color:         #555 !default;
702 | $list-group-link-hover-color:   $list-group-link-color !default;
703 | $list-group-link-heading-color: #333 !default;
704 | 
705 | 
706 | //== Panels
707 | //
708 | //##
709 | 
710 | $panel-bg:                    #fff !default;
711 | $panel-body-padding:          15px !default;
712 | $panel-heading-padding:       10px 15px !default;
713 | $panel-footer-padding:        $panel-heading-padding !default;
714 | $panel-border-radius:         $border-radius-base !default;
715 | 
716 | //** Border color for elements within panels
717 | $panel-inner-border:          #ddd !default;
718 | $panel-footer-bg:             #f5f5f5 !default;
719 | 
720 | $panel-default-text:          $gray-dark !default;
721 | $panel-default-border:        #ddd !default;
722 | $panel-default-heading-bg:    #f5f5f5 !default;
723 | 
724 | $panel-primary-text:          #fff !default;
725 | $panel-primary-border:        $brand-primary !default;
726 | $panel-primary-heading-bg:    $brand-primary !default;
727 | 
728 | $panel-success-text:          $state-success-text !default;
729 | $panel-success-border:        $state-success-border !default;
730 | $panel-success-heading-bg:    $state-success-bg !default;
731 | 
732 | $panel-info-text:             $state-info-text !default;
733 | $panel-info-border:           $state-info-border !default;
734 | $panel-info-heading-bg:       $state-info-bg !default;
735 | 
736 | $panel-warning-text:          $state-warning-text !default;
737 | $panel-warning-border:        $state-warning-border !default;
738 | $panel-warning-heading-bg:    $state-warning-bg !default;
739 | 
740 | $panel-danger-text:           $state-danger-text !default;
741 | $panel-danger-border:         $state-danger-border !default;
742 | $panel-danger-heading-bg:     $state-danger-bg !default;
743 | 
744 | 
745 | //== Thumbnails
746 | //
747 | //##
748 | 
749 | //** Padding around the thumbnail image
750 | $thumbnail-padding:           4px !default;
751 | //** Thumbnail background color
752 | $thumbnail-bg:                $body-bg !default;
753 | //** Thumbnail border color
754 | $thumbnail-border:            #ddd !default;
755 | //** Thumbnail border radius
756 | $thumbnail-border-radius:     $border-radius-base !default;
757 | 
758 | //** Custom text color for thumbnail captions
759 | $thumbnail-caption-color:     $text-color !default;
760 | //** Padding around the thumbnail caption
761 | $thumbnail-caption-padding:   9px !default;
762 | 
763 | 
764 | //== Wells
765 | //
766 | //##
767 | 
768 | $well-bg:                     #f5f5f5 !default;
769 | $well-border:                 darken($well-bg, 7%) !default;
770 | 
771 | 
772 | //== Badges
773 | //
774 | //##
775 | 
776 | $badge-color:                 #fff !default;
777 | //** Linked badge text color on hover
778 | $badge-link-hover-color:      #fff !default;
779 | $badge-bg:                    $gray-light !default;
780 | 
781 | //** Badge text color in active nav link
782 | $badge-active-color:          $link-color !default;
783 | //** Badge background color in active nav link
784 | $badge-active-bg:             #fff !default;
785 | 
786 | $badge-font-weight:           bold !default;
787 | $badge-line-height:           1 !default;
788 | $badge-border-radius:         10px !default;
789 | 
790 | 
791 | //== Breadcrumbs
792 | //
793 | //##
794 | 
795 | $breadcrumb-padding-vertical:   8px !default;
796 | $breadcrumb-padding-horizontal: 15px !default;
797 | //** Breadcrumb background color
798 | $breadcrumb-bg:                 #f5f5f5 !default;
799 | //** Breadcrumb text color
800 | $breadcrumb-color:              #ccc !default;
801 | //** Text color of current page in the breadcrumb
802 | $breadcrumb-active-color:       $gray-light !default;
803 | //** Textual separator for between breadcrumb elements
804 | $breadcrumb-separator:          "/" !default;
805 | 
806 | 
807 | //== Carousel
808 | //
809 | //##
810 | 
811 | $carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6) !default;
812 | 
813 | $carousel-control-color:                      #fff !default;
814 | $carousel-control-width:                      15% !default;
815 | $carousel-control-opacity:                    .5 !default;
816 | $carousel-control-font-size:                  20px !default;
817 | 
818 | $carousel-indicator-active-bg:                #fff !default;
819 | $carousel-indicator-border-color:             #fff !default;
820 | 
821 | $carousel-caption-color:                      #fff !default;
822 | 
823 | 
824 | //== Close
825 | //
826 | //##
827 | 
828 | $close-font-weight:           bold !default;
829 | $close-color:                 #000 !default;
830 | $close-text-shadow:           0 1px 0 #fff !default;
831 | 
832 | 
833 | //== Code
834 | //
835 | //##
836 | 
837 | $code-color:                  #c7254e !default;
838 | $code-bg:                     #f9f2f4 !default;
839 | 
840 | $kbd-color:                   #fff !default;
841 | $kbd-bg:                      #333 !default;
842 | 
843 | $pre-bg:                      #f5f5f5 !default;
844 | $pre-color:                   $gray-dark !default;
845 | $pre-border-color:            #ccc !default;
846 | $pre-scrollable-max-height:   340px !default;
847 | 
848 | 
849 | //== Type
850 | //
851 | //##
852 | 
853 | //** Horizontal offset for forms and lists.
854 | $component-offset-horizontal: 180px !default;
855 | //** Text muted color
856 | $text-muted:                  $gray-light !default;
857 | //** Abbreviations and acronyms border color
858 | $abbr-border-color:           $gray-light !default;
859 | //** Headings small color
860 | $headings-small-color:        $gray-light !default;
861 | //** Blockquote small color
862 | $blockquote-small-color:      $gray-light !default;
863 | //** Blockquote font size
864 | $blockquote-font-size:        ($font-size-base * 1.25) !default;
865 | //** Blockquote border color
866 | $blockquote-border-color:     $gray-lighter !default;
867 | //** Page header border color
868 | $page-header-border-color:    $gray-lighter !default;
869 | //** Width of horizontal description list titles
870 | $dl-horizontal-offset:        $component-offset-horizontal !default;
871 | //** Point at which .dl-horizontal becomes horizontal
872 | $dl-horizontal-breakpoint:    $grid-float-breakpoint !default;
873 | //** Horizontal line color.
874 | $hr-border:                   $gray-lighter !default;
875 | 


--------------------------------------------------------------------------------
/src/assets/form-wizard/mixins/_buttons.scss:
--------------------------------------------------------------------------------
 1 | // Button sizes
 2 | @mixin button-size($padding-vertical, $padding-horizontal, $font-size, $line-height, $border-radius) {
 3 |   padding: $padding-vertical $padding-horizontal;
 4 |   font-size: $font-size;
 5 |   line-height: $line-height;
 6 |   border-radius: $border-radius;
 7 | }
 8 | 
 9 | // WebKit-style focus
10 | 
11 | @mixin tab-focus() {
12 |   // WebKit-specific. Other browsers will keep their default outline style.
13 |   // (Initially tried to also force default via `outline: initial`,
14 |   // but that seems to erroneously remove the outline in Firefox altogether.)
15 |   outline: 5px auto -webkit-focus-ring-color;
16 |   outline-offset: -2px;
17 | }
18 | 


--------------------------------------------------------------------------------
/src/assets/form-wizard/mixins/_transparency.scss:
--------------------------------------------------------------------------------
1 | @mixin opacity($opacity) {
2 |   opacity: $opacity;
3 |   // IE8 filter
4 |   $opacity-ie: ($opacity * 100);
5 |   filter: #{alpha(opacity=$opacity-ie)};
6 | }
7 | 


--------------------------------------------------------------------------------
/src/assets/form-wizard/mixins/_vendor-prefixes.scss:
--------------------------------------------------------------------------------
 1 | @mixin box-shadow($shadow...) {
 2 |   -webkit-box-shadow: $shadow; // iOS <4.3 & Android <4.1
 3 |           box-shadow: $shadow;
 4 | }
 5 | 
 6 | @mixin transition($time, $type){
 7 |     -webkit-transition: all $time $type;
 8 |     -moz-transition: all $time $type;
 9 |     -o-transition: all $time $type;
10 |     -ms-transition: all $time $type;
11 |     transition: all $time $type;
12 | }
13 | 
14 | @mixin transition-font-size($time, $type) {
15 |     -webkit-transition: font-size $time $type;
16 |     -moz-transition: font-size $time $type;
17 |     -o-transition: font-size $time $type;
18 |     -ms-transition: font-size $time $type;
19 |     transition: font-size $time $type;
20 | }
21 | 


--------------------------------------------------------------------------------
/src/assets/form-wizard/mixins/_wizard-size.scss:
--------------------------------------------------------------------------------
 1 | @mixin wizard-size($name, $size, $font-size){
 2 |   $computed-font-size: $size/2 + 5px;
 3 | 
 4 |   &.#{$name}{
 5 |     .wizard-icon-circle{
 6 |       width: $size;
 7 |       height: $size;
 8 |       font-size: $font-size;
 9 |       &.tab_shape {
10 |         height: $computed-font-size;
11 |       }
12 |     }
13 |     .wizard-nav-pills > li.active > a .wizard-icon {
14 |       font-size: $font-size;
15 |     }
16 |     .wizard-navigation .wizard-progress-with-circle {
17 |       position: relative;
18 |       top: $computed-font-size;
19 |       height: 4px;
20 |     }
21 |   }
22 | }
23 | 


--------------------------------------------------------------------------------
/src/assets/wizard.scss:
--------------------------------------------------------------------------------
 1 | //Bootstrap imports just for general styling
 2 | @import "form-wizard/bs_button";
 3 | @import "form-wizard/bs_nav_pills";
 4 | @import "form-wizard/bs_progress_bar";
 5 | 
 6 | @import "form-wizard/variables";
 7 | @import "form-wizard/mixins";
 8 | //Form wizard related
 9 | @import "form-wizard/buttons";
10 | @import "form-wizard/navs-pagination";
11 | @import "form-wizard/wizard-card";
12 | 


--------------------------------------------------------------------------------
/src/components/FormWizard.vue:
--------------------------------------------------------------------------------
  1 | 
 75 | 
473 | 
476 | 


--------------------------------------------------------------------------------
/src/components/TabContent.vue:
--------------------------------------------------------------------------------
 1 | 
11 | 
86 | 


--------------------------------------------------------------------------------
/src/components/WizardButton.vue:
--------------------------------------------------------------------------------
 1 | 
 6 | 
 9 | 
11 | 


--------------------------------------------------------------------------------
/src/components/WizardStep.vue:
--------------------------------------------------------------------------------
 1 | 
41 | 
92 | 
94 | 


--------------------------------------------------------------------------------
/src/components/helpers.js:
--------------------------------------------------------------------------------
 1 | export function getFocusedElementId () {
 2 |   return document.activeElement.id
 3 | }
 4 | export function getFocusedTabIndex (tabs = []) {
 5 |   let activeId = getFocusedElementId()
 6 |   let tabIndex = tabs.findIndex(tab => tab.tabId === activeId)
 7 |   return tabIndex
 8 | }
 9 | export function findElementAndFocus (elemId) {
10 |   let elem = document.getElementById(elemId)
11 |   elem.focus()
12 | }
13 | export function isPromise (func) {
14 |   return func.then && typeof func.then === 'function'
15 | }
16 | 


--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
 1 | import FormWizard from './components/FormWizard.vue'
 2 | import TabContent from './components/TabContent.vue'
 3 | import WizardButton from './components/WizardButton.vue'
 4 | import WizardStep from './components/WizardStep.vue'
 5 | const VueFormWizard = {
 6 |   install (Vue) {
 7 |     Vue.component('form-wizard', FormWizard)
 8 |     Vue.component('tab-content', TabContent)
 9 |     Vue.component('wizard-button', WizardButton)
10 |     Vue.component('wizard-step', WizardStep)
11 |   }
12 | }
13 | // Automatic installation if Vue has been added to the global scope.
14 | if (typeof window !== 'undefined' && window.Vue) {
15 |   window.Vue.use(VueFormWizard)
16 | }
17 | 
18 | export default VueFormWizard
19 | export {
20 |   FormWizard,
21 |   TabContent,
22 |   WizardButton,
23 |   WizardStep
24 | }
25 | 


--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BinarCode/vue-form-wizard/4ba261ade8a1c284681e2d9af3c1c838daa6c246/static/.gitkeep


--------------------------------------------------------------------------------
/static/fonts/themify.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BinarCode/vue-form-wizard/4ba261ade8a1c284681e2d9af3c1c838daa6c246/static/fonts/themify.eot


--------------------------------------------------------------------------------
/static/fonts/themify.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BinarCode/vue-form-wizard/4ba261ade8a1c284681e2d9af3c1c838daa6c246/static/fonts/themify.ttf


--------------------------------------------------------------------------------
/static/fonts/themify.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BinarCode/vue-form-wizard/4ba261ade8a1c284681e2d9af3c1c838daa6c246/static/fonts/themify.woff


--------------------------------------------------------------------------------
/static/themify-icons.css:
--------------------------------------------------------------------------------
   1 | @font-face {
   2 | 	font-family: 'themify';
   3 | 	src:url('/static/fonts/themify.eot?-fvbane');
   4 | 	src:url('/static/fonts/themify.eot?#iefix-fvbane') format('embedded-opentype'),
   5 | 		url('/static/fonts/themify.woff?-fvbane') format('woff'),
   6 | 		url('/static/fonts/themify.ttf?-fvbane') format('truetype'),
   7 | 		url('/static/fonts/themify.svg?-fvbane#themify') format('svg');
   8 | 	font-weight: normal;
   9 | 	font-style: normal;
  10 | }
  11 | 
  12 | [class^="ti-"], [class*=" ti-"] {
  13 | 	font-family: 'themify';
  14 | 	speak: none;
  15 | 	font-style: normal;
  16 | 	font-weight: bold;
  17 | 	font-variant: normal;
  18 | 	text-transform: none;
  19 | 	line-height: 1;
  20 | 
  21 | 	/* Better Font Rendering =========== */
  22 | 	-webkit-font-smoothing: antialiased;
  23 | 	-moz-osx-font-smoothing: grayscale;
  24 | }
  25 | 
  26 | .ti-wand:before {
  27 | 	content: "\e600";
  28 | }
  29 | .ti-volume:before {
  30 | 	content: "\e601";
  31 | }
  32 | .ti-user:before {
  33 | 	content: "\e602";
  34 | }
  35 | .ti-unlock:before {
  36 | 	content: "\e603";
  37 | }
  38 | .ti-unlink:before {
  39 | 	content: "\e604";
  40 | }
  41 | .ti-trash:before {
  42 | 	content: "\e605";
  43 | }
  44 | .ti-thought:before {
  45 | 	content: "\e606";
  46 | }
  47 | .ti-target:before {
  48 | 	content: "\e607";
  49 | }
  50 | .ti-tag:before {
  51 | 	content: "\e608";
  52 | }
  53 | .ti-tablet:before {
  54 | 	content: "\e609";
  55 | }
  56 | .ti-star:before {
  57 | 	content: "\e60a";
  58 | }
  59 | .ti-spray:before {
  60 | 	content: "\e60b";
  61 | }
  62 | .ti-signal:before {
  63 | 	content: "\e60c";
  64 | }
  65 | .ti-shopping-cart:before {
  66 | 	content: "\e60d";
  67 | }
  68 | .ti-shopping-cart-full:before {
  69 | 	content: "\e60e";
  70 | }
  71 | .ti-settings:before {
  72 | 	content: "\e60f";
  73 | }
  74 | .ti-search:before {
  75 | 	content: "\e610";
  76 | }
  77 | .ti-zoom-in:before {
  78 | 	content: "\e611";
  79 | }
  80 | .ti-zoom-out:before {
  81 | 	content: "\e612";
  82 | }
  83 | .ti-cut:before {
  84 | 	content: "\e613";
  85 | }
  86 | .ti-ruler:before {
  87 | 	content: "\e614";
  88 | }
  89 | .ti-ruler-pencil:before {
  90 | 	content: "\e615";
  91 | }
  92 | .ti-ruler-alt:before {
  93 | 	content: "\e616";
  94 | }
  95 | .ti-bookmark:before {
  96 | 	content: "\e617";
  97 | }
  98 | .ti-bookmark-alt:before {
  99 | 	content: "\e618";
 100 | }
 101 | .ti-reload:before {
 102 | 	content: "\e619";
 103 | }
 104 | .ti-plus:before {
 105 | 	content: "\e61a";
 106 | }
 107 | .ti-pin:before {
 108 | 	content: "\e61b";
 109 | }
 110 | .ti-pencil:before {
 111 | 	content: "\e61c";
 112 | }
 113 | .ti-pencil-alt:before {
 114 | 	content: "\e61d";
 115 | }
 116 | .ti-paint-roller:before {
 117 | 	content: "\e61e";
 118 | }
 119 | .ti-paint-bucket:before {
 120 | 	content: "\e61f";
 121 | }
 122 | .ti-na:before {
 123 | 	content: "\e620";
 124 | }
 125 | .ti-mobile:before {
 126 | 	content: "\e621";
 127 | }
 128 | .ti-minus:before {
 129 | 	content: "\e622";
 130 | }
 131 | .ti-medall:before {
 132 | 	content: "\e623";
 133 | }
 134 | .ti-medall-alt:before {
 135 | 	content: "\e624";
 136 | }
 137 | .ti-marker:before {
 138 | 	content: "\e625";
 139 | }
 140 | .ti-marker-alt:before {
 141 | 	content: "\e626";
 142 | }
 143 | .ti-arrow-up:before {
 144 | 	content: "\e627";
 145 | }
 146 | .ti-arrow-right:before {
 147 | 	content: "\e628";
 148 | }
 149 | .ti-arrow-left:before {
 150 | 	content: "\e629";
 151 | }
 152 | .ti-arrow-down:before {
 153 | 	content: "\e62a";
 154 | }
 155 | .ti-lock:before {
 156 | 	content: "\e62b";
 157 | }
 158 | .ti-location-arrow:before {
 159 | 	content: "\e62c";
 160 | }
 161 | .ti-link:before {
 162 | 	content: "\e62d";
 163 | }
 164 | .ti-layout:before {
 165 | 	content: "\e62e";
 166 | }
 167 | .ti-layers:before {
 168 | 	content: "\e62f";
 169 | }
 170 | .ti-layers-alt:before {
 171 | 	content: "\e630";
 172 | }
 173 | .ti-key:before {
 174 | 	content: "\e631";
 175 | }
 176 | .ti-import:before {
 177 | 	content: "\e632";
 178 | }
 179 | .ti-image:before {
 180 | 	content: "\e633";
 181 | }
 182 | .ti-heart:before {
 183 | 	content: "\e634";
 184 | }
 185 | .ti-heart-broken:before {
 186 | 	content: "\e635";
 187 | }
 188 | .ti-hand-stop:before {
 189 | 	content: "\e636";
 190 | }
 191 | .ti-hand-open:before {
 192 | 	content: "\e637";
 193 | }
 194 | .ti-hand-drag:before {
 195 | 	content: "\e638";
 196 | }
 197 | .ti-folder:before {
 198 | 	content: "\e639";
 199 | }
 200 | .ti-flag:before {
 201 | 	content: "\e63a";
 202 | }
 203 | .ti-flag-alt:before {
 204 | 	content: "\e63b";
 205 | }
 206 | .ti-flag-alt-2:before {
 207 | 	content: "\e63c";
 208 | }
 209 | .ti-eye:before {
 210 | 	content: "\e63d";
 211 | }
 212 | .ti-export:before {
 213 | 	content: "\e63e";
 214 | }
 215 | .ti-exchange-vertical:before {
 216 | 	content: "\e63f";
 217 | }
 218 | .ti-desktop:before {
 219 | 	content: "\e640";
 220 | }
 221 | .ti-cup:before {
 222 | 	content: "\e641";
 223 | }
 224 | .ti-crown:before {
 225 | 	content: "\e642";
 226 | }
 227 | .ti-comments:before {
 228 | 	content: "\e643";
 229 | }
 230 | .ti-comment:before {
 231 | 	content: "\e644";
 232 | }
 233 | .ti-comment-alt:before {
 234 | 	content: "\e645";
 235 | }
 236 | .ti-close:before {
 237 | 	content: "\e646";
 238 | }
 239 | .ti-clip:before {
 240 | 	content: "\e647";
 241 | }
 242 | .ti-angle-up:before {
 243 | 	content: "\e648";
 244 | }
 245 | .ti-angle-right:before {
 246 | 	content: "\e649";
 247 | }
 248 | .ti-angle-left:before {
 249 | 	content: "\e64a";
 250 | }
 251 | .ti-angle-down:before {
 252 | 	content: "\e64b";
 253 | }
 254 | .ti-check:before {
 255 | 	content: "\e64c";
 256 | }
 257 | .ti-check-box:before {
 258 | 	content: "\e64d";
 259 | }
 260 | .ti-camera:before {
 261 | 	content: "\e64e";
 262 | }
 263 | .ti-announcement:before {
 264 | 	content: "\e64f";
 265 | }
 266 | .ti-brush:before {
 267 | 	content: "\e650";
 268 | }
 269 | .ti-briefcase:before {
 270 | 	content: "\e651";
 271 | }
 272 | .ti-bolt:before {
 273 | 	content: "\e652";
 274 | }
 275 | .ti-bolt-alt:before {
 276 | 	content: "\e653";
 277 | }
 278 | .ti-blackboard:before {
 279 | 	content: "\e654";
 280 | }
 281 | .ti-bag:before {
 282 | 	content: "\e655";
 283 | }
 284 | .ti-move:before {
 285 | 	content: "\e656";
 286 | }
 287 | .ti-arrows-vertical:before {
 288 | 	content: "\e657";
 289 | }
 290 | .ti-arrows-horizontal:before {
 291 | 	content: "\e658";
 292 | }
 293 | .ti-fullscreen:before {
 294 | 	content: "\e659";
 295 | }
 296 | .ti-arrow-top-right:before {
 297 | 	content: "\e65a";
 298 | }
 299 | .ti-arrow-top-left:before {
 300 | 	content: "\e65b";
 301 | }
 302 | .ti-arrow-circle-up:before {
 303 | 	content: "\e65c";
 304 | }
 305 | .ti-arrow-circle-right:before {
 306 | 	content: "\e65d";
 307 | }
 308 | .ti-arrow-circle-left:before {
 309 | 	content: "\e65e";
 310 | }
 311 | .ti-arrow-circle-down:before {
 312 | 	content: "\e65f";
 313 | }
 314 | .ti-angle-double-up:before {
 315 | 	content: "\e660";
 316 | }
 317 | .ti-angle-double-right:before {
 318 | 	content: "\e661";
 319 | }
 320 | .ti-angle-double-left:before {
 321 | 	content: "\e662";
 322 | }
 323 | .ti-angle-double-down:before {
 324 | 	content: "\e663";
 325 | }
 326 | .ti-zip:before {
 327 | 	content: "\e664";
 328 | }
 329 | .ti-world:before {
 330 | 	content: "\e665";
 331 | }
 332 | .ti-wheelchair:before {
 333 | 	content: "\e666";
 334 | }
 335 | .ti-view-list:before {
 336 | 	content: "\e667";
 337 | }
 338 | .ti-view-list-alt:before {
 339 | 	content: "\e668";
 340 | }
 341 | .ti-view-grid:before {
 342 | 	content: "\e669";
 343 | }
 344 | .ti-uppercase:before {
 345 | 	content: "\e66a";
 346 | }
 347 | .ti-upload:before {
 348 | 	content: "\e66b";
 349 | }
 350 | .ti-underline:before {
 351 | 	content: "\e66c";
 352 | }
 353 | .ti-truck:before {
 354 | 	content: "\e66d";
 355 | }
 356 | .ti-timer:before {
 357 | 	content: "\e66e";
 358 | }
 359 | .ti-ticket:before {
 360 | 	content: "\e66f";
 361 | }
 362 | .ti-thumb-up:before {
 363 | 	content: "\e670";
 364 | }
 365 | .ti-thumb-down:before {
 366 | 	content: "\e671";
 367 | }
 368 | .ti-text:before {
 369 | 	content: "\e672";
 370 | }
 371 | .ti-stats-up:before {
 372 | 	content: "\e673";
 373 | }
 374 | .ti-stats-down:before {
 375 | 	content: "\e674";
 376 | }
 377 | .ti-split-v:before {
 378 | 	content: "\e675";
 379 | }
 380 | .ti-split-h:before {
 381 | 	content: "\e676";
 382 | }
 383 | .ti-smallcap:before {
 384 | 	content: "\e677";
 385 | }
 386 | .ti-shine:before {
 387 | 	content: "\e678";
 388 | }
 389 | .ti-shift-right:before {
 390 | 	content: "\e679";
 391 | }
 392 | .ti-shift-left:before {
 393 | 	content: "\e67a";
 394 | }
 395 | .ti-shield:before {
 396 | 	content: "\e67b";
 397 | }
 398 | .ti-notepad:before {
 399 | 	content: "\e67c";
 400 | }
 401 | .ti-server:before {
 402 | 	content: "\e67d";
 403 | }
 404 | .ti-quote-right:before {
 405 | 	content: "\e67e";
 406 | }
 407 | .ti-quote-left:before {
 408 | 	content: "\e67f";
 409 | }
 410 | .ti-pulse:before {
 411 | 	content: "\e680";
 412 | }
 413 | .ti-printer:before {
 414 | 	content: "\e681";
 415 | }
 416 | .ti-power-off:before {
 417 | 	content: "\e682";
 418 | }
 419 | .ti-plug:before {
 420 | 	content: "\e683";
 421 | }
 422 | .ti-pie-chart:before {
 423 | 	content: "\e684";
 424 | }
 425 | .ti-paragraph:before {
 426 | 	content: "\e685";
 427 | }
 428 | .ti-panel:before {
 429 | 	content: "\e686";
 430 | }
 431 | .ti-package:before {
 432 | 	content: "\e687";
 433 | }
 434 | .ti-music:before {
 435 | 	content: "\e688";
 436 | }
 437 | .ti-music-alt:before {
 438 | 	content: "\e689";
 439 | }
 440 | .ti-mouse:before {
 441 | 	content: "\e68a";
 442 | }
 443 | .ti-mouse-alt:before {
 444 | 	content: "\e68b";
 445 | }
 446 | .ti-money:before {
 447 | 	content: "\e68c";
 448 | }
 449 | .ti-microphone:before {
 450 | 	content: "\e68d";
 451 | }
 452 | .ti-menu:before {
 453 | 	content: "\e68e";
 454 | }
 455 | .ti-menu-alt:before {
 456 | 	content: "\e68f";
 457 | }
 458 | .ti-map:before {
 459 | 	content: "\e690";
 460 | }
 461 | .ti-map-alt:before {
 462 | 	content: "\e691";
 463 | }
 464 | .ti-loop:before {
 465 | 	content: "\e692";
 466 | }
 467 | .ti-location-pin:before {
 468 | 	content: "\e693";
 469 | }
 470 | .ti-list:before {
 471 | 	content: "\e694";
 472 | }
 473 | .ti-light-bulb:before {
 474 | 	content: "\e695";
 475 | }
 476 | .ti-Italic:before {
 477 | 	content: "\e696";
 478 | }
 479 | .ti-info:before {
 480 | 	content: "\e697";
 481 | }
 482 | .ti-infinite:before {
 483 | 	content: "\e698";
 484 | }
 485 | .ti-id-badge:before {
 486 | 	content: "\e699";
 487 | }
 488 | .ti-hummer:before {
 489 | 	content: "\e69a";
 490 | }
 491 | .ti-home:before {
 492 | 	content: "\e69b";
 493 | }
 494 | .ti-help:before {
 495 | 	content: "\e69c";
 496 | }
 497 | .ti-headphone:before {
 498 | 	content: "\e69d";
 499 | }
 500 | .ti-harddrives:before {
 501 | 	content: "\e69e";
 502 | }
 503 | .ti-harddrive:before {
 504 | 	content: "\e69f";
 505 | }
 506 | .ti-gift:before {
 507 | 	content: "\e6a0";
 508 | }
 509 | .ti-game:before {
 510 | 	content: "\e6a1";
 511 | }
 512 | .ti-filter:before {
 513 | 	content: "\e6a2";
 514 | }
 515 | .ti-files:before {
 516 | 	content: "\e6a3";
 517 | }
 518 | .ti-file:before {
 519 | 	content: "\e6a4";
 520 | }
 521 | .ti-eraser:before {
 522 | 	content: "\e6a5";
 523 | }
 524 | .ti-envelope:before {
 525 | 	content: "\e6a6";
 526 | }
 527 | .ti-download:before {
 528 | 	content: "\e6a7";
 529 | }
 530 | .ti-direction:before {
 531 | 	content: "\e6a8";
 532 | }
 533 | .ti-direction-alt:before {
 534 | 	content: "\e6a9";
 535 | }
 536 | .ti-dashboard:before {
 537 | 	content: "\e6aa";
 538 | }
 539 | .ti-control-stop:before {
 540 | 	content: "\e6ab";
 541 | }
 542 | .ti-control-shuffle:before {
 543 | 	content: "\e6ac";
 544 | }
 545 | .ti-control-play:before {
 546 | 	content: "\e6ad";
 547 | }
 548 | .ti-control-pause:before {
 549 | 	content: "\e6ae";
 550 | }
 551 | .ti-control-forward:before {
 552 | 	content: "\e6af";
 553 | }
 554 | .ti-control-backward:before {
 555 | 	content: "\e6b0";
 556 | }
 557 | .ti-cloud:before {
 558 | 	content: "\e6b1";
 559 | }
 560 | .ti-cloud-up:before {
 561 | 	content: "\e6b2";
 562 | }
 563 | .ti-cloud-down:before {
 564 | 	content: "\e6b3";
 565 | }
 566 | .ti-clipboard:before {
 567 | 	content: "\e6b4";
 568 | }
 569 | .ti-car:before {
 570 | 	content: "\e6b5";
 571 | }
 572 | .ti-calendar:before {
 573 | 	content: "\e6b6";
 574 | }
 575 | .ti-book:before {
 576 | 	content: "\e6b7";
 577 | }
 578 | .ti-bell:before {
 579 | 	content: "\e6b8";
 580 | }
 581 | .ti-basketball:before {
 582 | 	content: "\e6b9";
 583 | }
 584 | .ti-bar-chart:before {
 585 | 	content: "\e6ba";
 586 | }
 587 | .ti-bar-chart-alt:before {
 588 | 	content: "\e6bb";
 589 | }
 590 | .ti-back-right:before {
 591 | 	content: "\e6bc";
 592 | }
 593 | .ti-back-left:before {
 594 | 	content: "\e6bd";
 595 | }
 596 | .ti-arrows-corner:before {
 597 | 	content: "\e6be";
 598 | }
 599 | .ti-archive:before {
 600 | 	content: "\e6bf";
 601 | }
 602 | .ti-anchor:before {
 603 | 	content: "\e6c0";
 604 | }
 605 | .ti-align-right:before {
 606 | 	content: "\e6c1";
 607 | }
 608 | .ti-align-left:before {
 609 | 	content: "\e6c2";
 610 | }
 611 | .ti-align-justify:before {
 612 | 	content: "\e6c3";
 613 | }
 614 | .ti-align-center:before {
 615 | 	content: "\e6c4";
 616 | }
 617 | .ti-alert:before {
 618 | 	content: "\e6c5";
 619 | }
 620 | .ti-alarm-clock:before {
 621 | 	content: "\e6c6";
 622 | }
 623 | .ti-agenda:before {
 624 | 	content: "\e6c7";
 625 | }
 626 | .ti-write:before {
 627 | 	content: "\e6c8";
 628 | }
 629 | .ti-window:before {
 630 | 	content: "\e6c9";
 631 | }
 632 | .ti-widgetized:before {
 633 | 	content: "\e6ca";
 634 | }
 635 | .ti-widget:before {
 636 | 	content: "\e6cb";
 637 | }
 638 | .ti-widget-alt:before {
 639 | 	content: "\e6cc";
 640 | }
 641 | .ti-wallet:before {
 642 | 	content: "\e6cd";
 643 | }
 644 | .ti-video-clapper:before {
 645 | 	content: "\e6ce";
 646 | }
 647 | .ti-video-camera:before {
 648 | 	content: "\e6cf";
 649 | }
 650 | .ti-vector:before {
 651 | 	content: "\e6d0";
 652 | }
 653 | .ti-themify-logo:before {
 654 | 	content: "\e6d1";
 655 | }
 656 | .ti-themify-favicon:before {
 657 | 	content: "\e6d2";
 658 | }
 659 | .ti-themify-favicon-alt:before {
 660 | 	content: "\e6d3";
 661 | }
 662 | .ti-support:before {
 663 | 	content: "\e6d4";
 664 | }
 665 | .ti-stamp:before {
 666 | 	content: "\e6d5";
 667 | }
 668 | .ti-split-v-alt:before {
 669 | 	content: "\e6d6";
 670 | }
 671 | .ti-slice:before {
 672 | 	content: "\e6d7";
 673 | }
 674 | .ti-shortcode:before {
 675 | 	content: "\e6d8";
 676 | }
 677 | .ti-shift-right-alt:before {
 678 | 	content: "\e6d9";
 679 | }
 680 | .ti-shift-left-alt:before {
 681 | 	content: "\e6da";
 682 | }
 683 | .ti-ruler-alt-2:before {
 684 | 	content: "\e6db";
 685 | }
 686 | .ti-receipt:before {
 687 | 	content: "\e6dc";
 688 | }
 689 | .ti-pin2:before {
 690 | 	content: "\e6dd";
 691 | }
 692 | .ti-pin-alt:before {
 693 | 	content: "\e6de";
 694 | }
 695 | .ti-pencil-alt2:before {
 696 | 	content: "\e6df";
 697 | }
 698 | .ti-palette:before {
 699 | 	content: "\e6e0";
 700 | }
 701 | .ti-more:before {
 702 | 	content: "\e6e1";
 703 | }
 704 | .ti-more-alt:before {
 705 | 	content: "\e6e2";
 706 | }
 707 | .ti-microphone-alt:before {
 708 | 	content: "\e6e3";
 709 | }
 710 | .ti-magnet:before {
 711 | 	content: "\e6e4";
 712 | }
 713 | .ti-line-double:before {
 714 | 	content: "\e6e5";
 715 | }
 716 | .ti-line-dotted:before {
 717 | 	content: "\e6e6";
 718 | }
 719 | .ti-line-dashed:before {
 720 | 	content: "\e6e7";
 721 | }
 722 | .ti-layout-width-full:before {
 723 | 	content: "\e6e8";
 724 | }
 725 | .ti-layout-width-default:before {
 726 | 	content: "\e6e9";
 727 | }
 728 | .ti-layout-width-default-alt:before {
 729 | 	content: "\e6ea";
 730 | }
 731 | .ti-layout-tab:before {
 732 | 	content: "\e6eb";
 733 | }
 734 | .ti-layout-tab-window:before {
 735 | 	content: "\e6ec";
 736 | }
 737 | .ti-layout-tab-v:before {
 738 | 	content: "\e6ed";
 739 | }
 740 | .ti-layout-tab-min:before {
 741 | 	content: "\e6ee";
 742 | }
 743 | .ti-layout-slider:before {
 744 | 	content: "\e6ef";
 745 | }
 746 | .ti-layout-slider-alt:before {
 747 | 	content: "\e6f0";
 748 | }
 749 | .ti-layout-sidebar-right:before {
 750 | 	content: "\e6f1";
 751 | }
 752 | .ti-layout-sidebar-none:before {
 753 | 	content: "\e6f2";
 754 | }
 755 | .ti-layout-sidebar-left:before {
 756 | 	content: "\e6f3";
 757 | }
 758 | .ti-layout-placeholder:before {
 759 | 	content: "\e6f4";
 760 | }
 761 | .ti-layout-menu:before {
 762 | 	content: "\e6f5";
 763 | }
 764 | .ti-layout-menu-v:before {
 765 | 	content: "\e6f6";
 766 | }
 767 | .ti-layout-menu-separated:before {
 768 | 	content: "\e6f7";
 769 | }
 770 | .ti-layout-menu-full:before {
 771 | 	content: "\e6f8";
 772 | }
 773 | .ti-layout-media-right-alt:before {
 774 | 	content: "\e6f9";
 775 | }
 776 | .ti-layout-media-right:before {
 777 | 	content: "\e6fa";
 778 | }
 779 | .ti-layout-media-overlay:before {
 780 | 	content: "\e6fb";
 781 | }
 782 | .ti-layout-media-overlay-alt:before {
 783 | 	content: "\e6fc";
 784 | }
 785 | .ti-layout-media-overlay-alt-2:before {
 786 | 	content: "\e6fd";
 787 | }
 788 | .ti-layout-media-left-alt:before {
 789 | 	content: "\e6fe";
 790 | }
 791 | .ti-layout-media-left:before {
 792 | 	content: "\e6ff";
 793 | }
 794 | .ti-layout-media-center-alt:before {
 795 | 	content: "\e700";
 796 | }
 797 | .ti-layout-media-center:before {
 798 | 	content: "\e701";
 799 | }
 800 | .ti-layout-list-thumb:before {
 801 | 	content: "\e702";
 802 | }
 803 | .ti-layout-list-thumb-alt:before {
 804 | 	content: "\e703";
 805 | }
 806 | .ti-layout-list-post:before {
 807 | 	content: "\e704";
 808 | }
 809 | .ti-layout-list-large-image:before {
 810 | 	content: "\e705";
 811 | }
 812 | .ti-layout-line-solid:before {
 813 | 	content: "\e706";
 814 | }
 815 | .ti-layout-grid4:before {
 816 | 	content: "\e707";
 817 | }
 818 | .ti-layout-grid3:before {
 819 | 	content: "\e708";
 820 | }
 821 | .ti-layout-grid2:before {
 822 | 	content: "\e709";
 823 | }
 824 | .ti-layout-grid2-thumb:before {
 825 | 	content: "\e70a";
 826 | }
 827 | .ti-layout-cta-right:before {
 828 | 	content: "\e70b";
 829 | }
 830 | .ti-layout-cta-left:before {
 831 | 	content: "\e70c";
 832 | }
 833 | .ti-layout-cta-center:before {
 834 | 	content: "\e70d";
 835 | }
 836 | .ti-layout-cta-btn-right:before {
 837 | 	content: "\e70e";
 838 | }
 839 | .ti-layout-cta-btn-left:before {
 840 | 	content: "\e70f";
 841 | }
 842 | .ti-layout-column4:before {
 843 | 	content: "\e710";
 844 | }
 845 | .ti-layout-column3:before {
 846 | 	content: "\e711";
 847 | }
 848 | .ti-layout-column2:before {
 849 | 	content: "\e712";
 850 | }
 851 | .ti-layout-accordion-separated:before {
 852 | 	content: "\e713";
 853 | }
 854 | .ti-layout-accordion-merged:before {
 855 | 	content: "\e714";
 856 | }
 857 | .ti-layout-accordion-list:before {
 858 | 	content: "\e715";
 859 | }
 860 | .ti-ink-pen:before {
 861 | 	content: "\e716";
 862 | }
 863 | .ti-info-alt:before {
 864 | 	content: "\e717";
 865 | }
 866 | .ti-help-alt:before {
 867 | 	content: "\e718";
 868 | }
 869 | .ti-headphone-alt:before {
 870 | 	content: "\e719";
 871 | }
 872 | .ti-hand-point-up:before {
 873 | 	content: "\e71a";
 874 | }
 875 | .ti-hand-point-right:before {
 876 | 	content: "\e71b";
 877 | }
 878 | .ti-hand-point-left:before {
 879 | 	content: "\e71c";
 880 | }
 881 | .ti-hand-point-down:before {
 882 | 	content: "\e71d";
 883 | }
 884 | .ti-gallery:before {
 885 | 	content: "\e71e";
 886 | }
 887 | .ti-face-smile:before {
 888 | 	content: "\e71f";
 889 | }
 890 | .ti-face-sad:before {
 891 | 	content: "\e720";
 892 | }
 893 | .ti-credit-card:before {
 894 | 	content: "\e721";
 895 | }
 896 | .ti-control-skip-forward:before {
 897 | 	content: "\e722";
 898 | }
 899 | .ti-control-skip-backward:before {
 900 | 	content: "\e723";
 901 | }
 902 | .ti-control-record:before {
 903 | 	content: "\e724";
 904 | }
 905 | .ti-control-eject:before {
 906 | 	content: "\e725";
 907 | }
 908 | .ti-comments-smiley:before {
 909 | 	content: "\e726";
 910 | }
 911 | .ti-brush-alt:before {
 912 | 	content: "\e727";
 913 | }
 914 | .ti-youtube:before {
 915 | 	content: "\e728";
 916 | }
 917 | .ti-vimeo:before {
 918 | 	content: "\e729";
 919 | }
 920 | .ti-twitter:before {
 921 | 	content: "\e72a";
 922 | }
 923 | .ti-time:before {
 924 | 	content: "\e72b";
 925 | }
 926 | .ti-tumblr:before {
 927 | 	content: "\e72c";
 928 | }
 929 | .ti-skype:before {
 930 | 	content: "\e72d";
 931 | }
 932 | .ti-share:before {
 933 | 	content: "\e72e";
 934 | }
 935 | .ti-share-alt:before {
 936 | 	content: "\e72f";
 937 | }
 938 | .ti-rocket:before {
 939 | 	content: "\e730";
 940 | }
 941 | .ti-pinterest:before {
 942 | 	content: "\e731";
 943 | }
 944 | .ti-new-window:before {
 945 | 	content: "\e732";
 946 | }
 947 | .ti-microsoft:before {
 948 | 	content: "\e733";
 949 | }
 950 | .ti-list-ol:before {
 951 | 	content: "\e734";
 952 | }
 953 | .ti-linkedin:before {
 954 | 	content: "\e735";
 955 | }
 956 | .ti-layout-sidebar-2:before {
 957 | 	content: "\e736";
 958 | }
 959 | .ti-layout-grid4-alt:before {
 960 | 	content: "\e737";
 961 | }
 962 | .ti-layout-grid3-alt:before {
 963 | 	content: "\e738";
 964 | }
 965 | .ti-layout-grid2-alt:before {
 966 | 	content: "\e739";
 967 | }
 968 | .ti-layout-column4-alt:before {
 969 | 	content: "\e73a";
 970 | }
 971 | .ti-layout-column3-alt:before {
 972 | 	content: "\e73b";
 973 | }
 974 | .ti-layout-column2-alt:before {
 975 | 	content: "\e73c";
 976 | }
 977 | .ti-instagram:before {
 978 | 	content: "\e73d";
 979 | }
 980 | .ti-google:before {
 981 | 	content: "\e73e";
 982 | }
 983 | .ti-github:before {
 984 | 	content: "\e73f";
 985 | }
 986 | .ti-flickr:before {
 987 | 	content: "\e740";
 988 | }
 989 | .ti-facebook:before {
 990 | 	content: "\e741";
 991 | }
 992 | .ti-dropbox:before {
 993 | 	content: "\e742";
 994 | }
 995 | .ti-dribbble:before {
 996 | 	content: "\e743";
 997 | }
 998 | .ti-apple:before {
 999 | 	content: "\e744";
1000 | }
1001 | .ti-android:before {
1002 | 	content: "\e745";
1003 | }
1004 | .ti-save:before {
1005 | 	content: "\e746";
1006 | }
1007 | .ti-save-alt:before {
1008 | 	content: "\e747";
1009 | }
1010 | .ti-yahoo:before {
1011 | 	content: "\e748";
1012 | }
1013 | .ti-wordpress:before {
1014 | 	content: "\e749";
1015 | }
1016 | .ti-vimeo-alt:before {
1017 | 	content: "\e74a";
1018 | }
1019 | .ti-twitter-alt:before {
1020 | 	content: "\e74b";
1021 | }
1022 | .ti-tumblr-alt:before {
1023 | 	content: "\e74c";
1024 | }
1025 | .ti-trello:before {
1026 | 	content: "\e74d";
1027 | }
1028 | .ti-stack-overflow:before {
1029 | 	content: "\e74e";
1030 | }
1031 | .ti-soundcloud:before {
1032 | 	content: "\e74f";
1033 | }
1034 | .ti-sharethis:before {
1035 | 	content: "\e750";
1036 | }
1037 | .ti-sharethis-alt:before {
1038 | 	content: "\e751";
1039 | }
1040 | .ti-reddit:before {
1041 | 	content: "\e752";
1042 | }
1043 | .ti-pinterest-alt:before {
1044 | 	content: "\e753";
1045 | }
1046 | .ti-microsoft-alt:before {
1047 | 	content: "\e754";
1048 | }
1049 | .ti-linux:before {
1050 | 	content: "\e755";
1051 | }
1052 | .ti-jsfiddle:before {
1053 | 	content: "\e756";
1054 | }
1055 | .ti-joomla:before {
1056 | 	content: "\e757";
1057 | }
1058 | .ti-html5:before {
1059 | 	content: "\e758";
1060 | }
1061 | .ti-flickr-alt:before {
1062 | 	content: "\e759";
1063 | }
1064 | .ti-email:before {
1065 | 	content: "\e75a";
1066 | }
1067 | .ti-drupal:before {
1068 | 	content: "\e75b";
1069 | }
1070 | .ti-dropbox-alt:before {
1071 | 	content: "\e75c";
1072 | }
1073 | .ti-css3:before {
1074 | 	content: "\e75d";
1075 | }
1076 | .ti-rss:before {
1077 | 	content: "\e75e";
1078 | }
1079 | .ti-rss-alt:before {
1080 | 	content: "\e75f";
1081 | }
1082 | 


--------------------------------------------------------------------------------
/test/e2e/custom-assertions/elementCount.js:
--------------------------------------------------------------------------------
 1 | // A custom Nightwatch assertion.
 2 | // the name of the method is the filename.
 3 | // can be used in tests like this:
 4 | //
 5 | //   browser.assert.elementCount(selector, count)
 6 | //
 7 | // for how to write custom assertions see
 8 | // http://nightwatchjs.org/guide#writing-custom-assertions
 9 | exports.assertion = function (selector, count) {
10 |   this.message = 'Testing if element <' + selector + '> has count: ' + count
11 |   this.expected = count
12 |   this.pass = function (val) {
13 |     return val === this.expected
14 |   }
15 |   this.value = function (res) {
16 |     return res.value
17 |   }
18 |   this.command = function (cb) {
19 |     var self = this
20 |     return this.api.execute(function (selector) {
21 |       return document.querySelectorAll(selector).length
22 |     }, [selector], function (res) {
23 |       cb.call(self, res)
24 |     })
25 |   }
26 | }
27 | 


--------------------------------------------------------------------------------
/test/e2e/nightwatch.conf.js:
--------------------------------------------------------------------------------
 1 | require('babel-register')
 2 | var config = require('../../config')
 3 | 
 4 | // http://nightwatchjs.org/gettingstarted#settings-file
 5 | module.exports = {
 6 |   src_folders: ['test/e2e/specs'],
 7 |   output_folder: 'test/e2e/reports',
 8 |   custom_assertions_path: ['test/e2e/custom-assertions'],
 9 | 
10 |   selenium: {
11 |     start_process: true,
12 |     server_path: require('selenium-server').path,
13 |     host: '127.0.0.1',
14 |     port: 4444,
15 |     cli_args: {
16 |       'webdriver.chrome.driver': require('chromedriver').path
17 |     }
18 |   },
19 | 
20 |   test_settings: {
21 |     default: {
22 |       selenium_port: 4444,
23 |       selenium_host: 'localhost',
24 |       silent: true,
25 |       globals: {
26 |         devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port)
27 |       }
28 |     },
29 | 
30 |     chrome: {
31 |       desiredCapabilities: {
32 |         browserName: 'chrome',
33 |         javascriptEnabled: true,
34 |         acceptSslCerts: true
35 |       }
36 |     },
37 | 
38 |     firefox: {
39 |       desiredCapabilities: {
40 |         browserName: 'firefox',
41 |         javascriptEnabled: true,
42 |         acceptSslCerts: true
43 |       }
44 |     }
45 |   }
46 | }
47 | 


--------------------------------------------------------------------------------
/test/e2e/runner.js:
--------------------------------------------------------------------------------
 1 | // 1. start the dev server using production config
 2 | process.env.NODE_ENV = 'testing'
 3 | var server = require('../../build/dev-server.js')
 4 | 
 5 | server.ready.then(() => {
 6 |   // 2. run the nightwatch test suite against it
 7 |   // to run in additional browsers:
 8 |   //    1. add an entry in test/e2e/nightwatch.conf.json under "test_settings"
 9 |   //    2. add it to the --env flag below
10 |   // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
11 |   // For more information on Nightwatch's config file, see
12 |   // http://nightwatchjs.org/guide#settings-file
13 |   var opts = process.argv.slice(2)
14 |   if (opts.indexOf('--config') === -1) {
15 |     opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
16 |   }
17 |   if (opts.indexOf('--env') === -1) {
18 |     opts = opts.concat(['--env', 'chrome'])
19 |   }
20 | 
21 |   var spawn = require('cross-spawn')
22 |   var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })
23 | 
24 |   runner.on('exit', function (code) {
25 |     server.close()
26 |     process.exit(code)
27 |   })
28 | 
29 |   runner.on('error', function (err) {
30 |     server.close()
31 |     throw err
32 |   })
33 | })
34 | 


--------------------------------------------------------------------------------
/test/e2e/specs/test.js:
--------------------------------------------------------------------------------
 1 | // For authoring Nightwatch tests, see
 2 | // http://nightwatchjs.org/guide#usage
 3 | 
 4 | module.exports = {
 5 |   'default e2e tests': function (browser) {
 6 |     // automatically uses dev Server port from /config.index.js
 7 |     // default: http://localhost:8080
 8 |     // see nightwatch.conf.js
 9 |     const devServer = browser.globals.devServerURL
10 | 
11 |     browser
12 |       .url(devServer)
13 |       .waitForElementVisible('#app', 5000)
14 |       .assert.elementPresent('.hello')
15 |       .assert.containsText('h1', 'Welcome to Your Vue.js App')
16 |       .assert.elementCount('img', 1)
17 |       .end()
18 |   }
19 | }
20 | 


--------------------------------------------------------------------------------
/test/unit/.eslintrc:
--------------------------------------------------------------------------------
 1 | {
 2 |   "env": {
 3 |     "mocha": true
 4 |   },
 5 |   "globals": {
 6 |     "expect": true,
 7 |     "sinon": true
 8 |   }
 9 | }
10 | 


--------------------------------------------------------------------------------
/test/unit/index.js:
--------------------------------------------------------------------------------
 1 | import Vue from 'vue'
 2 | import 'phantomjs-polyfill'
 3 | 
 4 | Vue.config.productionTip = false
 5 | 
 6 | // require all test files (files that ends with .spec.js)
 7 | const testsContext = require.context('./specs', true, /\.spec$/)
 8 | testsContext.keys().forEach(testsContext)
 9 | 
10 | // require all src files except main.js for coverage.
11 | // you can also change this to match only the subset of files that
12 | // you want coverage for.
13 | const srcContext = require.context('../../src', true, /^\.\/(?!index\.js$).+\.(js|vue)$/i)
14 | srcContext.keys().forEach(srcContext)
15 | 


--------------------------------------------------------------------------------
/test/unit/karma.conf.js:
--------------------------------------------------------------------------------
 1 | // This is a karma config file. For more details see
 2 | //   http://karma-runner.github.io/0.13/config/configuration-file.html
 3 | // we are also using it with karma-webpack
 4 | //   https://github.com/webpack/karma-webpack
 5 | 
 6 | var webpackConfig = require('../../build/webpack.test.conf')
 7 | 
 8 | module.exports = function (config) {
 9 |   config.set({
10 |     // to run in additional browsers:
11 |     // 1. install corresponding karma launcher
12 |     //    http://karma-runner.github.io/0.13/config/browsers.html
13 |     // 2. add it to the `browsers` array below.
14 |     browsers: ['PhantomJS'],
15 |     frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim', 'polyfill'],
16 |     polyfill: ['findIndex'],
17 |     reporters: ['spec','progress', 'coverage'],
18 |     files: ['./index.js',
19 |       './../../node_modules/phantomjs-polyfill-find-index/findIndex-polyfill.js',
20 |       './../../node_modules/phantomjs-polyfill-find/find-polyfill.js'],
21 |     preprocessors: {
22 |       './index.js': ['webpack', 'sourcemap']
23 |     },
24 |     webpack: webpackConfig,
25 |     webpackMiddleware: {
26 |       noInfo: true
27 |     },
28 |     coverageReporter: {
29 |       dir: './coverage',
30 |       reporters: [
31 |         { type: 'lcov', subdir: '.' },
32 |         { type: 'text-summary' }
33 |       ]
34 |     }
35 |   })
36 | }
37 | 


--------------------------------------------------------------------------------
/test/unit/specs/FormWizard.spec.js:
--------------------------------------------------------------------------------
  1 | import VueFormWizard, {TabContent as WizardTab, WizardStep, FormWizard} from './../../../src/index'
  2 | import {mount, createLocalVue} from 'vue-test-utils'
  3 | import sinon from 'sinon'
  4 | import Vue from 'vue'
  5 | import VueRouter from 'vue-router'
  6 | 
  7 | const localVue = createLocalVue()
  8 | localVue.use(VueFormWizard)
  9 | localVue.use(VueRouter)
 10 | 
 11 | const First = {template: '
First page
'} 12 | const Second = {template: '
Second page
'} 13 | const Third = {template: '
Third page
'} 14 | 15 | const router = new VueRouter({ 16 | routes: [ 17 | {path: '/first', component: First}, 18 | {path: '/second', component: Second}, 19 | {path: '/third', component: Third} 20 | ] 21 | }) 22 | 23 | 24 | const startIndex = 0 25 | let validationMethod = sinon.stub() 26 | validationMethod.returns(true) 27 | let secondValidationMethod = sinon.stub() 28 | let initialWizard = { 29 | template: ` 30 | 32 | My first tab content 33 | 34 | 37 | My second tab content 38 | 39 | 41 | My third tab content 42 | 43 | `, 44 | data () { 45 | return { 46 | startIndex: startIndex, 47 | showLastStep: true, 48 | showSecondStep: true 49 | } 50 | } 51 | } 52 | let threeStepWizard = initialWizard 53 | 54 | let routerWizard = { 55 | template: ` 56 | 59 | 60 | 63 | 64 | 67 | 68 | 69 | ` 70 | } 71 | 72 | const divSlot = `
73 | 75 | First 76 | 77 | 79 | Second 80 | 81 | 83 | Third 84 | 85 |
` 86 | 87 | describe('FormWizard.vue', () => { 88 | it('contains wizard class', () => { 89 | const wizard = mount(threeStepWizard, {localVue}) 90 | wizard.hasClass('vue-form-wizard') 91 | }) 92 | describe('renders', () => { 93 | it('steps', (done) => { 94 | const wizard = mount(threeStepWizard, {localVue}) 95 | Vue.nextTick(() => { 96 | const steps = wizard.findAll(WizardStep) 97 | const firsStep = steps.at(0) 98 | expect(steps.length).to.equal(3) 99 | expect(firsStep.hasClass('active')) 100 | const stepTitle = firsStep.find('.stepTitle') 101 | expect(stepTitle.is('span')).to.equal(true) 102 | const stepText = stepTitle.text().trim() 103 | expect(stepText).to.equal('Personal details') 104 | done() 105 | }) 106 | }) 107 | it('tabs', () => { 108 | const wizard = mount(threeStepWizard, {localVue}) 109 | const tabs = wizard.findAll(WizardTab) 110 | expect(tabs.length).to.equal(3) 111 | }) 112 | it('only one tab content', () => { 113 | const wizard = mount(threeStepWizard, {localVue}) 114 | const tabs = wizard.findAll(WizardTab).wrappers 115 | const activeTabs = tabs.filter((tab) => tab.vm.active) 116 | const inactiveTabs = tabs.filter((tab) => !tab.vm.active) 117 | expect(activeTabs.length).to.equal(1) 118 | 119 | inactiveTabs.forEach((tab) => { 120 | expect(tab.hasStyle('display', 'none')).to.equal(true) 121 | }) 122 | }) 123 | it('slot wrapped in another tag', done => { 124 | const wizard = mount(FormWizard, { 125 | localVue, 126 | slots: { 127 | default: divSlot 128 | } 129 | }) 130 | Vue.nextTick(() => { 131 | const tabs = wizard.findAll(WizardTab) 132 | expect(tabs.length).to.equal(3) 133 | done() 134 | }) 135 | }) 136 | it('less tabs when one tab is removed', (done) => { 137 | const wizard = mount(threeStepWizard, {localVue}) 138 | const tabs = wizard.findAll(WizardTab) 139 | expect(tabs.length).to.equal(3) 140 | wizard.setData({showLastStep: false}) 141 | Vue.nextTick(() => { 142 | const newTabs = wizard.findAll(WizardTab) 143 | expect(newTabs.length).to.equal(2) 144 | done() 145 | }) 146 | }) 147 | it('tabs in the same order when a tab before the active tab is removed', (done) => { 148 | const wizard = mount(threeStepWizard, {localVue}) 149 | const wizardInstance = wizard.find(FormWizard) 150 | const tabs = wizard.findAll(WizardTab) 151 | expect(tabs.length).to.equal(3) 152 | // navigate to last step 153 | wizardInstance.vm.nextTab() 154 | wizardInstance.vm.nextTab() 155 | // remove second step 156 | wizard.setData({showSecondStep: false}) 157 | Vue.nextTick(() => { 158 | const newTabs = wizard.findAll(WizardTab) 159 | expect(newTabs.length).to.equal(2) 160 | const lastTab = newTabs.at(1) 161 | expect(lastTab.vm.active).to.equal(true) 162 | expect(lastTab.vm.title).to.equal('Last step') 163 | done() 164 | }) 165 | }) 166 | }) 167 | 168 | it('warns when start index is incorrect', () => { 169 | let originalConsole = window.console 170 | window.console = {warn: sinon.stub()} 171 | const wizard = mount(FormWizard, {localVue, slots: {default: divSlot}, propsData: {startIndex: 15}}) 172 | expect(wizard.vm.activeTabIndex).to.not.equal(15) 173 | expect(window.console.warn.called).to.equal(true) 174 | window.console = originalConsole 175 | }) 176 | 177 | it('resets wizard', () => { 178 | const wizard = mount(threeStepWizard, {localVue}) 179 | const wizardInstance = wizard.find(FormWizard) 180 | let tabs = wizard.findAll(WizardTab) 181 | expect(tabs.length).to.equal(3) 182 | wizardInstance.vm.nextTab() 183 | wizardInstance.vm.nextTab() 184 | const lastTab = tabs.at(2) 185 | expect(lastTab.vm.active).to.equal(true) 186 | expect(wizardInstance.vm.maxStep).to.equal(tabs.length - 1) 187 | expect(wizardInstance.vm.activeTabIndex).to.equal(tabs.length - 1) 188 | wizardInstance.vm.reset() 189 | const firstTab = tabs.at(0) 190 | expect(firstTab.vm.active).to.equal(true) 191 | expect(wizardInstance.vm.maxStep).to.equal(0) 192 | expect(wizardInstance.vm.activeTabIndex).to.equal(0) 193 | }) 194 | 195 | it('activates all steps', (done) => { 196 | const wizard = mount(threeStepWizard, {localVue}) 197 | const wizardInstance = wizard.find(FormWizard) 198 | let tabs = wizard.findAll(WizardTab) 199 | let maxStepIndex = tabs.length - 1 200 | wizardInstance.vm.activateAll() 201 | 202 | Vue.nextTick(() => { 203 | expect(wizardInstance.vm.activeTabIndex).to.equal(0) 204 | expect(wizardInstance.vm.maxStep).to.equal(maxStepIndex) 205 | // direct navigation should be available 206 | wizardInstance.vm.navigateToTab(maxStepIndex) 207 | expect(wizardInstance.vm.activeTabIndex).to.equal(maxStepIndex) 208 | let steps = wizardInstance.findAll('.wizard-icon-circle') 209 | expect(steps.hasClass('checked')).to.equal(true) 210 | done() 211 | }) 212 | }) 213 | 214 | describe('navigation', () => { 215 | it('next tab is called', () => { 216 | const wizard = mount(threeStepWizard, {localVue}) 217 | const wizardInstance = wizard.find(FormWizard) 218 | const nextButton = wizard.find('.wizard-footer-right span') 219 | nextButton.trigger('click') 220 | expect(wizardInstance.vm.activeTabIndex).to.equal(1) 221 | }) 222 | 223 | it('prev tab is called', (done) => { 224 | const wizard = mount(threeStepWizard, {localVue}) 225 | const wizardInstance = wizard.find(FormWizard) 226 | const nextButton = wizard.find('.wizard-footer-right span') 227 | nextButton.trigger('click') 228 | expect(wizardInstance.vm.activeTabIndex).to.equal(1) 229 | Vue.nextTick(() => { 230 | const backButton = wizardInstance.find('.wizard-footer-left span') 231 | backButton.trigger('click') 232 | expect(wizardInstance.vm.activeTabIndex).to.equal(0) 233 | done() 234 | }) 235 | }) 236 | it('is restricted to unvisted tabs by click', () => { 237 | const wizard = mount(threeStepWizard, {localVue}) 238 | const wizardInstance = wizard.find(FormWizard) 239 | wizardInstance.vm.navigateToTab(1) 240 | expect(wizardInstance.vm.activeTabIndex).to.equal(0) 241 | }) 242 | it('starts at a given index', () => { 243 | const wizard = mount(threeStepWizard, {localVue}) 244 | const tabs = wizard.findAll(WizardTab) 245 | const activeTab = tabs.at(startIndex) 246 | expect(activeTab.vm.active).to.equal(true) 247 | const formWizard = wizard.find(FormWizard) 248 | expect(formWizard.vm.activeTabIndex).to.equal(startIndex) 249 | }) 250 | 251 | it('navigates to a visited tab', () => { 252 | const wizard = mount(threeStepWizard, {localVue}) 253 | const wizardInstance = wizard.find(FormWizard) 254 | let tabs = wizard.findAll(WizardTab) 255 | wizardInstance.vm.nextTab() 256 | wizardInstance.vm.nextTab() 257 | wizardInstance.vm.navigateToTab(0) 258 | const firstTab = tabs.at(0) 259 | expect(firstTab.vm.active).to.equal(true) 260 | expect(wizardInstance.vm.activeTabIndex).to.equal(0) 261 | expect(wizardInstance.vm.maxStep).to.equal(tabs.length - 1) 262 | 263 | wizardInstance.vm.navigateToTab(2) 264 | expect(wizardInstance.vm.activeTabIndex).to.equal(2) 265 | expect(wizardInstance.vm.maxStep).to.equal(tabs.length - 1) 266 | }) 267 | 268 | it('active tab is prev when current active tab is removed', (done) => { 269 | const wizard = mount(threeStepWizard, {localVue}) 270 | const wizardInstance = wizard.find(FormWizard) 271 | // navigate to last tab 272 | wizardInstance.vm.nextTab() 273 | wizardInstance.vm.nextTab() 274 | const tabs = wizard.findAll(WizardTab) 275 | expect(tabs.length).to.equal(3) 276 | wizard.setData({showLastStep: false}) 277 | Vue.nextTick(() => { 278 | const newTabs = wizard.findAll(WizardTab) 279 | expect(newTabs.length).to.equal(2) 280 | expect(wizardInstance.vm.activeTabIndex).to.equal(1) 281 | done() 282 | }) 283 | }) 284 | 285 | it('with arrow keys on visited tabs', () => { 286 | const wizard = mount(threeStepWizard, {localVue, attachToDocument: true}) 287 | const wizardInstance = wizard.find(FormWizard) 288 | wizardInstance.vm.nextTab() 289 | wizardInstance.vm.nextTab() 290 | wizard.trigger('tab') 291 | wizard.trigger('keyup.right') 292 | wizard.trigger('keyup.left') 293 | expect(wizardInstance.vm.activeTabIndex).to.equal(2) 294 | }) 295 | }) 296 | 297 | describe('emits', () => { 298 | it('on-complete upon last step', () => { 299 | const wizard = mount(threeStepWizard, {localVue}) 300 | const wizardInstance = wizard.find(FormWizard) 301 | const nextButton = wizard.find('.wizard-footer-right span') 302 | nextButton.trigger('click') 303 | nextButton.trigger('click') 304 | nextButton.trigger('click') 305 | expect(wizardInstance.vm.activeTabIndex).to.equal(2) 306 | expect(wizardInstance.emitted()['on-complete'].length).to.equal(1) 307 | }) 308 | }) 309 | describe('validation with', () => { 310 | beforeEach(() => { 311 | threeStepWizard = { 312 | template: ` 313 | 317 | My first tab content 318 | 319 | 322 | My second tab content 323 | 324 | 326 | My third tab content 327 | 328 | `, 329 | methods: { 330 | validationMethod, 331 | secondValidationMethod 332 | } 333 | } 334 | }) 335 | 336 | it('simple method', () => { 337 | threeStepWizard.methods.validationMethod = sinon.stub() 338 | threeStepWizard.methods.validationMethod.returns(true) 339 | const wizard = mount(threeStepWizard, {localVue}) 340 | const wizardInstance = wizard.find(FormWizard) 341 | wizardInstance.vm.nextTab() 342 | expect(threeStepWizard.methods.validationMethod.called).to.equal(true) 343 | expect(wizardInstance.vm.activeTabIndex).to.equal(1) 344 | }) 345 | it('simple method on back navigation', () => { 346 | threeStepWizard.methods.secondValidationMethod = sinon.stub() 347 | threeStepWizard.methods.secondValidationMethod.returns(true) 348 | const wizard = mount(threeStepWizard, {localVue}) 349 | const wizardInstance = wizard.find(FormWizard) 350 | wizardInstance.vm.nextTab() 351 | wizardInstance.vm.prevTab() 352 | expect(threeStepWizard.methods.secondValidationMethod.called).to.equal(true) 353 | }) 354 | it('falsy method', () => { 355 | threeStepWizard.methods.validationMethod = sinon.stub() 356 | threeStepWizard.methods.validationMethod.returns(false) 357 | const wizard = mount(threeStepWizard, {localVue}) 358 | const wizardInstance = wizard.find(FormWizard) 359 | wizardInstance.vm.nextTab() 360 | expect(threeStepWizard.methods.validationMethod.called).to.equal(true) 361 | expect(wizardInstance.vm.activeTabIndex).to.equal(0) 362 | }) 363 | it('promise', (done) => { 364 | threeStepWizard.methods.validationMethod = sinon.stub() 365 | threeStepWizard.methods.validationMethod.returns(Promise.resolve(true)) 366 | const wizard = mount(threeStepWizard, {localVue}) 367 | const wizardInstance = wizard.find(FormWizard) 368 | wizardInstance.vm.nextTab() 369 | expect(threeStepWizard.methods.validationMethod.called).to.equal(true) 370 | Vue.nextTick(() => { 371 | expect(wizardInstance.vm.activeTabIndex).to.equal(1) 372 | done() 373 | }) 374 | }) 375 | 376 | it('failing promise', (done) => { 377 | threeStepWizard.methods.validationMethod = sinon.stub() 378 | threeStepWizard.methods.validationMethod.returns(Promise.reject(false)) 379 | const wizard = mount(threeStepWizard, {localVue}) 380 | const wizardInstance = wizard.find(FormWizard) 381 | wizardInstance.vm.nextTab() 382 | expect(threeStepWizard.methods.validationMethod.called).to.equal(true) 383 | Vue.nextTick(() => { 384 | expect(wizardInstance.vm.activeTabIndex).to.equal(0) 385 | done() 386 | }) 387 | }) 388 | }) 389 | describe('after change', () => { 390 | beforeEach(() => { 391 | threeStepWizard = { 392 | template: ` 393 | 397 | My first tab content 398 | 399 | 402 | My second tab content 403 | 404 | 406 | My third tab content 407 | 408 | `, 409 | methods: { 410 | validationMethod, 411 | } 412 | } 413 | }) 414 | it('simple method on after change', () => { 415 | threeStepWizard.methods.validationMethod = sinon.stub() 416 | threeStepWizard.methods.validationMethod.returns(null) 417 | const wizard = mount(threeStepWizard, {localVue}) 418 | const wizardInstance = wizard.find(FormWizard) 419 | wizardInstance.vm.nextTab() 420 | expect(threeStepWizard.methods.validationMethod.should.have.been.called) 421 | expect(wizardInstance.vm.activeTabIndex).to.equal(1) 422 | }) 423 | it('simple method on back navigation after change', () => { 424 | threeStepWizard.methods.validationMethod = sinon.stub() 425 | threeStepWizard.methods.validationMethod.returns(null) 426 | const wizard = mount(threeStepWizard, {localVue}) 427 | const wizardInstance = wizard.find(FormWizard) 428 | wizardInstance.vm.nextTab() 429 | wizardInstance.vm.prevTab() 430 | expect(threeStepWizard.methods.validationMethod.should.have.been.called) 431 | }) 432 | }) 433 | describe('with vue-router', ()=> { 434 | it('renders correct tab contents', () => { 435 | const wizard = mount(routerWizard, {localVue, router}) 436 | const wizardInstance = wizard.find(FormWizard) 437 | let tabs = wizardInstance.findAll(WizardTab) 438 | let firstTab = tabs.at(0) 439 | expect(tabs.length).to.equal(3) 440 | expect(firstTab.vm.route).to.equal(wizardInstance.vm.$route.path) 441 | }) 442 | 443 | it('adapts to valid route changes when steps are visited', (done) => { 444 | const wizard = mount(routerWizard, {localVue, router}) 445 | const wizardInstance = wizard.find(FormWizard) 446 | let tabs = wizardInstance.findAll(WizardTab) 447 | wizardInstance.vm.activateAll() 448 | wizardInstance.vm.$router.push('/second') 449 | Vue.nextTick(()=> { 450 | let secondTab = tabs.at(1) 451 | expect(secondTab.vm.route).to.equal(wizardInstance.vm.$route.path) 452 | expect(secondTab.vm.active).to.equal(true) 453 | done() 454 | }) 455 | 456 | 457 | }) 458 | }) 459 | }) 460 | -------------------------------------------------------------------------------- /types/FormWizard.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export type ShapeType = 'circle' | 'square' | 'tab' 4 | export type LayoutType = 'vertical' | 'horizontal' 5 | export type StepSizeType = 'xs' | 'sm' | 'md' | 'lg' 6 | 7 | export declare class Wizard extends Vue { 8 | /** 9 | * Wizard identifier. 10 | * 11 | * Default value: 'fw_' 12 | */ 13 | id: string 14 | 15 | /** 16 | * Wizard title. 17 | * 18 | * Default value: 'Awesome Wizard' 19 | */ 20 | title: string 21 | 22 | /** 23 | * Wizard subtitle. 24 | * 25 | * Default value: 'Split a complicated flow in multiple steps' 26 | */ 27 | subtitle: string 28 | 29 | /** 30 | * Text to display on next button. 31 | * 32 | * Default value: 'Next' 33 | */ 34 | nextButtonText: string 35 | 36 | /** 37 | * Text to display on back button. 38 | * 39 | * Default value: 'Back' 40 | */ 41 | backButtonText: string 42 | 43 | /** 44 | * Text to display on finish button. 45 | * 46 | * Default value: 'Finish' 47 | */ 48 | finishButtonText: string 49 | 50 | /** 51 | * Whether or not buttons should be hidden. 52 | * 53 | * Default value: false 54 | */ 55 | hideButtons: boolean 56 | 57 | /** 58 | * Whether or not to trigger `beforeChange` function when navigating back. 59 | * 60 | * Default value: false 61 | */ 62 | validateOnBack: boolean 63 | 64 | /** 65 | * Color to apply to text, border and circle. 66 | * 67 | * Default value: '#e74c3c' 68 | */ 69 | color: string 70 | 71 | /** 72 | * Step color when the current step is not valid. 73 | * 74 | * Default value: '#8b0000' 75 | */ 76 | errorColor: string 77 | 78 | /** 79 | * Shape of steps. 80 | * 81 | * Default value: 'circle' 82 | */ 83 | shape: ShapeType 84 | 85 | /** 86 | * Layout of wizard. 87 | * 88 | * Default value: 'horizontal' 89 | */ 90 | layout: LayoutType 91 | 92 | /** 93 | * Additional CSS classes to apply to steps. 94 | * 95 | * Default value: '' 96 | */ 97 | stepsClasses: string[] 98 | 99 | /** 100 | * Size of steps. 101 | * 102 | * Default value: 'md' 103 | */ 104 | stepSize: StepSizeType 105 | 106 | /** 107 | * Name of the transition when transitioning between steps. 108 | * 109 | * Default value: '' 110 | */ 111 | transition: string 112 | 113 | /** 114 | * Zero-based index of the initial tab to be displayed. 115 | * 116 | * Default value: 0 117 | */ 118 | startIndex: number 119 | 120 | /** 121 | * Resets the wizard to its initial state. 122 | */ 123 | reset: () => void 124 | 125 | /** 126 | * Activates all steps, as if the user has navigated to each of them. 127 | */ 128 | activateAll: () => void 129 | 130 | /** 131 | * Navigates to the next tab. 132 | */ 133 | nextTab: () => void 134 | 135 | /** 136 | * Navigates to the previous tab. 137 | */ 138 | prevTab: () => void 139 | 140 | /** 141 | * Navigates from one tab to another. Note that this method does not trigger validaiton methods. 142 | */ 143 | changeTab: (oldIndex: number, newIndex: number, emitChangeEvent?: boolean) => boolean 144 | } 145 | -------------------------------------------------------------------------------- /types/TabContent.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export declare class Tab extends Vue { 4 | /** 5 | * Title to be displayed under each step. 6 | * 7 | * Default value: '' 8 | */ 9 | title: string 10 | 11 | /** CSS class to be applied to each step icon. 12 | * 13 | * Default value: '' 14 | */ 15 | icon: string 16 | 17 | /** 18 | * Function to execute before changing tabs. If the return result is false, the tab switch is restricted. 19 | */ 20 | beforeChange: () => boolean | Promise 21 | 22 | /** 23 | * Function to execute after changing tabs. It is safe to assume that necessary validation has already occurred. 24 | */ 25 | afterChange: () => void 26 | 27 | /** 28 | * Vue router route object. 29 | */ 30 | route: string | object 31 | 32 | /** 33 | * Default value: () => {} 34 | */ 35 | additionalInfo: object 36 | } 37 | -------------------------------------------------------------------------------- /types/WizardStep.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | export declare class Step extends Vue { 4 | /** 5 | * Tab component to use for the step. This should be usually passed through the `step` scoped slot. 6 | * 7 | * Default value: () => {} 8 | */ 9 | tab: object 10 | 11 | /** 12 | * Default value: '' 13 | */ 14 | transition: string 15 | 16 | /** 17 | * Default value: 0 18 | */ 19 | index: number 20 | } 21 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { PluginFunction } from "vue"; 3 | 4 | export function install (vue: typeof Vue): void 5 | 6 | export { 7 | Wizard as FormWizard, 8 | ShapeType, 9 | LayoutType, 10 | StepSizeType 11 | } from "./FormWizard" 12 | export { Tab as TabContent } from "./TabContent" 13 | export { Step as WizardStep } from "./WizardStep" 14 | 15 | import { Wizard as FormWizard } from "./FormWizard" 16 | import { Tab as TabContent } from "./TabContent" 17 | import { Step as WizardStep } from "./WizardStep" 18 | 19 | declare class VueFormWizard { 20 | static install: PluginFunction; 21 | 22 | FormWizard: FormWizard; 23 | TabContent: TabContent; 24 | WizardStep: WizardStep; 25 | } 26 | 27 | export default VueFormWizard; 28 | -------------------------------------------------------------------------------- /webpack.build.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var path = require("path"); 3 | // var version = require("./package.json").version; 4 | // var banner = "/**\n" + " * vue-form-generator v" + version + "\n" + " * https://github.com/icebob/vue-form-generator\n" + " * Released under the MIT License.\n" + " */\n"; 5 | var ExtractTextPlugin = require("extract-text-webpack-plugin"); 6 | var StatsPlugin = require("stats-webpack-plugin"); 7 | var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 8 | 9 | var cssLoader = { 10 | loader: "css-loader", 11 | options: { 12 | minimize: true 13 | } 14 | }; 15 | var sassLoader = { 16 | loader: "sass-loader", 17 | options: { 18 | minimize: true 19 | } 20 | }; 21 | 22 | var loaders = [ 23 | { 24 | test: /\.js?$/, 25 | exclude: /node_modules/, 26 | loader: "babel-loader" 27 | }, 28 | { 29 | test: /\.css$/, 30 | loader: 'css-loader' 31 | }, 32 | { 33 | test: /\.scss$/, 34 | loaders: ['css-loader', 'sass-loader'] 35 | }, 36 | { 37 | test: /\.vue?$/, 38 | loader: "vue-loader", 39 | /* options:{ 40 | loaders:{ 41 | scss: 'css-loader!sass-loader' 42 | } 43 | }*/ 44 | options:{ 45 | loaders: { 46 | css: ExtractTextPlugin.extract({use:[cssLoader]}), 47 | postcss: ExtractTextPlugin.extract({use:[cssLoader]}), 48 | scss: ExtractTextPlugin.extract({use:[cssLoader,sassLoader]}), 49 | } 50 | } 51 | } 52 | ]; 53 | 54 | module.exports = [ 55 | { 56 | entry: "./src/index.js", 57 | output: { 58 | path: path.resolve(__dirname, './dist'), 59 | filename: "vue-form-wizard.js", 60 | library: "VueFormWizard", 61 | libraryTarget: "umd" 62 | }, 63 | 64 | plugins: [ 65 | new webpack.DefinePlugin({ 66 | 'process.env' : { 67 | NODE_ENV : '"production"' 68 | } 69 | }), 70 | new webpack.optimize.UglifyJsPlugin({ 71 | compress: { 72 | warnings: false 73 | } 74 | }), 75 | /* new webpack.BannerPlugin(banner, { 76 | raw: true 77 | }),*/ 78 | new ExtractTextPlugin({filename:"vue-form-wizard.min.css", allChunks: true, fallback:"style-loader" }), 79 | new StatsPlugin( {filename:"./stats.json", 80 | chunkModules: true 81 | //exclude: [/node_modules[\\\/]react/] 82 | }), 83 | ], 84 | 85 | module: { 86 | rules:loaders 87 | }, 88 | 89 | } 90 | ]; 91 | --------------------------------------------------------------------------------