Spoiler layout & fixes

This commit is contained in:
ef3d0c3e 2024-10-31 11:03:09 +01:00
parent 448739132a
commit fc7ff70090
5 changed files with 169 additions and 25 deletions

View file

@ -60,3 +60,17 @@ If you wish to modify the relative width of the splits: add `style=flex: 0.5` in
####+* Properties
* ``style`` Added css style to the div (defaults to none)
## Spoiler
The spoiler layout creates a collapsed element which can be opened.
#+LAYOUT_BEGIN[title=Spoiler demo] Spoiler
This content is *hidden*.
#+LAYOUT_END
####+* Style
The ``Spoiler`` layout uses the `.spoiler` class, combined with `<details>/<summary>` to create the desired layout.
####+* Properties
* ``title`` The spoiler title

View file

@ -351,7 +351,7 @@ impl Rule for CustomStyleRule {
tokens: CustomStyleToken::Toggle(token),
name: name.clone(),
start: unsafe { std::mem::transmute(on_start.clone()) },
end: unsafe { std::mem::transmute(on_start.clone()) },
end: unsafe { std::mem::transmute(on_end.clone()) },
};
CTX.with_borrow(|ctx| {
@ -414,7 +414,7 @@ impl Rule for CustomStyleRule {
tokens: CustomStyleToken::Pair(token_start, token_end),
name: name.clone(),
start: unsafe { std::mem::transmute(on_start.clone()) },
end: unsafe { std::mem::transmute(on_start.clone()) },
end: unsafe { std::mem::transmute(on_end.clone()) },
};
CTX.with_borrow(|ctx| {
@ -476,8 +476,8 @@ end
function red_style_end()
nml.raw.push("inline", "</a>")
end
nml.custom_style.define_toggled("My Style", "|", "my_style_start()", "my_style_end()")
nml.custom_style.define_toggled("My Style2", "°", "red_style_start()", "red_style_end()")
nml.custom_style.define_toggled("My Style", "|", my_style_start, my_style_end)
nml.custom_style.define_toggled("My Style2", "°", red_style_start, red_style_end)
>%
pre |styled| post °Hello°.
"#
@ -525,8 +525,8 @@ end
function red_style_end()
nml.raw.push("inline", "</a>")
end
nml.custom_style.define_paired("My Style", "[", "]", "my_style_start()", "my_style_end()")
nml.custom_style.define_paired("My Style2", "(", ")", "red_style_start()", "red_style_end()")
nml.custom_style.define_paired("My Style", "[", "]", my_style_start, my_style_end)
nml.custom_style.define_paired("My Style2", "(", ")", red_style_start, red_style_end)
>%
pre [styled] post (Hello).
"#

View file

@ -216,6 +216,81 @@ mod default_layouts {
}
}
}
#[derive(Debug)]
pub struct Spoiler(PropertyParser);
impl Default for Spoiler {
fn default() -> Self {
let mut properties = HashMap::new();
properties.insert(
"title".to_string(),
Property::new(
true,
"Spoiler title".to_string(),
Some("".to_string())
),
);
Self(PropertyParser { properties })
}
}
impl LayoutType for Spoiler {
fn name(&self) -> &'static str { "Spoiler" }
fn expects(&self) -> Range<usize> { 1..1 }
fn parse_properties(&self, properties: &str) -> Result<Option<Box<dyn Any>>, String> {
let props = if properties.is_empty() {
self.0.default()
} else {
self.0.parse(properties)
}
.map_err(|err| {
format!(
"Failed to parse properties for layout {}: {err}",
self.name()
)
})?;
let title = props
.get("title", |_, value| -> Result<String, ()> {
Ok(value.clone())
})
.map_err(|err| format!("Failed to parse style: {err:#?}"))
.map(|(_, value)| value)?;
Ok(Some(Box::new(title)))
}
fn compile(
&self,
token: LayoutToken,
_id: usize,
properties: &Option<Box<dyn Any>>,
compiler: &Compiler,
_document: &dyn Document,
) -> Result<String, String> {
match compiler.target() {
Target::HTML => {
let title = properties
.as_ref()
.unwrap()
.downcast_ref::<String>()
.unwrap();
match token {
LayoutToken::Begin => Ok(format!(
r#"<details class="spoiler"><summary>{}</summary>"#, Compiler::sanitize(compiler.target(), title)
)),
LayoutToken::End => Ok(r#"</details>"#.to_string()),
_ => panic!()
}
}
_ => todo!(""),
}
}
}
}
#[derive(Debug)]
@ -254,7 +329,10 @@ impl RuleState for LayoutState {
let mut reports = vec![];
let doc_borrow = document.content().borrow();
let at = doc_borrow.last().unwrap().location();
let at = doc_borrow.last().map_or(
Token::new(document.source().content().len()..document.source().content().len(), document.source()),
|last| last.location().to_owned()
);
for (tokens, layout_type) in &self.stack {
let start = tokens.first().unwrap();
@ -909,6 +987,7 @@ impl RegexRule for LayoutRule {
fn register_layouts(&self, holder: &mut LayoutHolder) {
holder.insert(Rc::new(default_layouts::Centered::default()));
holder.insert(Rc::new(default_layouts::Split::default()));
holder.insert(Rc::new(default_layouts::Spoiler::default()));
}
}
@ -942,6 +1021,9 @@ mod tests {
E
#+LAYOUT_END
#+LAYOUT_END
#+LAYOUT_BEGIN[title=F] Spoiler
F
#+LAYOUT_END
"#
.to_string(),
None,
@ -978,6 +1060,12 @@ mod tests {
};
Layout { token == LayoutToken::End, id == 2 };
Layout { token == LayoutToken::End, id == 2 };
Layout { token == LayoutToken::Begin, id == 0 };
Paragraph {
Text { content == "F" };
};
Layout { token == LayoutToken::End, id == 1 };
);
}
@ -999,6 +1087,10 @@ mod tests {
E
%<nml.layout.push("End", "Split", "")>%
%<nml.layout.push("End", "Split", "")>%
%<nml.layout.push("Begin", "Spoiler", "title=Test Spoiler")>%
F
%<nml.layout.push("End", "Spoiler", "")>%
"#
.to_string(),
None,
@ -1035,6 +1127,12 @@ mod tests {
};
Layout { token == LayoutToken::End, id == 2 };
Layout { token == LayoutToken::End, id == 2 };
Paragraph;
Layout { token == LayoutToken::Begin, id == 0 };
Paragraph {
Text { content == "F" };
};
Layout { token == LayoutToken::End, id == 1 };
);
}

View file

@ -105,6 +105,12 @@ impl<'b> Parser for LangParser<'b> {
.downcast_rc::<SourceFile>()
.ok()
.map(|source| {
if source.path().is_empty() // Test mode
{
None
}
else
{
let start = if source.path().starts_with("file:///") {
7
} else {
@ -116,9 +122,11 @@ impl<'b> Parser for LangParser<'b> {
Err(err) => eprintln!("Failed to canonicalize path `{}`: {err}", source.path()),
}
path.pop();
path
});
if let Some(path) = path {
Some(path)
}
})
.unwrap();
if let Some(path) = &path {
if let Err(err) = std::env::set_current_dir(&path) {
eprintln!(
"Failed to set working directory to `{}`: {err}",
@ -209,12 +217,15 @@ impl<'b> Parser for LangParser<'b> {
);
}
if path.is_some()
{
if let Err(err) = std::env::set_current_dir(&current_dir) {
println!(
"Failed to set working directory to `{}`: {err} {source:#?}",
current_dir.to_str().unwrap_or("")
);
}
}
(Box::new(doc), state)
}

View file

@ -35,6 +35,27 @@ div.split-container > div.split {
margin: 0.5em;
}
details.spoiler {
border: 1px solid #235;
border-radius: 4px;
padding: 0.5em 0.5em 0;
}
details.spoiler summary {
margin: -0.5em -0.5em 0;
padding: 0.5em;
}
details[open].spoiler {
border: 1px solid #235;
border-radius: 4px;
}
details[open].spoiler summary {
border-bottom: 1px solid #235;
padding: .5em;
}
/* Styles */
em {
padding-left: .1em;