Browse Source

feat: new style

main
nicolas.marsal 3 years ago
parent
commit
5a7337f378
No known key found for this signature in database
GPG Key ID: 268AB819B6453541
  1. 41
      src/App.vue
  2. 2
      src/components/common/LeftSection.vue
  3. 27
      src/components/common/RightSection.vue
  4. 72
      src/components/common/WordCloud.vue
  5. 91
      src/components/experiences/MyExperiences.vue
  6. 25
      src/components/skills/MySkills.vue
  7. 32
      src/components/skills/ProgressBar.vue
  8. 37
      src/data/experiences.ts
  9. 5
      src/data/skills.ts
  10. 1
      src/data/types.ts

41
src/App.vue

@ -1,5 +1,4 @@
<script setup lang="ts">
import FirstNameAndLastName from "@/components/informations/FirstNameAndLastName.vue";
import MyInformations from "@/components/informations/MyInformations.vue";
import MyEducation from "@/components/education/MyEducation.vue";
import MyLanguages from "@/components/informations/MyLanguages.vue";
@ -20,7 +19,7 @@ import WordCloud from "@/components/common/WordCloud.vue";
width: 100%;
"
>
<WordCloud :width="290" :height="200"></WordCloud>
<WordCloud :width="250" :height="200"></WordCloud>
</div>
<MyInformations />
<MyEducation />
@ -35,39 +34,8 @@ import WordCloud from "@/components/common/WordCloud.vue";
</template>
<script lang="ts">
import {
Ansible,
Git,
Helm,
Javascript,
Jenkins,
Kubernetes,
Skaffold,
Typescript,
} from "@/data/skills";
export default {
name: "app",
methods: {
wordClickHandler(name, value, vm) {
console.log("wordClickHandler", name, value, vm);
},
},
data() {
return {
myColors: ["#1f77b4", "#629fc9", "#94bedb", "#c9e0ef"],
defaultWords: [
Kubernetes,
Skaffold,
Ansible,
Helm,
Javascript,
Typescript,
Git,
Jenkins,
].map((s) => ({ name: s.name, value: s.score })),
};
},
};
</script>
<style scoped>
@ -79,10 +47,10 @@ export default {
}
.left {
margin: 8px;
width: 320px;
margin: 0;
width: 250px;
flex-shrink: 0;
border-right: 1px solid gray;
border-right: 3px solid gray;
display: flex;
flex-direction: column;
}
@ -90,7 +58,6 @@ export default {
.right {
flex-grow: 1;
flex-shrink: 1;
margin: 8px;
display: flex;
flex-direction: column;
}

2
src/components/common/LeftSection.vue

@ -39,7 +39,7 @@ defineProps<{
.title::before,
.title::after {
content: "";
border-top: 1px solid gray;
border-top: 3px solid gray;
width: 100%;
}

27
src/components/common/RightSection.vue

@ -21,10 +21,33 @@ defineProps<{
flex-direction: column;
}
.title {
font-size: 18px;
font-size: 24px;
font-weight: bolder;
font-variant: titling-caps;
font-variant: small-caps;
text-transform: capitalize;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding-top: 8px;
padding-bottom: 8px;
padding-right: 8px;
text-align: center;
width: auto;
}
.title::before,
.title::after {
content: "";
border-top: 3px solid gray;
width: 100%;
}
.title::before {
margin-right: 12px;
}
.title::after {
margin-left: 12px;
}
.body {
flex: 1;

72
src/components/common/WordCloud.vue

@ -1,5 +1,11 @@
<template>
<svg :width="width" :height="height">
<svg
:width="width"
:height="height"
@mousemove="onMousemove"
@mouseover="(e) => (this.hover = true)"
@mouseout="this.hover = false"
>
<g :transform="`translate(${width / 2}, ${height / 2})`">
<text
v-for="word in words"
@ -7,10 +13,12 @@
:style="{
fontSize: word.size + 'px',
fontFamily: word.font,
cursor: 'pointer',
}"
:transform="`translate(${word.x}, ${word.y})rotate(${word.rotate})`"
text-anchor="middle"
@click="highlightText(word.text)"
class="word"
>
{{ word.text }}
</text>
@ -18,6 +26,12 @@
</svg>
</template>
<style scoped>
.word {
transition: 1s transform ease-in-out;
}
</style>
<script lang="ts">
import cloud from "d3-cloud";
import * as skills from "@/data/skills";
@ -30,24 +44,37 @@ export default defineComponent({
},
data() {
return {
loading: false,
animate: true,
hover: false,
words: [] as cloud.Word[],
minFontSize: 8,
maxFontSize: 40,
mouseX: undefined,
mouseY: undefined,
};
},
created() {
this.computeWords();
// setInterval(() => {
// if (!this.animate || this.hover) {
// return;
// }
// this.computeWords();
// }, 1500);
},
methods: {
onMousemove(e: any) {
this.mouseX = e.clientX;
this.mouseY = e.clientY;
},
highlightText(text: string) {
console.log(text);
},
computeWords() {
this.loading = true;
let words: (cloud.Word & { size: number })[] = Object.keys(skills)
.map((p) => skills[p])
.map((s) => ({ text: s.name, size: s.score }));
let words: (cloud.Word & { size: number; fixed?: boolean })[] =
Object.keys(skills)
.map((p) => skills[p])
.map((s) => ({ text: s.name, size: s.score }));
const min = words
.map((s) => s.size)
@ -55,11 +82,18 @@ export default defineComponent({
const max = words
.map((s) => s.size)
.reduce((prev, current) => Math.max(prev, current));
const scaleFontSize = (size: number) => {
const number =
((size - min) / (max - min)) * (this.maxFontSize - this.minFontSize) +
this.minFontSize;
return number;
const scaleFontSize = ({
size,
fixed,
}: {
size: number;
fixed: boolean;
}) => {
return fixed
? size
: ((size - min) / (max - min)) *
(this.maxFontSize - this.minFontSize) +
this.minFontSize;
};
const randomRotate = (s: number) => {
const range = 120 * (1 - ((s - min) / (max - min)) * 0.7);
@ -67,7 +101,8 @@ export default defineComponent({
};
words = [
{
size: max * 1.6,
size: 30,
fixed: true,
text: "Nicolas Marsal",
rotate: -30,
padding: 6,
@ -81,19 +116,22 @@ export default defineComponent({
.words(words)
.padding((w) => w.padding ?? 1)
.rotate((w) => w.rotate ?? randomRotate(w.size))
.spiral("archimedean")
.fontSize((d) => scaleFontSize(d.size))
.spiral("rectangular")
.fontSize((d) => scaleFontSize(d))
.on("end", (d: cloud.Word[]) => {
console.log(
`d: ${d.length} - w: ${words.length} - f: ${this.maxFontSize}`
);
if (d.length < words.length) {
console.log("On recommence !");
// console.log("On recommence !");
this.maxFontSize = this.maxFontSize - 2;
this.computeWords();
if (this.maxFontSize > 2 * this.minFontSize) {
this.computeWords();
} else {
this.words = d;
}
} else {
this.words = d;
this.loading = false;
}
})
.start();

91
src/components/experiences/MyExperiences.vue

@ -5,16 +5,31 @@ import RightSection from "@/components/common/RightSection.vue";
<template>
<RightSection :title="$t('section.experiences')">
<div class="experiences">
<div class="experience" v-for="experience in experiences">
<div class="header">{{ experience.company }}</div>
<div class="body-wrapper">
<div class="border">
<div class="end">{{ experience.endAt }}</div>
<div style="flex-grow: 1"> </div>
<div class="start">{{ experience.startAt }}</div>
<div
class="experience"
v-for="experience in experiences"
:key="experience.company"
>
<div class="header">
<div class="role">{{ experience.role }}</div>
<div class="company">
{{ experience.company }}
<div class="date">
{{ experience.startAt }} - {{ experience.endAt }}
</div>
</div>
<div class="body">
<div v-for="mission in experience.missions">
</div>
<div class="missions">
<div
class="mission"
v-for="mission in experience.missions"
:key="mission.description"
style="display: flex; flex-direction: row; align-items: center"
>
<div style="flex: 1 1; font-size: 12px">
{{ mission.skills.map((s) => s.name).join(", ") }}
</div>
<div style="flex: 3 1">
{{ mission.description }}
</div>
</div>
@ -39,50 +54,36 @@ export default defineComponent({
</script>
<style scoped>
.experiences {
padding-top: 14px;
display: flex;
flex-direction: column;
}
.header {
}
.border {
display: flex;
flex-direction: row;
justify-content: space-between;
writing-mode: tb-rl;
}
.experiences > div:nth-child(even) .start {
transform: none;
}
.experiences > div:nth-child(odd) .start {
transform: rotate(180deg);
}
.experiences > div:nth-child(even) .end {
transform: none;
.experience {
margin-left: 20px;
}
.experiences > div:nth-child(odd) .end {
transform: rotate(180deg);
.experience:nth-child(n + 2) {
padding-top: 20px;
}
.experiences > div:nth-child(even) .header {
text-align: right;
margin-right: 20px;
.missions {
margin: 8px;
}
.experiences > div:nth-child(odd) .header {
text-align: left;
margin-left: 20px;
.mission:nth-child(n + 2) {
padding-top: 8px;
}
.experiences > div:nth-child(even) .border {
order: 2;
.header {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: baseline;
}
.experiences > div:nth-child(odd) .border {
order: 0;
.role {
font-size: larger;
}
.body-wrapper {
.company {
font-size: large;
display: flex;
flex-direction: row;
align-items: baseline;
}
.body {
margin: 16px;
.date {
font-style: italic;
font-size: small;
padding-left: 20px;
}
</style>

25
src/components/skills/MySkills.vue

@ -1,10 +1,22 @@
<script setup lang="ts">
import RightSection from "@/components/common/RightSection.vue";
import ProgressBar from "@/components/skills/ProgressBar.vue";
</script>
<template>
<RightSection :title="$t('section.skills')">
Here are my skills
<div class="skill">
<div class="skill-name">Test</div>
<ProgressBar style="width: 70%" percent="60" />
</div>
<div class="skill">
<div class="skill-name">Java</div>
<ProgressBar style="width: 70%" percent="70" />
</div>
<div class="skill">
<div class="skill-name">Docker</div>
<ProgressBar style="width: 70%" percent="80" />
</div>
</RightSection>
</template>
@ -14,4 +26,13 @@ export default {
};
</script>
<style scoped></style>
<style scoped>
.skill {
width: 300px;
display: flex;
flex-direction: row;
justify-content: space-between;
margin-top: 8px;
margin-left: 8px;
}
</style>

32
src/components/skills/ProgressBar.vue

@ -0,0 +1,32 @@
<template>
<div class="progress-bar">
<div class="fill" :style="{ width: `${percent}%` }"></div>
<div class="empty" :style="{ width: `${100 - percent}%` }"></div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
name: "ProgressBar",
props: {
percent: Number,
},
});
</script>
<style>
.progress-bar {
display: flex;
flex-direction: row;
align-items: center;
}
.progress-bar .fill {
border: 1px solid gray;
height: 0.9em;
}
.progress-bar .empty {
border-top: 1px solid gray;
}
</style>

37
src/data/experiences.ts

@ -21,6 +21,7 @@ import {
Kubernetes,
Linux,
Pencil,
React,
Scrum,
Skaffold,
SpringBoot,
@ -31,10 +32,34 @@ import {
} from "@/data/skills";
const experience = (): Experience[] => {
return [
{
company: "Sunday",
startAt: 2021,
endAt: 2022,
role: "Ingénieur Logiciel Senior",
missions: [
{
description:
"Conception/Développement/DevOps pour un logiciel de déploiement d'un ERP, avec la création d'une stack dev entièrement sur cluster",
skills: [SpringBoot, React, Kubernetes],
},
{
description:
"Développement d'une application de gestion de compatibilités de versions entre les modules d'un ERP",
skills: [SpringBoot, Angular, Docker, Scrum],
},
{
description:
"Diffuseur technique : conception, développement et utilisation d'outils de livraison en production",
skills: [Bash, Ansible, Linux],
},
],
},
{
company: "Mipih",
startAt: 2017,
endAt: 2021,
role: "Ingénieur Logiciel Senior",
missions: [
{
description:
@ -57,10 +82,11 @@ const experience = (): Experience[] => {
company: "Lyra Network",
startAt: 2012,
endAt: 2017,
role: "Ingénieur Logiciel",
missions: [
{
description:
"Acteur et animateur de la cellule Industrialisa5on : standardisation et mise en place d'une chaîne CI",
"Acteur et animateur de la cellule Industrialisation : standardisation et mise en place d'une chaîne CI",
skills: [Agility, DevOps, Jenkins],
},
{
@ -72,15 +98,10 @@ const experience = (): Experience[] => {
description: "Responsable Gestion de configuration du service",
skills: [Svn, Git],
},
{
description:
"Concepteur et développeur API Rest / UX WEB pour application web sur tablette",
skills: [Jee, HtmlCss, ExtJS],
},
{
description:
"Concepteur et développeur UX pour la plateforme de paiement",
skills: [Jee, HtmlCss, ExtJS],
skills: [Jee, HtmlCss, ExtJS, UX],
},
],
},
@ -88,6 +109,7 @@ const experience = (): Experience[] => {
company: "Studec",
startAt: 2010,
endAt: 2012,
role: "Ingénieur Logiciel",
missions: [
{
description:
@ -115,6 +137,7 @@ const experience = (): Experience[] => {
company: "SOGETI High Tech",
startAt: 2006,
endAt: 2010,
role: "Ingénieur Logiciel Débutant",
missions: [
{
description:

5
src/data/skills.ts

@ -46,6 +46,11 @@ export const Angular: Skill = {
score: 17,
parent: [Typescript, UX],
};
export const React: Skill = {
name: "Angular",
score: 17,
parent: [Typescript, UX],
};
export const ExtJS: Skill = {
name: "ExtJS",
score: 14,

1
src/data/types.ts

@ -8,6 +8,7 @@ export interface Experience {
startAt: number;
endAt: number;
missions: Mission[];
role: string;
}
export type Skill = {

Loading…
Cancel
Save