Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac80065e82 | ||
|
|
3610f338aa | ||
|
|
bec4c608f1 | ||
|
|
f76a7a8c4c |
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -114,6 +114,7 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"manganis",
|
"manganis",
|
||||||
"serde",
|
"serde",
|
||||||
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
dioxus = { version = "0.5", features = ["fullstack", "router"] }
|
dioxus = { version = "0.5", features = ["fullstack", "router"] }
|
||||||
|
tokio = { version = "1", features = ["full"], optional = true }
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM rust:1.79.0 AS dioxus
|
FROM rust:1.80.1 AS dioxus
|
||||||
RUN cargo install dioxus-cli@^0.5
|
RUN cargo install dioxus-cli@^0.5
|
||||||
|
|
||||||
FROM dioxus AS builder
|
FROM dioxus AS builder
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
My personal website.
|
My personal website.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npx tailwindcss -i ./input.css -o ./assets/tailwind.css --watch
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh
|
||||||
|
dx serve --platform fullstack
|
||||||
|
```
|
||||||
|
|
||||||
## Screenshot
|
## Screenshot
|
||||||
|
|
||||||
[[./resources/screenshot.webp]]
|
[[./resources/screenshot.webp]]
|
||||||
|
|||||||
1
assets/pictures/Gitea_Logo.svg
Normal file
1
assets/pictures/Gitea_Logo.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" style="enable-background:new 0 0 640 640;" xml:space="preserve" viewBox="5.67 143.05 628.65 387.55"> <g> <path id="teabag" style="fill:#FFFFFF" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8 c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4 c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"></path> <g> <g> <path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2 c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5 c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5 c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3 c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1 C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4 c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7 S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55 c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8 l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"></path> <path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4 c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1 c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9 c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3 c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3 c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29 c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8 C343.2,346.5,335,363.3,326.8,380.1z"></path> </g> </g> </g> </svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
5
assets/pictures/LI-Bug.svg.original.svg
Normal file
5
assets/pictures/LI-Bug.svg.original.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg id="Group_1282" data-name="Group 1282" xmlns="http://www.w3.org/2000/svg" width="76.624" height="65.326" viewBox="0 0 76.624 65.326">
|
||||||
|
<path id="Path_2525" data-name="Path 2525" d="M1165,274.515a1.2,1.2,0,0,0,1.21-1.269c0-.9-.543-1.33-1.657-1.33h-1.8v4.712h.677v-2.054h.832l.019.025,1.291,2.029h.724l-1.389-2.1Zm-.783-.472h-.785V272.45h.995c.514,0,1.1.084,1.1.757,0,.774-.593.836-1.314.836" transform="translate(-1092.136 -213.406)" fill="#0a66c2"/>
|
||||||
|
<path id="Path_2520" data-name="Path 2520" d="M958.98,112.559h-9.6V97.525c0-3.585-.064-8.2-4.993-8.2-5,0-5.765,3.906-5.765,7.939v15.294h-9.6V81.642h9.216v4.225h.129a10.1,10.1,0,0,1,9.093-4.994c9.73,0,11.524,6.4,11.524,14.726ZM918.19,77.416a5.571,5.571,0,1,1,5.57-5.572,5.571,5.571,0,0,1-5.57,5.572m4.8,35.143h-9.61V81.642h9.61Zm40.776-55.2h-55.21a4.728,4.728,0,0,0-4.781,4.67v55.439a4.731,4.731,0,0,0,4.781,4.675h55.21a4.741,4.741,0,0,0,4.8-4.675V62.025a4.738,4.738,0,0,0-4.8-4.67" transform="translate(-903.776 -57.355)" fill="#0a66c2"/>
|
||||||
|
<path id="Path_2526" data-name="Path 2526" d="M1156.525,264.22a4.418,4.418,0,1,0,.085,0h-.085m0,8.33a3.874,3.874,0,1,1,3.809-3.938c0,.022,0,.043,0,.065a3.791,3.791,0,0,1-3.708,3.871h-.1" transform="translate(-1084.362 -207.809)" fill="#0a66c2"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -608,11 +608,6 @@ video {
|
|||||||
inset-inline-start: -0.375rem;
|
inset-inline-start: -0.375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx-16 {
|
|
||||||
margin-left: 4rem;
|
|
||||||
margin-right: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx-auto {
|
.mx-auto {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
@@ -676,15 +671,6 @@ video {
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.size-auto {
|
|
||||||
width: auto;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-16 {
|
|
||||||
height: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h-24 {
|
.h-24 {
|
||||||
height: 6rem;
|
height: 6rem;
|
||||||
}
|
}
|
||||||
@@ -705,18 +691,10 @@ video {
|
|||||||
height: 1px;
|
height: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h-screen {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.min-h-screen {
|
.min-h-screen {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.w-16 {
|
|
||||||
width: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.w-24 {
|
.w-24 {
|
||||||
width: 6rem;
|
width: 6rem;
|
||||||
}
|
}
|
||||||
@@ -786,10 +764,20 @@ video {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gap-2 {
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.gap-4 {
|
.gap-4 {
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
|
||||||
|
--tw-space-x-reverse: 0;
|
||||||
|
margin-right: calc(1rem * var(--tw-space-x-reverse));
|
||||||
|
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
|
||||||
|
}
|
||||||
|
|
||||||
.space-x-8 > :not([hidden]) ~ :not([hidden]) {
|
.space-x-8 > :not([hidden]) ~ :not([hidden]) {
|
||||||
--tw-space-x-reverse: 0;
|
--tw-space-x-reverse: 0;
|
||||||
margin-right: calc(2rem * var(--tw-space-x-reverse));
|
margin-right: calc(2rem * var(--tw-space-x-reverse));
|
||||||
@@ -802,6 +790,12 @@ video {
|
|||||||
margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
|
margin-bottom: calc(0.5rem * var(--tw-space-y-reverse));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.space-y-8 > :not([hidden]) ~ :not([hidden]) {
|
||||||
|
--tw-space-y-reverse: 0;
|
||||||
|
margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
|
||||||
|
margin-bottom: calc(2rem * var(--tw-space-y-reverse));
|
||||||
|
}
|
||||||
|
|
||||||
.overflow-x-auto {
|
.overflow-x-auto {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
@@ -1286,6 +1280,12 @@ video {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sm\:space-x-8 > :not([hidden]) ~ :not([hidden]) {
|
||||||
|
--tw-space-x-reverse: 0;
|
||||||
|
margin-right: calc(2rem * var(--tw-space-x-reverse));
|
||||||
|
margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse)));
|
||||||
|
}
|
||||||
|
|
||||||
.sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) {
|
.sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) {
|
||||||
--tw-space-y-reverse: 0;
|
--tw-space-y-reverse: 0;
|
||||||
margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
|
margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
|
||||||
|
|||||||
@@ -142,3 +142,105 @@ pub fn HR() -> Element {
|
|||||||
hr { class:"h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"}
|
hr { class:"h-px my-8 bg-gray-200 border-0 dark:bg-gray-700"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Props)]
|
||||||
|
pub struct BoldingProp {
|
||||||
|
authors: String,
|
||||||
|
patterns: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Bolding(prop: BoldingProp) -> Element {
|
||||||
|
let mut elements = vec![];
|
||||||
|
let mut last_index = 0;
|
||||||
|
let authors_text = &prop.authors;
|
||||||
|
|
||||||
|
let mut matches: Vec<(usize, usize, &str)> = vec![];
|
||||||
|
|
||||||
|
for pattern in &prop.patterns {
|
||||||
|
for (start, _) in authors_text.match_indices(pattern) {
|
||||||
|
matches.push((start, start + pattern.len(), pattern));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matches.sort_by_key(|(start, _, _)| *start);
|
||||||
|
|
||||||
|
for (start, end, matched_pattern) in matches {
|
||||||
|
if last_index < start {
|
||||||
|
elements.push(rsx! { "{&authors_text[last_index..start]}" });
|
||||||
|
}
|
||||||
|
|
||||||
|
elements.push(rsx! { b { "{matched_pattern}" } });
|
||||||
|
last_index = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if last_index < authors_text.len() {
|
||||||
|
elements.push(rsx! { "{&authors_text[last_index..]}" });
|
||||||
|
}
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
P {
|
||||||
|
for i in elements {
|
||||||
|
{i}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Props)]
|
||||||
|
pub struct UrlingProp {
|
||||||
|
#[props(default = "".to_string())]
|
||||||
|
class: String,
|
||||||
|
text: String,
|
||||||
|
patterns: Vec<String>,
|
||||||
|
url: String,
|
||||||
|
#[props(default = true)]
|
||||||
|
new_tab: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Urling(prop: UrlingProp) -> Element {
|
||||||
|
let mut elements = vec![];
|
||||||
|
let mut last_index = 0;
|
||||||
|
let text = &prop.text;
|
||||||
|
|
||||||
|
let mut matches: Vec<(usize, usize, &str)> = vec![];
|
||||||
|
|
||||||
|
for pattern in &prop.patterns {
|
||||||
|
for (start, _) in text.match_indices(pattern) {
|
||||||
|
matches.push((start, start + pattern.len(), pattern));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matches.sort_by_key(|(start, _, _)| *start);
|
||||||
|
|
||||||
|
for (start, end, matched_pattern) in matches {
|
||||||
|
if last_index < start {
|
||||||
|
elements.push(rsx! { "{&text[last_index..start]}" });
|
||||||
|
}
|
||||||
|
|
||||||
|
elements.push(rsx! {
|
||||||
|
Link {
|
||||||
|
class: "{prop.class}",
|
||||||
|
to: "{prop.url}",
|
||||||
|
new_tab: prop.new_tab,
|
||||||
|
"{matched_pattern}"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
last_index = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if last_index < text.len() {
|
||||||
|
elements.push(rsx! { "{&text[last_index..]}" });
|
||||||
|
}
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
P {
|
||||||
|
for i in elements {
|
||||||
|
{i}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
use dioxus::prelude::*;
|
|
||||||
|
|
||||||
use crate::components::UnderConstruction;
|
|
||||||
|
|
||||||
#[component]
|
|
||||||
pub fn Consulting() -> Element {
|
|
||||||
rsx! {
|
|
||||||
UnderConstruction { },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
36
src/cv.rs
36
src/cv.rs
@@ -7,16 +7,25 @@ use crate::components::{H4, HR};
|
|||||||
pub fn CV() -> Element {
|
pub fn CV() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "flex flex-col ",
|
class: "flex flex-col",
|
||||||
|
div {
|
||||||
|
class: "flex flex-col sm:flex-row justify-center items-center sm:space-x-8 space-y-8 sm:space-y-0",
|
||||||
|
img {
|
||||||
|
class: "rounded-full w-24 h-24",
|
||||||
|
alt: "headshot",
|
||||||
|
src: "/pictures/headshot.webp"
|
||||||
|
}
|
||||||
Introduction {},
|
Introduction {},
|
||||||
|
Socials {}
|
||||||
|
},
|
||||||
|
|
||||||
HR {}
|
HR {}
|
||||||
div {
|
div {
|
||||||
class: "flex justify-between",
|
class: "flex flex-col justify-between sm:flex-row",
|
||||||
WorkExperience {},
|
WorkExperience {},
|
||||||
Miscellaneous {},
|
Miscellaneous {},
|
||||||
},
|
},
|
||||||
HR {},
|
HR {},
|
||||||
Socials {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,12 +36,9 @@ fn Introduction() -> Element {
|
|||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "flex",
|
class: "flex",
|
||||||
img {
|
P { { translate!(i18, "cv.introduction_0") } },
|
||||||
class: "rounded-full w-16 h-16 mx-16",
|
P { { translate!(i18, "cv.introduction_1") } }
|
||||||
src: "/pictures/headshot.webp"
|
},
|
||||||
}
|
|
||||||
P { { translate!(i18, "cv.introduction") }}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +195,7 @@ fn CVEntry(props: CVEntryProps) -> Element {
|
|||||||
time { class:"mb-1 text-sm font-normal leading-none text-gray-400 dark:text-gray-500", "{props.time}"},
|
time { class:"mb-1 text-sm font-normal leading-none text-gray-400 dark:text-gray-500", "{props.time}"},
|
||||||
h6 { class: "text-lg font-semibold text-gray-900 dark:text-white", "{props.title}"}
|
h6 { class: "text-lg font-semibold text-gray-900 dark:text-white", "{props.title}"}
|
||||||
ul {
|
ul {
|
||||||
class: "flex",
|
class: "flex flex-wrap gap-2",
|
||||||
for (index, value) in props.technologies.iter().enumerate() {
|
for (index, value) in props.technologies.iter().enumerate() {
|
||||||
li { key: "{index}", RandomBadge { text: "{value}"} }
|
li { key: "{index}", RandomBadge { text: "{value}"} }
|
||||||
}
|
}
|
||||||
@@ -228,9 +234,17 @@ fn random_badge_color(seed: usize) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn Socials() -> Element {
|
fn Socials() -> Element {
|
||||||
|
let i18 = use_i18();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
"todo!()"
|
class: "ms-8 max-w-3/4",
|
||||||
|
H4 { { translate!(i18, "cv.socials.title") } },
|
||||||
|
div {
|
||||||
|
class: "flex justify-center items-center space-x-4",
|
||||||
|
P { Link { to:"https://www.linkedin.com/in/tudattr/", class:"hover:underline", new_tab: true, img { class: "h-8", src:"/pictures/LI-Bug.svg.original.svg", alt:"LinkedIn Logo" } }},
|
||||||
|
P { Link { to:"https://git.tudattr.dev/tudattr", class:"hover:underline", new_tab: true, img { class: "h-8", src:"/pictures/Gitea_Logo.svg", alt:"Gitea Logo" } }},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/home.rs
21
src/home.rs
@@ -1,10 +1,11 @@
|
|||||||
use crate::components::{Card, P};
|
use crate::components::{Card, Urling, P};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_sdk::{i18n::use_i18, translate};
|
use dioxus_sdk::{i18n::use_i18, translate};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Home() -> Element {
|
pub fn Home() -> Element {
|
||||||
let i18 = use_i18();
|
let i18 = use_i18();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "container mx-auto p-4 flex items-center justify-center max-w-md w-full",
|
class: "container mx-auto p-4 flex items-center justify-center max-w-md w-full",
|
||||||
@@ -16,16 +17,16 @@ pub fn Home() -> Element {
|
|||||||
class: "py-4",
|
class: "py-4",
|
||||||
div {
|
div {
|
||||||
class: "mb-2",
|
class: "mb-2",
|
||||||
P { { translate!(i18, "home.card.l1") } },
|
for line in translate!(i18, "home.card.text").split("\n").into_iter() {
|
||||||
P { { translate!(i18, "home.card.l2") } },
|
P {
|
||||||
P { { translate!(i18, "home.card.l3") },
|
Urling {
|
||||||
Link {
|
|
||||||
to: "https://git.tudattr.dev/explore/repos",
|
|
||||||
new_tab: true,
|
|
||||||
class: "items-center font-medium hover:underline",
|
class: "items-center font-medium hover:underline",
|
||||||
"gitea"},
|
text: line,
|
||||||
{ translate!(i18, "home.card.l3_1") }
|
patterns: vec!["Gitea".to_string()],
|
||||||
},
|
url: "https://git.tudattr.dev/explore/repos"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Link {
|
Link {
|
||||||
|
|||||||
@@ -1,48 +1,60 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_sdk::{i18n::use_i18, translate};
|
use dioxus_sdk::{i18n::use_i18, translate};
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
use crate::components::{H1, HR, P};
|
use crate::components::{H1, HR, P};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Impressum() -> Element {
|
pub fn Impressum() -> Element {
|
||||||
let mut show_impressum = use_signal(|| false);
|
|
||||||
let i18 = use_i18();
|
let i18 = use_i18();
|
||||||
|
let mut impressum = use_signal(Vec::<String>::new);
|
||||||
|
let mut contact = use_signal(Vec::<String>::new);
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
if show_impressum() {
|
|
||||||
div {
|
div {
|
||||||
div {
|
div {
|
||||||
class: "flex flex-col items-center",
|
class: "flex flex-col items-center",
|
||||||
button {
|
button {
|
||||||
onclick: move |_| {
|
onclick: move |_| async move {
|
||||||
info!("Hide impressum.");
|
if let Ok(data) = get_impressum().await {
|
||||||
|
impressum.set(data.clone());
|
||||||
|
}
|
||||||
|
if let Ok(data) = get_contact().await {
|
||||||
|
contact.set(data.clone());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
H1 { { translate!(i18, "impressum.on") } },
|
H1 { { translate!(i18, "impressum.on") } },
|
||||||
},
|
},
|
||||||
P { "Tuan-Dat Tran" },
|
|
||||||
P { "c/o AutorenServices.de" },
|
|
||||||
P { "Birkenallee 24" },
|
|
||||||
P { "36037 Fulda" },
|
|
||||||
},
|
},
|
||||||
HR {}
|
|
||||||
div {
|
div {
|
||||||
class: "flex flex-col items-center",
|
class: "flex flex-col items-center",
|
||||||
P { "tuan-dat.tran@tudattr.dev" },
|
for line in impressum() {
|
||||||
P { "+49 176 83468388" },
|
P { {line} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
if !impressum.read().is_empty() { HR{} },
|
||||||
div {
|
div {
|
||||||
class: "flex flex-col items-center p-3",
|
class: "flex flex-col items-center",
|
||||||
button {
|
for line in contact() {
|
||||||
onclick: move |_| async move {
|
P { {line} }
|
||||||
show_impressum.set(true);
|
}
|
||||||
},
|
|
||||||
|
|
||||||
H1 { { translate!(i18, "impressum.off") } },
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[server(GetServerData)]
|
||||||
|
async fn get_impressum() -> Result<Vec<String>, ServerFnError> {
|
||||||
|
Ok(vec![
|
||||||
|
"Tuan-Dat Tran".to_string(),
|
||||||
|
"c/o AutorenServices.de".to_string(),
|
||||||
|
"Birkenallee 24".to_string(),
|
||||||
|
"36037 Fulda".to_string(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_contact() -> Result<Vec<String>, ServerFnError> {
|
||||||
|
Ok(vec![
|
||||||
|
"tuan-dat.tran@tudattr.dev".to_string(),
|
||||||
|
"+49 176 83468388".to_string(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,15 +16,12 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"name": "Tuan-Dat Tran",
|
"name": "Tuan-Dat Tran",
|
||||||
"gender": "",
|
"gender": "",
|
||||||
"l1": "Hallihallo! 👋🏻👋🏼👋🏽👋🏾👋🏿",
|
"text": "Hallihallo! 👋🏻👋🏼👋🏽👋🏾👋🏿\nWillkommen auf meiner kleinen Webseite im World Wide Web.\nMein Name ist Tuan und ich bin Linux-Bastler, IT-Security Nerd und Automatisierer aus Leidenschaft.\nWährend du hier bist, schau dir doch meine Projekte auf Gitea an.\n",
|
||||||
"l2": "Willkommen auf meiner kleinen Webseite im World Wide Web.",
|
|
||||||
"l3": "Während du hier bist, schau dir doch meine Projekte auf ",
|
|
||||||
"l3_1": " an.",
|
|
||||||
"contact_button": "Get in touch."
|
"contact_button": "Get in touch."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cv": {
|
"cv": {
|
||||||
"introduction": "Während meines Bachelorstudiums habe ich viele Erfahrungen in der Industrie und im Studium gesammelt. Meine beruflichen und persönlichen Interessen sind DevOps/IaC, Systems/Software Security und Computer Networking. All diese Interessen vertiefe ich in persönlichen Projekten wie meinem Homelab und CTF-Challenges.",
|
"introduction_0": "Während meines Bachelorstudiums habe ich viele Erfahrungen in der Industrie und im Studium gesammelt. Meine beruflichen und persönlichen Interessen sind DevOps/IaC, Systems/Software Security und Computer Networking. All diese Interessen vertiefe ich in persönlichen Projekten wie meinem Homelab und CTF-Challenges.",
|
||||||
"workexperience": {
|
"workexperience": {
|
||||||
"title": "Berufserfahrung",
|
"title": "Berufserfahrung",
|
||||||
"se1_gefeba": {
|
"se1_gefeba": {
|
||||||
@@ -53,6 +50,9 @@
|
|||||||
"description": "Während meiner Tätigkeit als wissenschaftlicher Mitarbeiter in der Network Communication System Research Group an der Universität Duisburg-Essen habe ich an der Forschung rund um Software Defined Networking, 5G, Staukontrollalgorithmen und föderiertes maschinelles Lernen mitgearbeitet. Ich habe die On-Premise- und Cloud-Infrastruktur, das Inventarsystem und die Online-Präsenz der Forschungsgruppe aufgebaut und verwaltet."
|
"description": "Während meiner Tätigkeit als wissenschaftlicher Mitarbeiter in der Network Communication System Research Group an der Universität Duisburg-Essen habe ich an der Forschung rund um Software Defined Networking, 5G, Staukontrollalgorithmen und föderiertes maschinelles Lernen mitgearbeitet. Ich habe die On-Premise- und Cloud-Infrastruktur, das Inventarsystem und die Online-Präsenz der Forschungsgruppe aufgebaut und verwaltet."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"socials": {
|
||||||
|
"title": "Profile"
|
||||||
|
},
|
||||||
"education": {
|
"education": {
|
||||||
"title": "Bildungsweg",
|
"title": "Bildungsweg",
|
||||||
"bachelor": {
|
"bachelor": {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"texts": {
|
"texts": {
|
||||||
"headers": {
|
"headers": {
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
"cv": "CV",
|
"cv": "Résumé",
|
||||||
"publications_projects": "Publications/Projects",
|
"publications_projects": "Publications/Projects",
|
||||||
"consulting": "Consulting",
|
"consulting": "Consulting",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
@@ -16,15 +16,12 @@
|
|||||||
"card": {
|
"card": {
|
||||||
"name": "Tuan-Dat Tran",
|
"name": "Tuan-Dat Tran",
|
||||||
"gender": "(He/Him)",
|
"gender": "(He/Him)",
|
||||||
"l1": "Hey there! 👋🏻👋🏼👋🏽👋🏾👋🏿",
|
"text": "Hey there! 👋🏻👋🏼👋🏽👋🏾👋🏿\nWelcome to my little place on the internet\nMy name is Tuan and I'm passionate about Linux, system security, automation, network performance tweaking and all things tech.\nWhile you're here, why don't you check out my projects over on Gitea?",
|
||||||
"l2": "Welcome to my little place on the internet.",
|
|
||||||
"l3": "While you're here, why don't you check out my projects over on ",
|
|
||||||
"l3_1": "?",
|
|
||||||
"contact_button": "Get in touch."
|
"contact_button": "Get in touch."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cv": {
|
"cv": {
|
||||||
"introduction": "While studying for my bachelors degree I accumulated a lot of industry and academic experience. My professional and personal intererests are DevOps/IaC, Systems/Software Security and Computer Networking. All of which I deepen in personal projects such as my homelab and CTF challenges.",
|
"introduction_0": "While studying for my bachelors degree I accumulated a lot of industry and academic experience. My professional and personal intererests are DevOps/IaC, Systems/Software Security and Computer Networking. All of which I deepen in personal projects such as my homelab and CTF challenges.",
|
||||||
"workexperience": {
|
"workexperience": {
|
||||||
"title": "Work Experience",
|
"title": "Work Experience",
|
||||||
"se1_gefeba": {
|
"se1_gefeba": {
|
||||||
@@ -53,6 +50,9 @@
|
|||||||
"description": "While working for the Network Communication System Research Group at the University Duisburg-Essen as a research assistant I've assisted in research around software defined networking, 5G, congestion control algorithms and federated machine learning. I've established and managed the research groups on-premise and cloud infractructure, inventory system and online presence."
|
"description": "While working for the Network Communication System Research Group at the University Duisburg-Essen as a research assistant I've assisted in research around software defined networking, 5G, congestion control algorithms and federated machine learning. I've established and managed the research groups on-premise and cloud infractructure, inventory system and online presence."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"socials": {
|
||||||
|
"title": "Socials"
|
||||||
|
},
|
||||||
"education": {
|
"education": {
|
||||||
"title": "Education",
|
"title": "Education",
|
||||||
"bachelor": {
|
"bachelor": {
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_sdk::{i18n::use_i18, translate};
|
use dioxus_sdk::{i18n::use_i18, translate};
|
||||||
|
|
||||||
use crate::components::H1;
|
|
||||||
|
|
||||||
pub fn Footer() -> Element {
|
pub fn Footer() -> Element {
|
||||||
let i18 = use_i18();
|
let i18 = use_i18();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "container mx-auto",
|
class: "container mx-auto pb-4",
|
||||||
// ToolsUsed {},
|
|
||||||
footer {
|
footer {
|
||||||
class:"bg-white rounded-lg shadow dark:bg-gray-800",
|
class:"bg-white rounded-lg shadow dark:bg-gray-800",
|
||||||
div {
|
div {
|
||||||
@@ -32,42 +29,6 @@ pub fn Footer() -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn ToolsUsed() -> Element {
|
|
||||||
rsx! {
|
|
||||||
div {
|
|
||||||
class:"items-center bg-white rounded-lg shadow dark:bg-gray-800 p-4 my-4",
|
|
||||||
div {
|
|
||||||
H1 {class: "justify-center", "Tools used" },
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
class: "flex h-fill overflow-x-auto",
|
|
||||||
Logo {
|
|
||||||
src: "https://raw.githubusercontent.com/SAWARATSUKI/Logogs/main/Rust/Rust.png",
|
|
||||||
alt: "Rust"
|
|
||||||
},
|
|
||||||
Logo {
|
|
||||||
src: "https://raw.githubusercontent.com/SAWARATSUKI/Logogs/main/Tailwindcss/Tailwindcss6.png",
|
|
||||||
alt: "Tailwindcss"
|
|
||||||
},
|
|
||||||
Logo {
|
|
||||||
src: "https://raw.githubusercontent.com/SAWARATSUKI/Logogs/main/Html/HTML.png",
|
|
||||||
alt: "HTML"
|
|
||||||
},
|
|
||||||
Logo {
|
|
||||||
src: "https://raw.githubusercontent.com/Aikoyori/ProgrammingVTuberLogos/main/Docker/DockerLogo.png",
|
|
||||||
alt: "Docker"
|
|
||||||
},
|
|
||||||
Logo {
|
|
||||||
src: "https://raw.githubusercontent.com/Aikoyori/ProgrammingVTuberLogos/main/Neovim/NeovimLogo.png",
|
|
||||||
alt: "NeoVim"
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn Logo(src: String, alt: String) -> Element {
|
fn Logo(src: String, alt: String) -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ pub fn Header() -> Element {
|
|||||||
rsx! {
|
rsx! {
|
||||||
nav {
|
nav {
|
||||||
div {
|
div {
|
||||||
// class: "justify-between p-4 space-x-8",
|
class: "container mx-auto py-4",
|
||||||
class: "container mx-auto p-4",
|
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
class:"flex flex-col justify-between sm:flex-row justify-center space-y-2 sm:space-y-0 sm:space-s-4",
|
class:"flex flex-col justify-between items-center justify-center space-y-2 sm:flex-row sm:space-y-0 sm:space-s-4",
|
||||||
li {
|
li {
|
||||||
Link {
|
Link {
|
||||||
to: Route::Home {},
|
to: Route::Home {},
|
||||||
@@ -24,7 +23,6 @@ pub fn Header() -> Element {
|
|||||||
li { HeaderLink { url: Route::Home {}, text: translate!(i18, "headers.home")} },
|
li { HeaderLink { url: Route::Home {}, text: translate!(i18, "headers.home")} },
|
||||||
li { HeaderLink { url: Route::CV {}, text: translate!(i18, "headers.cv") } },
|
li { HeaderLink { url: Route::CV {}, text: translate!(i18, "headers.cv") } },
|
||||||
li { HeaderLink { url: Route::PublicationsProjects {}, text: translate!(i18, "headers.publications_projects") } },
|
li { HeaderLink { url: Route::PublicationsProjects {}, text: translate!(i18, "headers.publications_projects") } },
|
||||||
li { HeaderLink { url: Route::Consulting {}, text: translate!(i18, "headers.consulting") } },
|
|
||||||
li { HeaderLink { url: Route::Impressum {}, text: translate!(i18, "headers.about") } },
|
li { HeaderLink { url: Route::Impressum {}, text: translate!(i18, "headers.about") } },
|
||||||
li { LanguageButtonGroup {} },
|
li { LanguageButtonGroup {} },
|
||||||
},
|
},
|
||||||
@@ -43,8 +41,14 @@ fn LanguageButtonGroup() -> Element {
|
|||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
class: "rounded-md shadow-sm justify-end",
|
class: "rounded-md shadow-sm justify-end",
|
||||||
button { class: "px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-s-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white", onclick: change_to_english, label { { translate!(i18, "headers.language_buttons.english") } } },
|
button {
|
||||||
button { class: "px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-e-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white", onclick: change_to_german, label { { translate!(i18, "headers.language_buttons.german") } } }
|
class: "px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-s-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white",
|
||||||
|
onclick: change_to_english,
|
||||||
|
label { { translate!(i18, "headers.language_buttons.english") } } },
|
||||||
|
button {
|
||||||
|
class: "px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-e-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:ring-blue-500 dark:focus:text-white",
|
||||||
|
onclick: change_to_german,
|
||||||
|
label { { translate!(i18, "headers.language_buttons.german") } } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ pub fn Layout() -> Element {
|
|||||||
Header {},
|
Header {},
|
||||||
Body {
|
Body {
|
||||||
Outlet::<Route> {},
|
Outlet::<Route> {},
|
||||||
Footer {},
|
},
|
||||||
}
|
Footer {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ use layout::header::Header;
|
|||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
|
|
||||||
pub mod components;
|
pub mod components;
|
||||||
mod consulting;
|
|
||||||
mod cv;
|
mod cv;
|
||||||
mod home;
|
mod home;
|
||||||
mod impressum;
|
mod impressum;
|
||||||
@@ -19,7 +18,6 @@ mod languages;
|
|||||||
mod layout;
|
mod layout;
|
||||||
mod publications;
|
mod publications;
|
||||||
|
|
||||||
use crate::consulting::Consulting;
|
|
||||||
use crate::cv::CV;
|
use crate::cv::CV;
|
||||||
use crate::home::Home;
|
use crate::home::Home;
|
||||||
use crate::impressum::Impressum;
|
use crate::impressum::Impressum;
|
||||||
@@ -39,8 +37,6 @@ pub enum Route {
|
|||||||
PublicationsProjects {},
|
PublicationsProjects {},
|
||||||
#[route("/resume")]
|
#[route("/resume")]
|
||||||
CV {},
|
CV {},
|
||||||
#[route("/consulting")]
|
|
||||||
Consulting {},
|
|
||||||
#[end_layout]
|
#[end_layout]
|
||||||
#[route("/:..route")]
|
#[route("/:..route")]
|
||||||
PageNotFound { route: Vec<String> },
|
PageNotFound { route: Vec<String> },
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_sdk::{i18n::use_i18, translate};
|
use dioxus_sdk::{i18n::use_i18, translate};
|
||||||
|
|
||||||
use crate::components::{UnderConstruction, H1, HR};
|
use crate::components::{Bolding, UnderConstruction, H1, HR};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn PublicationsProjects() -> Element {
|
pub fn PublicationsProjects() -> Element {
|
||||||
@@ -60,7 +60,7 @@ fn Publications() -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn Publication(prop: PublicationProp) -> Element {
|
fn Publication(prop: PublicationProp) -> Element {
|
||||||
let pattern = "T.-D. Tran";
|
let pattern = vec!["T.-D. Tran".to_string(), "Tuan-Dat Tran".to_string()];
|
||||||
rsx! {
|
rsx! {
|
||||||
Link {
|
Link {
|
||||||
class:"block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700",
|
class:"block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700",
|
||||||
@@ -73,9 +73,9 @@ fn Publication(prop: PublicationProp) -> Element {
|
|||||||
span { class: "text-lg text-gray-900 dark:text-white", "{prop.conference}" },
|
span { class: "text-lg text-gray-900 dark:text-white", "{prop.conference}" },
|
||||||
p {
|
p {
|
||||||
class:"font-normal text-gray-700 dark:text-gray-400 italic",
|
class:"font-normal text-gray-700 dark:text-gray-400 italic",
|
||||||
Authors {
|
Bolding {
|
||||||
authors: "{prop.authors}",
|
authors: "{prop.authors}",
|
||||||
pattern: "{pattern}",
|
patterns: pattern,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
@@ -129,7 +129,7 @@ struct ProjectProp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn Project(prop: ProjectProp) -> Element {
|
fn Project(prop: ProjectProp) -> Element {
|
||||||
let pattern = "T.-D. Tran";
|
let pattern = vec!["T.-D. Tran".to_string(), "Tuan-Dat Tran".to_string()];
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
Link {
|
Link {
|
||||||
@@ -143,9 +143,9 @@ fn Project(prop: ProjectProp) -> Element {
|
|||||||
p { class: "text-lg text-gray-900 dark:text-white", "{prop.kind}" },
|
p { class: "text-lg text-gray-900 dark:text-white", "{prop.kind}" },
|
||||||
p {
|
p {
|
||||||
class:"font-normal text-gray-700 dark:text-gray-400",
|
class:"font-normal text-gray-700 dark:text-gray-400",
|
||||||
Authors {
|
Bolding {
|
||||||
authors: "{prop.authors}",
|
authors: "{prop.authors}",
|
||||||
pattern: "{pattern}",
|
patterns: pattern,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
@@ -155,23 +155,3 @@ fn Project(prop: ProjectProp) -> Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Props)]
|
|
||||||
struct AuthorProp {
|
|
||||||
authors: String,
|
|
||||||
pattern: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn Authors(prop: AuthorProp) -> Element {
|
|
||||||
if let Some(start) = prop.authors.find(&prop.pattern) {
|
|
||||||
let end = start + prop.pattern.len();
|
|
||||||
let left = &prop.authors[..start];
|
|
||||||
let middle = &prop.authors[start..end];
|
|
||||||
let right = &prop.authors[end..];
|
|
||||||
rsx! {
|
|
||||||
"{left}" , b { "{middle}" }, "{right}",
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rsx! { "{prop.authors}" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user