This commit is contained in:
masnn committed 2020-04-08 21:10:24 +08:00
1 parent 777a4bc927
commit 9c45527522
396 files changed
+13772

No files matched your search

+151
View File
@@ -0,0 +1,151 @@
import _ from 'lodash';
import DOMAttachedObject from 'vj/components/DOMAttachedObject';
const TAB_TRANSITION_DURATION = 300;
export default class Tab extends DOMAttachedObject {
static DOMAttachKey = 'vjTabInstance';
static initAll() {
$('.section__tabs').get().forEach((tab) => {
Tab.getOrConstruct($(tab));
});
}
static initEventListeners() {
$(document).on('click', '.section__tab-header-item', (ev) => {
const targetIndex = $(ev.currentTarget).attr('data-tab-index');
const $container = $(ev.currentTarget).closest('.section__tab-container');
const tabInstance = Tab.get($container);
tabInstance.switchToTab(parseInt(targetIndex, 10));
});
}
constructor($dom) {
super($dom);
this.attached = false;
this.attach();
}
async switchToTab(idx) {
if (idx === this.currentIndex) {
return;
}
if (this.isAnimating) {
return;
}
const $tabs = this.$content.children();
const $currentTab = $tabs.eq(this.currentIndex);
const $newTab = $tabs.eq(idx);
// 0. Change header
this.$header
.children()
.removeClass('selected')
.eq(idx)
.addClass('selected');
// 1. Prepare for animating
this.isAnimating = true;
$newTab.addClass('active');
const animateParameter = {};
if (idx < this.currentIndex) {
animateParameter.from = '-100%';
animateParameter.to = '0%';
} else {
animateParameter.from = '0%';
animateParameter.to = '-100%';
}
$newTab
.css('opacity', 0);
this.$content
.css('x', animateParameter.from)
.width();
// 2. Animate transition
$currentTab
.transition({
opacity: 0,
}, {
duration: TAB_TRANSITION_DURATION,
easing: 'linear',
});
$newTab
.transition({
opacity: 1,
}, {
duration: TAB_TRANSITION_DURATION,
easing: 'linear',
});
await this.$content
.transition({
x: animateParameter.to,
}, {
duration: TAB_TRANSITION_DURATION,
easing: 'easeOutCubic',
})
.promise();
// 3. Hide previous content
this.$content
.children()
.eq(this.currentIndex)
.removeClass('active');
this.$content
.css('x', '0');
// 4. Finalize
this.currentIndex = idx;
this.isAnimating = false;
}
attach() {
if (this.attached) {
return false;
}
const $container = this.$dom
.closest('.section__tab-container');
const $headerWrapper = $(document.createElement('div'))
.addClass('section__tab-header-wrapper')
.appendTo($container);
const $contentWrapper = $(document.createElement('div'))
.addClass('section__tab-content-wrapper')
.appendTo($container);
this.$header = $(document.createElement('ul'))
.addClass('section__tab-header')
.attr('data-slideout-ignore', 'on')
.appendTo($headerWrapper);
this.$content = $(document.createElement('div'))
.addClass('section__tab-content')
.appendTo($contentWrapper);
this.$dom.find('.section__tab-title').get().forEach((element, idx) => {
$(document.createElement('li')).text($(element).text())
.addClass('section__tab-header-item')
.attr('data-tab-index', idx)
.appendTo(this.$header);
});
this.$dom.find('.section__tab-main')
.appendTo(this.$content);
this.$dom.remove();
this.$dom = $container;
this.currentIndex = 0;
this.$content
.children()
.eq(0)
.addClass('active');
this.$header
.children()
.eq(0)
.addClass('selected');
return true;
}
}
_.assign(Tab, DOMAttachedObject);
+9
View File
@@ -0,0 +1,9 @@
import { AutoloadPage } from 'vj/misc/PageLoader';
import Tab from './Tab';
const tabPage = new AutoloadPage('tabPage', () => {
Tab.initAll();
Tab.initEventListeners();
});
export default tabPage;
+89
View File
@@ -0,0 +1,89 @@
@import './var.inc.styl'
// No JavaScript
.section__tab-item
margin: rem(20px) 0
border-bottom: 1px solid #DDD
padding: 0 rem($section-gap-h) rem($section-gap-v)
.section__tab-title
font-size: rem($font-size-title)
color: #888
margin-bottom: 0.8em
// Has JavaScript
.section__tab-header-wrapper
overflow: hidden
.section__tab-header
padding: 0 rem($section-gap-h)
padding-top: rem($tab-item-shadow-size)
overflow-x: auto
white-space: nowrap
padding-bottom: 20px
margin-bottom: -20px
+mobile()
padding-top: 0
padding-right: rem($section-gap-h + 50px)
webkit-overflow-scrolling: touch
mask-image: linear-gradient(to right, rgba(0,0,0,1) 80%, rgba(0,0,0,0));
.section__tab-header-item
display: inline-block
font-size: rem($font-size-secondary)
line-height: rem($tab-item-height)
height: rem($tab-item-height)
padding: 0 rem(20px)
margin-right: rem(10px)
user-select: none
position: relative
+mobile()
line-height: rem($tab-item-height-mobile)
height: rem($tab-item-height-mobile)
.section__tab-container.immersive
.section__tab-header-item
color: rgba(#FFF, 0.9)
border-radius: 1px 1px 0 0
cursor: pointer
transition: background .2s ease-out-cubic
&:hover
color: #FFF
&.selected
background: #FFF
color: #666
box-shadow: 0 0 10px rgba(#000, 0.3)
+mobile()
&.selected
box-shadow: none
background: transparent
color: #FFF
&::after
position: absolute
left: 0
width: 100%
bottom: 0
height: 3px
background: $secondary-color
content: ''
.section__tab-content-wrapper
overflow: hidden
.section__tab-content
position: relative
white-space: nowrap
.section__tab-main
display: none
vertical-align: top
width: 100%
&.active
display: inline-block
+3
View File
@@ -0,0 +1,3 @@
$tab-item-height = 35px
$tab-item-shadow-size = 10px
$tab-item-height-mobile = 32px // 32 * (13 / 16) is an integer