Compare commits

..

No commits in common. "master" and "v0.3.0" have entirely different histories.

5 changed files with 163 additions and 186 deletions

View File

@ -12,14 +12,14 @@ Gem::Specification.new do |spec|
spec.description = "Menus (site navigation) for your Jekyll website" spec.description = "Menus (site navigation) for your Jekyll website"
spec.summary = "Menus (navigation) for your very own Jekyll website." spec.summary = "Menus (navigation) for your very own Jekyll website."
spec.files = %W(Gemfile) + Dir["lib/**/*"] spec.files = %W(Gemfile) + Dir["lib/**/*"]
spec.required_ruby_version = ">= 2.4.0" spec.required_ruby_version = ">= 2.1.0"
spec.email = ["jordon@envygeeks.io"] spec.email = ["jordon@envygeeks.io"]
spec.require_paths = ["lib"] spec.require_paths = ["lib"]
spec.name = "jekyll-menus" spec.name = "jekyll-menus"
spec.has_rdoc = false spec.has_rdoc = false
spec.license = "MIT" spec.license = "MIT"
spec.add_runtime_dependency("jekyll", ">= 3.6", "< 5.0" ) spec.add_runtime_dependency("jekyll", "~> 3.1")
spec.add_development_dependency( spec.add_development_dependency(
"rspec", ">= 3", "< 4" "rspec", ">= 3", "< 4"
) )

289
README.md
View File

