LeftSideBar

This commit is contained in:
2020-09-01 12:55:00 +03:00
parent 2357418ee9
commit 0ef02edde9
28 changed files with 644 additions and 32 deletions

View File

@@ -1,32 +1,227 @@
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
<div class="left-panel"
:class="{'left-panel-closed': leftPanelClosed}">
<LeftPanel></LeftPanel>
</div>
<div class="content w-100"
:class="{'content-when-left-panel-open': !leftPanelClosed}">
<div class="vertical-panel"
:class="{'vertical-panel-when-left-panel-open': !leftPanelClosed}"></div>
<div class="close-btn"
:class="{'close-btn-when-left-panel-open': !leftPanelClosed,
'close-btn-hover': leftPanelClosed}"
@click="switchLeftPanel()">
<LottieAnimation
class="close-btn-icon"
path="./animations/menu-close2.json"
:width="30"
:autoPlay="false"
:loop="false"
@AnimControl="setAnimController"
ref="close-icon"></LottieAnimation>
<span class="close-btn-label"
:class="{'close-btn-label-when-left-panel-open': !leftPanelClosed}">MENU</span>
</div>
<router-view class="w-100 content-body"
:class="{'content-body-when-left-panel-open': !leftPanelClosed}"
style="min-height: 100vh"
:leftPanelClosed="leftPanelClosed"/>
</div>
<router-view/>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Ref } from 'vue-property-decorator';
import LeftPanel from '@/components/LeftPanel.vue'
import LottieAnimation from "lottie-vuejs/src/LottieAnimation.vue";
interface AnimData {
direction: number;
currentTime: number;
totalTime: number;
}
interface IAnimController {
addEventListener(event: string, foo: (data: any) => void): void;
play(): void;
pause(): void;
setSpeed(speed: number): void;
setDirection(direction: 1 | -1): void;
}
interface ILottieAnimation {
anim: IAnimController;
}
@Component({
components: {
LeftPanel,
LottieAnimation
},
})
export default class App extends Vue {
public leftPanelClosed: boolean = true;
@Ref('close-icon') public readonly closeIcon!: ILottieAnimation;
setAnimController(controller: IAnimController) {
controller.addEventListener("enterFrame", this.onEnterFrame)
}
onEnterFrame(data: AnimData) {
if (data.direction === 1 && data.currentTime >= data.totalTime / 2)
this.closeIcon.anim.pause();
}
switchLeftPanel() {
this.closeIcon.anim.setSpeed(1.2);
if (this.leftPanelClosed) {
this.closeIcon.anim.setDirection(1);
this.closeIcon.anim.play();
} else {
this.closeIcon.anim.setDirection(-1);
this.closeIcon.anim.play();
}
this.leftPanelClosed = !this.leftPanelClosed;
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700;900&family=Open+Sans&display=swap');
body, #app {
min-height: 100vh;
overflow-x: hidden;
font-family: 'Lato', sans-serif;
letter-spacing: 1px;
font-weight: 700;
background-color: black;
}
#nav {
padding: 30px;
.close-btn-hover:hover {
background-color: white!important;
}
#nav a {
font-weight: bold;
color: #2c3e50;
.vertical-panel {
position: absolute;
height: 0px;
width: 0px;
}
#nav a.router-link-exact-active {
color: #42b983;
.content-body {
transition: background-color linear .3s;
}
.content-body-when-left-panel-open {
transition: margin-left linear .3s;
margin-left: 58px;
opacity: 0.5;
}
@media (max-width: 961px) {
.close-btn {
position: absolute;
width: 100%;
height: 50px;
outline: none!important;
background-color: black!important;
transition: background-color linear .3s;
opacity: 1!important;
}
.close-btn-when-left-panel-open {
background-color: black!important;
transition: background-color linear .3s;
}
.close-btn-icon {
position: absolute;
left: 14px;
z-index: 2;
}
.close-btn-label {
position: absolute;
font-size: 13px;
font-weight: 400;
color: white;
top: 14px;
left: 60px;
}
.vertical-panel-when-left-panel-open {
position: absolute;
width: 58px;
height: 100%;
background: white!important;
z-index: 1;
}
.close-btn-label-when-left-panel-open {
color: black;
}
.content-when-left-panel-open {
margin-left: 192px;
}
}
@media (min-width: 960px) {
.close-btn {
position: absolute;
width: 58px;
height: 100%;
outline: none!important;
background-color: rgba(0, 0, 0, 0.7)!important;
transition: background-color linear .3s;
opacity: 1!important;
}
.close-btn-when-left-panel-open {
background-color: white!important;
transition: background-color linear .3s;
}
.close-btn-icon {
position: absolute;
left: 14px;
}
.close-btn-label {
position: absolute;
font-size: 12px;
font-weight: 300;
color: white;
top: calc(49% + 20px);
left: 10px;
}
.content-when-left-panel-open {
margin-left: 192px;
}
}
.left-panel {
position: absolute;
width: 192px;
background-color: white;
transition: margin-left linear .3s;
}
.left-panel-closed {
margin-left: -192px;
}
.content {
transition: margin-left linear .3s;
min-height: 100vh;
}
</style>

78
src/components/Album.vue Normal file
View File

@@ -0,0 +1,78 @@
<template>
<div ref="element" class="album"
:style="{'background-image': backgroundStyle,
'min-height': height}">
{{ data.name }}
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Ref } from 'vue-property-decorator';
export interface IAlbum {
name: string;
description: string;
protected: boolean;
folderName: string;
files: string[];
}
@Component
export default class Album extends Vue {
@Prop({required: true}) public readonly data!: IAlbum;
@Ref('element') public readonly element!: HTMLElement;
public height: string = '10px';
get backgroundStyle() {
return `url('pictures/albums/${this.data.folderName}/${this.data.files[1]}')`;
}
updateHeight(): void {
if (this.element === undefined)
return;
console.log("update");
this.height = `${this.element.clientWidth}px`;
}
created() {
window.addEventListener("resize", this.updateHeight);
}
destroyed() {
window.removeEventListener("resize", this.updateHeight);
}
mounted() {
this.updateHeight();
}
}
</script>
<style scoped>
.album {
background-size: cover;
}
@media (max-width: 461px) {
.album {
flex: 1 100%;
}
}
@media (min-width: 460px) and (max-width: 1401px) {
.album {
flex: 1 50%;
}
}
@media (min-width: 1400px) and (max-width: 1801px) {
.album {
flex: 1 33%;
}
}
</style>

