Generate a menu in Jekyll - menu

I'm building a static site with Jekyll (no blog), just with pages.
I want to find a way to generate (automatically or with a menu.yml file) a menu (like this website, on the footer).
I define title and chapter properties in the pages' YAML:
---
layout: page
title: Title of the page
chapter: Title of the chapter
permalink: /title-of-the-chapter/title-of-the-chapter/
order: 3250
published: true
---
I want to get a menu like that:
<ul>
<li>Title of the chapter</li>
<ul>
<li>Title of the page</li>
</ul>
</ul>
By the way my files are organised in folders like that :
01-chapter-one
01-subchapter-one
index.md
02-subchapter-two
index.md
02-chapter-one
01-subchapter-one
index.md
Is there a solution (perhaps with no plugin) to do this automatically (I have a lot of pages) ?

Full automation is possible only with a plugin, i.e. vanilla Jekyll is not able to automatically loop your folder and generate a hierarchical list of your files.
So if you want a solution without plugins, you need to maintain data file with your menu hierarchy: each time you add a page, you need to modify the data file as well.
There are different approaches how to generate the menu out of the data file, depending on the complexity you want:
Simple: Accessing _data in Jekyll (loop in loop)
Complex (with "dynamic" menu): Excluding page from Jekyll navigation bar
That should be enough to get you started. If you run into problems, please ask a more specific question.

Grouping data is the trick here, to do this you will need a secondary processor. The most convenient place to put this post processing is in javascript, but that means you need to leave yourself some data.
The following code embeds an array of all the pages in your as an array. That can then be post processed by in page javascript.
<script>
let menu = [
{% for p in site.pages %}
{
'chapter':'{{ p.chapter }}',
'title':'{{ p.title }}',
'url':'{{ p.url }}',
}
{% endfor %}
].filter(function(d){
return d.title != '';
})
.reduce(function(a,d){
a[d.chapter] = a[d.chapter] || [];
a[d.chapter].push(d);
},{});
menu = Object
.keys(menu)
.map(function(key){
let html = menu[key].map(function(d){
return "<li>"+d.title+"</li>";
})
.join('');
html = '<li>' + key + '<ol>'+html+'</ol></li>';
return html.join('');
})
;
menu.push('</ol>');
menu.unshift('<ol>');
document.write(menu.join(''));
</script>
I'm fairly certain that sorting is not enforced correctly, but that seems roughly correct.
A nice thing about this solution would be that you could actually embed the "chapters" as folders. You could generate nesting based on common folder structure, and use 'index' pages as markers for the 'chapter' titles.

Related

In MkDocs / Material, is it possible to put the table of contents on the left?

I'm using MkDocs with the Material theme. I'd like to build a site that looks a little like the Stripe API docs.
By default, the Material theme puts .md documents in the sidebar on the left, and the headings withing those documents in a Table of Contents sidebar on the right. I'd like to put it all on the left:
Introduction
A ## heading here
Another heading
Getting Started
Subsection
etc.
I could do this by making every entry its own document, but I'd prefer to have only one document per major heading, with the table of contents consisting of subheadings underneath. Possible?
Update:
I have made some progress.
First, extend the theme by by following these instructions: https://squidfunk.github.io/mkdocs-material/customization/
Second, get rid of the TOC on the right by overriding the "site_nav" template block. In main.html, just copy the existing site_nav block from base.html, then comment out the "Table of contents" section.
Third, copy the nav-item.html partial into the /partials directory and make modifications.
Now I'm stuck. It looks like nav-item.html does have code to render the TOC under each item, and it does get rendered with a display:none. When I turn that off, though, the TOC does not appear properly.
I've done an hour of fiddling with CSS to get it to work with no success. Any ideas?
In your custom css file, write:
.md-sidebar--secondary {
order: 0;
}
And in your mkdocs.yml, write:
extra_css:
- assets/<custom_css_filename>.css
Your update to the question is exactly what I did so I'm unsure if this will be useful.
In mkdocs.yml add custom_dir to the theme:
theme:
name: material
custom_dir: overrides
Create a file overrides/main.html and observe the file you are replacing https://github.com/squidfunk/mkdocs-material/blob/master/material/base.html. Use the nav code but include the toc partial to get the following:
{% extends "base.html" %}
{% block site_nav %}
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
{% include "partials/toc.html" %}
</div>
</div>
</div>
{% endblock %}
Here is the result:
If you are looking at changing the table of contents to also include pages look into the partials in the git repo.
The easiest hack I've come up with is the following:
In your mkdocs.yml, write:
extra_javascript:
- 'javascripts/extra.js'
In your docs/javascripts/extra.js, write:
document.addEventListener("DOMContentLoaded", function() {
show_toc_left();
});
function show_toc_left(){
document.querySelectorAll('.md-nav .md-nav--secondary')[0].setAttribute("style", "display: block; overflow: visible; color: #7d7f8e9c")
document.querySelectorAll('.md-nav .md-nav--secondary label')[0].remove()
}
This will show the TOC within your left side navigation menu of the currently active page.
You can also add this to the top of the individual page:
---
hide:
- toc
---

