Compare commits

..

3 Commits

7 changed files with 351 additions and 109 deletions

View File

@ -42,4 +42,35 @@ export default class App extends Vue {
background: #fff; background: #fff;
} }
a {
display: inline-block;
position: relative;
color: #fff;
cursor: pointer;
&:visited {
color: #fefefa;
}
&:after {
content: '';
display: block;
position: absolute;
margin-left: 5%;
width: 90%;
height: .15rem;
background: #3333be;
transition: width .2s ease-in-out, margin-left .2s ease-in-out;
}
&:hover:after {
margin-left: 0;
width: 100%;
}
}
</style> </style>

View File

@ -9,12 +9,13 @@
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'; import { Component, Prop, Vue } from 'vue-property-decorator';
import { tID, Skills } from '@/model/skills'; import { tID } from '@/model/skills';
import * as skills from '@/service/skills';
@Component({}) @Component({})
export default class SkillCard extends Vue { export default class SkillCard extends Vue {
// id of the skill to display (string representation) // id of the skill to display (string representation)
@Prop(String) readonly id: string|undefined; @Prop(Number) readonly id: tID|undefined;
// undefined -> automatic size // undefined -> automatic size
// value -> fixed size applied (valid css value with unit) // value -> fixed size applied (valid css value with unit)
@ -23,7 +24,9 @@
// whether it has the active style // whether it has the active style
@Prop(Boolean) active: boolean|undefined; @Prop(Boolean) active: boolean|undefined;
// whether id is a folder name to display // folder name to display instead of a skill to fetch
// not empty -> used as displayed folder name
// empty -> this.id is used to fetch the skill
@Prop(String) folder: string|undefined; @Prop(String) folder: string|undefined;
protected mounted() { protected mounted() {
@ -34,40 +37,40 @@
el.style.width = this.width; el.style.width = this.width;
} }
private tid(): tID|null { protected icon(): string {
const unknown = () => require('../assets/skills/unknown.svg');
if( this.id == undefined ){ if( this.id == undefined ){
return null; return unknown();
}
const id = parseInt(this.id!) as tID;
if( Skills[id] == undefined ){
return null;
}
return id;
} }
protected icon(): string { if( this.folder != undefined ){
if( this.folder == '1' ){
return require('../assets/skills/folder.svg'); return require('../assets/skills/folder.svg');
} }
let icon: string|null = null; const skill = skills.get(this.id);
if( this.tid() != undefined ){ if( skill == null ){
icon = Skills[this.tid()!].icon; return unknown();
} }
if( icon == null ){
return require('../assets/skills/unknown.svg'); if( skill.icon == null ){
return unknown();
} }
return require(`../assets/${icon}`); return require(`../assets/${skill.icon}`);
} }
protected name(): string { protected name(): string {
if( this.folder == '1' ){ if( this.folder != undefined ){
return this.id!; return this.folder;
}
if( this.tid() != undefined ){
return Skills[this.tid()!].name!;
} }
if( this.id == undefined ){
return '?'; return '?';
} }
const skill = skills.get(this.id);
if( skill == null ){
return '';
}
return skill.name;
}
private onClick(e: MouseEvent) { private onClick(e: MouseEvent) {
this.$emit('pick', !this.active); this.$emit('pick', !this.active);

View File

@ -6,9 +6,8 @@
<section class='categories'> <section class='categories'>
<SkillCard v-for='(t) of tags' <SkillCard v-for='(t) of tags'
:key='t' :key='t'
:id='t'
:active='t == tag' :active='t == tag'
folder='1' :folder='t'
width='18rem' width='18rem'
@pick='onTag(t, $event)'/> @pick='onTag(t, $event)'/>
</section> </section>
@ -16,13 +15,28 @@
<section class='skills'> <section class='skills'>
<SkillCard v-for='(id) of ids' <SkillCard v-for='(id) of ids'
:key='id' :key='id'
v-show='hide.indexOf(id) < 0' v-show='filtered.indexOf(id) >= 0'
:id='id' :id='id'
:active='id == sel' :active='id == sel'
width='18rem' width='18rem'
@pick='onPick(id, $event)' /> @pick='onPick(id, $event)' />
</section> </section>
<section class='details' v-if='details != null'>
<img :src='details.icon'/>
<h1 v-html='details.title'></h1>
<h2>Featured in <b>{{ details.projects.length }}</b> {{ details.projects.length > 1 ? 'projects' : 'project' }}</h2>
<h3>
<template v-for='(proj, i) of details.projects'>
<a :key='proj.name' :href='"#p-" + proj.name'>
{{ proj.name }}
</a>
<span :key='proj.name'>, </span>
</template>
</h3>
<p v-html='details.text'></p>
</section>
</div> </div>
<input type='button' v-show='this.sel != null' value='Browse projects' @click='browse()'/> <input type='button' v-show='this.sel != null' value='Browse projects' @click='browse()'/>
@ -33,7 +47,17 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; import { Component, Vue } from 'vue-property-decorator';
import SkillCard from './SkillCard.vue'; import SkillCard from './SkillCard.vue';
import { tID, Skills } from '@/model/skills'; import { tID } from '@/model/skills';
import { Project } from '@/model/projects';
import * as skills from '@/service/skills';
import * as projects from '@/service/projects';
interface Details {
icon: string|null;
title: string;
projects: Project[];
text: string;
}
@Component({ @Component({
components: { components: {
@ -42,52 +66,38 @@
}) })
export default class SkillPicker extends Vue { export default class SkillPicker extends Vue {
// list of available skills // list of available skills
readonly ids: string[] = Object.keys(Skills); readonly ids: tID[] = skills.available();
// currently selected skill // currently selected skill
private sel: string|null = tID.Vue.toString(); private sel: tID|null = tID.Vue;
// list of ids to hide according to the current tag // list of ids to display according to the current tag
private hide: string[] = []; private filtered: tID[] = [];
// available categories (tags) // available categories (tags)
private tags: string[] = []; private tags: string[] = skills.tags();
// currently selected tag // currently selected tag
private tag: string|null = "web"; private tag: string|null = "web";
// details section when a skill is selected
private details: Details|null = null;
private mounted() { private mounted() {
this.tags = this.fetchTags();
this.filterByTag(); this.filterByTag();
this.loadDetails(this.sel!);
} }
private fetchTags(): string[] { protected onPick(id: tID, picked: boolean){
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 if( picked ){ // select
this.sel = id; this.sel = id;
this.loadDetails(id);
return; return;
} }
if( !picked && id == this.sel ){ // deselect // deselect
this.sel = null; this.sel = null;
return; this.details = null;
} }
}
private onTag(t: string, picked: boolean){ protected onTag(t: string, picked: boolean){
if( picked ){ // select if( picked ){ // select
this.tag = t; this.tag = t;
this.filterByTag(); this.filterByTag();
@ -103,44 +113,48 @@
// apply filter by tag // apply filter by tag
private filterByTag(){ private filterByTag(){
const tag = this.tag; const tag = this.tag;
if( tag == null ){
// no folder selected -> hide everything
if( tag == null || this.tags.indexOf(tag) < 0 ){
this.tag = null; this.tag = null;
this.hide = this.ids; this.filtered = [];
} else {
this.filtered = skills.filtered(tag);
}
// deselect if selection has been filtered out // deselect if selection has been filtered out
if( this.sel != null && this.hide.indexOf(this.sel) >= 0 ){ if( this.sel != null && this.filtered.indexOf(this.sel) < 0 ){
this.sel = null; this.sel = null;
} }
}
// loads details about a skill
private loadDetails(id: tID){
const skill = skills.get(id);
if( skill == null ){
this.details = null;
return; return;
} }
// clear filter let icon: string|null = null;
this.hide = []; if( skill.icon != null ){
icon = require(`../assets/${skill.icon}`);
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;
} }
this.details = {
icon: icon,
title: skill.name,
projects: projects.bySkill(id),
text: skill.info
};
} }
protected browse(){ protected browse(){
document.querySelector("#timeline")!.scrollIntoView(); const el = document.querySelector('#timeline');
if( el != null ){
el.scrollIntoView();
} }
} }
}
</script> </script>
<!-- Add "scoped" attribute to limit CSS to this component only --> <!-- Add "scoped" attribute to limit CSS to this component only -->
@ -177,9 +191,10 @@
} }
.categories { .categories {
flex: 20em 0 0;
display: flex; display: flex;
height: 100%; height: 100%;
width: 20em;
flex-flow: column nowrap; flex-flow: column nowrap;
align-items: flex-start; align-items: flex-start;
@ -194,9 +209,10 @@
} }
.skills { .skills {
flex: 20em 0 0;
display: flex; display: flex;
height: 100%; height: 100%;
width: 20em;
flex-flow: column nowrap; flex-flow: column nowrap;
align-items: flex-start; align-items: flex-start;
@ -210,6 +226,77 @@
} }
} }
.details {
flex: auto;
display: flex;
height: 100%;
margin: auto;
flex-flow: column nowrap;
align-items: center;
justify-content: flex-start;
img {
display: block;
flex: 8em 0 0;
}
h1 {
font-size: 4em;
color: #cacaca;
i { display: none; }
}
h2 {
margin-top: .3em;
font-size: 1.8em;
color: #616c7c;
font-weight: 400;
b { color: #fff; }
}
h3 {
margin-top: .8em;
font-size: 1.3em;
font-weight: 400;
color: #616c7c;
& > span:last-child {
display: none;
}
}
p {
flex: auto 0 1;
display: block;
position: relative;
margin: 0 2em;
margin-top: 2em;
margin-bottom: 4rem;
font-size: 1.7em;
font-weight: 500;
color: #c1c1c1;
padding: 1em;
background: #323841;
border: .1rem solid #45454b;
border-radius: .4em / .4em;
overflow: hidden;
overflow-y: auto;
}
}
input { input {
display: block; display: block;
position: absolute; position: absolute;

View File

@ -5,7 +5,8 @@
</div> </div>
<template class='project' v-for='(proj) of projects'> <template class='project' v-for='(proj) of projects'>
<div :key="'start-'+proj.name" class='start'> <!-- id is used for navigation -->
<div :key="'start-'+proj.name" class='start' :id='"p-" + proj.name'>
{{ proj.started_at | short_date }} {{ proj.started_at | short_date }}
</div> </div>
@ -333,24 +334,6 @@
.src, .doc { .src, .doc {
color: #999999; color: #999999;
a {
display: inline-block;
position: relative;
color: #fff;
&:after {
content: '';
display: block;
position: absolute;
margin-left: 0;
width: 100%;
height: .15rem;
background: #3333be;
}
}
span { span {
margin-left: .5em; margin-left: .5em;
color: #4d4d4d; color: #4d4d4d;

View File

@ -46,11 +46,12 @@ export enum tID {
RnD, RnD,
} }
interface tSkill { export interface tSkill {
icon: string|null; icon: string|null;
name: string; name: string;
link: string; link: string;
tags: string[]; tags: string[];
info: string;
} }
export type tSkills = { [id in tID]: tSkill }; export type tSkills = { [id in tID]: tSkill };
@ -61,18 +62,21 @@ export const Skills: tSkills = {
link: 'https://mariadb.org', link: 'https://mariadb.org',
icon: 'skills/mariadb.svg', icon: 'skills/mariadb.svg',
tags: ['web', 'storage'], tags: ['web', 'storage'],
info: "",
}, },
[tID.Postgres]: { [tID.Postgres]: {
name: 'PostgreSQL', name: 'PostgreSQL',
link: 'https://postgresql.org', link: 'https://postgresql.org',
icon: 'skills/postgres.svg', icon: 'skills/postgres.svg',
tags: ['web', 'storage'], tags: ['web', 'storage'],
info: "",
}, },
[tID.Mongo]: { [tID.Mongo]: {
name: 'MongoDB', name: 'MongoDB',
link: 'https://mongodb.com', link: 'https://mongodb.com',
icon: 'skills/mongo.svg', icon: 'skills/mongo.svg',
tags: ['web', 'storage'], tags: ['web', 'storage'],
info: "",
}, },
[tID.Vue]: { [tID.Vue]: {
@ -80,30 +84,35 @@ export const Skills: tSkills = {
link: 'https://vuejs.org', link: 'https://vuejs.org',
icon: 'skills/vue.svg', icon: 'skills/vue.svg',
tags: ['web', 'UI'], tags: ['web', 'UI'],
info: "I started learning Vue (.js) back in 2016, and never stopped practicing since then for personal and professional projects. I view it as a better alternative than angular which provides you with a strict framework that can lack flexibility among teams and projects.<br><br>Vue makes it your responsability to properly structure your project which I like to take care of myself, as it tends to provide a better workflow adjusted for every project.<br><br>It is my top choice when considering a web framework for rendering pages.",
}, },
[tID.Angular]: { [tID.Angular]: {
name: 'Angular <i>(7+)</i>', name: 'Angular <i>(7+)</i>',
link: 'https://angular.io', link: 'https://angular.io',
icon: 'skills/angular.svg', icon: 'skills/angular.svg',
tags: ['web', 'UI'], tags: ['web', 'UI'],
info: "",
}, },
[tID.Parcel]: { [tID.Parcel]: {
name: 'Parcel', name: 'Parcel',
link: 'https://parceljs.org/', link: 'https://parceljs.org/',
icon: 'skills/parcel.svg', icon: 'skills/parcel.svg',
tags: ['web'], tags: ['web'],
info: "",
}, },
[tID.Cordova]: { [tID.Cordova]: {
name: 'Apache Cordova', name: 'Apache Cordova',
link: 'https://cordova.apache.org/', link: 'https://cordova.apache.org/',
icon: 'skills/cordova.svg', icon: 'skills/cordova.svg',
tags: ['system', 'web', 'mobile'], tags: ['system', 'web', 'mobile'],
info: "",
}, },
[tID.Webpack]: { [tID.Webpack]: {
name: 'Webpack', name: 'Webpack',
link: 'https://webpack.js.org/', link: 'https://webpack.js.org/',
icon: 'skills/webpack.svg', icon: 'skills/webpack.svg',
tags: ['web'], tags: ['web'],
info: "",
}, },
[tID.WebGL]: { [tID.WebGL]: {
@ -111,18 +120,21 @@ export const Skills: tSkills = {
link: 'https://www.khronos.org/webgl/', link: 'https://www.khronos.org/webgl/',
icon: null, icon: null,
tags: ['web'], tags: ['web'],
info: "",
}, },
[tID.AudioAPI]: { [tID.AudioAPI]: {
name: 'Audio API', name: 'Audio API',
link: 'https://webaudio.github.io/web-audio-api/', link: 'https://webaudio.github.io/web-audio-api/',
icon: null, icon: null,
tags: ['web'], tags: ['web'],
info: "",
}, },
[tID.Websocket]: { [tID.Websocket]: {
name: 'Websocket', name: 'Websocket',
link: 'https://tools.ietf.org/html/rfc6455', link: 'https://tools.ietf.org/html/rfc6455',
icon: null, icon: null,
tags: ['web', 'networking', 'IoT'], tags: ['web', 'networking', 'IoT'],
info: "",
}, },
[tID.Docker]: { [tID.Docker]: {
@ -130,36 +142,42 @@ export const Skills: tSkills = {
link: 'https://docker.com', link: 'https://docker.com',
icon: 'skills/docker.svg', icon: 'skills/docker.svg',
tags: ['web', 'system'], tags: ['web', 'system'],
info: "",
}, },
[tID.Bash]: { [tID.Bash]: {
name: 'bash', name: 'bash',
link: 'https://www.gnu.org/software/bash/', link: 'https://www.gnu.org/software/bash/',
icon: null, icon: null,
tags: ['system'], tags: ['system'],
info: "",
}, },
[tID.Linux]: { [tID.Linux]: {
name: 'GNU/Linux', name: 'GNU/Linux',
link: 'https://www.linux.org', link: 'https://www.linux.org',
icon: 'skills/linux.svg', icon: 'skills/linux.svg',
tags: ['system'], tags: ['system'],
info: "",
}, },
[tID.Systemd]: { [tID.Systemd]: {
name: 'systemd', name: 'systemd',
link: 'https://freedesktop.org/wiki/Software/systemd/', link: 'https://freedesktop.org/wiki/Software/systemd/',
icon: null, icon: null,
tags: ['system'], tags: ['system'],
info: "",
}, },
[tID.Git]: { [tID.Git]: {
name: 'Git', name: 'Git',
link: 'https://git-scm.com/', link: 'https://git-scm.com/',
icon: 'skills/git.svg', icon: 'skills/git.svg',
tags: ['system', 'organization'], tags: ['system', 'organization'],
info: "",
}, },
[tID.Rpm]: { [tID.Rpm]: {
name: 'RPM packaging', name: 'RPM packaging',
link: 'https://rpm.org/', link: 'https://rpm.org/',
icon: null, icon: null,
tags: ['system'], tags: ['system'],
info: "",
}, },
[tID.RaspBerry]: { [tID.RaspBerry]: {
@ -167,12 +185,14 @@ export const Skills: tSkills = {
link: 'https://raspberrypi.org', link: 'https://raspberrypi.org',
icon: 'skills/raspberry.svg', icon: 'skills/raspberry.svg',
tags: ['system', 'IoT'], tags: ['system', 'IoT'],
info: "",
}, },
[tID.Arduino]: { [tID.Arduino]: {
name: 'Arduino', name: 'Arduino',
link: 'https://arduino.cc', link: 'https://arduino.cc',
icon: 'skills/arduino.svg', icon: 'skills/arduino.svg',
tags: ['system', 'IoT'], tags: ['system', 'IoT'],
info: "",
}, },
[tID.Php]: { [tID.Php]: {
@ -180,60 +200,70 @@ export const Skills: tSkills = {
link: 'https://www.php.net', link: 'https://www.php.net',
icon: 'skills/php.svg', icon: 'skills/php.svg',
tags: ['language', 'web', 'IoT'], tags: ['language', 'web', 'IoT'],
info: "",
}, },
[tID.Html]: { [tID.Html]: {
name: 'HTML5', name: 'HTML5',
link: 'https://www.w3.org/standards/webdesign/htmlcss', link: 'https://www.w3.org/standards/webdesign/htmlcss',
icon: 'skills/html.svg', icon: 'skills/html.svg',
tags: ['language', 'web', 'UI'], tags: ['language', 'web', 'UI'],
info: "",
}, },
[tID.Css]: { [tID.Css]: {
name: 'CSS3', name: 'CSS3',
link: 'https://www.w3.org/standards/webdesign/htmlcss', link: 'https://www.w3.org/standards/webdesign/htmlcss',
icon: 'skills/css.svg', icon: 'skills/css.svg',
tags: ['language', 'web', 'UI'], tags: ['language', 'web', 'UI'],
info: "",
}, },
[tID.Js]: { [tID.Js]: {
name: 'Javascript', name: 'Javascript',
link: 'http://www.ecma-international.org/publications-and-standards/standards/ecma-262/', link: 'http://www.ecma-international.org/publications-and-standards/standards/ecma-262/',
icon: 'skills/js.svg', icon: 'skills/js.svg',
tags: ['language', 'web', 'UI'], tags: ['language', 'web', 'UI'],
info: "",
}, },
[tID.Ajax]: { [tID.Ajax]: {
name: 'AJAX', name: 'AJAX',
link: 'https://www.w3schools.com/xml/ajax_intro.asp', link: 'https://www.w3schools.com/xml/ajax_intro.asp',
icon: null, icon: null,
tags: ['web', 'networking'], tags: ['web', 'networking'],
info: "",
}, },
[tID.Ts]: { [tID.Ts]: {
name: 'Typescript', name: 'Typescript',
link: 'https://www.typescript.org/', link: 'https://www.typescript.org/',
icon: 'skills/ts.svg', icon: 'skills/ts.svg',
tags: ['language', 'web', 'UI'], tags: ['language', 'web', 'UI'],
info: "",
}, },
[tID.C]: { [tID.C]: {
name: 'C (lang)', name: 'C (lang)',
link: 'https://www.open-std.org/jtc1/sc22/wg14/', link: 'https://www.open-std.org/jtc1/sc22/wg14/',
icon: 'skills/c.svg', icon: 'skills/c.svg',
tags: ['language', 'system'], tags: ['language', 'system'],
info: "",
}, },
[tID.Cpp]: { [tID.Cpp]: {
name: 'C++', name: 'C++',
link: 'https://isocpp.org/', link: 'https://isocpp.org/',
icon: 'skills/cpp.svg', icon: 'skills/cpp.svg',
tags: ['language', 'system'], tags: ['language', 'system'],
info: "",
}, },
[tID.Python]: { [tID.Python]: {
name: 'Python', name: 'Python',
link: 'https://python.org/', link: 'https://python.org/',
icon: 'skills/python.svg', icon: 'skills/python.svg',
tags: ['language', 'system'], tags: ['language', 'system'],
info: "",
}, },
[tID.Go]: { [tID.Go]: {
name: 'Go (lang)', name: 'Go (lang)',
link: 'https://go.dev', link: 'https://go.dev',
icon: 'skills/go.svg', icon: 'skills/go.svg',
tags: ['language', 'system', 'IoT', 'networking'], tags: ['language', 'system', 'IoT', 'networking'],
info: "",
}, },
[tID.Qt]: { [tID.Qt]: {
@ -241,6 +271,7 @@ export const Skills: tSkills = {
link: 'https://qt.io', link: 'https://qt.io',
icon: 'skills/qt.svg', icon: 'skills/qt.svg',
tags: ['language', 'system', 'IoT', 'networking'], tags: ['language', 'system', 'IoT', 'networking'],
info: "",
}, },
[tID.OpenSource]: { [tID.OpenSource]: {
@ -248,89 +279,104 @@ export const Skills: tSkills = {
link: 'https://opensource.org/', link: 'https://opensource.org/',
icon: null, icon: null,
tags: ['human'], tags: ['human'],
info: "",
}, },
[tID.Electronics]: { [tID.Electronics]: {
name: 'Electronics', name: 'Electronics',
link: 'https://en.wikipedia.org/wiki/Electronics', link: 'https://en.wikipedia.org/wiki/Electronics',
icon: 'skills/electronics.svg', icon: 'skills/electronics.svg',
tags: ['other', 'IoT'], tags: ['other', 'IoT'],
info: "",
}, },
[tID.Web]: { [tID.Web]: {
name: 'Web', name: 'Web',
link: 'https://en.wikipedia.org/wiki/World_Wide_Web', link: 'https://en.wikipedia.org/wiki/World_Wide_Web',
icon: null, icon: null,
tags: ['web'], tags: ['web'],
info: "",
}, },
[tID.Rest]: { [tID.Rest]: {
name: 'REST', name: 'REST',
link: 'https://en.wikipedia.org/wiki/Representational_state_transfer', link: 'https://en.wikipedia.org/wiki/Representational_state_transfer',
icon: null, icon: null,
tags: ['web', 'networking'], tags: ['web', 'networking'],
info: "",
}, },
[tID.Crypto]: { [tID.Crypto]: {
name: 'Security/crypto', name: 'Security/crypto',
link: 'https://en.wikipedia.org/wiki/Cryptography', link: 'https://en.wikipedia.org/wiki/Cryptography',
icon: 'skills/security.svg', icon: 'skills/security.svg',
tags: ['system', 'networking'], tags: ['system', 'networking'],
info: "",
}, },
[tID.ImageProcessing]: { [tID.ImageProcessing]: {
name: 'Image processing', name: 'Image processing',
link: 'https://en.wikipedia.org/wiki/Digital_image_processing', link: 'https://en.wikipedia.org/wiki/Digital_image_processing',
icon: 'skills/image-processing.svg', icon: 'skills/image-processing.svg',
tags: ['system'], tags: ['system'],
info: "",
}, },
[tID.AI]: { [tID.AI]: {
name: 'Artificial Intelligence', name: 'Artificial Intelligence',
link: 'https://en.wikipedia.org/wiki/Artificial_intelligence', link: 'https://en.wikipedia.org/wiki/Artificial_intelligence',
icon: null, icon: null,
tags: ['other'], tags: ['other'],
info: "",
}, },
[tID.DeepLearning]: { [tID.DeepLearning]: {
name: 'Deep Learning', name: 'Deep Learning',
link: 'https://en.wikipedia.org/wiki/Deep_learning', link: 'https://en.wikipedia.org/wiki/Deep_learning',
icon: null, icon: null,
tags: ['other'], tags: ['other'],
info: "",
}, },
[tID.NeuralNetwork]: { [tID.NeuralNetwork]: {
name: 'Neural Networks', name: 'Neural Networks',
link: 'https://en.wikipedia.org/wiki/Artificial_neural_network', link: 'https://en.wikipedia.org/wiki/Artificial_neural_network',
icon: null, icon: null,
tags: ['other'], tags: ['other'],
info: "",
}, },
[tID.Opti]: { [tID.Opti]: {
name: 'Program optimization', name: 'Program optimization',
link: 'https://en.wikipedia.org/wiki/Program_optimization', link: 'https://en.wikipedia.org/wiki/Program_optimization',
icon: 'skills/opti.svg', icon: 'skills/opti.svg',
tags: ['system', 'networking'], tags: ['system', 'networking'],
info: "",
}, },
[tID.Sockets]: { [tID.Sockets]: {
name: 'Sockets', name: 'Sockets',
link: 'https://en.wikipedia.org/wiki/Computer_network_programming', link: 'https://en.wikipedia.org/wiki/Computer_network_programming',
icon: null, icon: null,
tags: ['system', 'networking', 'IoT', 'web'], tags: ['system', 'networking', 'IoT', 'web'],
info: "",
}, },
[tID.Concurrency]: { [tID.Concurrency]: {
name: 'Concurrency', name: 'Concurrency',
link: 'https://en.wikipedia.org/wiki/Concurrent_computing', link: 'https://en.wikipedia.org/wiki/Concurrent_computing',
icon: null, icon: null,
tags: ['system', 'networking'], tags: ['system', 'networking'],
info: "",
}, },
[tID.UIUX]: { [tID.UIUX]: {
name: 'UI/UX', name: 'UI/UX',
link: 'https://en.wikipedia.org/wiki/UX', link: 'https://en.wikipedia.org/wiki/UX',
icon: null, icon: null,
tags: ['system', 'web', 'IoT'], tags: ['system', 'web', 'IoT'],
info: "",
}, },
[tID.Inkscape]: { [tID.Inkscape]: {
name: 'Inkscape', name: 'Inkscape',
link: 'https://inkscape.org/', link: 'https://inkscape.org/',
icon: 'skills/inkscape.svg', icon: 'skills/inkscape.svg',
tags: ['UI', 'web', 'organization'], tags: ['UI', 'web', 'organization'],
info: "",
}, },
[tID.RnD]: { [tID.RnD]: {
name: 'R&D', name: 'R&D',
link: 'https://en.wikipedia.org/wiki/R&D', link: 'https://en.wikipedia.org/wiki/R&D',
icon: null, icon: null,
tags: ['organization'], tags: ['organization'],
info: "",
}, },
}; };

15
src/service/projects.ts Normal file
View File

@ -0,0 +1,15 @@
import { Projects, Project } from '@/model/projects';
import { tID } from '@/model/skills';
// returns all projects featuring a specified skill. Keeping the order of the
// projects model
export function bySkill(skill: tID): Project[] {
const filtered: Project[] = [];
for( const proj of Projects ){
if( proj.skills.indexOf(skill) >= 0 ){
filtered.push(proj);
}
}
return filtered;
}

77
src/service/skills.ts Normal file
View File

@ -0,0 +1,77 @@
import { tID, Skills, tSkill } from '@/model/skills';
let available_cache: tID[]|null = null;
let tags_cache: string[]|null = null;
// returns available skill ids.
export function available(): tID[] {
if( available_cache != null ){
return available_cache;
}
available_cache = [];
for( const str_id in Skills ){
const id = str2ID(str_id)
if( id == null ){
continue;
}
available_cache.push(id);
}
return available_cache;
}
// returns the list of ids filtered by a tag. Skills NOT featuring the provided
// tag are filtered out of the list.
export function filtered(tag: string): tID[] {
if( available_cache == null ){
available();
}
const filtered = [];
for( const id of available_cache! ){
const skill = get(id);
if( skill == null ){
continue;
}
if( skill.tags.indexOf(tag) >= 0 ){
filtered.push(id);
continue;
}
}
return filtered;
}
// returns available tags used among skills
export function tags(): string[] {
if( tags_cache != null ){
return tags_cache;
}
tags_cache = [];
for( const skill of Object.values(Skills) ){
for( const tag of skill.tags ){
if( tags_cache.indexOf(tag) < 0 ){
tags_cache.push(tag);
}
}
}
return tags_cache;
}
// returns a skill from its id
export function get(id: tID): tSkill|null {
if( Skills[id] == undefined ){
return null;
}
return Skills[id];
}
// converts a string to a skill id
export function str2ID(str: string): tID|null {
const id = parseInt(str) as tID;
if( Skills[id] == undefined ){
return null;
}
return id;
}