mirror of
https://github.com/Hopiu/vue-material.git
synced 2026-03-16 22:10:27 +00:00
Merge pull request #549 from fergardi/components/mdOnboarding
Added Onboarding component
This commit is contained in:
commit
cb92ac6ad8
9 changed files with 912 additions and 0 deletions
|
|
@ -99,6 +99,10 @@
|
|||
<router-link exact to="/components/menu">Menu</router-link>
|
||||
</md-list-item>
|
||||
|
||||
<md-list-item class="md-inset">
|
||||
<router-link exact to="/components/onboarding">Onboarding</router-link>
|
||||
</md-list-item>
|
||||
|
||||
<md-list-item class="md-inset">
|
||||
<router-link exact to="/components/progress">Progress</router-link>
|
||||
</md-list-item>
|
||||
|
|
|
|||
202
docs/src/pages/components/Onboarding.vue
Normal file
202
docs/src/pages/components/Onboarding.vue
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
<template>
|
||||
<page-content page-title="Components - Onboarding">
|
||||
<docs-component>
|
||||
<div slot="description">
|
||||
<p>The onboarding offers a first and fast look for your page, with custom controls, automatized steps and swipe effects. Can be also used as a custom carousel.</p>
|
||||
<p>Can be themed used the following:</p>
|
||||
<ul class="md-body-4">
|
||||
<li><code>md-primary</code></li>
|
||||
<li><code>md-warn</code></li>
|
||||
<li><code>md-accent</code></li>
|
||||
<li><code>md-transparent</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div slot="api">
|
||||
<api-table name="md-onboarding">
|
||||
<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-auto</md-table-cell>
|
||||
<md-table-cell><code>Boolean</code></md-table-cell>
|
||||
<md-table-cell>Enable auto-slider. Default <code>false</code>.</md-table-cell>
|
||||
</md-table-row>
|
||||
|
||||
<md-table-row>
|
||||
<md-table-cell>md-infinite</md-table-cell>
|
||||
<md-table-cell><code>Boolean</code></md-table-cell>
|
||||
<md-table-cell>Enable infinite loop. Default <code>false</code>.</md-table-cell>
|
||||
</md-table-row>
|
||||
|
||||
<md-table-row>
|
||||
<md-table-cell>md-duration</md-table-cell>
|
||||
<md-table-cell><code>Number</code></md-table-cell>
|
||||
<md-table-cell>Set duration for <code>md-auto</code> in milliseconds. Default <code>5000</code>.</md-table-cell>
|
||||
</md-table-row>
|
||||
|
||||
<md-table-row>
|
||||
<md-table-cell>md-controls</md-table-cell>
|
||||
<md-table-cell><code>Boolean</code></md-table-cell>
|
||||
<md-table-cell>Enable prev/next controls. Default <code>false</code>.</md-table-cell>
|
||||
</md-table-row>
|
||||
|
||||
<md-table-row>
|
||||
<md-table-cell>md-swipeable</md-table-cell>
|
||||
<md-table-cell><code>Boolean</code></md-table-cell>
|
||||
<md-table-cell>Enable the swipe functionality. Default <code>false</code>.</md-table-cell>
|
||||
</md-table-row>
|
||||
|
||||
<md-table-row>
|
||||
<md-table-cell>md-swipe-distance</md-table-cell>
|
||||
<md-table-cell><code>Number</code></md-table-cell>
|
||||
<md-table-cell>Set the swipe distance needed to open/close the sidenav. Default <code>100</code>.</md-table-cell>
|
||||
</md-table-row>
|
||||
</md-table-body>
|
||||
|
||||
</md-table>
|
||||
</api-table>
|
||||
</div>
|
||||
|
||||
<div slot="example">
|
||||
<example-box card-title="Basic, automatic, infinite, uncontrolled, swipeable">
|
||||
<div slot="demo">
|
||||
<md-boards :md-auto="true" :md-infinite="true" :md-duration="5000" :md-swipeable="true">
|
||||
<md-board id="slide1">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide2">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide3">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
|
||||
</md-board>
|
||||
</md-boards>
|
||||
</div>
|
||||
|
||||
<div slot="code">
|
||||
<code-block lang="xml">
|
||||
<md-boards :md-auto="true" :md-infinite="true" :md-duration="5000" :md-swipeable="true">
|
||||
<md-board id="slide1">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide2">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide3">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
|
||||
</md-board>
|
||||
</md-boards>
|
||||
</code-block>
|
||||
</div>
|
||||
</example-box>
|
||||
|
||||
<example-box card-title="Themed, finite, controlled">
|
||||
<div slot="demo">
|
||||
<md-boards class="md-transparent" :md-controls="true" style="background-color: black">
|
||||
<md-board id="slide1" style="color: white">
|
||||
<p>Transparent themed, for background-imaged slides. Background color black added for demonstration purposes.</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide2" style="color: white">
|
||||
<p>Transparent themed, for background-imaged slides. Background color black added for demonstration purposes.</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide3" style="color: white">
|
||||
<p>Transparent themed, for background-imaged slides. Background color black added for demonstration purposes.</p>
|
||||
</md-board>
|
||||
</md-boards>
|
||||
|
||||
<md-boards class="md-primary" :md-controls="true">
|
||||
<md-board id="slide1">
|
||||
<p>Primary themed</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide2">
|
||||
<p>Primary themed</p>
|
||||
</md-board>
|
||||
</md-boards>
|
||||
|
||||
<md-boards class="md-accent" :md-controls="true">
|
||||
<md-board id="slide1">
|
||||
<p>Accent themed</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide2">
|
||||
<p>Accent themed</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide3">
|
||||
<p>Accent themed</p>
|
||||
</md-board>
|
||||
</md-boards>
|
||||
|
||||
<md-boards class="md-warn" :md-controls="true">
|
||||
<md-board id="slide1">
|
||||
<p>Warn themed</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide2">
|
||||
<p>Warn themed</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide3">
|
||||
<p>Warn themed</p>
|
||||
</md-board>
|
||||
|
||||
<md-board id="slide4">
|
||||
<p>Warn themed</p>
|
||||
</md-board>
|
||||
</md-boards>
|
||||
</div>
|
||||
|
||||
<div slot="code">
|
||||
<code-block lang="xml">
|
||||
<md-boards class="md-transparent" :md-controls="true">
|
||||
<md-board id="slide1">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
|
||||
</md-board>
|
||||
</md-boards>
|
||||
|
||||
<md-boards class="md-primary" :md-controls="true">
|
||||
<md-board id="slide1">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
|
||||
</md-board>
|
||||
</md-boards>
|
||||
|
||||
<md-boards class="md-accent" :md-controls="true">
|
||||
<md-board id="slide1">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
|
||||
</md-board>
|
||||
</md-boards>
|
||||
|
||||
<md-boards class="md-warn" :md-controls="true">
|
||||
<md-board id="slide1">
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt dolorum quas amet cum vitae, omnis! Illum quas voluptatem, expedita iste, dicta ipsum ea veniam dolore in, quod saepe reiciendis nihil.</p>
|
||||
</md-board>
|
||||
</md-boards>
|
||||
</code-block>
|
||||
</div>
|
||||
</example-box>
|
||||
</div>
|
||||
</docs-component>
|
||||
</page-content>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.md-boards-content {
|
||||
height: 150px !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -21,6 +21,7 @@ const InkRipple = (r) => require.ensure([], () => r(require('./pages/components/
|
|||
const Input = (r) => require.ensure([], () => r(require('./pages/components/Input')), 'input');
|
||||
const List = (r) => require.ensure([], () => r(require('./pages/components/List')), 'list');
|
||||
const Menu = (r) => require.ensure([], () => r(require('./pages/components/Menu')), 'menu');
|
||||
const Onboarding = (r) => require.ensure([], () => r(require('./pages/components/Onboarding')), 'onboarding');
|
||||
const Progress = (r) => require.ensure([], () => r(require('./pages/components/Progress')), 'progress');
|
||||
const Radio = (r) => require.ensure([], () => r(require('./pages/components/Radio')), 'radio');
|
||||
const Select = (r) => require.ensure([], () => r(require('./pages/components/Select')), 'select');
|
||||
|
|
@ -148,6 +149,11 @@ const components = [
|
|||
name: 'components:menu',
|
||||
component: Menu
|
||||
},
|
||||
{
|
||||
path: '/components/onboarding',
|
||||
name: 'components:onboarding',
|
||||
component: Onboarding
|
||||
},
|
||||
{
|
||||
path: '/components/progress',
|
||||
name: 'components:progress',
|
||||
|
|
|
|||
10
src/components/mdOnboarding/index.js
Normal file
10
src/components/mdOnboarding/index.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import mdBoards from './mdBoards.vue';
|
||||
import mdBoard from './mdBoard.vue';
|
||||
import mdBoardsTheme from './mdBoards.theme';
|
||||
|
||||
export default function install(Vue) {
|
||||
Vue.component('md-boards', mdBoards);
|
||||
Vue.component('md-board', mdBoard);
|
||||
|
||||
Vue.material.styles.push(mdBoardsTheme);
|
||||
}
|
||||
105
src/components/mdOnboarding/mdBoard.vue
Normal file
105
src/components/mdOnboarding/mdBoard.vue
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<template>
|
||||
<div class="md-board" :id="boardId" :style="styles">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniqueId from '../../core/utils/uniqueId';
|
||||
import getClosestVueParent from '../../core/utils/getClosestVueParent';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
id: [String, Number],
|
||||
mdLabel: [String, Number],
|
||||
mdIcon: String,
|
||||
mdActive: Boolean,
|
||||
mdDisabled: Boolean,
|
||||
mdTooltip: String,
|
||||
mdTooltipDelay: {
|
||||
type: String,
|
||||
default: '0'
|
||||
},
|
||||
mdTooltipDirection: {
|
||||
type: String,
|
||||
default: 'bottom'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mounted: false,
|
||||
boardId: this.id || 'board-' + uniqueId(),
|
||||
width: '0px',
|
||||
left: '0px'
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
mdActive() {
|
||||
this.updateBoardData();
|
||||
},
|
||||
mdDisabled() {
|
||||
this.updateBoardData();
|
||||
},
|
||||
mdIcon() {
|
||||
this.updateBoardData();
|
||||
},
|
||||
mdLabel() {
|
||||
this.updateBoardData();
|
||||
},
|
||||
mdTooltip() {
|
||||
this.updateBoardData();
|
||||
},
|
||||
mdTooltipDelay() {
|
||||
this.updateBoardData();
|
||||
},
|
||||
mdTooltipDirection() {
|
||||
this.updateBoardData();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
styles() {
|
||||
return {
|
||||
width: this.width,
|
||||
left: this.left
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getBoardData() {
|
||||
return {
|
||||
id: this.boardId,
|
||||
label: this.mdLabel,
|
||||
icon: this.mdIcon,
|
||||
active: this.mdActive,
|
||||
disabled: this.mdDisabled,
|
||||
tooltip: this.mdTooltip,
|
||||
tooltipDelay: this.mdTooltipDelay,
|
||||
tooltipDirection: this.mdTooltipDirection,
|
||||
ref: this
|
||||
};
|
||||
},
|
||||
updateBoardData() {
|
||||
this.parentBoards.updateBoard(this.getBoardData());
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let boardData = this.getBoardData();
|
||||
|
||||
this.parentBoards = getClosestVueParent(this.$parent, 'md-boards');
|
||||
|
||||
if (!this.parentBoards) {
|
||||
throw new Error('You must wrap the md-board in a md-boards');
|
||||
}
|
||||
|
||||
this.mounted = true;
|
||||
this.parentBoards.updateBoard(boardData);
|
||||
|
||||
if (this.mdActive) {
|
||||
this.parentBoards.setActiveBoard(boardData);
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.parentBoards.unregisterBoard(this.getBoardData());
|
||||
}
|
||||
};
|
||||
</script>
|
||||
105
src/components/mdOnboarding/mdBoards.scss
Normal file
105
src/components/mdOnboarding/mdBoards.scss
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
@import '../../core/stylesheets/variables.scss';
|
||||
|
||||
$board-width: 24px;
|
||||
$board-max-width: 24px;
|
||||
|
||||
.md-boards {
|
||||
width: 100%;
|
||||
height: 100% !important;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
position: relative;
|
||||
|
||||
&.md-transition-off * {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
&.md-dynamic-height {
|
||||
.md-boards-content {
|
||||
transition: height $swift-ease-out-duration $swift-ease-out-timing-function;
|
||||
}
|
||||
}
|
||||
|
||||
.md-boards-navigation {
|
||||
position: absolute !important;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
min-height: 48px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
transition: $swift-ease-out;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.md-board-header {
|
||||
min-width: $board-width;
|
||||
max-width: $board-max-width;
|
||||
margin: 0;
|
||||
padding: 0 12px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
background: none;
|
||||
transition: $swift-ease-out;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
|
||||
&.md-disabled {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
}
|
||||
|
||||
.md-board-header-container {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.md-icon {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.md-icon:not(.md-control) {
|
||||
width: 16px;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
min-height: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.md-boards-content {
|
||||
width: 100%;
|
||||
height: 100% !important;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.md-boards-wrapper {
|
||||
width: 9999em;
|
||||
height: 100% !important;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
transform: translate3d(0, 0, 0);
|
||||
transition: transform $swift-ease-out-duration $swift-ease-out-timing-function;
|
||||
}
|
||||
|
||||
.md-board {
|
||||
padding: 16px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
116
src/components/mdOnboarding/mdBoards.theme
Normal file
116
src/components/mdOnboarding/mdBoards.theme
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
.THEME_NAME {
|
||||
&.md-boards {
|
||||
> .md-boards-navigation {
|
||||
background-color: transparent;
|
||||
|
||||
.md-board-header {
|
||||
color: #{'BACKGROUND-CONTRAST-0.54'};
|
||||
|
||||
&.md-active,
|
||||
&:focus {
|
||||
color: #{'PRIMARY-COLOR'};
|
||||
}
|
||||
|
||||
&.md-disabled {
|
||||
color: #{'BACKGROUND-CONTRAST-0.26'}
|
||||
}
|
||||
}
|
||||
|
||||
.md-button {
|
||||
color: #{'PRIMARY-COLOR'};
|
||||
}
|
||||
}
|
||||
|
||||
&.md-transparent {
|
||||
> .md-boards-navigation {
|
||||
background-color: transparent;
|
||||
|
||||
.md-board-header {
|
||||
color: #{'PRIMARY-CONTRAST-0.54'};
|
||||
|
||||
&.md-active,
|
||||
&:focus {
|
||||
color: #{'PRIMARY-CONTRAST'};
|
||||
}
|
||||
|
||||
&.md-disabled {
|
||||
color: #{'PRIMARY-CONTRAST-0.26'}
|
||||
}
|
||||
}
|
||||
|
||||
.md-button {
|
||||
color: #{'PRIMARY-CONTRAST-0.54'};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.md-primary {
|
||||
> .md-boards-navigation {
|
||||
background-color: #{'PRIMARY-COLOR'};
|
||||
|
||||
.md-board-header {
|
||||
color: #{'PRIMARY-CONTRAST-0.54'};
|
||||
|
||||
&.md-active,
|
||||
&:focus {
|
||||
color: #{'PRIMARY-CONTRAST'};
|
||||
}
|
||||
|
||||
&.md-disabled {
|
||||
color: #{'PRIMARY-CONTRAST-0.26'}
|
||||
}
|
||||
}
|
||||
|
||||
.md-button {
|
||||
color: #{'PRIMARY-CONTRAST-0.54'};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.md-accent {
|
||||
> .md-boards-navigation {
|
||||
background-color: #{'ACCENT-COLOR'};
|
||||
|
||||
.md-board-header {
|
||||
color: #{'ACCENT-CONTRAST-0.54'};
|
||||
|
||||
&.md-active,
|
||||
&:focus {
|
||||
color: #{'ACCENT-CONTRAST'};
|
||||
}
|
||||
|
||||
&.md-disabled {
|
||||
color: #{'ACCENT-CONTRAST-0.26'}
|
||||
}
|
||||
}
|
||||
|
||||
.md-button {
|
||||
color: #{'ACCENT-CONTRAST-0.54'};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.md-warn {
|
||||
> .md-boards-navigation {
|
||||
background-color: #{'WARN-COLOR'};
|
||||
|
||||
.md-board-header {
|
||||
color: #{'WARN-CONTRAST-0.54'};
|
||||
|
||||
&.md-active,
|
||||
&:focus {
|
||||
color: #{'WARN-CONTRAST'};
|
||||
}
|
||||
|
||||
&.md-disabled {
|
||||
color: #{'WARN-CONTRAST-0.26'}
|
||||
}
|
||||
}
|
||||
|
||||
.md-button {
|
||||
color: #{'WARN-CONTRAST-0.54'};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
362
src/components/mdOnboarding/mdBoards.vue
Normal file
362
src/components/mdOnboarding/mdBoards.vue
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
<template>
|
||||
<div class="md-boards" :class="[themeClass, boardClasses]">
|
||||
|
||||
<div class="md-boards-content" ref="boardsContent" :style="{ height: contentHeight }">
|
||||
<div class="md-boards-wrapper" :style="{ transform: `translate3D(-${contentWidth}, 0, 0)` }">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="md-boards-navigation" :class="navigationClasses" ref="boardNavigation">
|
||||
|
||||
<span style="flex: 1" v-if="!mdControls"></span>
|
||||
|
||||
<md-button
|
||||
v-if="mdControls"
|
||||
@click.native="movePrevBoard()">
|
||||
<div class="md-board-header-container">
|
||||
<md-icon class="md-control">chevron_left</md-icon>
|
||||
</div>
|
||||
</md-button>
|
||||
|
||||
<span style="flex: 1"></span>
|
||||
|
||||
<button
|
||||
v-for="header in boardList"
|
||||
:key="header.id"
|
||||
type="button"
|
||||
class="md-board-header"
|
||||
:class="getHeaderClass(header)"
|
||||
:disabled="header.disabled"
|
||||
@click="setActiveBoard(header, true)"
|
||||
ref="boardHeader">
|
||||
<div class="md-board-header-container">
|
||||
<md-icon>fiber_manual_record</md-icon>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<span style="flex: 1"></span>
|
||||
|
||||
<md-button
|
||||
v-if="mdControls"
|
||||
@click.native="moveNextBoard()">
|
||||
<div class="md-board-header-container">
|
||||
<md-icon class="md-control">chevron_right</md-icon>
|
||||
</div>
|
||||
</md-button>
|
||||
|
||||
<span style="flex: 1" v-if="!mdControls"></span>
|
||||
|
||||
<span ref="indicator"></span>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" src="./mdBoards.scss"></style>
|
||||
|
||||
<script>
|
||||
import theme from '../../core/components/mdTheme/mixin';
|
||||
import throttle from '../../core/utils/throttle';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
mdFixed: Boolean,
|
||||
mdCentered: Boolean,
|
||||
mdRight: Boolean,
|
||||
mdDynamicHeight: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
mdElevation: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
mdAuto: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
mdDuration: {
|
||||
type: Number,
|
||||
default: 5000
|
||||
},
|
||||
mdControls: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
mdInfinite: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
mdSwipeable: Boolean,
|
||||
mdSwipeDistance: {
|
||||
type: Number,
|
||||
default: 100
|
||||
}
|
||||
},
|
||||
mixins: [theme],
|
||||
data: () => ({
|
||||
boardList: {},
|
||||
activeBoard: null,
|
||||
activeBoardNumber: 0,
|
||||
hasIcons: false,
|
||||
hasLabel: false,
|
||||
transitionControl: null,
|
||||
transitionOff: false,
|
||||
contentHeight: '0px',
|
||||
contentWidth: '0px',
|
||||
autoTransition: null
|
||||
}),
|
||||
computed: {
|
||||
boardClasses() {
|
||||
return {
|
||||
'md-dynamic-height': this.mdDynamicHeight,
|
||||
'md-transition-off': this.transitionOff
|
||||
};
|
||||
},
|
||||
navigationClasses() {
|
||||
return {
|
||||
'md-has-icon': this.hasIcons,
|
||||
'md-has-label': this.hasLabel,
|
||||
'md-fixed': this.mdFixed,
|
||||
'md-right': !this.mdCentered && this.mdRight,
|
||||
'md-centered': this.mdCentered || this.mdFixed
|
||||
};
|
||||
},
|
||||
indicatorClasses() {
|
||||
let toLeft = this.lastIndicatorNumber > this.activeBoardNumber;
|
||||
|
||||
this.lastIndicatorNumber = this.activeBoardNumber;
|
||||
|
||||
return {
|
||||
'md-transition-off': this.transitionOff,
|
||||
'md-to-right': !toLeft,
|
||||
'md-to-left': toLeft
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getHeaderClass(header) {
|
||||
return {
|
||||
'md-active': this.activeBoard === header.id,
|
||||
'md-disabled': header.disabled
|
||||
};
|
||||
},
|
||||
registerBoard(boardData) {
|
||||
this.boardList[boardData.id] = boardData;
|
||||
},
|
||||
unregisterBoard(boardData) {
|
||||
delete this.boardList[boardData.id];
|
||||
},
|
||||
updateBoard(boardData) {
|
||||
this.registerBoard(boardData);
|
||||
|
||||
if (boardData.active) {
|
||||
if (!boardData.disabled) {
|
||||
this.setActiveBoard(boardData);
|
||||
} else if (Object.keys(this.boardList).length) {
|
||||
let boardsIds = Object.keys(this.boardList);
|
||||
let targetIndex = boardsIds.indexOf(boardData.id) + 1;
|
||||
let target = boardsIds[targetIndex];
|
||||
|
||||
if (target) {
|
||||
this.setActiveBoard(this.boardList[target]);
|
||||
} else {
|
||||
this.setActiveBoard(this.boardList[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
observeElementChanges() {
|
||||
this.parentObserver = new MutationObserver(throttle(this.calculateOnWatch, 50));
|
||||
this.parentObserver.observe(this.$refs.boardsContent, {
|
||||
childList: true,
|
||||
attributes: true,
|
||||
subtree: true
|
||||
});
|
||||
},
|
||||
getBoardIndex(id) {
|
||||
const idList = Object.keys(this.boardList);
|
||||
|
||||
return idList.indexOf(id);
|
||||
},
|
||||
calculateIndicatorPos() {
|
||||
if (this.$refs.boardHeader && this.$refs.boardHeader[this.activeBoardNumber]) {
|
||||
const boardsWidth = this.$el.offsetWidth;
|
||||
const activeBoard = this.$refs.boardHeader[this.activeBoardNumber];
|
||||
const left = activeBoard.offsetLeft;
|
||||
const right = boardsWidth - left - activeBoard.offsetWidth;
|
||||
|
||||
this.$refs.indicator.style.left = left + 'px';
|
||||
this.$refs.indicator.style.right = right + 'px';
|
||||
}
|
||||
},
|
||||
calculateBoardsWidthAndPosition() {
|
||||
const width = this.$el.offsetWidth;
|
||||
let index = 0;
|
||||
|
||||
this.contentWidth = width * this.activeBoardNumber + 'px';
|
||||
|
||||
for (const boardId in this.boardList) {
|
||||
const board = this.boardList[boardId];
|
||||
|
||||
board.ref.width = width + 'px';
|
||||
board.ref.left = width * index + 'px';
|
||||
index++;
|
||||
}
|
||||
},
|
||||
calculateContentHeight() {
|
||||
this.$nextTick(() => {
|
||||
if (Object.keys(this.boardList).length) {
|
||||
let height = this.boardList[this.activeBoard].ref.$el.offsetHeight;
|
||||
|
||||
this.contentHeight = height + 'px';
|
||||
}
|
||||
});
|
||||
},
|
||||
calculatePosition() {
|
||||
window.requestAnimationFrame(() => {
|
||||
this.calculateIndicatorPos();
|
||||
this.calculateBoardsWidthAndPosition();
|
||||
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();
|
||||
},
|
||||
start() {
|
||||
if (this.autoTransition) {
|
||||
window.clearInterval(this.autoTransition);
|
||||
}
|
||||
this.autoTransition = window.setInterval(() => {
|
||||
this.moveNextBoard();
|
||||
}, this.mdDuration);
|
||||
},
|
||||
setActiveBoard(boardData, reset) {
|
||||
if (this.mdAuto && reset) {
|
||||
this.start();
|
||||
}
|
||||
this.hasIcons = !!boardData.icon;
|
||||
this.hasLabel = !!boardData.label;
|
||||
this.activeBoard = boardData.id;
|
||||
this.activeBoardNumber = this.getBoardIndex(this.activeBoard);
|
||||
this.calculatePosition();
|
||||
this.$emit('change', this.activeBoardNumber);
|
||||
},
|
||||
movePrevBoard() {
|
||||
let boardsIds = Object.keys(this.boardList);
|
||||
let targetIndex = boardsIds.indexOf(this.activeBoard) - 1;
|
||||
let target = boardsIds[targetIndex];
|
||||
|
||||
if (target) {
|
||||
this.setActiveBoard(this.boardList[target], true);
|
||||
} else if (this.mdInfinite) {
|
||||
let lastBoard = Object.keys(this.boardList)[Object.keys(this.boardList).length - 1];
|
||||
|
||||
this.setActiveBoard(this.boardList[lastBoard], true);
|
||||
}
|
||||
},
|
||||
moveNextBoard() {
|
||||
let boardsIds = Object.keys(this.boardList);
|
||||
let targetIndex = boardsIds.indexOf(this.activeBoard) + 1;
|
||||
let target = boardsIds[targetIndex];
|
||||
|
||||
if (target) {
|
||||
this.setActiveBoard(this.boardList[target], true);
|
||||
} else if (this.mdInfinite) {
|
||||
let firstBoard = Object.keys(this.boardList)[0];
|
||||
|
||||
this.setActiveBoard(this.boardList[firstBoard], true);
|
||||
}
|
||||
},
|
||||
isHorizontallyInside(positionX) {
|
||||
return positionX > this.mountedRect.left && positionX < this.mountedRect.left + this.mountedRect.width;
|
||||
},
|
||||
isVerticallyInside(positionY) {
|
||||
return positionY > this.mountedRect.top && positionY < this.mountedRect.top + this.mountedRect.height;
|
||||
},
|
||||
handleTouchStart(event) {
|
||||
this.mountedRect = this.$refs.boardsContent.getBoundingClientRect();
|
||||
const positionX = event.changedTouches[0].clientX;
|
||||
const positionY = event.changedTouches[0].clientY;
|
||||
|
||||
if (this.isHorizontallyInside(positionX) && this.isVerticallyInside(positionY)) {
|
||||
this.initialTouchPosition = positionX;
|
||||
this.canMove = true;
|
||||
}
|
||||
},
|
||||
handleTouchEnd(event) {
|
||||
if (this.canMove) {
|
||||
const positionX = event.changedTouches[0].clientX;
|
||||
|
||||
const difference = this.initialTouchPosition - positionX;
|
||||
|
||||
const action = difference > 0
|
||||
? 'moveNextBoard'
|
||||
: 'movePrevBoard';
|
||||
|
||||
if (Math.abs(difference) > this.mdSwipeDistance) {
|
||||
this[action]();
|
||||
}
|
||||
|
||||
this.canMove = false;
|
||||
this.initialTouchPosition = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.observeElementChanges();
|
||||
window.addEventListener('resize', this.calculateOnResize);
|
||||
|
||||
if (Object.keys(this.boardList).length && !this.activeBoard) {
|
||||
let firstBoard = Object.keys(this.boardList)[0];
|
||||
|
||||
this.setActiveBoard(this.boardList[firstBoard]);
|
||||
}
|
||||
|
||||
if (this.mdSwipeable) {
|
||||
this.mountedRect = this.$refs.boardsContent.getBoundingClientRect();
|
||||
this.initialTouchPosition = null;
|
||||
this.canMove = false;
|
||||
|
||||
document.addEventListener('touchstart', this.handleTouchStart);
|
||||
document.addEventListener('touchend', this.handleTouchEnd);
|
||||
}
|
||||
|
||||
if (this.mdAuto) {
|
||||
this.start();
|
||||
}
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.parentObserver) {
|
||||
this.parentObserver.disconnect();
|
||||
}
|
||||
|
||||
if (this.autoTransition) {
|
||||
window.clearTimeout(this.autoTransition);
|
||||
}
|
||||
|
||||
window.removeEventListener('resize', this.calculateOnResize);
|
||||
|
||||
if (this.mdSwipeable) {
|
||||
document.removeEventListener('touchstart', this.handleTouchStart);
|
||||
document.removeEventListener('touchend', this.handleTouchEnd);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -16,6 +16,7 @@ import MdInputContainer from './components/mdInputContainer';
|
|||
import MdLayout from './components/mdLayout';
|
||||
import MdList from './components/mdList';
|
||||
import MdMenu from './components/mdMenu';
|
||||
import MdOnboarding from './components/mdOnboarding';
|
||||
import MdProgress from './components/mdProgress';
|
||||
import MdRadio from './components/mdRadio';
|
||||
import MdSelect from './components/mdSelect';
|
||||
|
|
@ -50,6 +51,7 @@ const options = {
|
|||
MdLayout,
|
||||
MdList,
|
||||
MdMenu,
|
||||
MdOnboarding,
|
||||
MdProgress,
|
||||
MdRadio,
|
||||
MdSelect,
|
||||
|
|
|
|||
Loading…
Reference in a new issue