mirror of
https://github.com/mfocko/blog.git
synced 2024-12-22 11:21:27 +01:00
feat: add talks
Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
parent
ab4ba212d1
commit
7155a112ab
10 changed files with 267 additions and 0 deletions
|
@ -138,6 +138,10 @@ const config = {
|
|||
to: "contributions",
|
||||
label: "Contributions",
|
||||
},
|
||||
{
|
||||
to: "talks",
|
||||
label: "Talks",
|
||||
},
|
||||
{
|
||||
to: "blog",
|
||||
position: "right",
|
||||
|
|
52
src/components/talks/Talk.module.scss
Normal file
52
src/components/talks/Talk.module.scss
Normal file
|
@ -0,0 +1,52 @@
|
|||
@import "../../css/mixins";
|
||||
|
||||
.card {
|
||||
margin-bottom: calc(var(--ifm-spacing-horizontal) * 2);
|
||||
border: 1px solid var(--ifm-color-emphasis-300);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.eventDetailsContainer {
|
||||
border-left: 1px solid var(--ifm-color-emphasis-300);
|
||||
|
||||
@include small-size {
|
||||
border-left: 0;
|
||||
border-top: 1px solid var(--ifm-color-emphasis-300);
|
||||
padding-top: var(--ifm-spacing-vertical);
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
> a {
|
||||
@include small-size {
|
||||
width: 100%;
|
||||
margin-top: calc(var(--ifm-spacing-vertical) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
a + a {
|
||||
margin-left: 0.5em;
|
||||
|
||||
@include small-size {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-right: 0.3rem;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
|
||||
[data-theme="dark"] & {
|
||||
fill: var(--ifm-font-color-base);
|
||||
}
|
||||
}
|
138
src/components/talks/Talk.tsx
Normal file
138
src/components/talks/Talk.tsx
Normal file
|
@ -0,0 +1,138 @@
|
|||
import clsx from "clsx";
|
||||
import React, { FunctionComponent } from "react";
|
||||
|
||||
import styles from "./Talk.module.scss";
|
||||
import RecordingIcon from "./assets/icon-recording.svg";
|
||||
import SlidesIcon from "./assets/icon-slides.svg";
|
||||
import RepositoryIcon from "./assets/icon-repository.svg";
|
||||
import CalendarIcon from "./assets/icon-calendar.svg";
|
||||
import MessageIcon from "./assets/icon-message.svg";
|
||||
import LocationIcon from "./assets/icon-location.svg";
|
||||
|
||||
export interface TalkMetadata {
|
||||
title: string;
|
||||
description: React.ReactNode;
|
||||
events: EventMetadata[];
|
||||
recordingURL?: string;
|
||||
slidesURL?: string;
|
||||
repoURL?: string;
|
||||
}
|
||||
|
||||
export interface EventMetadata {
|
||||
name: string;
|
||||
location: string;
|
||||
date: Date;
|
||||
}
|
||||
|
||||
const Talk: FunctionComponent<TalkMetadata> = ({
|
||||
title,
|
||||
description,
|
||||
events = [],
|
||||
recordingURL,
|
||||
slidesURL,
|
||||
repoURL,
|
||||
}) => {
|
||||
return (
|
||||
<div className="col col--12">
|
||||
<div className={clsx("card", styles.card)}>
|
||||
<div className="card__header">
|
||||
<h2>{title}</h2>
|
||||
</div>
|
||||
<div className="card__body">
|
||||
<div className="row">
|
||||
<div className="col col--7">{description}</div>
|
||||
<div className={clsx("col col--5", styles.eventDetailsContainer)}>
|
||||
<EventDetails data={events} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card__footer">
|
||||
<div className={styles.buttons}>
|
||||
{recordingURL && (
|
||||
<a
|
||||
href={recordingURL}
|
||||
target="_blank"
|
||||
className="button button--primary button--outline"
|
||||
>
|
||||
<span className="button__icon">
|
||||
<RecordingIcon />
|
||||
</span>
|
||||
Watch recording
|
||||
</a>
|
||||
)}
|
||||
{slidesURL && (
|
||||
<a
|
||||
href={slidesURL}
|
||||
target="_blank"
|
||||
className="button button--secondary button--outline"
|
||||
>
|
||||
<span className="button__icon">
|
||||
<SlidesIcon />
|
||||
</span>
|
||||
See slides
|
||||
</a>
|
||||
)}
|
||||
{repoURL && (
|
||||
<a
|
||||
href={repoURL}
|
||||
target="_blank"
|
||||
className="button button--secondary button--outline"
|
||||
>
|
||||
<span className="button__icon">
|
||||
<RepositoryIcon />
|
||||
</span>
|
||||
See repository
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const EventDetails: FunctionComponent<{ data: EventMetadata[] }> = ({
|
||||
data,
|
||||
}) => {
|
||||
if (data.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [firstEvent, ...otherEvents] = data;
|
||||
const { name, location, date } = firstEvent;
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col col--12">
|
||||
<ul className={styles.list}>
|
||||
<li>
|
||||
<MessageIcon className={styles.icon} /> <strong>{name}</strong>
|
||||
</li>
|
||||
<li>
|
||||
<LocationIcon className={styles.icon} /> {location}
|
||||
</li>
|
||||
<li>
|
||||
<CalendarIcon className={styles.icon} /> {formatDateString(date)}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{data.length > 1 && (
|
||||
<div className="col col--12">
|
||||
<p className="margin--none">Also presented on:</p>
|
||||
<ul>
|
||||
{otherEvents.map(({ name, location, date }) => (
|
||||
<li key={name}>
|
||||
<strong>{name}</strong> in {location} ({formatDateString(date)})
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function formatDateString(date: Date): string {
|
||||
return `${date.getMonth() + 1}/${date.getUTCFullYear()}`;
|
||||
}
|
||||
|
||||
export default Talk;
|
2
src/components/talks/assets/icon-calendar.svg
Normal file
2
src/components/talks/assets/icon-calendar.svg
Normal file
|
@ -0,0 +1,2 @@
|
|||
<!-- Source: https://remixicon.com/ -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M17 3h4a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h4V1h2v2h6V1h2v2zm-2 2H9v2H7V5H4v4h16V5h-3v2h-2V5zm5 6H4v8h16v-8z"/></svg>
|
After Width: | Height: | Size: 290 B |
2
src/components/talks/assets/icon-location.svg
Normal file
2
src/components/talks/assets/icon-location.svg
Normal file
|
@ -0,0 +1,2 @@
|
|||
<!-- Source: https://remixicon.com/ -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 23.728l-6.364-6.364a9 9 0 1 1 12.728 0L12 23.728zm4.95-7.778a7 7 0 1 0-9.9 0L12 20.9l4.95-4.95zM12 13a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></svg>
|
After Width: | Height: | Size: 289 B |
2
src/components/talks/assets/icon-message.svg
Normal file
2
src/components/talks/assets/icon-message.svg
Normal file
|
@ -0,0 +1,2 @@
|
|||
<!-- Source: https://remixicon.com/ -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M6.455 19L2 22.5V4a1 1 0 0 1 1-1h18a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H6.455zm-.692-2H20V5H4v13.385L5.763 17zM11 10h2v2h-2v-2zm-4 0h2v2H7v-2zm8 0h2v2h-2v-2z"/></svg>
|
After Width: | Height: | Size: 307 B |
2
src/components/talks/assets/icon-recording.svg
Normal file
2
src/components/talks/assets/icon-recording.svg
Normal file
|
@ -0,0 +1,2 @@
|
|||
<!-- Source: https://remixicon.com/ -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M2 3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H2.992A.993.993 0 0 1 2 20.007V3.993zM8 5v14h8V5H8zM4 5v2h2V5H4zm14 0v2h2V5h-2zM4 9v2h2V9H4zm14 0v2h2V9h-2zM4 13v2h2v-2H4zm14 0v2h2v-2h-2zM4 17v2h2v-2H4zm14 0v2h2v-2h-2z"/></svg>
|
After Width: | Height: | Size: 407 B |
2
src/components/talks/assets/icon-repository.svg
Normal file
2
src/components/talks/assets/icon-repository.svg
Normal file
|
@ -0,0 +1,2 @@
|
|||
<!-- Source: https://remixicon.com/ -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M13 21v2.5l-3-2-3 2V21h-.5A3.5 3.5 0 0 1 3 17.5V5a3 3 0 0 1 3-3h14a1 1 0 0 1 1 1v17a1 1 0 0 1-1 1h-7zm0-2h6v-3H6.5a1.5 1.5 0 0 0 0 3H7v-2h6v2zm6-5V4H6v10.035A3.53 3.53 0 0 1 6.5 14H19zM7 5h2v2H7V5zm0 3h2v2H7V8zm0 3h2v2H7v-2z"/></svg>
|
After Width: | Height: | Size: 379 B |
2
src/components/talks/assets/icon-slides.svg
Normal file
2
src/components/talks/assets/icon-slides.svg
Normal file
|
@ -0,0 +1,2 @@
|
|||
<!-- Source: https://remixicon.com/ -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M13 18v2h4v2H7v-2h4v-2H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h18a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-8zM4 5v11h16V5H4zm6 2.5l5 3-5 3v-6z"/></svg>
|
After Width: | Height: | Size: 280 B |
61
src/pages/talks.tsx
Normal file
61
src/pages/talks.tsx
Normal file
|
@ -0,0 +1,61 @@
|
|||
import React from "react";
|
||||
import Layout from "@theme/Layout";
|
||||
|
||||
import Talk, { TalkMetadata } from "../components/talks/Talk";
|
||||
|
||||
const talks: TalkMetadata[] = [
|
||||
{
|
||||
title: "Packit: RPM integration, all in one",
|
||||
description: (
|
||||
<>
|
||||
Do you want to automate how you build and test your RPM packages?
|
||||
Do you maintain any package in Fedora and want to automate the
|
||||
releases? Or are you just interested in CI/CD on GitHub or GitLab,
|
||||
Fedora and integration of upstream projects with RPM-based Linux
|
||||
distributions? In this session, we are going to deep-dive into
|
||||
features of Packit that can help you do your day-to-day job.
|
||||
</>
|
||||
),
|
||||
events: [
|
||||
{
|
||||
name: "DevConf.cz",
|
||||
location: "Brno, Czechia",
|
||||
date: new Date(2023, 6, 17),
|
||||
},
|
||||
{
|
||||
name: "DevConf.cz Mini",
|
||||
location: "Brno, Czechia",
|
||||
date: new Date(2023, 3, 31),
|
||||
},
|
||||
],
|
||||
recordingURL: "https://www.youtube.com/watch?v=FxhXzgxWO18",
|
||||
slidesURL: "https://static.sched.com/hosted_files/devconfcz2023/37/DevConf.cz%20June%202023%20Packit%20talk-1.pdf",
|
||||
},
|
||||
];
|
||||
|
||||
const title = "Talks";
|
||||
const description = "Featured talks I presented on various events.";
|
||||
|
||||
export default function Talks(): JSX.Element {
|
||||
return (
|
||||
<Layout title={title} description={description}>
|
||||
<main className="container container--fluid margin-vert--lg">
|
||||
<h1>{title}</h1>
|
||||
<p>{description}</p>
|
||||
|
||||
<div className="row">
|
||||
{talks.map((talkData) => (
|
||||
<Talk key={talkData.title} {...talkData} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
<p>
|
||||
Credits to <a href="https://kosiec.dev/" target="_blank">
|
||||
Paweł Kosiec</a> for implementing his own React components
|
||||
for talks.
|
||||
</p>
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
}
|
Loading…
Reference in a new issue