Merge pull request #717 from vin-e/develop

Stepper component implementation
This commit is contained in:
Pablo Henrique 2017-05-22 00:42:08 -03:00 committed by GitHub
commit 9970988656
13 changed files with 1052 additions and 1 deletions

View file

@ -13,6 +13,10 @@
<a href="https://www.npmjs.com/package/vue-material">
<img src="https://img.shields.io/npm/v/vue-material.svg" alt="Version">
</a>
<a href="https://cdnjs.com/libraries/vue-material">
<img src="https://img.shields.io/cdnjs/v/vue-material.svg" alt="Version">
</a>
<a href="https://cdnjs.com/libraries/vue-material">

View file

@ -131,6 +131,10 @@
<router-link exact to="/components/spinner">Spinner</router-link>
</md-list-item>
<md-list-item class="md-inset">
<router-link exact to="/components/stepper">Stepper</router-link>
</md-list-item>
<md-list-item class="md-inset">
<router-link exact to="/components/subheader">Subheader</router-link>
</md-list-item>

View file

@ -0,0 +1,252 @@
<template>
<page-content page-title="Components - Stepper">
<docs-component>
<div slot="description">
<p>Step indicators are visual indications of an activity progression.</p>
</div>
<div slot="api">
<api-table name="md-stepper">
<md-table slot="properties">
<md-table-header>
<md-table-row>
<md-table-head>Name</md-table-head>
<md-table-head>Type</md-table-head>
<md-table-head>Description</md-table-head>
</md-table-row>
</md-table-header>
<md-table-body>
<md-table-row>
<md-table-cell>md-alternate-labels</md-table-cell>
<md-table-cell><code>Boolean</code></md-table-cell>
<md-table-cell>On the horizontal display it will place the labels underneigh the step icon. Default: false</md-table-cell>
</md-table-row>
<md-table-row>
<md-table-cell>md-elevation</md-table-cell>
<md-table-cell><code>Number</code></md-table-cell>
<md-table-cell>Sets the elevation of the container for each content and the horizontal header. Default: 1</md-table-cell>
</md-table-row>
<md-table-row>
<md-table-cell>md-vertical</md-table-cell>
<md-table-cell><code>Boolean</code></md-table-cell>
<md-table-cell>Will place the steps in a vertical position. Default: false</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</api-table>
<api-table name="md-step">
<md-table slot="properties">
<md-table-header>
<md-table-row>
<md-table-head>Name</md-table-head>
<md-table-head>Type</md-table-head>
<md-table-head>Description</md-table-head>
</md-table-row>
</md-table-header>
<md-table-body>
<md-table-row>
<md-table-cell>md-button-back</md-table-cell>
<md-table-cell><code>String</code></md-table-cell>
<md-table-cell>The text to be displayed in the back button. Default: 'BACK'.</md-table-cell>
</md-table-row>
<md-table-row>
<md-table-cell>md-back-continue</md-table-cell>
<md-table-cell><code>String</code></md-table-cell>
<md-table-cell>The text to be displayed in the coninue button. Default: 'CONTINUE' or 'FINISH' (if is the last step)</md-table-cell>
</md-table-row>
<md-table-row>
<md-table-cell>md-continue</md-table-cell>
<md-table-cell><code>Boolean</code></md-table-cell>
<md-table-cell>Ability to define if the step is completed and allowed to continue. Default: true</md-table-cell>
</md-table-row>
<md-table-row>
<md-table-cell>md-disabled</md-table-cell>
<md-table-cell><code>Boolean</code></md-table-cell>
<md-table-cell>Ability to disable the step. Default: false</md-table-cell>
</md-table-row>
<md-table-row>
<md-table-cell>md-editable</md-table-cell>
<md-table-cell><code>Boolean</code></md-table-cell>
<md-table-cell>If the step is allowed to go 'back' after it has been completed. Default: false</md-table-cell>
</md-table-row>
<md-table-row>
<md-table-cell>md-icon</md-table-cell>
<md-table-cell><code>String</code></md-table-cell>
<md-table-cell>Icon to use instead of the index number. Default: will use the step index</md-table-cell>
</md-table-row>
<md-table-row>
<md-table-cell>md-label</md-table-cell>
<md-table-cell><code>String</code></md-table-cell>
<md-table-cell>The label of step header. Default: undefined</md-table-cell>
</md-table-row>
<md-table-row>
<md-table-cell>md-message</md-table-cell>
<md-table-cell><code>String</code></md-table-cell>
<md-table-cell>The sub message to be used underneigh the step header label. Default: undefined</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
<md-table slot="classes">
<md-table-header>
<md-table-row>
<md-table-head>Name</md-table-head>
<md-table-head>Description</md-table-head>
</md-table-row>
</md-table-header>
<md-table-body>
<md-table-row>
<md-table-cell>md-disabled</md-table-cell>
<md-table-cell>Disabled class.</md-table-cell>
</md-table-row>
</md-table-body>
</md-table>
</api-table>
</div>
<div slot="example">
<example-box card-title="Default steps">
<div class="stepper-demo" slot="demo">
<md-stepper>
<md-step>
<p>This seems something basic I need to do first before the next step.</p>
</md-step>
<md-step>
<p>This seems something I need to focus on just after the first step.</p>
</md-step>
<md-step>
<p>This seems something important I need to fix just right before the last step.</p>
</md-step>
</md-stepper>
</div>
<div slot="code">
<code-block lang="xml">
&lt;md-stepper&gt;
&lt;md-step&gt;
&lt;p&gt;This seems something basic I need to do first before the next step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;md-step&gt;
&lt;p&gt;This seems something I need to focus on just after the first step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;md-step&gt;
&lt;p&gt;This seems something important I need to fix just right before the last step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;/md-stepper&gt;
</code-block>
</div>
</example-box>
<example-box card-title="Labels">
<div class="stepper-demo" slot="demo">
<md-stepper>
<md-step md-label="Dunder Miflin">
<p>This seems something basic I need to do first before the next step.</p>
</md-step>
<md-step md-label="Scraton" md-message="Pennsylvania">
<p>This seems something I need to focus on just after the first step.</p>
</md-step>
</md-stepper>
</div>
<div slot="code">
<code-block lang="xml">
&lt;md-stepper&gt;
&lt;md-step md-label="Dunder Miflin"&gt;
&lt;p&gt;This seems something basic I need to do first before the next step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;md-step md-label="Scraton" md-message="Pennsylvania"&gt;
&lt;p&gt;This seems something I need to focus on just after the first step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;/md-stepper&gt;
</code-block>
</div>
</example-box>
<example-box card-title="Alternate Labels">
<div class="stepper-demo" slot="demo">
<md-stepper md-alternate-labels>
<md-step md-label="Dunder Miflin">
<p>This seems something basic I need to do first before the next step.</p>
</md-step>
<md-step md-label="Scraton" md-message="Pennsylvania">
<p>This seems something I need to focus on just after the first step.</p>
</md-step>
</md-stepper>
</div>
<div slot="code">
<code-block lang="xml">
&lt;md-stepper md-alternate-labels&gt;
&lt;md-step md-label="Dunder Miflin"&gt;
&lt;p&gt;This seems something basic I need to do first before the next step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;md-step md-label="Scraton" md-message="Pennsylvania"&gt;
&lt;p&gt;This seems something I need to focus on just after the first step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;/md-stepper&gt;
</code-block>
</div>
</example-box>
<example-box card-title="Vertical steps">
<div class="stepper-demo" slot="demo">
<md-stepper md-vertical>
<md-step>
<p>This seems something basic I need to do first before the next step.</p>
</md-step>
<md-step>
<p>This seems something I need to focus on just after the first step.</p>
</md-step>
<md-step>
<p>This seems something important I need to fix just right before the last step.</p>
</md-step>
</md-stepper>
</div>
<div slot="code">
<code-block lang="xml">
&lt;md-stepper md-vertical&gt;
&lt;md-step&gt;
&lt;p&gt;This seems something basic I need to do first before the next step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;md-step&gt;
&lt;p&gt;This seems something I need to focus on just after the first step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;md-step&gt;
&lt;p&gt;This seems something important I need to fix just right before the last step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;/md-stepper&gt;
</code-block>
</div>
</example-box>
<example-box card-title="Vertical with Labels">
<div class="stepper-demo" slot="demo">
<md-stepper md-vertical>
<md-step md-label="Dunder Miflin">
<p>This seems something basic I need to do first before the next step.</p>
</md-step>
<md-step md-label="Scraton" md-message="Pennsylvania">
<p>This seems something I need to focus on just after the first step.</p>
</md-step>
</md-stepper>
</div>
<div slot="code">
<code-block lang="xml">
&lt;md-stepper md-vertical&gt;
&lt;md-step md-label="Dunder Miflin"&gt;
&lt;p&gt;This seems something basic I need to do first before the next step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;md-step md-label="Scraton" md-message="Pennsylvania"&gt;
&lt;p&gt;This seems something I need to focus on just after the first step.&lt;/p&gt;
&lt;/md-step&gt;
&lt;/md-stepper&gt;
</code-block>
</div>
</example-box>
</div>
</docs-component>
</page-content>
</template>

