Skip to content

Commit

Permalink
[minisite] enable to configure prev/next links from the page attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
rmannibucau committed Feb 16, 2024
1 parent d10d46e commit 78a5bf3
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 9 deletions.
2 changes: 2 additions & 0 deletions _documentation/src/main/minisite/content/mojos.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ Some specific attributes enables to customize the generation. Here is their list
* `minisite-skip=[true|false]` enables to skip a `.adoc` rendering even if not in `_partials` directory.
* `minisite-path=<string>` enables to force the relative path of the file, for example a file name foo-bar.adoc with the attribute `minisite-path` set to `foo/bar.html` will output a `foo/bar.html` file instead of `foo-bar.html`. Note however it does not rewrite the links to ensure to use `link:.....html[]` instead of `ref` to link this page then.
* `minisite-highlightjs-skip` enables to not setup highlight.js for the page (useful with swagger-ui for example).
* `minisite-nav-prev-label`/`minisite-nav-next-label` enables to add a bottom page "previous"/"next" link to another page, this attribute defines its label.
* `minisite-nav-prev-link`/`minisite-nav-next-link` enables to add a bottom page "previous"/"next" link to another page, this attribute defines its link (label is required), if label is defined but not the link, the link is the label lowercased with iphens instead of spaces and html extension.

=== Index generation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ void inject(@TempDir final Path workDir) throws IOException {
"<p>Content.</p>\n" +
"</div>\n" +
" \n" +
" \n" +
" </div>\n" +
" </div>",
extractContent(Files.readString(output.resolve("page.html"))).strip().replace("\r", ""));
Expand Down
69 changes: 61 additions & 8 deletions minisite-core/src/main/java/io/yupiik/tools/minisite/MiniSite.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.emptyList;
import static java.util.Comparator.comparing;
import static java.util.Locale.ROOT;
import static java.util.Map.entry;
import static java.util.Objects.requireNonNull;
import static java.util.Optional.of;
Expand Down Expand Up @@ -94,6 +95,7 @@ public void run() {
configuration.getAsciidoctorConfiguration().info().accept("Rendering (and upload) skipped");
return;
}

