mirror of
https://github.com/tenrok/vue-form-wizard.git
synced 2026-06-25 01:20:33 +03:00
Merge pull request #2 from cristijora/1-async-validation
1 async validation
This commit is contained in:
@@ -1,14 +0,0 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
|
||||||
*.scss linguist-language=Vue
|
|
||||||
|
|
||||||
# Standard to msysgit
|
|
||||||
*.doc diff=astextplain
|
|
||||||
*.DOC diff=astextplain
|
|
||||||
*.docx diff=astextplain
|
|
||||||
*.DOCX diff=astextplain
|
|
||||||
*.dot diff=astextplain
|
|
||||||
*.DOT diff=astextplain
|
|
||||||
*.pdf diff=astextplain
|
|
||||||
*.PDF diff=astextplain
|
|
||||||
*.rtf diff=astextplain
|
|
||||||
*.RTF diff=astextplain
|
|
||||||
@@ -2,10 +2,7 @@
|
|||||||
A dynamic form wizard to split your forms easier
|
A dynamic form wizard to split your forms easier
|
||||||
|
|
||||||
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
|
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
|
||||||
wasting time on details. Just forget about id's, external scripts and jQuery dependencies.
|
wasting time on details. Just forget about id's, external scripts and jQuery dependencies
|
||||||
|
|
||||||
Vue-form-wizard is inspired by [creative-tim wizards](https://www.creative-tim.com/bootstrap-themes/wizard) but simplified and
|
|
||||||
more customizable
|
|
||||||
|
|
||||||
# Demos
|
# Demos
|
||||||
Basic [demo](https://jsfiddle.net/bt5dhqtf/97/)
|
Basic [demo](https://jsfiddle.net/bt5dhqtf/97/)
|
||||||
@@ -18,8 +15,6 @@ Other demos:
|
|||||||
* [Custom title slot](https://jsfiddle.net/bt5dhqtf/102/)
|
* [Custom title slot](https://jsfiddle.net/bt5dhqtf/102/)
|
||||||
* [Customized buttons with slots](https://jsfiddle.net/bt5dhqtf/103/) Replace stuff you don't like
|
* [Customized buttons with slots](https://jsfiddle.net/bt5dhqtf/103/) Replace stuff you don't like
|
||||||
* [Call a function before tab switch](https://jsfiddle.net/bt5dhqtf/105/)
|
* [Call a function before tab switch](https://jsfiddle.net/bt5dhqtf/105/)
|
||||||
* [Complete form example](https://jsfiddle.net/bt5dhqtf/150/) integrated with [vue-form-generator](https://github.com/icebob/vue-form-generator)
|
|
||||||
* [Vue router integration](https://jsfiddle.net/CristiJ/bt5dhqtf/252/) 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
|
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
@@ -37,7 +32,7 @@ Download the css and js files from `dist` folder or reference them directly from
|
|||||||
//global registration
|
//global registration
|
||||||
import 'vue-form-wizard'
|
import 'vue-form-wizard'
|
||||||
import 'vue-form-wizard/dist/vue-form-wizard.min.css'
|
import 'vue-form-wizard/dist/vue-form-wizard.min.css'
|
||||||
Vue.use(VueFormWizard)
|
Vue.use(VueTabWizard)
|
||||||
|
|
||||||
//local registration
|
//local registration
|
||||||
import {FormWizard, TabContent} from 'vue-form-wizard'
|
import {FormWizard, TabContent} from 'vue-form-wizard'
|
||||||
@@ -140,12 +135,6 @@ props: {
|
|||||||
*/
|
*/
|
||||||
beforeChange: {
|
beforeChange: {
|
||||||
type: Function
|
type: Function
|
||||||
},
|
|
||||||
/***
|
|
||||||
* Used to handle routing with vue-router. Refer to [router.push](https://router.vuejs.org/en/essentials/navigation.html) for a valid prop in this case
|
|
||||||
*/
|
|
||||||
route: {
|
|
||||||
type: [String, Object]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -156,15 +145,3 @@ props: {
|
|||||||
* **prev** - Previous button content (no need to worry about handling the button functionality)
|
* **prev** - Previous button content (no need to worry about handling the button functionality)
|
||||||
* **next** - Next button content
|
* **next** - Next button content
|
||||||
* **finish** - Finish button content
|
* **finish** - Finish button content
|
||||||
|
|
||||||
## Contribution
|
|
||||||
Open an issue or send a Pull request if you feel that something is missing or doesn't work.
|
|
||||||
|
|
||||||
## License
|
|
||||||
vue-form-wizard is available under the [MIT license](https://tldrlegal.com/license/mit-license).
|
|
||||||
|
|
||||||
## Contact
|
|
||||||
|
|
||||||
Copyright (C) 2017 Cristi Jora
|
|
||||||
|
|
||||||
[](https://github.com/cristijora)
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ function resolve (dir) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: {
|
entry: {
|
||||||
app: './dev/main.js'
|
app: './dev-example/main.js'
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: config.build.assetsRoot,
|
path: config.build.assetsRoot,
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<form-wizard @on-complete="onComplete"
|
||||||
|
shape="circle"
|
||||||
|
color="#e74c3c"
|
||||||
|
@on-loading="setLoading"
|
||||||
|
class="card">
|
||||||
|
<tab-content title="Personal details"
|
||||||
|
:before-change="validateAsync"
|
||||||
|
icon="ti-user">
|
||||||
|
My first tab content
|
||||||
|
</tab-content>
|
||||||
|
<tab-content title="Additional Info"
|
||||||
|
:before-change="validate"
|
||||||
|
icon="ti-settings">
|
||||||
|
My second tab content
|
||||||
|
</tab-content>
|
||||||
|
<tab-content title="Last step"
|
||||||
|
:before-change="validateAsync"
|
||||||
|
icon="ti-check">
|
||||||
|
Yuhuuu! This seems pretty damn simple
|
||||||
|
</tab-content>
|
||||||
|
<div class="loader" v-if="loadingWizard"></div>
|
||||||
|
</form-wizard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'app',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loadingWizard: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onComplete () {
|
||||||
|
alert('Yay!')
|
||||||
|
},
|
||||||
|
setLoading (value) {
|
||||||
|
this.loadingWizard = value
|
||||||
|
},
|
||||||
|
validateAsync () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve(true)
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
validate () {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@import "loader.css";
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
$border-radius-extreme: 6px !default;
|
||||||
|
$white-color: white;
|
||||||
|
$gray-input-bg: #F3F2EE !default;
|
||||||
|
$card-black-color: #252422 !default;
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin-top: 20px;
|
||||||
|
background-color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
padding: 0px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border-radius: $border-radius-extreme;
|
||||||
|
box-shadow: 0 2px 2px rgba(204, 197, 185, 0.5);
|
||||||
|
background-color: $white-color;
|
||||||
|
color: $card-black-color;
|
||||||
|
padding: 10px 0;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
.loader,
|
||||||
|
.loader:after {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 10em;
|
||||||
|
height: 10em;
|
||||||
|
}
|
||||||
|
.loader {
|
||||||
|
margin: 60px auto;
|
||||||
|
font-size: 10px;
|
||||||
|
position: relative;
|
||||||
|
text-indent: -9999em;
|
||||||
|
border-top: 1.1em solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-right: 1.1em solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-bottom: 1.1em solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-left: 1.1em solid #e74c3c;
|
||||||
|
-webkit-transform: translateZ(0);
|
||||||
|
-ms-transform: translateZ(0);
|
||||||
|
transform: translateZ(0);
|
||||||
|
-webkit-animation: load8 1.1s infinite linear;
|
||||||
|
animation: load8 1.1s infinite linear;
|
||||||
|
}
|
||||||
|
@-webkit-keyframes load8 {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes load8 {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// The Vue build version to load with the `import` command
|
||||||
|
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
|
||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import FormWizard from '../src/index'
|
||||||
|
Vue.use(FormWizard)
|
||||||
|
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
/* eslint-disable no-new */
|
||||||
|
new Vue({
|
||||||
|
el: '#app',
|
||||||
|
template: '<App/>',
|
||||||
|
components: {App}
|
||||||
|
})
|
||||||
-61
@@ -1,61 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<form-wizard @on-complete="onComplete"
|
|
||||||
shape="circle"
|
|
||||||
color="#e74c3c"
|
|
||||||
class="card">
|
|
||||||
<tab-content title="Personal details"
|
|
||||||
route="first"
|
|
||||||
icon="ti-user">
|
|
||||||
</tab-content>
|
|
||||||
<tab-content title="Additional Info"
|
|
||||||
route="second"
|
|
||||||
icon="ti-settings">
|
|
||||||
</tab-content>
|
|
||||||
<tab-content title="Last step"
|
|
||||||
route="third"
|
|
||||||
icon="ti-check">
|
|
||||||
</tab-content>
|
|
||||||
<router-view></router-view>
|
|
||||||
</form-wizard>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'app',
|
|
||||||
methods: {
|
|
||||||
onComplete () {
|
|
||||||
alert('Yay!')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
|
||||||
$border-radius-extreme: 6px !default;
|
|
||||||
$white-color: white;
|
|
||||||
$gray-input-bg: #F3F2EE !default;
|
|
||||||
$card-black-color: #252422 !default;
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin-top:20px;
|
|
||||||
background-color:#ecf0f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-footer{
|
|
||||||
padding:0px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card{
|
|
||||||
border-radius: $border-radius-extreme;
|
|
||||||
box-shadow: 0 2px 2px rgba(204, 197, 185, 0.5);
|
|
||||||
background-color: $white-color;
|
|
||||||
color: $card-black-color;
|
|
||||||
padding: 10px 0;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
-28
@@ -1,28 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import VueRouter from 'vue-router'
|
|
||||||
import App from './App.vue'
|
|
||||||
import FormWizard from './../src/index'
|
|
||||||
const First = { template: '<div>First</div>' }
|
|
||||||
const Second = { template: '<div>Second</div>' }
|
|
||||||
const Third = { template: '<div>Third</div>' }
|
|
||||||
|
|
||||||
const router = new VueRouter({
|
|
||||||
mode: 'history',
|
|
||||||
routes: [
|
|
||||||
{ path: '/first', component: First },
|
|
||||||
{ path: '/second', component: Second },
|
|
||||||
{ path: '/third', component: Third }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
Vue.use(VueRouter)
|
|
||||||
Vue.use(FormWizard)
|
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
|
||||||
|
|
||||||
/* eslint-disable no-new */
|
|
||||||
new Vue({
|
|
||||||
router,
|
|
||||||
el: '#app',
|
|
||||||
template: '<App/>',
|
|
||||||
components: {App}
|
|
||||||
})
|
|
||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+3
-4
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-form-wizard",
|
"name": "vue-form-wizard",
|
||||||
"version": "0.1.11",
|
"version": "0.1.6",
|
||||||
"description": "A vue based tab/form wizard",
|
"description": "A vue based tab/form wizard",
|
||||||
"main": "dist/vue-form-wizard.js",
|
"main": "dist/vue-tab-wizard.js",
|
||||||
"homepage": "https://github.com/cristijora/vue-form-wizard",
|
"homepage": "https://github.com/cristijora/vue-form-wizard",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node build/dev-server.js",
|
"dev": "node build/dev-server.js",
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
"url": "https://github.com/cristijora/vue-form-wizard"
|
"url": "https://github.com/cristijora/vue-form-wizard"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"vue": "^2.2.2",
|
||||||
"autoprefixer": "^6.7.2",
|
"autoprefixer": "^6.7.2",
|
||||||
"babel-core": "^6.22.1",
|
"babel-core": "^6.22.1",
|
||||||
"babel-eslint": "^7.1.1",
|
"babel-eslint": "^7.1.1",
|
||||||
@@ -83,9 +84,7 @@
|
|||||||
"sinon-chai": "^2.8.0",
|
"sinon-chai": "^2.8.0",
|
||||||
"stats-webpack-plugin": "^0.6.0",
|
"stats-webpack-plugin": "^0.6.0",
|
||||||
"url-loader": "^0.5.8",
|
"url-loader": "^0.5.8",
|
||||||
"vue": "^2.2.2",
|
|
||||||
"vue-loader": "^11.1.4",
|
"vue-loader": "^11.1.4",
|
||||||
"vue-router": "^2.4.0",
|
|
||||||
"vue-style-loader": "^2.0.0",
|
"vue-style-loader": "^2.0.0",
|
||||||
"vue-template-compiler": "^2.2.4",
|
"vue-template-compiler": "^2.2.4",
|
||||||
"webpack": "^2.2.1",
|
"webpack": "^2.2.1",
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
.btn,
|
|
||||||
.navbar .navbar-nav > li > a.btn{
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-width: $border-thick;
|
|
||||||
background-color: $transparent-bg;
|
|
||||||
font-size: $font-size-base;
|
|
||||||
font-weight: $font-weight-bold;
|
|
||||||
|
|
||||||
padding: $padding-base-vertical $padding-base-horizontal;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus{
|
|
||||||
outline: 0 !important;
|
|
||||||
}
|
|
||||||
&:active,
|
|
||||||
&.active,
|
|
||||||
.open > &.dropdown-toggle {
|
|
||||||
@include box-shadow(none);
|
|
||||||
outline: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.btn-icon{
|
|
||||||
border-radius: 25px;
|
|
||||||
padding: 7px 10px;
|
|
||||||
i{
|
|
||||||
margin-right: 0px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.btn-disabled{
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .navbar-nav > li > a.btn.btn-wd,
|
|
||||||
.btn-wd{
|
|
||||||
min-width: 140px;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
.btn,
|
||||||
|
.navbar .navbar-nav > li > a.btn{
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-width: $border-thick;
|
||||||
|
background-color: $transparent-bg;
|
||||||
|
font-size: $font-size-base;
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
|
||||||
|
padding: $padding-base-vertical $padding-base-horizontal;
|
||||||
|
|
||||||
|
@include btn-styles($default-color, $default-states-color);
|
||||||
|
@include transition($fast-transition-time, linear);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus{
|
||||||
|
outline: 0 !important;
|
||||||
|
}
|
||||||
|
&:active,
|
||||||
|
&.active,
|
||||||
|
.open > &.dropdown-toggle {
|
||||||
|
@include box-shadow(none);
|
||||||
|
outline: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-icon{
|
||||||
|
border-radius: 25px;
|
||||||
|
padding: 7px 10px;
|
||||||
|
i{
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*="ti-"]{
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group .btn + .btn,
|
||||||
|
.btn-group .btn + .btn-group,
|
||||||
|
.btn-group .btn-group + .btn,
|
||||||
|
.btn-group .btn-group + .btn-group{
|
||||||
|
margin-left: -2px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the mixin to the buttons
|
||||||
|
//.btn-default { @include btn-styles($default-color, $default-states-color); }
|
||||||
|
.navbar .navbar-nav > li > a.btn.btn-primary, .btn-primary { @include btn-styles($primary-color, $primary-states-color); }
|
||||||
|
.navbar .navbar-nav > li > a.btn.btn-info, .btn-info { @include btn-styles($info-color, $info-states-color); }
|
||||||
|
|
||||||
|
.btn{
|
||||||
|
&:disabled,
|
||||||
|
&[disabled],
|
||||||
|
&.disabled,
|
||||||
|
&.btn-disabled{
|
||||||
|
@include opacity(.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btn-disabled{
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.btn-simple{
|
||||||
|
border: $none;
|
||||||
|
padding: $padding-base-vertical $padding-base-horizontal;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar .navbar-nav > li > a.btn.btn-wd,
|
||||||
|
.btn-wd{
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group.select{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.btn-group.select .btn{
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.btn-group.select .caret{
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -1px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base);
|
@include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base);
|
||||||
//@include user-select(none);
|
@include user-select(none);
|
||||||
|
|
||||||
&,
|
&,
|
||||||
&:active,
|
&:active,
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
/*@import "form-wizard/bs_button";
|
//@import "tab-wizard/bs_button";
|
||||||
@import "form-wizard/bs_nav_pills";
|
//@import "tab-wizard/bs_nav_pills";
|
||||||
@import "form-wizard/bs_progress_bar";*/
|
//@import "tab-wizard/bs_progress_bar";
|
||||||
@import "form-wizard/variables";
|
@import "tab-wizard/variables";
|
||||||
@import "form-wizard/mixins";
|
@import "tab-wizard/mixins";
|
||||||
@import "form-wizard/buttons";
|
@import "tab-wizard/buttons";
|
||||||
@import "form-wizard/navs-pagination";
|
@import "tab-wizard/navs-pagination";
|
||||||
@import "form-wizard/wizard-card";
|
@import "tab-wizard/wizard-card";
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<span @click="prevTab" v-if="displayPrevButton">
|
<span @click="prevTab" v-if="displayPrevButton">
|
||||||
<slot name="prev">
|
<slot name="prev">
|
||||||
<button type="button" class="btn btn-default btn-wd" :style="fillButtonStyle">
|
<button type="button" class="btn btn-default btn-wd" :style="fillButtonStyle" :disabled="loading">
|
||||||
{{backButtonText}}
|
{{backButtonText}}
|
||||||
</button>
|
</button>
|
||||||
</slot>
|
</slot>
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<span @click="nextTab" class="pull-right" v-if="!isLastStep">
|
<span @click="nextTab" class="pull-right" v-if="!isLastStep">
|
||||||
<slot name="next">
|
<slot name="next">
|
||||||
<button type="button" class="btn btn-fill btn-wd btn-next" :style="fillButtonStyle">
|
<button type="button" class="btn btn-fill btn-wd btn-next" :style="fillButtonStyle" :disabled="loading">
|
||||||
{{nextButtonText}}
|
{{nextButtonText}}
|
||||||
</button>
|
</button>
|
||||||
</slot>
|
</slot>
|
||||||
@@ -130,6 +130,7 @@
|
|||||||
isLastStep: false,
|
isLastStep: false,
|
||||||
currentPercentage: 0,
|
currentPercentage: 0,
|
||||||
maxStep: 0,
|
maxStep: 0,
|
||||||
|
loading: false,
|
||||||
tabs: []
|
tabs: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -195,38 +196,65 @@
|
|||||||
return index <= this.maxStep
|
return index <= this.maxStep
|
||||||
},
|
},
|
||||||
navigateToTab (index) {
|
navigateToTab (index) {
|
||||||
if (index <= this.maxStep && this.beforeTabChange(this.activeTabIndex)) {
|
if (index <= this.maxStep) {
|
||||||
this.changeTab(this.activeTabIndex, index)
|
let cb = () => {
|
||||||
|
this.changeTab(this.activeTabIndex, index)
|
||||||
|
}
|
||||||
|
this.beforeTabChange(this.activeTabIndex, cb)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeTabChange (index) {
|
setLoading (value) {
|
||||||
|
this.loading = value
|
||||||
|
this.$emit('on-loading', value)
|
||||||
|
},
|
||||||
|
validateBeforeChange (promiseFn, callback) {
|
||||||
|
// we have a promise
|
||||||
|
if (promiseFn.then && typeof promiseFn.then === 'function') {
|
||||||
|
this.setLoading(true)
|
||||||
|
promiseFn.then((res) => {
|
||||||
|
this.setLoading(false)
|
||||||
|
let validationResult = res === true
|
||||||
|
this.executeBeforeChange(validationResult, callback)
|
||||||
|
}).catch(() => {
|
||||||
|
this.setLoading(false)
|
||||||
|
})
|
||||||
|
// we have a simple function
|
||||||
|
} else {
|
||||||
|
let validationResult = promiseFn === true
|
||||||
|
this.executeBeforeChange(validationResult, callback)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
executeBeforeChange (validationResult, callback) {
|
||||||
|
this.$emit('on-validate', validationResult, this.activeTabIndex)
|
||||||
|
if (validationResult) {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeTabChange (index, callback) {
|
||||||
|
if (this.loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
let oldTab = this.tabs[index]
|
let oldTab = this.tabs[index]
|
||||||
if (oldTab && oldTab.beforeChange !== undefined) {
|
if (oldTab && oldTab.beforeChange !== undefined) {
|
||||||
return oldTab.beforeChange()
|
let tabChangeRes = oldTab.beforeChange()
|
||||||
|
this.validateBeforeChange(tabChangeRes, callback)
|
||||||
|
} else {
|
||||||
|
callback()
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
},
|
},
|
||||||
changeTab (oldIndex, newIndex) {
|
changeTab (oldIndex, newIndex) {
|
||||||
let oldTab = this.tabs[oldIndex]
|
let oldTab = this.tabs[oldIndex]
|
||||||
let newTab = this.tabs[newIndex]
|
let newTab = this.tabs[newIndex]
|
||||||
if (oldTab) {
|
if (oldTab) {
|
||||||
oldTab.show = false
|
|
||||||
oldTab.active = false
|
oldTab.active = false
|
||||||
}
|
}
|
||||||
if (newTab) {
|
if (newTab) {
|
||||||
newTab.show = true
|
|
||||||
newTab.active = true
|
newTab.active = true
|
||||||
}
|
}
|
||||||
this.activeTabIndex = newIndex
|
this.activeTabIndex = newIndex
|
||||||
this.checkStep()
|
this.checkStep()
|
||||||
this.tryChangeRoute(newTab)
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
tryChangeRoute (tab) {
|
|
||||||
if (this.$router && tab.route) {
|
|
||||||
this.$router.push(tab.route)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
checkStep () {
|
checkStep () {
|
||||||
if (this.activeTabIndex === this.tabCount - 1) {
|
if (this.activeTabIndex === this.tabCount - 1) {
|
||||||
this.isLastStep = true
|
this.isLastStep = true
|
||||||
@@ -239,58 +267,53 @@
|
|||||||
this.maxStep = this.activeTabIndex
|
this.maxStep = this.activeTabIndex
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
nextTab () {
|
nextTab () {
|
||||||
if (!this.beforeTabChange(this.activeTabIndex)) return
|
let cb = () => {
|
||||||
|
if (this.activeTabIndex < this.tabCount - 1) {
|
||||||
if (this.activeTabIndex < this.tabCount - 1) {
|
this.changeTab(this.activeTabIndex, this.activeTabIndex + 1)
|
||||||
this.activeTabIndex++
|
this.increaseMaxStep()
|
||||||
this.increaseMaxStep()
|
} else {
|
||||||
this.checkStep()
|
this.isLastStep = true
|
||||||
} else {
|
this.$emit('finished')
|
||||||
this.isLastStep = true
|
}
|
||||||
this.$emit('finished')
|
|
||||||
}
|
}
|
||||||
|
this.beforeTabChange(this.activeTabIndex, cb)
|
||||||
},
|
},
|
||||||
prevTab () {
|
prevTab () {
|
||||||
if (!this.beforeTabChange(this.activeTabIndex)) return
|
let cb = () => {
|
||||||
|
if (this.activeTabIndex > 0) {
|
||||||
if (this.activeTabIndex > 0) {
|
this.changeTab(this.activeTabIndex, this.activeTabIndex - 1)
|
||||||
this.activeTabIndex--
|
this.isLastStep = false
|
||||||
this.isLastStep = false
|
}
|
||||||
}
|
}
|
||||||
|
this.beforeTabChange(this.activeTabIndex, cb)
|
||||||
},
|
},
|
||||||
finish () {
|
finish () {
|
||||||
this.$emit('on-complete')
|
let cb = () => {
|
||||||
|
this.$emit('on-complete')
|
||||||
|
}
|
||||||
|
this.beforeTabChange(this.activeTabIndex, cb)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.tabs = this.$children.filter((comp) => comp.$options.name === 'tab-content')
|
this.tabs = this.$children.filter((comp) => comp.$options.name === 'tab-content')
|
||||||
if (this.tabs.length > 0) {
|
if (this.tabs.length > 0 && this.startIndex === 0) {
|
||||||
let firstTab = this.tabs[this.activeTabIndex]
|
let firstTab = this.tabs[this.activeTabIndex]
|
||||||
firstTab.show = true
|
|
||||||
firstTab.active = true
|
firstTab.active = true
|
||||||
this.tryChangeRoute(firstTab)
|
|
||||||
}
|
}
|
||||||
if (this.startIndex < this.tabs.length) {
|
if (this.startIndex < this.tabs.length) {
|
||||||
|
let tabToActivate = this.tabs[this.startIndex]
|
||||||
this.activeTabIndex = this.startIndex
|
this.activeTabIndex = this.startIndex
|
||||||
|
tabToActivate.active = true
|
||||||
this.maxStep = this.startIndex
|
this.maxStep = this.startIndex
|
||||||
this.tryChangeRoute(this.tabs[this.startIndex])
|
|
||||||
} else {
|
} else {
|
||||||
console.warn(`Prop startIndex set to ${this.startIndex} is greater than the number of tabs - ${this.tabs.length}. Make sure that the starting index is less than the number of tabs registered`)
|
console.warn(`Prop startIndex set to ${this.startIndex} is greater than the number of tabs - ${this.tabs.length}. Make sure that the starting index is less than the number of tabs registered`)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
activeTabIndex: function (newVal, oldVal) {
|
|
||||||
if (this.beforeTabChange(oldVal)) {
|
|
||||||
this.changeTab(oldVal, newVal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
@import "../assets/form-wizard/bootstrap.min.css";
|
@import "./../assets/tab-wizard/bootstrap.min.css";
|
||||||
</style>
|
</style>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import "./../assets/wizard";
|
@import "./../assets/wizard";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-show="show" class="tab-container">
|
<div v-if="active" class="tab-container">
|
||||||
<slot>
|
<slot>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
@@ -26,14 +26,10 @@
|
|||||||
*/
|
*/
|
||||||
beforeChange: {
|
beforeChange: {
|
||||||
type: Function
|
type: Function
|
||||||
},
|
|
||||||
route: {
|
|
||||||
type: [String, Object]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
show: false,
|
|
||||||
active: false
|
active: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user