2
0
mirror of https://github.com/tenrok/vue-form-wizard.git synced 2026-06-17 14:40:34 +03:00
# Conflicts:
#	package.json
This commit is contained in:
cristijora
2017-04-26 20:52:11 +03:00
14 changed files with 280 additions and 139 deletions
+17 -23
View File
@@ -2,10 +2,7 @@
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
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
wasting time on details. Just forget about id's, external scripts and jQuery dependencies
# Demos
Basic [demo](https://jsfiddle.net/bt5dhqtf/97/)
@@ -19,7 +16,8 @@ Other demos:
* [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/)
* [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
* [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
* [Async validation](https://jsfiddle.net/bt5dhqtf/272/) `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)
# Usage
@@ -37,7 +35,7 @@ Download the css and js files from `dist` folder or reference them directly from
//global registration
import 'vue-form-wizard'
import 'vue-form-wizard/dist/vue-form-wizard.min.css'
Vue.use(VueFormWizard)
Vue.use(VueTabWizard)
//local registration
import {FormWizard, TabContent} from 'vue-form-wizard'
@@ -140,17 +138,16 @@ props: {
*/
beforeChange: {
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]
}
}
```
## Events
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/)
* **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')`
* **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)`
* **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)`
# Slots
## Slots
* **Default** - Used for tab-contents
* **title** - Upper title section including sub-title
* **prev** - Previous button content (no need to worry about handling the button functionality)
@@ -158,13 +155,10 @@ props: {
* **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
[![@cristijora](https://img.shields.io/badge/github-cristijora-green.svg)](https://github.com/cristijora)
1. Fork the repo
2. run `npm install`
3. `npm run dev` for launching the dev example
4. After making your changes make sure to pull the changes from the source repo to avoid conflicts
5. `npm run build` to generate the new js and css bundles
6. Commit your changes + the js and css bundles so it's easy to test them right away in fiddles, codepen etc
7. Open a Pull Request. For more information refer to [github forking workflow](https://gist.github.com/Chaser324/ce0505fbed06b947d962)
+1 -1
View File
@@ -9,7 +9,7 @@ function resolve (dir) {
module.exports = {
entry: {
app: './dev/main.js'
app: './dev-example/main.js'
},
output: {
path: config.build.assetsRoot,
+89
View File
@@ -0,0 +1,89 @@
<template>
<div>
<form-wizard @on-complete="onComplete"
shape="circle"
color="#e74c3c"
@on-loading="setLoading"
class="card">
<tab-content title="Personal details"
:before-change="validateAsync"
route="/first"
icon="ti-user">
</tab-content>
<tab-content title="Additional Info"
:before-change="validate"
route="/second"
icon="ti-settings">
</tab-content>
<tab-content title="Last step"
:before-change="validateAsync"
route="/third"
icon="ti-check">
</tab-content>
<transition name="fade" mode="out-in">
<router-view></router-view>
</transition>
<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>
+41
View File
@@ -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);
}
}
+6 -4
View File
@@ -1,10 +1,12 @@
// 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 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>' }
import FormWizard from '../src/index'
const First = { template: '<div>First page</div>' }
const Second = { template: '<div>Second page</div>' }
const Third = { template: '<div>Third page</div>' }
const router = new VueRouter({
mode: 'history',
-61
View File
@@ -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>
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "vue-form-wizard",
"version": "0.1.12",
"version": "0.1.6",
"description": "A vue based tab/form wizard",
"main": "dist/vue-form-wizard.js",
"homepage": "https://github.com/cristijora/vue-form-wizard",
@@ -85,7 +85,7 @@
"url-loader": "^0.5.8",
"vue": "^2.2.2",
"vue-loader": "^11.1.4",
"vue-router": "^2.4.0",
"vue-router": "^2.5.1",
"vue-style-loader": "^2.0.0",
"vue-template-compiler": "^2.2.4",
"webpack": "^2.2.1",
+46
View File
@@ -8,6 +8,9 @@
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;
@@ -26,14 +29,57 @@
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;
}
+1 -1
View File
@@ -13,7 +13,7 @@
border: 1px solid transparent;
white-space: nowrap;
@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,
+8 -8
View File
@@ -1,8 +1,8 @@
/*@import "form-wizard/bs_button";
@import "form-wizard/bs_nav_pills";
@import "form-wizard/bs_progress_bar";*/
@import "form-wizard/variables";
@import "form-wizard/mixins";
@import "form-wizard/buttons";
@import "form-wizard/navs-pagination";
@import "form-wizard/wizard-card";
//@import "tab-wizard/bs_button";
//@import "tab-wizard/bs_nav_pills";
//@import "tab-wizard/bs_progress_bar";
@import "tab-wizard/variables";
@import "tab-wizard/mixins";
@import "tab-wizard/buttons";
@import "tab-wizard/navs-pagination";
@import "tab-wizard/wizard-card";
+66 -35
View File
@@ -43,7 +43,7 @@
<template>
<span @click="prevTab" v-if="displayPrevButton">
<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}}
</button>
</slot>
@@ -63,7 +63,7 @@
<template>
<span @click="nextTab" class="pull-right" v-if="!isLastStep">
<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}}
</button>
</slot>
@@ -130,6 +130,7 @@
isLastStep: false,
currentPercentage: 0,
maxStep: 0,
loading: false,
tabs: []
}
},
@@ -195,26 +196,59 @@
return index <= this.maxStep
},
navigateToTab (index) {
if (index <= this.maxStep && this.beforeTabChange(this.activeTabIndex)) {
this.changeTab(this.activeTabIndex, index)
if (index <= this.maxStep) {
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]
if (oldTab && oldTab.beforeChange !== undefined) {
return oldTab.beforeChange()
let tabChangeRes = oldTab.beforeChange()
this.validateBeforeChange(tabChangeRes, callback)
} else {
callback()
}
return true
},
changeTab (oldIndex, newIndex) {
let oldTab = this.tabs[oldIndex]
let newTab = this.tabs[newIndex]
if (oldTab) {
oldTab.show = false
oldTab.active = false
}
if (newTab) {
newTab.show = true
newTab.active = true
}
this.activeTabIndex = newIndex
@@ -239,58 +273,55 @@
this.maxStep = this.activeTabIndex
}
},
nextTab () {
if (!this.beforeTabChange(this.activeTabIndex)) return
if (this.activeTabIndex < this.tabCount - 1) {
this.activeTabIndex++
this.increaseMaxStep()
this.checkStep()
} else {
this.isLastStep = true
this.$emit('finished')
let cb = () => {
if (this.activeTabIndex < this.tabCount - 1) {
this.changeTab(this.activeTabIndex, this.activeTabIndex + 1)
this.increaseMaxStep()
} else {
this.isLastStep = true
this.$emit('finished')
}
}
this.beforeTabChange(this.activeTabIndex, cb)
},
prevTab () {
if (!this.beforeTabChange(this.activeTabIndex)) return
if (this.activeTabIndex > 0) {
this.activeTabIndex--
this.isLastStep = false
let cb = () => {
if (this.activeTabIndex > 0) {
this.changeTab(this.activeTabIndex, this.activeTabIndex - 1)
this.isLastStep = false
}
}
this.beforeTabChange(this.activeTabIndex, cb)
},
finish () {
this.$emit('on-complete')
let cb = () => {
this.$emit('on-complete')
}
this.beforeTabChange(this.activeTabIndex, cb)
}
},
mounted () {
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]
firstTab.show = true
firstTab.active = true
this.tryChangeRoute(firstTab)
}
if (this.startIndex < this.tabs.length) {
let tabToActivate = this.tabs[this.startIndex]
this.activeTabIndex = this.startIndex
tabToActivate.active = true
this.maxStep = this.startIndex
this.tryChangeRoute(this.tabs[this.startIndex])
} 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`)
}
},
watch: {
activeTabIndex: function (newVal, oldVal) {
if (this.beforeTabChange(oldVal)) {
this.changeTab(oldVal, newVal)
}
}
}
}
</script>
<style>
@import "../assets/form-wizard/bootstrap.min.css";
@import "./../assets/tab-wizard/bootstrap.min.css";
</style>
<style lang="scss">
@import "./../assets/wizard";
+1 -2
View File
@@ -1,5 +1,5 @@
<template>
<div v-show="show" class="tab-container">
<div v-if="active" class="tab-container">
<slot>
</slot>
</div>
@@ -33,7 +33,6 @@
},
data () {
return {
show: false,
active: false
}
}