feat: create the skill picker page

This commit is contained in:
Adrien Marquès 2022-10-04 18:29:27 +02:00
parent d6ab16026d
commit d136b9d1e2
Signed by: xdrm-brackets
GPG Key ID: D75243CA236D825E
4 changed files with 258 additions and 10 deletions

View File

@ -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 {

View File

@ -34,7 +34,7 @@
width: $width;
height: $height;
background: #07142d;
background: #202228;
}
@keyframes wave-x {

View File

@ -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>

View File

@ -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);