final Object options = createOptions();
configuration.getAsciidoc().withInstance(configuration.getAsciidoctorConfiguration(), a -> {
executeInMinisiteClassLoader(() -> doRender(a, options));
Expand Down Expand Up @@ -141,7 +143,8 @@ protected Consumer<Function<Page, String>> render(final Page page, final Path ht
final Asciidoc.AsciidocInstance asciidoctor, final Object options,
final boolean withLeftMenuIfConfigured,
final Function<String, String> postProcessor,
final Function<String, String> customInterpolations) {
final Function<String, String> customInterpolations,
final Function<Page, String> footerNavTemplate) {
final Path templates = getTemplatesDir();
final String titleTemplate = findPageTemplate(templates, "page-title");
final String contentTemplate = findPageTemplate(templates, "page-content");
Expand All @@ -161,7 +164,14 @@ protected Consumer<Function<Page, String>> render(final Page page, final Path ht
if ("title".equals(key)) {
return title;
}
return getDefaultInterpolation(key, page, asciidoctor, options, customInterpolations);
return getDefaultInterpolation(key, page, asciidoctor, options, k -> {
switch (k) {
case "pageFooterNav":
return footerNavTemplate.apply(page);
default:
return getDefaultInterpolation(k, page, asciidoctor, options, customInterpolations);
}
});
}).replace(contentTemplate);
final String content = template.apply(new Page(
'/' + configuration.getTarget().relativize(html).toString().replace(File.separatorChar, '/'),
Expand Down Expand Up @@ -543,7 +553,8 @@ public void doRender(final Asciidoc.AsciidocInstance asciidoctor, final Object o
if (Files.exists(content)) {
final List<BlogPage> blog = new ArrayList<>();
final List<Consumer<Function<Page, String>>> pageToRender = new ArrayList<Consumer<Function<Page, String>>>();
pages.forEach(page -> pageToRender.add(onVisitedFile(page, asciidoctor, options, files, now, blog)));
final Function<Page, String> footerNavTemplate = loadNavTemplates();
pages.forEach(page -> pageToRender.add(onVisitedFile(page, asciidoctor, options, files, now, blog, footerNavTemplate)));
hasBlog = (!blog.isEmpty() && configuration.isGenerateBlog());
template = createTemplate(options, asciidoctor, hasBlog);
final Function<Page, String> tpl = template;
Expand Down Expand Up @@ -749,7 +760,8 @@ protected String xmlEscape(final String text) {
}

protected Consumer<Function<Page, String>> onVisitedFile(final Page page, final Asciidoc.AsciidocInstance asciidoctor, final Object options,
final Map<Page, Path> files, final OffsetDateTime now, final List<BlogPage> blog) {
final Map<Page, Path> files, final OffsetDateTime now, final List<BlogPage> blog,
final Function<Page, String> footerNavTemplate) {
if (page.attributes.containsKey("minisite-skip")) {
return t -> {
};
Expand All @@ -773,7 +785,7 @@ protected Consumer<Function<Page, String>> onVisitedFile(final Page page, final
};
} else {
return template -> {
render(page, out, asciidoctor, options, true, identity(), null).accept(template);
render(page, out, asciidoctor, options, true, identity(), null, footerNavTemplate).accept(template);
configuration.getAsciidoctorConfiguration().debug().accept("Rendered " + page.relativePath + " to " + out);
};
}
Expand Down Expand Up @@ -859,7 +871,7 @@ protected List<String> generateBlog(final List<BlogPage> blog, final Asciidoc.As
default:
return null;
}
}).accept(template);
}, p -> "").accept(template);
configuration.getAsciidoctorConfiguration().debug().accept("Rendered " + bp.page.relativePath + " to " + out);
});
return allCategories;
Expand Down Expand Up @@ -976,7 +988,7 @@ protected Collection<String> paginatePer(final String singular, final String plu
.collect(joining("\n"))),
baseBlog.resolve(singular + "/index.html"), asciidoctor, options,
false,
this::markPageAsBlog, null).accept(template);
this::markPageAsBlog, null, p -> "").accept(template);
return perCriteria.keySet();
}

Expand Down Expand Up @@ -1051,7 +1063,7 @@ protected void paginateBlogPages(final BiFunction<Integer, Integer, String> pref
}
}).replace(contentTemplate)),
output, asciidoctor, options, false,
this::markPageAsBlog, null).accept(template);
this::markPageAsBlog, null, p -> "").accept(template);
});

final Path indexRedirect = baseBlog.resolve(pageRelativeFolder + "index.html");
Expand Down Expand Up @@ -1082,6 +1094,27 @@ protected boolean hasSearch() {
return !"none".equals(configuration.getSearchIndexName()) && configuration.getSearchIndexName() != null;
}

protected Function<Page, String> loadNavTemplates() {
final Path templatesDir = getTemplatesDir();
final String globalTemplate = readTemplates(templatesDir, List.of("page-footer-nav.html"));
final String linkTemplate = readTemplates(templatesDir, List.of("page-footer-nav-link.html"));
return page -> {
if (page.attributes == null) {
return "";
}

final NavLink prev = new NavLink(page.attributes, "minisite-nav-prev-");
final NavLink next = new NavLink(page.attributes, "minisite-nav-next-");
if (prev.link == null && next.link == null) {
return "";
}

return globalTemplate
.replace("{{previousLink}}", prev.render(linkTemplate, "prev", "Previous"))
.replace("{{nextLink}}", next.render(linkTemplate, "next", "Next"));
};
}

