mirror of
https://github.com/Hopiu/vue-material.git
synced 2026-03-16 22:10:27 +00:00
Merge pull request #717 from vin-e/develop
Stepper component implementation
This commit is contained in:
commit
9970988656
13 changed files with 1052 additions and 1 deletions
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
252
docs/src/pages/components/Stepper.vue
Normal file
252
docs/src/pages/components/Stepper.vue
Normal 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">
|
||||
<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>
|
||||
</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">
|
||||
<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>
|
||||
</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">
|
||||
<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>
|
||||
</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">
|
||||
<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>
|
||||
</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">
|
||||
<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>
|
||||
</code-block>
|
||||
</div>
|
||||
</example-box>
|
||||
</div>
|
||||
</docs-component>
|
||||
</page-content>
|
||||
</template>
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
14
src/components/mdStepper/index.js
Normal file
14
src/components/mdStepper/index.js
Normal 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);
|
||||
}
|
||||
197
src/components/mdStepper/mdStep.vue
Normal file
197
src/components/mdStepper/mdStep.vue
Normal 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>
|
||||
71
src/components/mdStepper/mdStepHeader.vue
Normal file
71
src/components/mdStepper/mdStepHeader.vue
Normal 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>
|
||||
33
src/components/mdStepper/mdStepHeaderContainer.js
Normal file
33
src/components/mdStepper/mdStepHeaderContainer.js
Normal 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);
|
||||
}
|
||||
};
|
||||
190
src/components/mdStepper/mdStepper.scss
Normal file
190
src/components/mdStepper/mdStepper.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/components/mdStepper/mdStepper.theme
Normal file
40
src/components/mdStepper/mdStepper.theme
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
238
src/components/mdStepper/mdStepper.vue
Normal file
238
src/components/mdStepper/mdStepper.vue
Normal 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>
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue