Compare commits

..

9 Commits

12 changed files with 581 additions and 144 deletions

View File

@ -1,25 +1,55 @@
<template> <template>
<div id="app"> <div id="app">
<Home/> <Home/>
<SkillPicker/> <SkillPicker ref='picker' @pick='onPick($event)'/>
<Timeline/> <Timeline ref='timeline' @pick='onPicked($event)'/>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; import { Component, Vue } from 'vue-property-decorator';
import { tID } from '@/model/skills';
import Home from './components/Home.vue'; import Home from './components/Home.vue';
import Timeline from './components/Timeline.vue'; import Timeline from './components/Timeline.vue';
import SkillPicker from './components/SkillPicker.vue'; import SkillPicker from './components/SkillPicker.vue';
@Component({ @Component({
components: { components: {
Home, Home,
Timeline, Timeline,
SkillPicker, SkillPicker,
}, }
}) })
export default class App extends Vue { export default class App extends Vue {
private selected: tID|null = null;
private mounted() {
const picker = this.$refs.picker as SkillPicker;
if( picker == null ){
return;
}
picker.select(tID.Vue, false);
}
// skill picker selection -> filters the timeline
protected onPick(id: tID|null) {
const timeline = this.$refs.timeline as Timeline;
if( timeline == null ){
return;
}
timeline.filter(id);
}
// skill picked from the timeline -> select on the skill picker
protected onPicked(id: tID) {
const picker = this.$refs.picker as SkillPicker;
if( picker == null ){
return;
}
picker.select(id, false);
}
} }
</script> </script>
@ -66,6 +96,8 @@ export default class App extends Vue {
background: #3333be; background: #3333be;
transition: width .2s ease-in-out, margin-left .2s ease-in-out; transition: width .2s ease-in-out, margin-left .2s ease-in-out;
transform-style: preserve-3d;
} }
&:hover:after { &:hover:after {

View File

@ -1,13 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="4.2333mm" height="4.2333mm" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg"> <svg
<g transform="translate(-139.71 -124.82)"> width="4.2333mm"
<g transform="matrix(.49451 0 0 .49451 38.616 -321.26)"> height="4.2333mm"
<g transform="matrix(.64049 0 0 .64049 75.033 338.39)"> version="1.1"
<g transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"> viewBox="0 0 4.2333 4.2333"
<circle cx="208.71" cy="865.03" r="4.9253" fill="#292a2e" style="paint-order:stroke fill markers"/> id="svg25433"
<path d="m207.85 862.56a0.33661 0.33661 0 0 0-0.31641 0.18946l-0.96289 1.9766h-0.59961a0.33657 0.33657 0 0 0-0.33789 0.33594 0.33657 0.33657 0 0 0 0.33789 0.33789h0.81055a0.33661 0.33661 0 0 0 0.30273-0.18946l0.71875-1.4746 1.3867 3.543a0.33661 0.33661 0 0 0 0.61133 0.0332l0.99804-1.9121h0.64649a0.33657 0.33657 0 0 0 0.33594-0.33789 0.33657 0.33657 0 0 0-0.33594-0.33594h-0.84961a0.33661 0.33661 0 0 0-0.29883 0.18164l-0.75 1.4375-1.3984-3.5703a0.33661 0.33661 0 0 0-0.29882-0.21485z" color="#000000" fill="#9b9ea1" style="paint-order:stroke fill markers"/> sodipodi:docname="activity.svg"
</g> inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
</g> xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
</g> xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
</g> xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs25437" />
<sodipodi:namedview
id="namedview25435"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg25433" />
<g
transform="translate(-139.71 -124.82)"
id="g25431">
<g
transform="matrix(.49451 0 0 .49451 38.616 -321.26)"
id="g25429">
<g
transform="matrix(.64049 0 0 .64049 75.033 338.39)"
id="g25427">
<g
transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"
id="g25425">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#292a2e"
style="paint-order:stroke fill markers;fill:#2a2e36;fill-opacity:1.0"
id="circle25421" />
<path
d="m207.85 862.56a0.33661 0.33661 0 0 0-0.31641 0.18946l-0.96289 1.9766h-0.59961a0.33657 0.33657 0 0 0-0.33789 0.33594 0.33657 0.33657 0 0 0 0.33789 0.33789h0.81055a0.33661 0.33661 0 0 0 0.30273-0.18946l0.71875-1.4746 1.3867 3.543a0.33661 0.33661 0 0 0 0.61133 0.0332l0.99804-1.9121h0.64649a0.33657 0.33657 0 0 0 0.33594-0.33789 0.33657 0.33657 0 0 0-0.33594-0.33594h-0.84961a0.33661 0.33661 0 0 0-0.29883 0.18164l-0.75 1.4375-1.3984-3.5703a0.33661 0.33661 0 0 0-0.29882-0.21485z"
color="#000000"
fill="#9b9ea1"
style="paint-order:stroke fill markers"
id="path25423" />
</g>
</g>
</g>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,13 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="4.2333mm" height="4.2333mm" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg"> <svg
<g transform="translate(-139.71 -124.82)"> width="4.2333mm"
<g transform="matrix(.49451 0 0 .49451 38.616 -321.26)"> height="4.2333mm"
<g transform="matrix(.64049 0 0 .64049 75.033 338.39)"> version="1.1"
<g transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"> viewBox="0 0 4.2333 4.2333"
<circle cx="208.71" cy="865.03" r="4.9253" fill="#292a2e" style="paint-order:stroke fill markers"/> id="svg25568"
</g> sodipodi:docname="doc.svg"
</g> inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
<path d="m208.16 907.55-0.20732 0.20589c-0.17972 0.17813-0.47201 0.17829-0.65163 0-0.0864-0.0857-0.13378-0.19948-0.13378-0.32046 0-0.12097 0.0474-0.23483 0.13373-0.32051l0.76312-0.75713c0.15816-0.15683 0.45567-0.38776 0.67252-0.1727 0.0995 0.0988 0.26023 0.0982 0.35903-2e-3 0.0988-0.0995 0.0982-0.26023-2e-3 -0.35897-0.3686-0.36579-0.91339-0.29819-1.3879 0.17259l-0.76317 0.75718c-0.1831 0.18177-0.28389 0.42351-0.28389 0.68082 0 0.25732 0.10079 0.49901 0.28394 0.68077 0.18843 0.18694 0.43585 0.28036 0.68338 0.28036 0.24758 0 0.4951-0.0934 0.68363-0.28046l0.20748-0.20594c0.0995-0.0987 0.10003-0.25946 8.7e-4 -0.35898-0.0987-0.0994-0.25956-0.1-0.35907-8.8e-4zm2.3127-2.9412c-0.39591-0.39282-0.94946-0.41413-1.316-0.0505l-0.25844 0.25654c-0.0995 0.0988-0.10012 0.25946-2e-3 0.35897 0.0989 0.0995 0.25957 0.10008 0.35909 2e-3l0.25833-0.25638c0.18985-0.18853 0.43846-0.11038 0.60061 0.0505 0.0865 0.0857 0.13393 0.19954 0.13393 0.32051 0 0.12103-0.0475 0.23488-0.13383 0.32056l-0.81418 0.80769c-0.37229 0.36937-0.54694 0.196-0.62145 0.12205-0.0995-0.0988-0.26019-0.0982-0.35904 2e-3 -0.0988 0.0995-0.0982 0.26023 2e-3 0.35897 0.17091 0.16963 0.36599 0.25372 0.5705 0.25372 0.2504 0 0.51488-0.12609 0.76636-0.37576l0.81418-0.80769c0.18305-0.18177 0.28394-0.42355 0.28394-0.68087 0-0.25721-0.10089-0.499-0.28404-0.68087z" fill="#9b9ea1"/> xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
</g> xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
</g> xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs25572" />
<sodipodi:namedview
id="namedview25570"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg25568" />
<g
transform="translate(-139.71 -124.82)"
id="g25566">
<g
transform="matrix(.49451 0 0 .49451 38.616 -321.26)"
id="g25564">
<g
transform="matrix(.64049 0 0 .64049 75.033 338.39)"
id="g25560">
<g
transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"
id="g25558">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#292a2e"
style="paint-order:stroke fill markers;fill:#2a2e36;fill-opacity:1.0"
id="circle25556" />
</g>
</g>
<path
d="m208.16 907.55-0.20732 0.20589c-0.17972 0.17813-0.47201 0.17829-0.65163 0-0.0864-0.0857-0.13378-0.19948-0.13378-0.32046 0-0.12097 0.0474-0.23483 0.13373-0.32051l0.76312-0.75713c0.15816-0.15683 0.45567-0.38776 0.67252-0.1727 0.0995 0.0988 0.26023 0.0982 0.35903-2e-3 0.0988-0.0995 0.0982-0.26023-2e-3 -0.35897-0.3686-0.36579-0.91339-0.29819-1.3879 0.17259l-0.76317 0.75718c-0.1831 0.18177-0.28389 0.42351-0.28389 0.68082 0 0.25732 0.10079 0.49901 0.28394 0.68077 0.18843 0.18694 0.43585 0.28036 0.68338 0.28036 0.24758 0 0.4951-0.0934 0.68363-0.28046l0.20748-0.20594c0.0995-0.0987 0.10003-0.25946 8.7e-4 -0.35898-0.0987-0.0994-0.25956-0.1-0.35907-8.8e-4zm2.3127-2.9412c-0.39591-0.39282-0.94946-0.41413-1.316-0.0505l-0.25844 0.25654c-0.0995 0.0988-0.10012 0.25946-2e-3 0.35897 0.0989 0.0995 0.25957 0.10008 0.35909 2e-3l0.25833-0.25638c0.18985-0.18853 0.43846-0.11038 0.60061 0.0505 0.0865 0.0857 0.13393 0.19954 0.13393 0.32051 0 0.12103-0.0475 0.23488-0.13383 0.32056l-0.81418 0.80769c-0.37229 0.36937-0.54694 0.196-0.62145 0.12205-0.0995-0.0988-0.26019-0.0982-0.35904 2e-3 -0.0988 0.0995-0.0982 0.26023 2e-3 0.35897 0.17091 0.16963 0.36599 0.25372 0.5705 0.25372 0.2504 0 0.51488-0.12609 0.76636-0.37576l0.81418-0.80769c0.18305-0.18177 0.28394-0.42355 0.28394-0.68087 0-0.25721-0.10089-0.499-0.28404-0.68087z"
fill="#9b9ea1"
id="path25562" />
</g>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -1,13 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="4.2333mm" height="4.2333mm" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg"> <svg
<g transform="translate(-139.71 -124.82)"> width="4.2333mm"
<g transform="matrix(.49451 0 0 .49451 38.616 -321.26)"> height="4.2333mm"
<g transform="matrix(.64049 0 0 .64049 75.033 338.39)"> version="1.1"
<g transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"> viewBox="0 0 4.2333 4.2333"
<circle cx="208.71" cy="865.03" r="4.9253" fill="#292a2e" style="paint-order:stroke fill markers"/> id="svg25838"
</g> sodipodi:docname="info.svg"
</g> inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
<path d="m210.17 904.55h-2.9336c-0.40336 0-0.73339 0.33003-0.73339 0.7334v1.8335c0 0.40337 0.33003 0.73339 0.73339 0.73339v0.55005c0 0.16502 0.20169 0.23836 0.3117 0.12835l0.67839-0.6784h1.9435c0.40336 0 0.73339-0.33002 0.73339-0.73339v-1.8335c0-0.40337-0.33003-0.7334-0.73339-0.7334zm-1.1001 2.2002h-1.1001c-0.11001 0-0.18336-0.0733-0.18336-0.18335 0-0.11001 0.0734-0.18334 0.18336-0.18334h1.1001c0.11001 0 0.18335 0.0733 0.18335 0.18334 0 0.11001-0.0733 0.18335-0.18335 0.18335zm0.36669-0.7334h-1.4668c-0.11001 0-0.18336-0.0733-0.18336-0.18334 0-0.11001 0.0734-0.18336 0.18336-0.18336h1.4668c0.11001 0 0.18336 0.0734 0.18336 0.18336 0 0.11001-0.0734 0.18334-0.18336 0.18334z" fill="#9b9ea1"/> xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
</g> xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
</g> xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs25842" />
<sodipodi:namedview
id="namedview25840"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg25838" />
<g
transform="translate(-139.71 -124.82)"
id="g25836">
<g
transform="matrix(.49451 0 0 .49451 38.616 -321.26)"
id="g25834">
<g
transform="matrix(.64049 0 0 .64049 75.033 338.39)"
id="g25830">
<g
transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"
id="g25828">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#292a2e"
style="paint-order:stroke fill markers;fill:#2a2e36;fill-opacity:1.0"
id="circle25826" />
</g>
</g>
<path
d="m210.17 904.55h-2.9336c-0.40336 0-0.73339 0.33003-0.73339 0.7334v1.8335c0 0.40337 0.33003 0.73339 0.73339 0.73339v0.55005c0 0.16502 0.20169 0.23836 0.3117 0.12835l0.67839-0.6784h1.9435c0.40336 0 0.73339-0.33002 0.73339-0.73339v-1.8335c0-0.40337-0.33003-0.7334-0.73339-0.7334zm-1.1001 2.2002h-1.1001c-0.11001 0-0.18336-0.0733-0.18336-0.18335 0-0.11001 0.0734-0.18334 0.18336-0.18334h1.1001c0.11001 0 0.18335 0.0733 0.18335 0.18334 0 0.11001-0.0733 0.18335-0.18335 0.18335zm0.36669-0.7334h-1.4668c-0.11001 0-0.18336-0.0733-0.18336-0.18334 0-0.11001 0.0734-0.18336 0.18336-0.18336h1.4668c0.11001 0 0.18336 0.0734 0.18336 0.18336 0 0.11001-0.0734 0.18334-0.18336 0.18334z"
fill="#9b9ea1"
id="path25832" />
</g>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,9 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="4.2333mm" height="4.2333mm" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg"> <svg
<g transform="translate(-55.204 -97.322)"> width="4.2333mm"
<g transform="matrix(.42975 0 0 .42975 -32.37 -272.31)"> height="4.2333mm"
<circle cx="208.71" cy="865.03" r="4.9253" fill="#2c55cf" style="paint-order:stroke fill markers"/> version="1.1"
<path d="m209.6 862.95c0-0.0945-0.0733-0.29631-0.29592-0.29631-0.12969 0-0.24927 0.0842-0.28656 0.21504l-1.1874 4.156c-8e-3 0.0272-0.0115 0.0547-0.0115 0.0816-8e-3 0.0957 0.0745 0.29704 0.29715 0.29704 0.12913 0 0.24797-0.0849 0.28526-0.21541l1.1874-4.156c8e-3 -0.0276 0.0116-0.055 0.0116-0.082zm-2.2264 1.0312c0-0.16958-0.13869-0.29686-0.29686-0.29686-0.076 0-0.15195 0.029-0.20984 0.087l-1.039 1.039c-0.058 0.0661-0.087 0.14216-0.087 0.20987 0 0.0677 0.029 0.16049 0.087 0.21801l1.039 1.039c0.0577 0.0584 0.13382 0.0789 0.20988 0.0789 0.15817 0 0.29686-0.12737 0.29686-0.29686 0-0.076-0.029-0.15195-0.087-0.20993l-0.82911-0.82907 0.82916-0.82915c0.0582-0.0503 0.0869-0.12542 0.0869-0.20984zm4.3044 1.039c0-0.076-0.029-0.15195-0.087-0.20993l-1.039-1.039c-0.0577-0.0498-0.13382-0.0869-0.20989-0.0869-0.15816 0-0.29685 0.12737-0.29685 0.29686 0 0.076 0.029 0.15195 0.087 0.20993l0.82915 0.82916-0.82915 0.82916c-0.0582 0.066-0.087 0.14202-0.087 0.20974 0 0.16958 0.13869 0.29686 0.29685 0.29686 0.076 0 0.15196-0.029 0.20985-0.087l1.039-1.039c0.0583-0.0494 0.087-0.12546 0.087-0.20988z" fill="#fff"/> viewBox="0 0 4.2333 4.2333"
</g> id="svg26102"
</g> sodipodi:docname="project.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs26106" />
<sodipodi:namedview
id="namedview26104"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg26102" />
<g
transform="translate(-55.204 -97.322)"
id="g26100">
<g
transform="matrix(.42975 0 0 .42975 -32.37 -272.31)"
id="g26098">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#2c55cf"
style="paint-order:stroke fill markers;fill:#5f50bf;fill-opacity:1"
id="circle26094" />
<path
d="m209.6 862.95c0-0.0945-0.0733-0.29631-0.29592-0.29631-0.12969 0-0.24927 0.0842-0.28656 0.21504l-1.1874 4.156c-8e-3 0.0272-0.0115 0.0547-0.0115 0.0816-8e-3 0.0957 0.0745 0.29704 0.29715 0.29704 0.12913 0 0.24797-0.0849 0.28526-0.21541l1.1874-4.156c8e-3 -0.0276 0.0116-0.055 0.0116-0.082zm-2.2264 1.0312c0-0.16958-0.13869-0.29686-0.29686-0.29686-0.076 0-0.15195 0.029-0.20984 0.087l-1.039 1.039c-0.058 0.0661-0.087 0.14216-0.087 0.20987 0 0.0677 0.029 0.16049 0.087 0.21801l1.039 1.039c0.0577 0.0584 0.13382 0.0789 0.20988 0.0789 0.15817 0 0.29686-0.12737 0.29686-0.29686 0-0.076-0.029-0.15195-0.087-0.20993l-0.82911-0.82907 0.82916-0.82915c0.0582-0.0503 0.0869-0.12542 0.0869-0.20984zm4.3044 1.039c0-0.076-0.029-0.15195-0.087-0.20993l-1.039-1.039c-0.0577-0.0498-0.13382-0.0869-0.20989-0.0869-0.15816 0-0.29685 0.12737-0.29685 0.29686 0 0.076 0.029 0.15195 0.087 0.20993l0.82915 0.82916-0.82915 0.82916c-0.0582 0.066-0.087 0.14202-0.087 0.20974 0 0.16958 0.13869 0.29686 0.29685 0.29686 0.076 0 0.15196-0.029 0.20985-0.087l1.039-1.039c0.0583-0.0494 0.087-0.12546 0.087-0.20988z"
fill="#fff"
id="path26096" />
</g>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,11 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="4.2333mm" height="4.2333mm" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg"> <svg
<g transform="translate(-139.71 -124.82)"> width="4.2333mm"
<g transform="matrix(.31672 0 0 .31672 75.72 -153.92)"> height="4.2333mm"
<g transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"> version="1.1"
<circle cx="208.71" cy="865.03" r="4.9253" fill="#292a2e" style="paint-order:stroke fill markers"/> viewBox="0 0 4.2333 4.2333"
</g> id="svg25971"
<path d="m211.76 887.29-0.13917-0.0804c-0.16563-0.0955-0.26458-0.26696-0.26458-0.45826 0-0.19129 0.099-0.36274 0.26458-0.45826l0.13917-0.0804c0.37994-0.2196 0.50985-0.70432 0.29051-1.0843l-0.26458-0.45826c-0.21881-0.37915-0.70538-0.50932-1.0843-0.29051l-0.13917 0.0802c-0.16563 0.0958-0.36381 0.0958-0.52917 0-0.16563-0.0958-0.26458-0.26697-0.26458-0.45826v-0.1606c0-0.43762-0.35613-0.79375-0.79375-0.79375h-0.52917c-0.43762 0-0.79375 0.35613-0.79375 0.79375v0.16086c0 0.1913-0.099 0.36248-0.26458 0.45826-0.16563 0.0955-0.36354 0.0958-0.52917 0l-0.13917-0.0804c-0.37888-0.21881-0.86545-0.0886-1.0845 0.29051l-0.26458 0.45826c-0.21934 0.37994-0.0894 0.86492 0.29051 1.0843l0.13944 0.0804c0.16563 0.0955 0.26458 0.26697 0.26458 0.45826 0 0.1913-0.099 0.36275-0.26458 0.45826l-0.13917 0.0804c-0.37995 0.21934-0.50986 0.70433-0.29052 1.0843l0.26459 0.45826c0.21907 0.37914 0.70564 0.50932 1.0843 0.29051l0.13917-0.0802c0.16563-0.096 0.36354-0.0955 0.52917 0 0.16563 0.0958 0.26458 0.26696 0.26458 0.45826v0.1606c0 0.43762 0.35613 0.79375 0.79375 0.79375h0.52917c0.43762 0 0.79375-0.35613 0.79375-0.79375v-0.16087c0-0.19129 0.099-0.36248 0.26458-0.45826 0.16536-0.0955 0.36354-0.0958 0.52917 0l0.13917 0.0804c0.37888 0.21854 0.86545 0.0884 1.0843-0.29051l0.26458-0.45826c0.21934-0.37994 0.0894-0.86493-0.29051-1.0843zm-3.0496 0.78423c-0.72945 0-1.3229-0.59346-1.3229-1.3229 0-0.72945 0.59346-1.3229 1.3229-1.3229 0.72946 0 1.3229 0.59346 1.3229 1.3229 0 0.72946-0.59346 1.3229-1.3229 1.3229z" fill="#9b9ea1"/> sodipodi:docname="skills.svg"
</g> inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
</g> xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs25975" />
<sodipodi:namedview
id="namedview25973"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg25971" />
<g
transform="translate(-139.71 -124.82)"
id="g25969">
<g
transform="matrix(.31672 0 0 .31672 75.72 -153.92)"
id="g25967">
<g
transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"
id="g25963">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#292a2e"
style="paint-order:stroke fill markers;fill:#2a2e36;fill-opacity:1.0"
id="circle25961" />
</g>
<path
d="m211.76 887.29-0.13917-0.0804c-0.16563-0.0955-0.26458-0.26696-0.26458-0.45826 0-0.19129 0.099-0.36274 0.26458-0.45826l0.13917-0.0804c0.37994-0.2196 0.50985-0.70432 0.29051-1.0843l-0.26458-0.45826c-0.21881-0.37915-0.70538-0.50932-1.0843-0.29051l-0.13917 0.0802c-0.16563 0.0958-0.36381 0.0958-0.52917 0-0.16563-0.0958-0.26458-0.26697-0.26458-0.45826v-0.1606c0-0.43762-0.35613-0.79375-0.79375-0.79375h-0.52917c-0.43762 0-0.79375 0.35613-0.79375 0.79375v0.16086c0 0.1913-0.099 0.36248-0.26458 0.45826-0.16563 0.0955-0.36354 0.0958-0.52917 0l-0.13917-0.0804c-0.37888-0.21881-0.86545-0.0886-1.0845 0.29051l-0.26458 0.45826c-0.21934 0.37994-0.0894 0.86492 0.29051 1.0843l0.13944 0.0804c0.16563 0.0955 0.26458 0.26697 0.26458 0.45826 0 0.1913-0.099 0.36275-0.26458 0.45826l-0.13917 0.0804c-0.37995 0.21934-0.50986 0.70433-0.29052 1.0843l0.26459 0.45826c0.21907 0.37914 0.70564 0.50932 1.0843 0.29051l0.13917-0.0802c0.16563-0.096 0.36354-0.0955 0.52917 0 0.16563 0.0958 0.26458 0.26696 0.26458 0.45826v0.1606c0 0.43762 0.35613 0.79375 0.79375 0.79375h0.52917c0.43762 0 0.79375-0.35613 0.79375-0.79375v-0.16087c0-0.19129 0.099-0.36248 0.26458-0.45826 0.16536-0.0955 0.36354-0.0958 0.52917 0l0.13917 0.0804c0.37888 0.21854 0.86545 0.0884 1.0843-0.29051l0.26458-0.45826c0.21934-0.37994 0.0894-0.86493-0.29051-1.0843zm-3.0496 0.78423c-0.72945 0-1.3229-0.59346-1.3229-1.3229 0-0.72945 0.59346-1.3229 1.3229-1.3229 0.72946 0 1.3229 0.59346 1.3229 1.3229 0 0.72946-0.59346 1.3229-1.3229 1.3229z"
fill="#9b9ea1"
id="path25965" />
</g>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -1,13 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="4.2333mm" height="4.2333mm" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg"> <svg
<g transform="translate(-139.71 -124.82)"> width="4.2333mm"
<g transform="matrix(.49451 0 0 .49451 38.616 -321.26)"> height="4.2333mm"
<g transform="matrix(.64049 0 0 .64049 75.033 338.39)"> version="1.1"
<g transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"> viewBox="0 0 4.2333 4.2333"
<circle cx="208.71" cy="865.03" r="4.9253" fill="#292a2e" style="paint-order:stroke fill markers"/> id="svg25703"
</g> sodipodi:docname="src.svg"
</g> inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
<path d="m210.25 905.42a0.61713 0.61713 0 1 0-0.92831 0.53266c-0.0221 0.28041-0.19362 0.35678-0.7232 0.4669-0.17126 0.0355-0.34646 0.0718-0.50827 0.13432v-1.2169a0.61713 0.61713 0 1 0-0.61713 0v2.0174a0.61713 0.61713 0 1 0 0.62118 2e-3c0.023-0.17357 0.15429-0.23461 0.62929-0.33287 0.2645-0.0547 0.53787-0.11128 0.76929-0.25987 0.27964-0.17916 0.42939-0.45109 0.44704-0.80892a0.61713 0.61713 0 0 0 0.31011-0.53508zm-2.4685-0.9257a0.30857 0.30857 0 1 1-0.30856 0.30857 0.30857 0.30857 0 0 1 0.30856-0.30857zm0 3.7028a0.30857 0.30857 0 1 1 0.30857-0.30856 0.30857 0.30857 0 0 1-0.30857 0.30856zm1.8514-2.4685a0.30857 0.30857 0 1 1 0.30857-0.30857 0.30857 0.30857 0 0 1-0.30857 0.30857z" fill="#9b9ea1"/> xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
</g> xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
</g> xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs25707" />
<sodipodi:namedview
id="namedview25705"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="51.1254"
inkscape:cx="8.0097173"
inkscape:cy="7.9999374"
inkscape:window-width="1920"
inkscape:window-height="1006"
inkscape:window-x="0"
inkscape:window-y="42"
inkscape:window-maximized="1"
inkscape:current-layer="svg25703" />
<g
transform="translate(-139.71 -124.82)"
id="g25701">
<g
transform="matrix(.49451 0 0 .49451 38.616 -321.26)"
id="g25699">
<g
transform="matrix(.64049 0 0 .64049 75.033 338.39)"
id="g25695">
<g
transform="matrix(1.3569 0 0 1.3569 -74.479 -286.98)"
id="g25693">
<circle
cx="208.71"
cy="865.03"
r="4.9253"
fill="#292a2e"
style="paint-order:stroke fill markers;fill:#2a2e36;fill-opacity:1.0"
id="circle25691" />
</g>
</g>
<path
d="m210.25 905.42a0.61713 0.61713 0 1 0-0.92831 0.53266c-0.0221 0.28041-0.19362 0.35678-0.7232 0.4669-0.17126 0.0355-0.34646 0.0718-0.50827 0.13432v-1.2169a0.61713 0.61713 0 1 0-0.61713 0v2.0174a0.61713 0.61713 0 1 0 0.62118 2e-3c0.023-0.17357 0.15429-0.23461 0.62929-0.33287 0.2645-0.0547 0.53787-0.11128 0.76929-0.25987 0.27964-0.17916 0.42939-0.45109 0.44704-0.80892a0.61713 0.61713 0 0 0 0.31011-0.53508zm-2.4685-0.9257a0.30857 0.30857 0 1 1-0.30856 0.30857 0.30857 0.30857 0 0 1 0.30856-0.30857zm0 3.7028a0.30857 0.30857 0 1 1 0.30857-0.30856 0.30857 0.30857 0 0 1-0.30857 0.30856zm1.8514-2.4685a0.30857 0.30857 0 1 1 0.30857-0.30857 0.30857 0.30857 0 0 1-0.30857 0.30857z"
fill="#9b9ea1"
id="path25697" />
</g>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,5 +1,5 @@
<template> <template>
<div class='skill-card' ref='root' :data-active='active ? "1" : "0"' @click='onClick()'> <div class='skill-card' ref='root' :data-active='active ? "1" : "0"' @click='onClick()' :title='name()'>
<img class='icon' :src='icon()' /> <img class='icon' :src='icon()' />
<span class='name'> <span class='name'>
<span v-html='name()'></span> <span v-html='name()'></span>
@ -17,10 +17,6 @@
// id of the skill to display (string representation) // id of the skill to display (string representation)
@Prop(Number) readonly id: tID|undefined; @Prop(Number) readonly id: tID|undefined;
// undefined -> automatic size
// value -> fixed size applied (valid css value with unit)
@Prop(String) readonly width: string|undefined;
// whether it has the active style // whether it has the active style
@Prop(Boolean) active: boolean|undefined; @Prop(Boolean) active: boolean|undefined;
@ -29,24 +25,18 @@
// empty -> this.id is used to fetch the skill // empty -> this.id is used to fetch the skill
@Prop(String) folder: string|undefined; @Prop(String) folder: string|undefined;
protected mounted() { protected mounted() {}
if( this.width == undefined ){
return;
}
const el = this.$refs.root as HTMLElement;
el.style.width = this.width;
}
protected icon(): string { protected icon(): string {
const unknown = () => require('../assets/skills/unknown.svg'); const unknown = () => require('../assets/skills/unknown.svg');
if( this.id == undefined ){
return unknown();
}
if( this.folder != undefined ){ if( this.folder != undefined ){
return require('../assets/skills/folder.svg'); return require('../assets/skills/folder.svg');
} }
if( this.id == undefined ){
return unknown();
}
const skill = skills.get(this.id); const skill = skills.get(this.id);
if( skill == null ){ if( skill == null ){
return unknown(); return unknown();

View File

@ -8,7 +8,6 @@
:key='t' :key='t'
:active='t == tag' :active='t == tag'
:folder='t' :folder='t'
width='18rem'
@pick='onTag(t, $event)'/> @pick='onTag(t, $event)'/>
</section> </section>
@ -18,8 +17,7 @@
v-show='filtered.indexOf(id) >= 0' v-show='filtered.indexOf(id) >= 0'
:id='id' :id='id'
:active='id == sel' :active='id == sel'
width='18rem' @pick='onPick(id, $event)'/>
@pick='onPick(id, $event)' />
</section> </section>
<section class='details' v-if='details != null'> <section class='details' v-if='details != null'>
@ -27,8 +25,8 @@
<h1 v-html='details.title'></h1> <h1 v-html='details.title'></h1>
<h2>Featured in <b>{{ details.projects.length }}</b> {{ details.projects.length > 1 ? 'projects' : 'project' }}</h2> <h2>Featured in <b>{{ details.projects.length }}</b> {{ details.projects.length > 1 ? 'projects' : 'project' }}</h2>
<h3> <h3>
<template v-for='(proj, i) of details.projects'> <template v-for='(proj) of details.projects'>
<a :key='proj.name' :href='"#p-" + proj.name'> <a :key='"pick-" + proj.name' href @click='$event.preventDefault(); scroll(proj.name.replaceAll(" ", "_"));'>
{{ proj.name }} {{ proj.name }}
</a> </a>
<span :key='proj.name'>, </span> <span :key='proj.name'>, </span>
@ -51,6 +49,7 @@
import { Project } from '@/model/projects'; import { Project } from '@/model/projects';
import * as skills from '@/service/skills'; import * as skills from '@/service/skills';
import * as projects from '@/service/projects'; import * as projects from '@/service/projects';
import { go } from '@/service/scroller';
interface Details { interface Details {
icon: string|null; icon: string|null;
@ -59,6 +58,8 @@
text: string; text: string;
} }
const DEFAULT_TAG = "all";
@Component({ @Component({
components: { components: {
SkillCard, SkillCard,
@ -74,27 +75,50 @@
private filtered: tID[] = []; private filtered: tID[] = [];
// available categories (tags) // available categories (tags)
private tags: string[] = skills.tags(); private tags: string[] = [DEFAULT_TAG, ...skills.tags()];
// currently selected tag // currently selected tag
private tag: string|null = "web"; private tag: string = "web";
// details section when a skill is selected // details section when a skill is selected
private details: Details|null = null; private details: Details|null = null;
private mounted() { private mounted() {
this.filterByTag(); this.filterByTag();
this.loadDetails(this.sel!); }
private scroll(proj_name: string) {
const head = document.querySelector(`#search-header`) as HTMLElement;
if( head == null ){
return;
}
go(`project-${proj_name}`, -1.2*head.offsetHeight)
}
// selects or deselects a skill. If the skill is not in the current
// folder, it navigates to the DEFAULT_TAG folder beforehand. Scrolls to
// the selected skill when selected.
public select(id: tID, deselect: boolean) {
const skill = skills.get(id);
if( deselect || skill == null ){
this.sel = null;
this.details = null;
this.$emit('pick', null);
return;
}
// not available in current tag filter (folder)
// -> navigate to default tag
if( skill.tags.indexOf(this.tag) < 0 ){
this.onTag(DEFAULT_TAG, true);
}
this.sel = id;
this.loadDetails(id);
this.$emit('pick', id);
} }
protected onPick(id: tID, picked: boolean){ protected onPick(id: tID, picked: boolean){
if( picked ){ // select this.select(id, !picked);
this.sel = id;
this.loadDetails(id);
return;
}
// deselect
this.sel = null;
this.details = null;
} }
protected onTag(t: string, picked: boolean){ protected onTag(t: string, picked: boolean){
@ -103,8 +127,8 @@
this.filterByTag(); this.filterByTag();
return; return;
} }
if( !picked && t == this.tag ){ // deselect if( !picked && t == this.tag ){ // back to default
this.tag = null; this.tag = DEFAULT_TAG;
this.filterByTag(); this.filterByTag();
return; return;
} }
@ -112,17 +136,17 @@
// apply filter by tag // apply filter by tag
private filterByTag(){ private filterByTag(){
const tag = this.tag; if( this.tag == DEFAULT_TAG ){
if( tag == null ){ this.filtered = this.ids;
this.tag = null;
this.filtered = [];
} else { } else {
this.filtered = skills.filtered(tag); this.filtered = skills.filtered(this.tag);
} }
// deselect if selection has been filtered out // maintain selection behavior:
// - deselect if current is no more present
// - keep if still listed
if( this.sel != null && this.filtered.indexOf(this.sel) < 0 ){ if( this.sel != null && this.filtered.indexOf(this.sel) < 0 ){
this.sel = null; this.select(0, true);
} }
} }
@ -173,6 +197,8 @@
background: linear-gradient(0, #564ba4, #745cfc); background: linear-gradient(0, #564ba4, #745cfc);
font-size: 1rem;
.container { .container {
display: flex; display: flex;
position: relative; position: relative;
@ -205,6 +231,7 @@
.skill-card { .skill-card {
margin: .3em 0; margin: .3em 0;
width: 12em;
} }
} }
@ -223,6 +250,7 @@
.skill-card { .skill-card {
margin: .3em 0; margin: .3em 0;
width: 12em;
} }
} }
@ -284,6 +312,7 @@
font-size: 1.7em; font-size: 1.7em;
font-weight: 500; font-weight: 500;
text-align: justify;
color: #c1c1c1; color: #c1c1c1;
padding: 1em; padding: 1em;
@ -333,4 +362,23 @@
} }
} }
// width > 1800px : center the container
@media screen and (min-width: 1800px) {
.container {
left: calc( 100vw/2 - 1800px/2 );
max-width: 1800px;
}
}
// screen less than 1400 p
@media screen and (max-width: 1400px) {
#skill-picker {
font-size: 1vw;
.skill-card {
width: 18vw;
}
}
}
</style> </style>

View File

@ -6,7 +6,7 @@
<template class='project' v-for='(proj) of projects'> <template class='project' v-for='(proj) of projects'>
<!-- id is used for navigation --> <!-- id is used for navigation -->
<div :key="'start-'+proj.name" class='start' :id='"p-" + proj.name'> <div :key="'start-'+proj.name" class='start' :id='"project-" + proj.name.replaceAll(" ", "_")'>
{{ proj.started_at | short_date }} {{ proj.started_at | short_date }}
</div> </div>
@ -17,7 +17,7 @@
<img :key="'skill-icon-'+proj.name" class='skill-icon' src='../assets/timeline/skills.svg' /> <img :key="'skill-icon-'+proj.name" class='skill-icon' src='../assets/timeline/skills.svg' />
<div :key="'skillset-'+proj.name" class='skillset'> <div :key="'skillset-'+proj.name" class='skillset'>
<span v-for='(skill) of proj.skills' :key='skill' v-html='skills[skill].name'></span> <SkillCard v-for='(id) of proj.skills' :key='"timeline-" + proj.name + "-" + id' :id='id' :active='id == skill' @pick='$emit("pick", id)'/>
</div> </div>
<img :key="'desc-icon-'+proj.name" class='desc-icon' src='../assets/timeline/info.svg' /> <img :key="'desc-icon-'+proj.name" class='desc-icon' src='../assets/timeline/info.svg' />
@ -53,8 +53,10 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'; import { Component, Vue } from 'vue-property-decorator';
import { Projects, Project } from '../model/projects'; import SkillCard from './SkillCard.vue';
import { Skills, tSkills } from '../model/skills'; import { Project } from '../model/projects';
import { tID } from '../model/skills';
import * as projects from '../service/projects';
function pluralize(n: number, s: string): string { function pluralize(n: number, s: string): string {
n = Math.floor(Math.abs(n)); n = Math.floor(Math.abs(n));
@ -76,6 +78,9 @@
} }
@Component({ @Component({
components: {
SkillCard,
},
filters: { filters: {
short_date: function(date: Date): string { short_date: function(date: Date): string {
return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }); return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' });
@ -110,15 +115,14 @@
return pluralize(diff/month, 'month'); return pluralize(diff/month, 'month');
} }
return pluralize(diff/year, 'year'); return pluralize(diff/year, 'year');
} },
} }
}) })
export default class Timeline extends Vue { export default class Timeline extends Vue {
private projects: Project[] = Projects; private skill: tID|null = null;
private skills: tSkills = Skills; private projects: Project[] = [];
private mounted() { private mounted() {
document.body.addEventListener('scroll', this.onScroll, { passive: true }); document.body.addEventListener('scroll', this.onScroll, { passive: true });
} }
@ -142,6 +146,15 @@
header.classList.remove('fixed'); header.classList.remove('fixed');
} }
} }
public filter(skill: tID|null) {
this.skill = skill;
if( skill == null ){
this.projects = [];
return;
}
this.projects = projects.bySkill(skill);
}
} }
</script> </script>
@ -176,7 +189,7 @@
display: grid; display: grid;
position: relative; position: relative;
width: 100vw; width: 100vw;
min-height: 100vh; // min-height: 100vh;
grid-template-columns: auto $icon-width $icon-width $space-width auto; grid-template-columns: auto $icon-width $icon-width $space-width auto;
grid-gap: .5em; grid-gap: .5em;
@ -203,9 +216,9 @@
.skill-icon, .desc-icon, .src-icon, .doc-icon, .end-icon { .skill-icon, .desc-icon, .src-icon, .doc-icon, .end-icon {
position: relative; position: relative;
width: #{$icon-width + .3rem}; width: #{$icon-width + .1rem};
height: #{$icon-width + .3rem}; height: #{$icon-width + .1rem};
margin-top: .3rem; margin-top: .1rem;
background: $bg-color; background: $bg-color;
} }
@ -223,6 +236,8 @@
font-size: .8em; font-size: .8em;
color: #535359; color: #535359;
padding-top: .3em; padding-top: .3em;
justify-self: flex-end;
} }
.name-icon { .name-icon {
@ -236,8 +251,11 @@
background: $bg-color; background: $bg-color;
} }
.name, .end { .name, .end {
color: #999999;
b { b {
color: #2c55cf; color: #745cfc;
font-weight: 500;
} }
span { span {
@ -271,21 +289,17 @@
.skillset { .skillset {
display: flex; display: flex;
flex-flow: row wrap; flex-flow: row wrap;
justify-content: flex-start; justify-content: flex-start;
align-items: flex-start; align-items: flex-start;
span { width: 50vw;
font-size: .8em;
padding: .4em .6em; .skill-card {
margin-right: .4em; margin-right: .4em;
margin-bottom: .4em; margin-bottom: .4em;
color: #fff;
background: #34343b;
border-radius: .4em / .4em;
user-select: none; user-select: none;
} }
} }

View File

@ -62,21 +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: "", info: "I started using mysql then MariaDB as my first introduction to SQL, mostly when I started learning web with PHP. I now prefer using postgreSQL instead as it tends to be more robust and performant overall.",
}, },
[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: "", info: "My main choice of dbms when using sql orfor relational databases. Had some experience with it mainly with go services with docker.",
}, },
[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: "", info: "I learned it during my Master's degree. I used advanced Mongo with schema validation and complex filter requests. I find this technology really interesting over SQL, but I find classical relational databases are easier to comprehend and I am more used to it.",
}, },
[tID.Vue]: { [tID.Vue]: {
@ -91,28 +91,28 @@ export const Skills: tSkills = {
link: 'https://angular.io', link: 'https://angular.io',
icon: 'skills/angular.svg', icon: 'skills/angular.svg',
tags: ['web', 'UI'], tags: ['web', 'UI'],
info: "", info: "I used it only once to break it apart in a R&D project. I had to \"hack\" it in order to inject services and components on-the-fly.<br><br>You had a kind of App store where you could install modules. The Angular-powered website would inject new navigation menus, pages and services according to the modules you had installed.<br><br>I do not like Angular, as I think Vue.js does a better job achieving the same goal with a cleaner API, and does not force you to embrace the Angular/Google way of coding and organizing things."
}, },
[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: "", info: "I used it often back in my Master's Degree period along with PHP backends. It allows to get away from webpack which I find overly complicated and a major point of failure for most frontends.<br><br>I haven't used it in a while but would highly consider it as it has no real prerequisite.",
}, },
[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: "", info: "I used it often to transform single-page applications into android, iOS or desktop apps at low cost. I haven't needed this kind of software for a while, it might now be favorable to use Capacitor or other \"alternatives\".",
}, },
[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: "", info: "... I used it a lot because it is often required. But I hate that it is so complex that most users use it without understanding what is really does. I avoid it whenever possible, the issue can be transposed to node_modules and the whole node ecosystem.",
}, },
[tID.WebGL]: { [tID.WebGL]: {
@ -120,21 +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: "", info: "I used it during my graduation years, the potential is great but I need to find a time to explore what's new.",
}, },
[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: "", info: "I used it mainly to build a discord clone (audio and video full-duplex streaming) as a school project. I also tried the Fourier transform to build nice graphics (c.f. Soundcloud) but have not succeeded at the time.",
}, },
[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: "", info: "I use them whenever possible to avoid the classical expensive polling.<br><br>I also created my own websocket client & server implementation in Go following nothing but the RFC for learning purposes.<br><br>I've used, designed and implemented websocket communications among personal and professional projects.",
}, },
[tID.Docker]: { [tID.Docker]: {
@ -142,21 +142,21 @@ 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: "", info: "I am using it for a while now. Most of my go projects use it as it costs no memory for your executable (multi-stage build from scratch) and allows for isolation and a better control over the running environment. I am learning docker a bit more every time I use it. It is often coupled with docker-compose in my projects.",
}, },
[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: "", info: "Felt in love with GNU/linux and its ecosystem a while back (was 14yo back then) and I've never stopped. I'm often the linux/bash guy of the team.<br><br>I use it extensively to automate anything a human can do as it avoids a lot of mistakes in most workflows.",
}, },
[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: "", info: "Felt in love with GNU/linux and its ecosystem a while back (was 14yo back then) and I've never stopped. <br><br>Linux has been my main OS choice since then. I stepped through Ubuntu, Debian, Manjaro, Elementary, Tails (live), and I am now on Solus for a few years.",
}, },
[tID.Systemd]: { [tID.Systemd]: {
name: 'systemd', name: 'systemd',

57
src/service/scroller.ts Normal file
View File

@ -0,0 +1,57 @@
let scroll_interval: number;
// scroll time in milliseconds
const SCROLL_MS = 500;
const SCROLL_MS_STEP = 5;
// go() scrolls to the element with the specified id with an optional offset.
// It achieves a smooth scrolling compared to the direct default one.
export function go(id: string, offset: number|undefined): void {
// find item with id
const item = document.querySelector(`#${id}`) as HTMLElement;
if( item == null ){
console.warn(`item with id '${id}' not found`);
return;
}
// get item absolute y coordinates
let targety = absY(item, 0);
if( offset != null ){
targety += offset;
}
clearInterval(scroll_interval);
const step = (targety - document.body.scrollTop) / (SCROLL_MS / SCROLL_MS_STEP);
scroll_interval = setInterval(
() => smooth_scroll(targety, step),
SCROLL_MS_STEP
);
}
function absY(item: HTMLElement|null, children_y: number): number {
if( item == null || item == document.body ){
return children_y;
}
return absY(item.parentElement, children_y + item.offsetTop);
}
// incremental smooth scroll
function smooth_scroll(target: number, step: number) {
const cur = document.body.scrollTop;
const max = (document.body as any).scrollTopMax;
// moving up & reached top or target -> stop
const upLimit = ( step < 0 && (cur <= 0 || cur <= target) );
// moving down & reached bottom or target -> stop
const downLimit = ( step > 0 && (cur >= max || cur >= target) );
if( upLimit || downLimit ){
clearInterval(scroll_interval);
document.body.scroll(0, target);
return
}
document.body.scroll(0, cur + step);
}