public Function<Page, String> createTemplate(final Object options,
final Asciidoc.AsciidocInstance asciidoctor,
final boolean hasBlog) {
Expand Down Expand Up @@ -1350,4 +1383,24 @@ public static class Page {
private final Map<String, String> attributes;
private final String content;
}

private static class NavLink {
private final String label;
private final String link;

private NavLink(final Map<String, String> props, final String prefix) {
this.label = props.get(prefix + "label");
this.link = ofNullable(props.get(prefix + "link"))
.filter(Predicate.not(String::isBlank))
.orElseGet(() -> label == null || label.isBlank() ? null : label.toLowerCase(ROOT).replace(' ', '-') + ".html");
}

public String render(final String template, final String clazz, final String subLabel) {
return link == null || link.isBlank() ? "" : template
.replace("{{class}}", "page-footer-nav-link-" + clazz)
.replace("{{link}}", link)
.replace("{{subLabel}}", subLabel)
.replace("{{label}}", label);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -412,4 +412,44 @@ div.colist.arabic > ol > li > p {
right: unset;
left: 1rem;
top: 0.2rem;
}
}

/* for page-footer-nav templates */
.page-footer-nav {
grid-gap: 1rem;
gap: 1rem;
margin-top: 3rem;
display: grid;
grid-template-columns: repeat(2,1fr);
}
.page-footer-nav > a {
border: 1px solid lightgrey;
border-radius: .4rem;
display: block;
height: 100%;
line-height: 1.65;
padding: 1rem;
transition: border-color 200ms cubic-bezier(0.08,0.52,0.52,1);
text-decoration: none;
}
.page-footer-nav > a > div:first-child {
color: grey;
font-size: .875rem;
font-weight: 500;
margin-bottom: 0.25rem;
}
.page-footer-nav > a > div:last-child {
font-size: 1rem;
font-weight: 700;
word-break: break-word;
}
.page-footer-nav > a.page-footer-nav-link-next {
grid-column: 2/3;
text-align: right;
}
.page-footer-nav > a.page-footer-nav-link-prev > div:last-child:before {
content: "« ";
}
.page-footer-nav > a.page-footer-nav-link-next > div:last-child:after {
content: " »";
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
<div class="page-content-body">
{{{body}}}
{{{defaultEndOfContent?emptyIfMissing=true}}}
{{{pageFooterNav?emptyIfMissing=true}}}
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<a href="{{link}}" class="{{class}}">
<div>{{subLabel}}</div>
<div>{{label}}</div>
</a>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<nav class="page-footer-nav" aria-label="Docs pages">
{{previousLink}}
{{nextLink}}
</nav>
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@

@MiniSiteConfigurationBuilderProvider
class MiniSiteTest {
@Test
void footerNav(final MiniSiteConfigurationBuilderProvider.Asserts asserts,
final MiniSiteConfiguration.MiniSiteConfigurationBuilder builder) {
new MiniSite(builder.build()).run();
asserts.assertContains("page.html", " <nav class=\"page-footer-nav\" aria-label=\"Docs pages\">\n" +
" <a href=\"introduction.html\" class=\"page-footer-nav-link-prev\">\n" +
" <div>Previous</div>\n" +
" <div>Introduction</div>\n" +
"</a>\n" +
" <a href=\"getting-started.html\" class=\"page-footer-nav-link-next\">\n" +
" <div>Next</div>\n" +
" <div>Getting Started</div>\n" +
"</a>\n" +
"</nav>");
}

@Test
void customTemplates(final MiniSiteConfigurationBuilderProvider.Asserts asserts,
final MiniSiteConfiguration.MiniSiteConfigurationBuilder builder) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
= Page 1
:minisite-nav-prev-label: Introduction
// :minisite-nav-prev-link: introduction.html
:minisite-nav-next-label: Getting Started
// :minisite-nav-next-link: getting-started.html

Content.
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ void run(@TempDir final Path workDir) throws IOException {
"<p>Content.</p>\n" +
"</div>\n" +
" \n" +
" \n" +
" </div>\n" +
" </div>",
extractContent(Files.readString(output.resolve("page.html"))).strip());
Expand Down

0 comments on commit 78a5bf3

Please sign in to comment.