View File

@@ -0,0 +1,93 @@
<template>
<div class="left-block text-center"
:style="{'background-image': backgroundStyle}">
<div class="text-block background"
:class="{'background-when-left-panel-opened': !leftPanelClosed}">
<p class="title">{{ title }}</p>
<p class="description">{{ description }}</p>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
@Component
export default class LeftBlock extends Vue {
@Prop({required: true}) public readonly title!: string;
@Prop({required: true}) public readonly description!: string;
@Prop({required: true}) public readonly picture!: string;
@Prop({required: true}) public readonly leftPanelClosed!: boolean;
get backgroundStyle(): string {
return `url('${this.picture}')`
}
}
</script>
<style scoped>
.left-block {
display: inline-block;
}
.background {
background-color: rgba(0, 0, 0, 0.5);
transition: background-color linear .3s;
}
.background-when-left-panel-opened {
background-color: rgba(0, 0, 0, 1);
transition: background-color linear .3s;
}
@media (max-width: 961px) {
.left-block {
background-position: center center;
background-size: cover;
width: 100%;
height: 300px;
}
.text-block {
padding-top: 130px;
}
.background {
width: 100%;
height: 300px;
}
}
@media (min-width: 960px) {
.left-block {
background-size: cover;
min-height: 100vh;
width: 405px;
}
.text-block {
padding-top: 43vh;
padding-left: 45px;
}
.background {
height: 100vh;
}
}
.title {
font-size: 32px;
color: white;
font-family: 'Open Sans', sans-serif;
}
.description {
font-size: 20px;
color: white;
font-family: 'Open Sans', sans-serif;
font-weight: 400;
}
</style>