@ -1,219 +1,196 @@
# Jekyll Menus # Jekyll Menus
A robust, simple-to-use menu plugin for Jekyll that allows for infinitely nested menus. Complex, and infinite Hugo-like menus for Jekyll.
## Installation
To install and use Jekyll Menus, you should have Ruby, and either [RubyGems](https://jekyllrb.com/docs/installation/#install-with-rubygems), or we recommend using [Bundler](https://bundler.io/#getting-started). Bundler is what Jekyll will prefer you to use by default if you `jekyll new`.
### Using Bundler
You can add our gem to the `jekyll_plugins` group in your `Gemfile`:
```ruby
group :jekyll_plugins do
gem "jekyll-menus"
end
```
And then install from shell.
```sh
bundle install
# --path vendor/bundle
```
***If you are using Jekyll Docker, you do not need to perform this step, Jekyll Docker will perform it on your behalf when you launch the image, you only need to perform this step if you are working directly on your system.***
### Using RubyGems
```sh
sudo gem install jekyll-menus
sudo gem update jekyll-menus
```
Once installed, add the Gem to your `_config.yml`:
```yaml
plugins:
- jekyll-menus
```
***Note in earlier versions of Jekyll, `plugins` should instead be `gems`***
## Usage ## Usage
Jekyll Menus allows you to create menus by attaching posts and pages to menus through their front matter, or by defining custom menu items via `_data/menus.yml`. In Jekyll Menus you can create `_data/menus.yml`, or add menu items via your front-matter in pages as well! Both are merged into the same menus if the identifiers match so you can even split off menus between the two, and so that you can have menus that have internal and external links.
Jekyll Menus adds a new option to the site variable called `site.menus`, which can be looped over just like pages, posts, and other content: ### Front-Matter Examples
#### Using a String key to add an item to the menu
```liquid You can add an item to any menu by simply doing `menus: identifier` inside of your front-matter. If you do it this way, the string is the identifier (the menu you wish to place the item on) and all the other data is inferred from Jekyll, such as the items own identifier (take from the files slug,) title, and it defaults the weight to `-1` for you, making it so that your menu item is pretty much automatic aside from needing to add itself.
<ul>
{% for item in site.menus.header %}
<li class="menu-item-{{ loop.index }}">
<a href="{{ item.url }}" title="Go to {{ item.title }}">{{ item.title }}</a>
</li>
{% endfor %}
</ul>
```
## Menus via Front Matter ##### Example
The easiest way to use Jekyll Menus is to start building menus using your existing posts and pages. This can be done by adding a `menus` variable to your front matter: ```yml
```markdown
--- ---
title: Homepage menus: main
menus: header
--- ---
``` ```
This will create the `header` menu with a single item, the homepage. The `url`, `title`, and `identifier` for the homepage menu item will be automatically generated from the pages title, file path, and permalink. #### Using an array to add an item to multiple menus
You can optionally set any of the available [menu item variables](#menu-items) yourself to customize the appearance and functionality of your menus. For example, to set a custom title and weight: Like string keys, you can create an array of string keys, that allows you place an item on multiple identifiers at once, and like the string version, it will infer the data from Jekyll.
```markdown ##### Example
```yml
--- ---
title: Homepage
menus: menus:
header: - header
title: Home - footer
weight: 1
--- ---
``` ```
## Custom Menu Items via `_data/menus.yml` #### Adding a menu item with data
The other option for configuring menus is creating menus using `_data/menus.yml`. In this scenario, you can add custom menu items to external content, or site content that isnt handled by Jekyll. Jekyll Menus uses the keys, `title`, `weight`, `identifier` (slug), and `url`, you can customize said data and even add your own data, to do that you make the menu item hash with the data and any data you do not override is inferred like the other ways to add menu items. And like adding it to multiple identifiers with an array, you can do the same here by turning menus into an array and adding multiple hashes.
In this file, you provide the menu key and an array of custom menu items. Custom menu items in the data file must have `url`, `title`, and `identifier` variable: ##### Examples
```markdown ```yml
--- ---
header: menus:
- url: /api main:
title: API Documentation url: "/custom-url"
identifier: api
--- ---
``` ```
## Sub-menus
Jekyll Menus supports infinitely nested menu items using the `identifier` variable. Any menu item can be used as a parent menu by using its identifier as the menu.
For example, in `_data/menus.yml`:
```yaml ```yaml
header: menus:
- url: /api - header
title: API Documentation - main:
identifier: api url: "/custom-url"
``` ```
In a content file called `/api-support.html`: ### Using `_data/menus.yml`
```markdown ***All data within _menus.yml must provide `url`, `title`, `identifier`, with `weight` being optional.***
---
title: Get API Support Menu items within data files must follow a key array format, or a key hash format, we do not accept string formats because we cannot infer data and to do so would be pretty expensive.
menus: api
--- #### Examples
```yml
main:
- title: Title
identifier: title
url: url
``` ```
Which can then be used in your templates by looping over the menu items `children` variable: ```yml
main:
title: Title
identifier: title
url: url
```
```liquid ***It should be noted that _data/menus.yml are both read and merged, so you can have one, or both... we won't judge you if you happen to use both of these files at once, it's your choice!***
### Adding menu items to menu items
In Jekyll-Menus each item has it's own identifier (slug) that identifier can be used to have other items on it, and is used to find associated menus. So you can create your own base identifiers (like header, footer, main or otherwise) and each item on that has it's own identifier.
So given you have a file called `about.html` or `about.md`, and you do the following:
```yaml
menus: main
```
and, then you have `about/person.md`, you can add that to the `about` item with the following front-matter:
```yaml
menus: about
```
... Because `about.md` didn't have it's own identifier, the identifier was extracted from the page name (slug) so the identifier would be `about`, if you have `my-custom-page.md` or `my_custom_page.md` then the identifiers would be those without the file extension.
#### Customizing the identifier on an item
You can customize the identifier on any item (the base identifiers are already custom as you have to create them yourself by adding an item onto an identifier that doesn't exist.) To customize the identifier you just add the identifier key like the following:
```
menus:
main:
identifier: my-identifier
```
At that point, you can add sub-menus or items onto that identifier by using it as the identifier for in your front-matter (or even inside of your data files if it really pleases you.)
### Custom Menu Data
You can add any amount of custom data you wish to an item, we do not remove
data, and we do not block it, we will pass any data you wish to put into the
into the menu item. It is up to you what data you put there, we only check
that our own keys exist, and if they don't then we fail in certain scenarios.
#### Example
```
menu:
main:
weight: 4
customData: value
```
## Outputting the menu's
### A basic menu
Given you have several pages with the following:
```yaml
menu: header
```
You can output those with the following:
```html
<header>
<nav>
<ul> <ul>
{% for item in site.menus.header %} {% for item in site.menus.header %}
<li class="menu-item-{{ loop.index }}"> <li><a href="{{ item.url }}">
<a href="{{ item.url }}" title="Go to {{ item.title }}">{{ item.title }}</a> {{ item.title }}
{% if item.children %} </a></li>
<ul class="sub-menu">
{% for item in item.children %}
<li class="menu-item-{{ loop.index }}">
<a href="{{ item.url }}" title="Go to {{ item.title }}">{{ item.title }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %} {% endfor %}
</ul> </ul>
</nav>
</header>
``` ```
You can also do this [recursively using a re-usable include](#recursive-menus), allowing for easily managed infinitely nested menus. ### Recursive Menu output
## Variables If you want to output recursive menus efficiently you should start working with an include, so you can recurse on that include and have infinite menus if you ***really*** want.
Jekyll Menus has the following variables: #### Example
### Menus `_includes/menus.html`
| Variable | Description | ```html
|---|---|
| menu.menu | Returns a JSON object with all of the menus items. |
| menu.identifier | The unique identifier for the current menu, generated from the menu key. Allows for nested menu items. |
| menu.parent | The parent menu. Resolves to the site.menus object for top-level menus. |
### Menu Items
| Variable | Description |
|---|---|
| item.title | The display title of the menu item. Automatically set as the post or page title if no value is provided in front matter. |
| item.url | The URL the menu item links to. Automatically set to the post or page URL if no value is provided in front matter. |
| item.weight | Handles the order of menu items through a weighted system, starting with 1 being first. |
| item.identifier | The unique identifier for the current menu item. Allows for nested menu items. Automatically resolved to the pages file path and filename if not provided in front matter. |
| item.parent | The parent menu. |
| item.children | An array of any child menu items. Used to create sub-menus. |
## Custom Variables
Menu items also support custom variables, which you add to each menu item in the front matter or data file.
For example, adding a `pre` or `post` variable to add text or HTML to your menu items:
```markdown
---
title: Homepage
menus:
header:
pre: <i class="icon-home"></i>
post: " · "
---
```
## Recursive Menus
If youre looking to build an infinitely nested menu (or a menu that is nested more than once up to a limit) then you should set up a reusable menu include that will handle this for you.
In `_includes/menu.html` :
```liquid
{% if menu %}
<ul> <ul>
{% for item in menu %} {% for item in menu %}
<li class="menu-item-{{ loop.index }}"> <li>
<a href="{{ item.url }}" title="Go to {{ item.title }}">{{ item.title }}</a> <span>
<a href="{{ item.url }}">
{{ item.title }}
</a>
</span>
{% if item.children %} {% if item.children %}
{% assign menu = item.children %} {% assign menu = item.children %}
{% include menu.html %} {% include
_menus.html
%}
{% endif %} {% endif %}
</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %}
``` ```
In `_layouts/default.html` (or any layout file): `_layouts/default.html`
```liquid ```html
<html> <html>
<body> <body>
<header> <header>
<nav> <nav>
{% assign menu = site.menus.header %} {% assign menu = site.menus.main %}
{% include menus.html %} {% include
_menus.html
%}
</nav> </nav>
</header> </header>
{{ content }} {{ content }}
</body> </body>
</html> </html>

View File

@ -100,7 +100,7 @@ module Jekyll
# -- # --
if menu.is_a?(Array) || menu.is_a?(String) if menu.is_a?(Array) || menu.is_a?(String)
_simple_front_matter_menu(menu, **{ _simple_front_matter_menu(menu, {
:mergeable => out, :page => page :mergeable => out, :page => page
}) })
@ -116,7 +116,7 @@ module Jekyll
# -- # --
if item.is_a?(String) if item.is_a?(String)
out[key] << _fill_front_matter_menu({ "identifier" => item }, **{ out[key] << _fill_front_matter_menu({ "identifier" => item }, {
:page => page :page => page
}) })
@ -127,7 +127,7 @@ module Jekyll
# -- # --
elsif item.is_a?(Hash) elsif item.is_a?(Hash)
out[key] << _fill_front_matter_menu(item, **{ out[key] << _fill_front_matter_menu(item, {
:page => page :page => page
}) })
@ -178,7 +178,7 @@ module Jekyll
else else
mergeable[menu] ||= [] mergeable[menu] ||= []
mergeable[menu] << _fill_front_matter_menu(nil, **{ mergeable[menu] << _fill_front_matter_menu(nil, {
:page => page :page => page
}) })
end end
@ -203,10 +203,9 @@ module Jekyll
private private
def slug(page) def slug(page)
ext = page.data["ext"] || page.ext out = File.join(File.dirname(page.path), File.basename(page.path, page.ext))
out = File.join(File.dirname(page.path), File.basename(page.path, ext)) out.tr("^a-z0-9-_\\/", "").gsub(
out.tr("^a-z0-9-_\\/", "").gsub(/\/|\-+/, "_").gsub( /\/|\-+/, "_"
/^_+/, ""
) )
end end

View File

@ -56,15 +56,16 @@ module Jekyll
# #
def before_method(method) def method_missing(method, *args)
if @item.has_key?(method.to_s) if args.size == 0 && @item.has_key?(method.to_s)
return @item[ return @item[
method.to_s method.to_s
] ]
end
end
alias_method :liquid_method_missing, :before_method else
super
end
end
end end
end end
end end

View File

@ -4,6 +4,6 @@
module Jekyll module Jekyll
class Menus class Menus
VERSION = "0.6.1" VERSION = "0.3.0"
end end
end end