Sending entry data to embedded template

I have a basic ecommerce site which has a products page, in which a full overview of the products is displayed, including thumbnails, brief description, etc. On the same page, and other pages on the site, there is a sidebar which lists the products by title only.
Since the sidebar is reused around the site I want to set this as an embedded template.
With this setup, must I retrieve all the products from the database twice; once in the main Products page, and again in the sidebar? Or is there any way to speed things up on the Products page by retrieving the entries once in the parent template and passing them to the embedded page?
For example:
products parent template:
<html>
{exp:channel:entries channel='products'}
// display full product info
{/exp:channel:entries}
{embed='includes/_products_sidbar' data={entries}} // ^ Can I pass all the data from the above loop to this embedded template?
</html>
embed:
{if data}
// if we already have the data available then use it without having to get it all again from the database
{else:if}
// if not then use another entries loop:
{exp:channel:entries channel='products'}
// display a list of products
{/exp:channel:entries}
{/if}
I think you are better of using a different approach. Have a look at layouts:
https://docs.expressionengine.com/latest/templates/layouts.html
or, if that's not possible, you can use stash https://github.com/croxton/Stash too.
oh yeah, you'll get better help on https://expressionengine.stackexchange.com/

Global Content Block in Grav CMS

I'm using the Grav CMS and would like to have a block of global content present on each page. In other words, every page on the site will have this same block of content (near the footer). I'd like the content of this block to be editable similar to other pages, through a .md file. Instead of hardcoding the content of this block into the base.html.twig (or another) template the content should be editable through a .md file similar to other pages.
One option, use find() to pull the content of the page I want into each page. The downside to this is that I don't want this page to appear in the navigation. If there is a way to hide a page from the navigation this might work however.
What is the recommended way to accomplish this?
I solved this by creating a variable containing the page object from the page I wanted. Within the base.html.twig template I did the following:
{% set secondpage = page.find('/_page_url') %}
{{secondpage.content}
The _page_url is hidden due to the _, but still editable as a normal page.

Meteor Iron-Router Layout Rendering

We have implemented a layout where the main content resides in dynamic sidebars. We defined following layoutTemplate:
<template name="layout">
{{> content}}
{{> leftbar}}
{{> rightbar}}
<nav class="navigation">
{{#if currentUser}}
{{> navigation_logged_in}}
{{else}}
{{> navigation_logged_out}}
{{/if}}
</nav>
</template>
We include e.g. the rightbar template in the layout template.
<template name="rightbar">
<aside class="rightbar">
<button id="closeRightBar" class="close-cross"></button>
{{yield 'rightbar'}}
</aside>
</template>
The rightbar template includes the rightbar yield where we yield the specific content into.
We have implemented following RouteController:
UserShowRouter = RouteController.extend({
before: function() {
var username = this.params.username;
if(App.subs.user) {
App.subs.user.stop();
}
App.subs.user = Meteor.subscribe('user', username);
},
waitOn: function () {
return Meteor.subscribe('user');
},
data: function() {
return Meteor.users.findOne({'profile.username': this.params.username});
},
action: function() {
this.render('profile', {to: 'rightbar'});
}
});
What we wanted to achieve is that for example the profile template is yielded into the rightbar yield and get's updated and re-rendered as the data changes.
The problem is now that the sidebars are dynamically animated, shown and hidden. Now every time the profile template gets re-rendered also the layout template gets re-rendered. Why is that? We thought one of the purposes of yield regions is that the whole site doesn`t need to be re-renderd. Now when the layout gets re-rendered the whole css of the animations are set back to the original values.
We now have tried several different approaches, but none of them seems to be a good and clean solution. Is there a way to keep the layout template from being re-rendered and just keep the yield region and template up-dated? Any suggestions or alternatives would be highly appreciated.
As I understand it, the behavior in which re-rendering of your templates "bubbles up" and causes re-rendering of their parent templates is not particular to iron-router or the way your code is implemented, but is inherent in Spark. Iron-router's {{yield}} pattern does not alter this behavior, as far as I can tell from its documentation.
The good news is that Spark is set to be replaced imminently with a newer, more fine-grained rendering engine, currently codenamed "Spacebars," which should alleviate the concern.
Here is a preview of the new rendering system:
https://github.com/meteor/meteor/wiki/New-Template-Engine-Preview
This talk from a week ago is also excellent at describing the benefits coming through the new rendering engine (while fairly long, an overview is given in the first 5 minutes):
https://www.youtube.com/watch?v=aPf0LMQHIqk
As for your options today, you can:
a) Use the {{#constant}} and {{#isolate}} parameters to try to limit re-rendering.
b) Work from a development branch as described in the link above:
You can play with the current work-in-progress version of the code using the template-engine-preview-4 release tag (run your app with meteor --release template-engine-preview-4), or by checking out the shark branch (it's an internal codename).
c) Best of all if the timeframe of your project allows is to allow the re-rendering to continue until Meteor 1.0 hits and "Spacebars" resides on the main branch - it sounds like this is 1-3 months away.
I think that the reason your layout template gets rerendered is because the data hook you implemented uses a reactive data source. If the current user object changes, the router probably decides to rerender the whole page because there is no simple way to decide which parts exactly depend on your reactive data.
If I'm right, a simple solution to your problem is to create a user helper that will fetch the necessary data only where they're actually needed, e.g.
Template.profile.user = function () {
return Meteor.users.findOne({/*...*/});
}
Then you can use it in your profile template along with the with helper (sic!), i.e.
{{#with user}}
...
{{/with}}
to prevent multiple calls to the Template.profile.user function.
Also, if I were you, I would use the data hook only for data which is required by templates in my layout.

Override part template Orchard

I starting with orchard. I want to override MenuWidgetPart to render as i want. I've created Parts.MenuWidget-MenuWidget.cshtml into Views folder of current theme. But i do not know, how to i can get list menu from Model. Please look my code below:
<nav>
<ul>
#foreach(var m in listMenu){
<li>#m.Text</li>
}
</ul>
</nav>
How to i can get listMenu from Model ?
Templates for Menu and MenuItem are Menu.cshtml and MenuItem.cshtml. You can start by copying those files from /Core/Shapes/Views/ directory to your theme directory. You can modify them as you wish afterwards.
This will, actually modify all your menus on the site. If you want it to be specifically for your widget (Parts.MenuWidget-MenuWidget.cshtml) you can start by copying the contents from Menu.cshtml to your widget template and continue with modifications from there.
EDIT:
To iterate over items you can use the following syntax:
#foreach (var item in Model.Menu.Items){
#Display(item)
}

Resources