feat: create the skill picker page
This commit is contained in:
parent
d6ab16026d
commit
d136b9d1e2
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<Home/>
|
||||
<SkillPicker/>
|
||||
<Timeline/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -9,11 +10,13 @@
|
|||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import Home from './components/Home.vue';
|
||||
import Timeline from './components/Timeline.vue';
|
||||
import SkillPicker from './components/SkillPicker.vue';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
Home,
|
||||
Timeline,
|
||||
SkillPicker,
|
||||
},
|
||||
})
|
||||
export default class App extends Vue {
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
width: $width;
|
||||
height: $height;
|
||||
|
||||
background: #07142d;
|
||||
background: #202228;
|
||||
}
|
||||
|
||||
@keyframes wave-x {
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
<template>
|
||||
<div id='skill-picker'>
|
||||
|
||||
<div class='container'>
|
||||
|
||||
<section class='categories'>
|
||||
<SkillCard v-for='(t) of tags'
|
||||
:key='t'
|
||||
:id='t'
|
||||
:active='t == tag'
|
||||
folder='1'
|
||||
width='18rem'
|
||||
@pick='onTag(t, $event)'/>
|
||||
</section>
|
||||
|
||||
<section class='skills'>
|
||||
<SkillCard v-for='(id) of ids'
|
||||
:key='id'
|
||||
v-show='hide.indexOf(id) < 0'
|
||||
:id='id'
|
||||
:active='id == sel'
|
||||
width='18rem'
|
||||
@pick='onPick(id, $event)' />
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<button v-show='this.sel != null'>Browse projects</button>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from 'vue-property-decorator';
|
||||
import SkillCard from './SkillCard.vue';
|
||||
import { tID, Skills } from '@/model/skills';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
SkillCard,
|
||||
},
|
||||
})
|
||||
export default class SkillPicker extends Vue {
|
||||
// list of available skills
|
||||
readonly ids: string[] = Object.keys(Skills);
|
||||
// currently selected skill
|
||||
private sel: string|null = tID.Vue.toString();
|
||||
|
||||
// list of ids to hide according to the current tag
|
||||
private hide: string[] = [];
|
||||
|
||||
// available categories (tags)
|
||||
private tags: string[] = [];
|
||||
// currently selected tag
|
||||
private tag: string|null = "web";
|
||||
|
||||
private mounted() {
|
||||
this.tags = this.fetchTags();
|
||||
this.filterByTag();
|
||||
}
|
||||
|
||||
private fetchTags(): string[] {
|
||||
const tags: string[] = [];
|
||||
|
||||
for( const sid of this.ids ){
|
||||
const id = parseInt(sid) as tID;
|
||||
const skill = Skills[id];
|
||||
if( skill == undefined ){
|
||||
continue;
|
||||
}
|
||||
for( const tag of skill.tags ){
|
||||
if( tags.indexOf(tag) < 0 ){
|
||||
tags.push(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
private onPick(id: string, picked: boolean){
|
||||
if( picked ){ // select
|
||||
this.sel = id;
|
||||
return;
|
||||
}
|
||||
if( !picked && id == this.sel ){ // deselect
|
||||
this.sel = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
private onTag(t: string, picked: boolean){
|
||||
if( picked ){ // select
|
||||
this.tag = t;
|
||||
this.filterByTag();
|
||||
return;
|
||||
}
|
||||
if( !picked && t == this.tag ){ // deselect
|
||||
this.tag = null;
|
||||
this.filterByTag();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// apply filter by tag
|
||||
private filterByTag(){
|
||||
const tag = this.tag;
|
||||
|
||||
// no folder selected -> hide everything
|
||||
if( tag == null || this.tags.indexOf(tag) < 0 ){
|
||||
this.tag = null;
|
||||
this.hide = this.ids;
|
||||
|
||||
// deselect if selection has been filtered out
|
||||
if( this.sel != null && this.hide.indexOf(this.sel) >= 0 ){
|
||||
this.sel = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// clear filter
|
||||
this.hide = [];
|
||||
|
||||
for( const sid of this.ids ){
|
||||
const id = parseInt(sid) as tID;
|
||||
const skill = Skills[id];
|
||||
if( skill == undefined ){
|
||||
continue;
|
||||
}
|
||||
if( skill.tags.indexOf(tag) < 0 ){
|
||||
this.hide.push(sid);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// deselect if selection has been filtered out
|
||||
if( this.sel != null && this.hide.indexOf(this.sel) >= 0 ){
|
||||
this.sel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped lang="scss">
|
||||
|
||||
$page-margin: 3rem;
|
||||
|
||||
#skill-picker {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
background: linear-gradient(45deg, #564ba4, #745cfc);
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: calc( 100vw - #{2*$page-margin} );
|
||||
height: calc( 100vh - #{2*$page-margin} );
|
||||
margin: $page-margin;
|
||||
|
||||
background: #202228;
|
||||
|
||||
border-radius: 1em / 1em;
|
||||
|
||||
padding: 2em;
|
||||
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.categories {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 20em;
|
||||
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
overflow: hidden;
|
||||
overflow-y: visible;
|
||||
|
||||
.skill-card {
|
||||
margin: .3em 0;
|
||||
}
|
||||
}
|
||||
|
||||
.skills {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 20em;
|
||||
|
||||
flex-flow: column nowrap;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
overflow: hidden;
|
||||
overflow-y: visible;
|
||||
|
||||
.skill-card {
|
||||
margin: .3em 0;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: calc( 100% - #{$page-margin} );
|
||||
left: 50%;
|
||||
|
||||
padding: .6em 2em;
|
||||
|
||||
color: #202228;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 600;
|
||||
background: #fff;
|
||||
|
||||
border: none;
|
||||
border-radius: 2em;
|
||||
|
||||
box-shadow: 0 0 .5em transparent;
|
||||
|
||||
transform-style: preserve-3d;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
transition: box-shadow .2s ease-in-out,
|
||||
transform .2s ease-in-out,
|
||||
color .2s ease-in-out;
|
||||
|
||||
z-index: 10;
|
||||
|
||||
&:hover {
|
||||
color: #000;
|
||||
box-shadow: 0 0 .3em #191b1f;
|
||||
transform: translateX(-50%) translateY(-50%) scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -121,6 +121,7 @@
|
|||
|
||||
$icon-width: 2.3rem;
|
||||
$space-width: 1rem;
|
||||
$bg-color: #202228;
|
||||
|
||||
#timeline {
|
||||
display: grid;
|
||||
|
@ -134,7 +135,7 @@
|
|||
font-size: 1.6rem;
|
||||
font-weight: 400;
|
||||
color: #fff;
|
||||
background: #161619;
|
||||
background: $bg-color;
|
||||
|
||||
padding: 0 5rem;
|
||||
|
||||
|
@ -157,7 +158,7 @@
|
|||
height: #{$icon-width + .3rem};
|
||||
margin-top: .3rem;
|
||||
|
||||
background: #161619;
|
||||
background: $bg-color;
|
||||
}
|
||||
.src-icon, .doc-icon, .end-icon {
|
||||
position: relative;
|
||||
|
@ -166,7 +167,7 @@
|
|||
height: #{$icon-width + .1rem};
|
||||
margin-top: .1rem;
|
||||
|
||||
background: #161619;
|
||||
background: $bg-color;
|
||||
}
|
||||
|
||||
.start {
|
||||
|
@ -183,7 +184,7 @@
|
|||
|
||||
padding-top: -.2rem;
|
||||
|
||||
background: #161619;
|
||||
background: $bg-color;
|
||||
}
|
||||
.name, .end {
|
||||
b {
|
||||
|
@ -232,7 +233,7 @@
|
|||
margin-bottom: .4em;
|
||||
|
||||
color: #fff;
|
||||
background: #28282d;
|
||||
background: #34343b;
|
||||
|
||||
border-radius: .4em / .4em;
|
||||
|
||||
|
@ -251,9 +252,9 @@
|
|||
padding-bottom: 2em;
|
||||
margin-bottom: 1.5em;
|
||||
|
||||
background: #28282d;
|
||||
background: #34343b;
|
||||
|
||||
border: .1rem solid #2f2f33;
|
||||
border: .1rem solid #45454b;
|
||||
border-radius: .4em / .4em;
|
||||
z-index: 100;
|
||||
|
||||
|
@ -269,8 +270,8 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background: #202024;
|
||||
border: .1rem solid #2f2f33;
|
||||
background: darken(#34343b, 4%);
|
||||
border: .1rem solid darken(#45454b, 4%);
|
||||
border-radius: .4em / .4em;
|
||||
|
||||
transform: translateZ(-1px);
|
||||
|
|
Loading…
Reference in New Issue