feat: add event, location, and date frontmatter (#317)

Hello!

Closes #312 


This PR adds fields for the presentation `event`, `location`, and
`date`---these are optional fields but are common frontmatter elements,
particularly if the presentation is for a larger conference / event.

Example:
~~~markdown
---
title: How to Think Like a Designer
sub_title: It's easier than you think
event: Laracon US 2024
location: Dallas, TX
date: 2024-08-28
author: Jack McDade
---
~~~
This commit is contained in:
Matias Fontanini 2024-07-31 05:54:44 -07:00 committed by GitHub
commit 7dba250b6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 210 additions and 28 deletions

View File

@ -493,6 +493,18 @@ pub(crate) struct PresentationMetadata {
#[serde(default)]
pub(crate) sub_title: Option<String>,
/// The presentation event.
#[serde(default)]
pub(crate) event: Option<String>,
/// The presentation location.
#[serde(default)]
pub(crate) location: Option<String>,
/// The presentation date.
#[serde(default)]
pub(crate) date: Option<String>,
/// The presentation author.
#[serde(default)]
pub(crate) author: Option<String>,
@ -510,6 +522,19 @@ pub(crate) struct PresentationMetadata {
pub(crate) options: Option<OptionsConfig>,
}
impl PresentationMetadata {
/// Check if this presentation has frontmatter.
pub(crate) fn has_frontmatter(&self) -> bool {
self.title.is_some()
|| self.sub_title.is_some()
|| self.event.is_some()
|| self.location.is_some()
|| self.date.is_some()
|| self.author.is_some()
|| !self.authors.is_empty()
}
}
/// A presentation's theme metadata.
#[derive(Clone, Debug, Default, Deserialize)]
pub(crate) struct PresentationThemeMetadata {

View File

@ -283,11 +283,7 @@ impl<'a> PresentationBuilder<'a> {
}
self.footer_context.borrow_mut().author = metadata.author.clone().unwrap_or_default();
self.set_theme(&metadata.theme)?;
if metadata.title.is_some()
|| metadata.sub_title.is_some()
|| metadata.author.is_some()
|| !metadata.authors.is_empty()
{
if metadata.has_frontmatter() {
self.push_slide_prelude();
self.push_intro_slide(metadata);
}
@ -336,15 +332,14 @@ impl<'a> PresentationBuilder<'a> {
}
fn push_intro_slide(&mut self, metadata: PresentationMetadata) {
let styles = &self.theme.intro_slide;
let title = Text::new(
metadata.title.unwrap_or_default().clone(),
TextStyle::default().bold().colors(styles.title.colors.clone()),
);
let sub_title = metadata
.sub_title
.as_ref()
.map(|text| Text::new(text.clone(), TextStyle::default().colors(styles.subtitle.colors.clone())));
let styles = self.theme.intro_slide.clone();
let create_text =
|text: Option<String>, style: TextStyle| -> Option<Text> { text.map(|text| Text::new(text, style)) };
let title = create_text(metadata.title, TextStyle::default().bold().colors(styles.title.colors));
let sub_title = create_text(metadata.sub_title, TextStyle::default().colors(styles.subtitle.colors));
let event = create_text(metadata.event, TextStyle::default().colors(styles.event.colors));
let location = create_text(metadata.location, TextStyle::default().colors(styles.location.colors));
let date = create_text(metadata.date, TextStyle::default().colors(styles.date.colors));
let authors: Vec<_> = metadata
.author
.into_iter()
@ -355,11 +350,24 @@ impl<'a> PresentationBuilder<'a> {
self.slide_state.ignore_footer = true;
}
self.chunk_operations.push(RenderOperation::JumpToVerticalCenter);
self.push_text(TextBlock::from(title), ElementType::PresentationTitle);
self.push_line_break();
if let Some(text) = sub_title {
self.push_text(TextBlock::from(text), ElementType::PresentationSubTitle);
if let Some(title) = title {
self.push_line(title, ElementType::PresentationTitle);
}
if let Some(sub_title) = sub_title {
self.push_line(sub_title, ElementType::PresentationSubTitle);
}
if event.is_some() || location.is_some() || date.is_some() {
self.push_line_break();
self.push_line_break();
if let Some(event) = event {
self.push_line(event, ElementType::PresentationEvent);
}
if let Some(location) = location {
self.push_line(location, ElementType::PresentationLocation);
}
if let Some(date) = date {
self.push_line(date, ElementType::PresentationDate);
}
}
if !authors.is_empty() {
match self.theme.intro_slide.author.positioning {
@ -373,8 +381,7 @@ impl<'a> PresentationBuilder<'a> {
}
};
for author in authors {
self.push_text(TextBlock::from(author), ElementType::PresentationAuthor);
self.push_line_break();
self.push_line(author, ElementType::PresentationAuthor);
}
}
self.slide_state.title = Some(TextBlock::from("[Introduction]"));
@ -671,6 +678,11 @@ impl<'a> PresentationBuilder<'a> {
self.chunk_operations.push(RenderOperation::SetColors(self.theme.default_style.colors.clone()));
}
fn push_line(&mut self, text: Text, element_type: ElementType) {
self.push_text(TextBlock::from(text), element_type);
self.push_line_break();
}
fn push_text(&mut self, text: TextBlock, element_type: ElementType) {
let alignment = self.theme.alignment(&element_type);
self.push_aligned_text(text, alignment);
@ -1084,6 +1096,15 @@ struct StrictPresentationMetadata {
#[serde(default)]
sub_title: Option<String>,
#[serde(default)]
event: Option<String>,
#[serde(default)]
location: Option<String>,
#[serde(default)]
date: Option<String>,
#[serde(default)]
author: Option<String>,
@ -1099,8 +1120,9 @@ struct StrictPresentationMetadata {
impl From<StrictPresentationMetadata> for PresentationMetadata {
fn from(strict: StrictPresentationMetadata) -> Self {
let StrictPresentationMetadata { title, sub_title, author, authors, theme, options } = strict;
Self { title, sub_title, author, authors, theme, options }
let StrictPresentationMetadata { title, sub_title, event, location, date, author, authors, theme, options } =
strict;
Self { title, sub_title, event, location, date, author, authors, theme, options }
}
}

View File

@ -206,6 +206,9 @@ impl PresentationTheme {
Code => &self.code.alignment,
PresentationTitle => &self.intro_slide.title.alignment,
PresentationSubTitle => &self.intro_slide.subtitle.alignment,
PresentationEvent => &self.intro_slide.event.alignment,
PresentationLocation => &self.intro_slide.location.alignment,
PresentationDate => &self.intro_slide.date.alignment,
PresentationAuthor => &self.intro_slide.author.alignment,
Table => &self.table,
BlockQuote => &self.block_quote.alignment,
@ -336,6 +339,18 @@ pub(crate) struct IntroSlideStyle {
#[serde(default)]
pub(crate) subtitle: BasicStyle,
/// The style of the event line.
#[serde(default)]
pub(crate) event: BasicStyle,
/// The style of the location line.
#[serde(default)]
pub(crate) location: BasicStyle,
/// The style of the date line.
#[serde(default)]
pub(crate) date: BasicStyle,
/// The style of the author line.
#[serde(default)]
pub(crate) author: AuthorStyle,
@ -580,6 +595,9 @@ pub(crate) enum ElementType {
Code,
PresentationTitle,
PresentationSubTitle,
PresentationEvent,
PresentationLocation,
PresentationDate,
PresentationAuthor,
Table,
BlockQuote,

View File

@ -1,3 +1,4 @@
---
default:
margin:
percent: 8
@ -48,6 +49,18 @@ intro_slide:
alignment: center
colors:
foreground: "85c1dc"
event:
alignment: center
colors:
foreground: "a6d189"
location:
alignment: center
colors:
foreground: "85c1dc"
date:
alignment: center
colors:
foreground: "e5c890"
author:
alignment: center
colors:

View File

@ -1,3 +1,4 @@
---
default:
margin:
percent: 8
@ -48,6 +49,18 @@ intro_slide:
alignment: center
colors:
foreground: "209fb5"
event:
alignment: center
colors:
foreground: "40a02b"
location:
alignment: center
colors:
foreground: "209fb5"
date:
alignment: center
colors:
foreground: "df8e1d"
author:
alignment: center
colors:

View File

@ -1,3 +1,4 @@
---
default:
margin:
percent: 8
@ -48,6 +49,18 @@ intro_slide:
alignment: center
colors:
foreground: "7dc4e4"
event:
alignment: center
colors:
foreground: "a6da95"
location:
alignment: center
colors:
foreground: "7dc4e4"
date:
alignment: center
colors:
foreground: "eed49f"
author:
alignment: center
colors:

View File

@ -1,3 +1,4 @@
---
default:
margin:
percent: 8
@ -48,6 +49,18 @@ intro_slide:
alignment: center
colors:
foreground: "74c7ec"
event:
alignment: center
colors:
foreground: "a6e3a1"
location:
alignment: center
colors:
foreground: "74c7ec"
date:
alignment: center
colors:
foreground: "f9e2af"
author:
alignment: center
colors:

View File

@ -1,3 +1,4 @@
---
default:
margin:
percent: 8
@ -49,6 +50,18 @@ intro_slide:
alignment: center
colors:
foreground: "a5d7e8"
event:
alignment: center
colors:
foreground: "b4ccff"
location:
alignment: center
colors:
foreground: "a5d7e8"
date:
alignment: center
colors:
foreground: "ee9322"
author:
alignment: center
colors:
@ -93,7 +106,7 @@ typst:
foreground: "f0f0f0"
background: "292e42"
footer:
footer:
style: progress_bar
colors:
foreground: "7aa2f7"

View File

@ -1,3 +1,4 @@
---
default:
margin:
percent: 8
@ -49,6 +50,18 @@ intro_slide:
alignment: center
colors:
foreground: "8e9aaf"
event:
alignment: center
colors:
foreground: "52b788"
location:
alignment: center
colors:
foreground: "f77f00"
date:
alignment: center
colors:
foreground: "f77f00"
author:
alignment: center
colors:
@ -93,7 +106,7 @@ typst:
foreground: "212529"
background: "e9ecef"
footer:
footer:
style: progress_bar
colors:
foreground: "7aa2f7"

View File

@ -1,9 +1,10 @@
---
default:
margin:
percent: 8
colors:
foreground: null
background: null
background: null
slide_title:
alignment: center
@ -47,6 +48,18 @@ intro_slide:
alignment: center
colors:
foreground: blue
event:
alignment: center
colors:
foreground: green
location:
alignment: center
colors:
foreground: blue
date:
alignment: center
colors:
foreground: yellow
author:
alignment: center
colors:
@ -91,7 +104,7 @@ typst:
foreground: "f0f0f0"
background: "292e42"
footer:
footer:
style: progress_bar
colors:
foreground: blue

View File

@ -1,3 +1,4 @@
---
default:
margin:
percent: 8
@ -47,6 +48,18 @@ intro_slide:
alignment: center
colors:
foreground: dark_blue
event:
alignment: center
colors:
foreground: dark_green
location:
alignment: center
colors:
foreground: dark_blue
date:
alignment: center
colors:
foreground: dark_yellow
author:
alignment: center
colors:
@ -91,7 +104,7 @@ typst:
foreground: "212529"
background: "e9ecef"
footer:
footer:
style: progress_bar
colors:
foreground: dark_blue

View File

@ -1,3 +1,4 @@
---
default:
margin:
percent: 8
@ -49,6 +50,18 @@ intro_slide:
alignment: center
colors:
foreground: "a9b1d6"
event:
alignment: center
colors:
foreground: "7aa2f7"
location:
alignment: center
colors:
foreground: "a9b1d6"
date:
alignment: center
colors:
foreground: "e0af68"
author:
alignment: center
colors:
@ -93,7 +106,7 @@ typst:
foreground: "f0f0f0"
background: "545c7e"
footer:
footer:
style: progress_bar
colors:
foreground: "7aa2f7"