use typst_syntax::Span;
use crate::foundations::{
Content, Depth, Label, NativeElement, Packed, ShowSet, Smart, StyleChain, Styles,
cast, elem,
};
use crate::introspection::{Locatable, Tagged};
use crate::layout::{BlockElem, Em, PadElem};
use crate::model::{CitationForm, CiteElem};
use crate::text::{SmartQuotes, SpaceElem, TextElem};
#[elem(Locatable, Tagged, ShowSet)]
pub struct QuoteElem {
pub block: bool,
pub quotes: Smart<bool>,
pub attribution: Option<Attribution>,
#[required]
pub body: Content,
#[internal]
#[fold]
#[ghost]
pub depth: Depth,
}
impl QuoteElem {
pub fn quoted(body: Content, styles: StyleChain<'_>) -> Content {
let quotes = SmartQuotes::get_in(styles);
let Depth(depth) = styles.get(QuoteElem::depth);
let double = depth % 2 == 0;
Content::sequence([
TextElem::packed(quotes.open(double)),
body,
TextElem::packed(quotes.close(double)),
])
.set(QuoteElem::depth, Depth(1))
}
}
#[derive(Debug, Clone, PartialEq, Hash)]
pub enum Attribution {
Content(Content),
Label(Label),
}
impl Attribution {
pub fn realize(&self, span: Span) -> Content {
Content::sequence([
TextElem::packed('—'),
SpaceElem::shared().clone(),
match self {
Attribution::Content(content) => content.clone(),
Attribution::Label(label) => CiteElem::new(*label)
.with_form(Some(CitationForm::Prose))
.pack()
.spanned(span),
},
])
}
}
cast! {
Attribution,
self => match self {
Self::Content(content) => content.into_value(),
Self::Label(label) => label.into_value(),
},
content: Content => Self::Content(content),
label: Label => Self::Label(label),
}
impl ShowSet for Packed<QuoteElem> {
fn show_set(&self, styles: StyleChain) -> Styles {
let mut out = Styles::new();
if self.block.get(styles) {
out.set(PadElem::left, Em::new(1.0).into());
out.set(PadElem::right, Em::new(1.0).into());
out.set(BlockElem::above, Smart::Custom(Em::new(2.4).into()));
out.set(BlockElem::below, Smart::Custom(Em::new(1.8).into()));
}
out
}
}