View File

@@ -0,0 +1,122 @@
<template>
<div>
<div class="left-panel-background">
</div>
<div class="main-column links text-center">
<div class="link-wrapper">
<router-link class="link"
:class="{'active-link': isActiveLink('Home')}"
:to="{'name': 'Home'}">HOME</router-link>
</div>
<div class="link-wrapper">
<router-link class="link"
:class="{'active-link': isActiveLink('About Me')}"
:to="{'name': 'About Me'}">ABOUT ME</router-link>
</div>
</div>
<div class="main-column socials text-center">
<div class="row no-gutters">
<div class="col-12">
<div class="social">
<i class="fa fa-twitter"></i>
</div>
<div class="social">
<i class="fa fa-facebook"></i>
</div>
<div class="social">
<i class="fa fa-flickr"></i>
</div>
<div class="social">
<i class="fa fa-instagram"></i>
</div>
</div>
</div>
</div>
<div class="main-column copyright text-center">
<span>© Kreorg</span>
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
@Component
export default class LeftPanel extends Vue {
public closed: boolean = true;
isActiveLink(name: string): boolean {
return this.$route.name === name;
}
}
</script>
<style scoped>
.left-panel-background {
background-color: white;
height: 100vh;
}
.main-column {
width: 142px;
position: absolute;
left: 58px;
}
.no-gutters {
padding: 0;
}
.links {
top: 45vh;
}
.socials {
top: 63vh
}
.social {
display: inline-block;
width: 25px;
height: 25px;
padding: 1px 1px;
border-radius: 100%;
font-size: 15px;
text-align: center;
margin-right: 3px;
margin-left: 3px;
color: white;
background-color: black;
}
.copyright {
position: absolute;
top: 93vh;
font-size: 13px;
font-family: 'Open Sans', sans-serif;
font-weight: 400;
letter-spacing: 0px;
}
.link {
font-size: 14px;
color: #999999;
text-decoration: none;
}
.link:hover {
color: black;
}
.active-link {
font-weight: 900;
color: black;
}
.link-wrapper {
padding-top: 8px;
padding-bottom: 8px;
}
</style>

View File

@@ -1,6 +1,8 @@
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import Home from '../views/Home.vue'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
Vue.use(VueRouter)
@@ -12,11 +14,8 @@ Vue.use(VueRouter)
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
name: 'About Me',
component: About
}
]

View File

@@ -1,18 +1,88 @@
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<div>
<LeftBlock title="HOME" description="kreorg photos" picture="/pictures/home.jpg" :leftPanelClosed="leftPanelClosed"></LeftBlock>
<div class="albums-wrapper">
<div class="albums">
<Album v-for="album in albums" :key="album.folderName" :data="album"></Album>
</div>
</div>
</div>
</template>
<script>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
export default {
import LeftBlock from '@/components/LeftBlock.vue';
import Album, { IAlbum } from '@/components/Album.vue';
@Component({
name: 'Home',
components: {
HelloWorld
LeftBlock,
Album,
}
})
export default class Home extends Vue {
@Prop({required: true}) public readonly leftPanelClosed!: boolean;
public albums: IAlbum[] = [
{
name: 'Test',
description: 'Test description',
protected: false,
folderName: 'test',
files: [
'p (1).jpg',
'p (2).jpg',
'p (3).jpg',
'p (4).jpg',
'p (5).jpg',
'p (6).jpg',
]
},
{
name: 'Test 2',
description: 'Test 2 description',
protected: false,
folderName: 'test2',
files: [
'p (1).jpg',
'p (2).jpg',
'p (3).jpg',
'p (4).jpg',
'p (5).jpg',
]
},
{
name: 'Test 3',
description: 'Test 3 description',
protected: false,
folderName: 'test3',
files: [
'p (1).jpg',
'p (2).jpg',
'p (3).jpg',
'p (4).jpg',
'p (5).jpg',
]
}
];
}
</script>
<style scoped>
.albums-wrapper {
display: inline-block;
width: calc(100vw - 425px);
vertical-align: top;
}
.albums {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
</style>