View file

@ -29,6 +29,7 @@ const Sidenav = (r) => require.ensure([], () => r(require('./pages/components/Si
const Snackbar = (r) => require.ensure([], () => r(require('./pages/components/Snackbar')), 'snackbar');
const SpeedDial = (r) => require.ensure([], () => r(require('./pages/components/SpeedDial')), 'speed-dial');
const Spinner = (r) => require.ensure([], () => r(require('./pages/components/Spinner')), 'spinner');
const Stepper = (r) => require.ensure([], () => r(require('./pages/components/Stepper')), 'stepper');
const Subheader = (r) => require.ensure([], () => r(require('./pages/components/Subheader')), 'subheader');
const Switch = (r) => require.ensure([], () => r(require('./pages/components/Switch')), 'switch');
const Table = (r) => require.ensure([], () => r(require('./pages/components/Table')), 'table');
@ -189,6 +190,11 @@ const components = [
name: 'components:spinner',
component: Spinner
},
{
path: '/components/stepper',
name: 'components:stepper',
component: Stepper
},
{
path: '/components/subheader',
name: 'components:subheader',

View file

@ -2,7 +2,7 @@
<transition name="md-spinner" appear>
<div class="md-spinner" :class="[themeClass, classes]":style="styles">
<svg class="md-spinner-draw" viewBox="25 25 50 50">
<circle class="md-spinner-path" cx="50" cy="50" r="20" :stroke-width="mdStroke" :stroke-dasharray="dashProgress" />
<circle class="md-spinner-path" cx="50" cy="50" r="20" :stroke-width="mdStroke" :stroke-dasharray="dashProgress"></circle>
</svg>
</div>
</transition>

View file

@ -0,0 +1,14 @@
import mdStepper from './mdStepper.vue';
import mdStep from './mdStep.vue';
import mdStepHeaderContainer from './mdStepHeaderContainer.js';
import mdStepHeader from './mdStepHeader.vue';
import mdStepperTheme from './mdStepper.theme';
export default function install(Vue) {
Vue.component('md-stepper', mdStepper);
Vue.component('md-step', mdStep);
Vue.component('md-step-header-container', mdStepHeaderContainer);
Vue.component('md-step-header', mdStepHeader);
Vue.material.styles.push(mdStepperTheme);
}

View file

@ -0,0 +1,197 @@
<template>
<div class="md-step" :id="stepId" :style="styles">
<md-step-header
v-if="vertical"
:step="getStepData()"
@click.native="setActiveStep()">
</md-step-header>
<div class="md-step-content" v-if="!vertical || (vertical && isCurrentStep)">
<slot></slot>
<div class="md-step-actions" v-if="!vertical || (vertical && isCurrentStep)">
<md-button class="md-raised md-primary" @click="moveNextStep" :disabled="!mdContinue">{{ continueText }}</md-button>
<md-button @click="movePreviousStep" :disabled="!canGoBack">{{ mdButtonBack }}</md-button>
</div>
</div>
</div>
</template>
<script>
import uniqueId from '../../core/utils/uniqueId';
import getClosestVueParent from '../../core/utils/getClosestVueParent';
export default {
name: 'md-step',
props: {
id: [String, Number],
mdActive: Boolean,
mdButtonBack: {
type: String,
default: 'BACK'
},
mdButtonContinue: {
type: String,
default: 'CONTINUE'
},
mdContinue: {
type: Boolean,
default: true
},
mdDisabled: Boolean,
mdEditable: {
type: Boolean,
default: true
},
mdIcon: String,
mdLabel: [String, Number],
mdMessage: [String],
mdToolTip: String,
mdTooltipDelay: {
type: String,
default: '0'
},
mdTooltipDirection: {
type: String,
default: 'bottom'
}
},
data() {
return {
index: 0,
left: '0px',
mounted: false,
parentStepper: undefined,
stepId: this.id || 'step-' + uniqueId(),
vertical: false,
width: '0px'
};
},
watch: {
mdActive() {
this.updateStepData();
},
mdContinue() {
this.updateStepData();
},
mdEditable() {
this.updateStepData();
},
mdDisabled() {
this.updateStepData();
},
mdIcon() {
this.updateStepData();
},
mdLabel() {
this.updateStepData();
},
mdMessage() {
this.updateStepData();
},
mdToolTip() {
this.updateStepData();
},
mdTooltipDelay() {
this.updateStepData();
},
mdTooltipDirection() {
this.updateStepData();
}
},
computed: {
canGoBack() {
if (this.index === 0) {
return false;
}
if (!this.parentStepper) {
return false;
}
var previousStep = this.parentStepper.getPreviousStep(this.stepId);
if (previousStep !== undefined && !previousStep.editable) {
return false;
}
return true;
},
continueText() {
if (!this.parentStepper) {
return this.mdButtonContinue;
}
if (this.index + 1 === this.parentStepper.getStepsCount() && this.mdButtonContinue === 'CONTINUE') {
return 'FINISH';
}
return this.mdButtonContinue;
},
isCurrentStep() {
return this.index === this.parentStepper.activeStepNumber;
},
styles() {
if (this.vertical) {
return {};
}
return {
width: this.width,
left: this.left
};
}
},
methods: {
getStepData() {
return {
id: this.stepId,
label: this.mdLabel,
message: this.mdMessage,
icon: this.mdIcon,
active: this.mdActive,
continue: this.mdContinue,
editable: this.mdEditable,
disabled: this.mdDisabled,
toolTip: this.mdToolTip,
tooltipDelay: this.mdTooltipDelay,
tooltipDirection: this.mdTooltipDirection,
ref: this
};
},
moveNextStep() {
this.parentStepper.moveNextStep();
},
movePreviousStep() {
this.parentStepper.movePreviousStep();
},
setActiveStep() {
this.parentStepper.setActiveStep(this.getStepData());
},
updateStepData() {
this.parentStepper.updateStep(this.getStepData());
}
},
mounted() {
let stepData = this.getStepData();
this.parentStepper = getClosestVueParent(this.$parent, 'md-stepper');
if (!this.parentStepper) {
throw new Error('You must wrap the md-step in a md-stepper');
}
this.mounted = true;
this.parentStepper.updateStep(stepData);
if (this.mdActive) {
this.parentStepper.setActiveStep(stepData);
}
this.vertical = this.parentStepper.mdVertical;
this.index = this.parentStepper.getStepIndex(this.stepId);
},
beforeDestroy() {
this.parentStepper.unregisterStep(this.getStepData());
}
};
</script>

View file

@ -0,0 +1,71 @@
<template>
<div class="md-step-header" :class="getHeaderClasses">
<div class="md-step-icons">
<md-icon v-if="icon" class="md-step-icon">{{ icon }}</md-icon>
<div v-if="!icon" class="md-step-number"><span>{{ stepNumber }}</span></div>
</div>
<div class="md-step-titles">
<div class="md-step-title">{{ step.label }}</div>
<small v-if="step.message">{{ step.message }}</small>
</div>
<md-tooltip v-if="step.toolTip" :md-direction="step.tooltipDirection" :md-delay="step.tooltipDelay">{{ step.toolTip }}</md-tooltip>
</div>
</template>
<script>
import getClosestVueParent from '../../core/utils/getClosestVueParent';
export default {
props: {
step: Object,
mdAlternateLabels: Boolean
},
data() {
return {
index: Number,
parentStepper: {}
};
},
computed: {
isCompleted() {
return this.index < this.parentStepper.activeStepNumber;
},
getHeaderClasses() {
return {
'md-active': this.parentStepper.activeStep === this.step.id,
'md-alternate-labels': this.mdAlternateLabels,
'md-disabled': this.step.disabled,
'md-has-sub-message': this.step.message,
'md-primary': this.isCompleted
};
},
icon() {
if (!this.step.disabled && this.step.editable && this.isCompleted) {
return 'mode_edit';
}
if (!this.step.disabled && this.isCompleted) {
return 'check';
}
return this.step.icon;
},
stepNumber() {
return this.index + 1;
}
},
mounted() {
this.$nextTick(() => {
this.parentStepper = getClosestVueParent(this.$parent, 'md-stepper');
if (!this.parentStepper) {
this.$destroy();
throw new Error('You should wrap the md-step-header in a md-stepper');
}
this.index = this.parentStepper.getStepIndex(this.step.id);
});
}
};
</script>

View file

@ -0,0 +1,33 @@
import uniqueId from '../../core/utils/uniqueId';
export default {
functional: true,
props: {
mdVertical: {
type: Boolean,
default: false
}
},
render(createElement, { children, props }) {
const insertDividerIntoArray = (arr) => {
return arr.reduce((result, element, index, array) => {
result.push(element);
if (index < array.length - 1) {
var mdDivider = createElement('md-divider', { key: 'divider-' + uniqueId() });
result.push(mdDivider);
}
return result;
}, []);
};
if (!props.mdVertical) {
children = insertDividerIntoArray(children);
}
return createElement('div', { class: 'md-steps-navigation-container'}, children);
}
};

View file

@ -0,0 +1,190 @@
@import '../../core/stylesheets/variables.scss';
@import '../../core/stylesheets/mixins.scss';
.md-stepper {
display: flex;
flex-flow: column;
position: relative;
width: 100%;
.md-step-header {
background: none;
border: 0;
cursor: pointer;
flex-shrink: 0;
font-family: inherit;
font-size: 12px;
font-weight: 500;
margin: 0;
max-height: 72px;
padding: 24px;
position: relative;
transition: $swift-ease-out;
.md-step-icons, .md-step-titles {
display: inline-block;
vertical-align: middle;
}
&.md-has-sub-message {
padding: 15px 24px;
.md-step-title {
margin-bottom: -4px;
}
}
.md-step-icon {
border-radius: 50%;
font-size: 12px;
height: 24px;
line-height: 24px;
margin-right: 8px;
min-width: 24px;
padding: 0px 6px;
pointer-events: none;
user-select: none;
width: 24px;
}
.md-step-number {
border-radius: 50%;
display: inline-block;
font-size: 12px;
margin-right: 8px;
width: 24px;
span {
display: block;
line-height: 24px;
text-align: center;
}
}
.md-step-title {
font-size: inherit;
}
&.md-disabled {
cursor: default;
pointer-events: none;
user-select: none;
-webkit-user-drag: none;
}
}
.md-steps-navigation {
display: flex;
height: 72px;
min-height: 72px;
overflow: hidden;
position: relative;
transition: $swift-ease-out;
z-index: 1;
&.md-alternate-labels {
height: 104px;
min-height: 104px;
}
.md-steps-navigation-container {
display: flex;
justify-content: space-between;
width: 100%;
@include layout-small-and-up {
margin-bottom: -15px;
}
.md-divider {
margin: 36px 0;
position: relative;
width: 100%;
}
.md-step-header {
&.md-alternate-labels {
max-height: 104px;
text-align: center;
&.md-has-sub-message {
padding: 24px;
}
.md-step-icon {
padding: 0;
}
.md-step-icons, .md-step-titles {
display: block;
}
.md-step-titles {
margin-top: 10px;
}
}
}
}
}
.md-steps-container {
height: 0;
overflow: hidden;
position: relative;
width: 100%;
.md-steps-wrapper {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
transform: translate3d(0, 0, 0);
transition: transform $swift-ease-out-duration $swift-ease-out-timing-function;
width: 9999em;
.md-step {
left: 0;
padding: 16px;
position: absolute;
right: 0;
top: 0;
.md-step-content {
padding: 16px;
font-size: 14px;
line-height: 22px;
&:last-child {
padding-bottom: 24px;
}
}
}
}
}
.md-steps-vertical-container {
.md-step {
.md-step-header {
padding-bottom: 8px;
}
&:not(:first-of-type) {
.md-step-header {
padding-top: 8px;
}
}
.md-step-content {
margin: 0 24px 0 34px;
padding-bottom: 32px;
padding-left: 24px;
padding-top: 8px;
}
&:not(:last-of-type) {
.md-step-content {
border-left: 1px solid #BDBDBD;
}
}
}
}
}

View file

@ -0,0 +1,40 @@
.THEME_NAME {
&.md-stepper {
.md-step-header {
.md-step-icon,
.md-step-number {
color: #{'BACKGROUND-CONTRAST'};
background-color: #bdbdbd;
}
&.md-primary, &.md-active {
.md-step-icon,
.md-step-number {
color: #{'PRIMARY-CONTRAST'};
background-color: #{'PRIMARY-COLOR'};
}
}
&.md-accent {
.md-step-icon,
.md-step-number {
color: #{'ACCENT-CONTRAST'};
background-color: #{'ACCENT-COLOR'};
}
}
&.md-warn {
.md-step-icon,
.md-step-number {
color: #{'WARN-CONTRAST'};
background-color: #{'WARN-COLOR'};
}
}
&.md-disabled {
color: #bdbdbd;
.md-step-icon,
.md-step-number {
color: white;
background-color: #bdbdbd;
}
}
}
}
}

View file

@ -0,0 +1,238 @@
<template>
<div class="md-stepper" :class="[themeClass, stepsClasses]">
<md-whiteframe md-tag="nav" class="md-steps-navigation" v-if="!mdVertical" :md-elevation="mdElevation" :class="navigationClasses" ref="stepNavigation">
<md-step-header-container :md-vertical="mdVertical" ref="stepHeader">
<md-step-header
v-for="(step, index) in stepList"
:key="step.id"
:step="step"
:md-alternate-labels="mdAlternateLabels"
@click.native="setActiveStep(step)">
</md-step-header>
</md-step-header-container>
</md-whiteframe>
<md-whiteframe :md-elevation="mdElevation">
<div class="md-steps-container" v-if="!mdVertical" ref="stepContent" :style="{ height: contentHeight }">
<div class="md-steps-wrapper" :style="{ transform: `translate3D(-${contentWidth}, 0, 0)` }">
<slot></slot>
</div>
</div>
<div class="md-steps-vertical-container" v-if="mdVertical" ref="stepContent">
<slot></slot>
</div>
</md-whiteframe>
</div>
</template>
<style lang="scss" src="./mdStepper.scss">
</style>
<script>
import theme from '../../core/components/mdTheme/mixin';
import throttle from '../../core/utils/throttle';
export default {
name: 'md-stepper',
props: {
mdAlternateLabels: {
type: Boolean,
default: false
},
mdElevation: {
type: [String, Number],
default: 1
},
mdVertical: {
type: Boolean,
default: false
}
},
mixins: [theme],
data: () => ({
stepList: {},
activeStep: null,
activeStepNumber: 0,
contentHeight: '0px',
contentWidth: '0px'
}),
computed: {
navigationClasses() {
return {
'md-alternate-labels': this.mdAlternateLabels
};
},
stepsClasses() {
return {
'md-steps-vertical': this.mdVertical
};
}
},
methods: {
getNextStep(id) {
var currentIndex = this.getStepIndex(id);
if (currentIndex === this.stepList.length) {
return undefined;
}
var nextStepId = Object.keys(this.stepList)[currentIndex + 1];
var nextStep = this.stepList[nextStepId];
return nextStep;
},
getPreviousStep(id) {
var currentIndex = this.getStepIndex(id);
if (currentIndex === 0) {
return undefined;
}
var previousStepId = Object.keys(this.stepList)[currentIndex - 1];
var previousStep = this.stepList[previousStepId];
return previousStep;
},
getStepsCount() {
const idList = Object.keys(this.stepList);
return idList.length;
},
getStepIndex(id) {
const idList = Object.keys(this.stepList);
return idList.indexOf(id);
},
registerStep(stepData) {
this.$set(this.stepList, stepData.id, stepData);
},
moveNextStep() {
if (this.activeStepNumber < this.getStepsCount() - 1) {
var nextStep = this.getNextStep(this.activeStep);
this.setActiveStep(nextStep);
} else {
this.$emit('completed');
}
},
movePreviousStep() {
if (this.activeStepNumber > 0 && this.activeStepNumber < this.getStepsCount()) {
var prevStep = this.getPreviousStep(this.activeStep);
this.setActiveStep(prevStep);
}
},
setActiveStep(stepData) {
if (this.activeStepNumber > this.getStepIndex(stepData.id) && !stepData.editable) {
return;
}
this.activeStep = stepData.id;
this.activeStepNumber = this.getStepIndex(this.activeStep);
this.calculatePosition();
this.$emit('change', this.activeStepNumber);
},
unregisterStep(stepData) {
this.$delete(this.stepList, stepData.id);
},
updateStep(stepData) {
this.registerStep(stepData);
if (stepData.active) {
if (!stepData.disabled) {
this.setActiveStep(stepData);
} else if (Object.keys(this.stepList).length) {
let stepIds = Object.keys(this.stepList);
let targetIndex = stepIds.indexOf(stepData.id) + 1;
let target = stepIds[targetIndex];
if (target) {
this.setActiveStep(this.stepList[target]);
} else {
this.setActiveStep(this.stepList[0]);
}
}
}
},
observeElementChanges() {
this.parentObserver = new MutationObserver(throttle(this.calculateOnWatch, 50));
this.parentObserver.observe(this.$refs.stepContent, {
childList: true,
attributes: true,
subtree: true
});
},
calculateStepsWidthAndPosition() {
if (!this.mdVertical) {
const width = this.$el.offsetWidth;
let index = 0;
this.contentWidth = width * this.activeStepNumber + 'px';
for (const stepId in this.stepList) {
const step = this.stepList[stepId];
step.ref.width = width + 'px';
step.ref.left = width * index + 'px';
index++;
}
} else {
this.contentWidth = 'initial';
}
},
calculateContentHeight() {
this.$nextTick(() => {
if (!this.mdVertical && Object.keys(this.stepList).length) {
let height = this.stepList[this.activeStep].ref.$el.offsetHeight;
this.contentHeight = height + 'px';
} else {
this.contentHeight = 'initial';
}
});
},
calculatePosition() {
window.requestAnimationFrame(() => {
this.calculateStepsWidthAndPosition();
this.calculateContentHeight();
});
},
debounceTransition() {
window.clearTimeout(this.transitionControl);
this.transitionControl = window.setTimeout(() => {
this.calculatePosition();
this.transitionOff = false;
}, 200);
},
calculateOnWatch() {
this.calculatePosition();
this.debounceTransition();
},
calculateOnResize() {
this.transitionOff = true;
this.calculateOnWatch();
}
},
mounted() {
this.$nextTick(() => {
this.observeElementChanges();
window.addEventListener('resize', this.calculateOnResize);
if (Object.keys(this.stepList).length && !this.activeStep) {
let firstStep = Object.keys(this.stepList)[0];
this.setActiveStep(this.stepList[firstStep]);
}
});
},
beforeDestroy() {
if (this.parentObserver) {
this.parentObserver.disconnect();
}
window.removeEventListener('resize', this.calculateOnResize);
}
};
</script>

View file

@ -24,6 +24,7 @@ import MdSidenav from './components/mdSidenav';
import MdSnackbar from './components/mdSnackbar';
import MdSpeedDial from './components/mdSpeedDial';
import MdSpinner from './components/mdSpinner';
import MdStepper from './components/mdStepper';
import MdSubheader from './components/mdSubheader';
import MdSwitch from './components/mdSwitch';
import MdTable from './components/mdTable';
@ -59,6 +60,7 @@ const options = {
MdSnackbar,
MdSpeedDial,
MdSpinner,
MdStepper,
MdSubheader,
MdSwitch,
MdTable,