Preface

Goal: Miscellanous range loop to achieved wordpress like widget.

We can make a sidebar, so that we can have more information, just like what common in wordpress site or other blogging platform. I’m using custom css based on Bulma box to suit example needs, and of course you can use any class other than this custom css.

Table of Content

  • Preface: Table of Content

  • 1: Prepare

  • 2: Simple Example

  • 3: Recent Post

  • 4: Categories and Tags

  • 5: Archives

  • What is Next ?

Source Code

You can download the source code of this article here.

Extract and run on CLI:

$ npm install

Related Article: SASS

The SASS part of this article is already discussed in this two article:

It is time to reuse the class for real world situation.


1: Prepare

Theme

Still with tutor-04.

# Extensions
theme: tutor-04

Layout: EJS Post

This post.ejs is very similar with page.ejs. The point is you can customize as creative as you want.

  <main role="main">
    ...
  </main>

  <aside class="sidebar column is-one-thirds is-paddingless">
    <%- partial('widget/related-posts') %>
  </aside>

Now we are using only related-posts. You can have as many widget as you want as shown below:

  <aside class="sidebar column is-one-thirds is-paddingless">
    <%- partial('widget/related-posts') %>
    <%- partial('widget/tags', {view_tag_type: 'before'}) %>
    <%- partial('widget/categories') %>
    <%- partial('widget/archives-grouped') %>
    <%- partial('widget/archives-github') %>
    <%- partial('widget/archives-gitlab') %>
  </aside>

We are going to discuss each partial, one by one.

Layout: EJS Page

The same method applied to page kind layout.

  <aside class="sidebar column is-one-thirds is-paddingless">
    <%- partial('widget/recent-posts') %>
    <%- partial('widget/affiliates') %>
    <%- partial('widget/friends') %>
    <%- partial('widget/archives-simple') %>
  </aside>

We are going to discuss each partial, one by one.

SASS: List

As already mention in other article, You can see the code here:

// -- -- -- -- --
// _list.scss

...

.


2: Simple Example

Consider start with simple example. We need something without any loop, EJS tag or whatsoever. Pure HTML with a list of URLs, that you might need to show on your side panel.

Layout: EJS Page

Consider examine only affiliate links:

  <aside class="sidebar column is-one-thirds is-paddingless">
    <%- partial('widget/affiliates') %>
  </aside>

Layout: Widget: HTML Affiliates

Notice the html file extension.

<section class="panel is-light">
  <div class="panel-header">
    <p>Affiliates</p>
    <span class="fa fa-child"></span>
  </div>
  <div class="panel-body has-background-white">
    <ul class="panel-list">
      <li><a href="http://epsi-rns.github.io/"
            >Linux/BSD Desktop Customization</a></li>
      <li><a href="http://epsi-rns.gitlab.io/"
            >Mobile/Web Development Blog</a></li>
      <li><a href="http://oto-spies.info/"
            >Car Painting and Body Repair.</a></li>
    </ul>
  </div>
</section>

Render: Browser

Just open one of the quote post:

Now you can see the result in the browser.

Hexo: Panel Affiliates

This the basic of HTML class required for the rest of this article.


3: Recent Post

Layout: EJS Page

Consider examine only recent posts:

  <aside class="sidebar column is-one-thirds is-paddingless">
    <%- partial('widget/recent-posts') %>
  </aside>

Layout: Widget: EJS Recent Post

Now with the usual ejs file extension.

<section class="panel is-light">
  <div class="panel-header">
    <p>Recent Posts</p>
    <span class="fa fa-newspaper"></span>
  </div>
  <div class="panel-body has-background-white">
    <ul class="panel-list">
      <% site.posts.sort('date', -1).limit(5).each(function(post){ %>
      <li><a href="<%- url_for(post.path) %>"
            ><%= post.title || '(no title)' %></a></li>
      <% }) %>
    </ul>
  </div>
</section>

The Loop

After sorting the posts array by date, I limit the result for only first five result.

      <% site.posts.sort('date', -1).limit(5).each(function(post){ %>
        ...
      <% }) %>

Render: Browser

Just open one of the page kind:

Now you can see the result in the browser.

Hexo: Panel Recent Posts


4: Categories and Tags

Both are Taxonomies. So the layouts is pretty similar.

Layout: EJS Post

Now using post kind instead of page kind.

  <aside class="sidebar column is-one-thirds is-paddingless">
    <%- partial('widget/categories') %>
  </aside>

Layout: Widget: EJS Categories

Using Bulma’s tag class tag is-small is-info:

<section class="panel is-light">
  <div class="panel-header">
    <p>Categories</p>
    <span class="fa fa-tag"></span>
  </div>
  <div class="panel-body has-background-white">
    <% site.categories.forEach(function(cat){ %>
      <a class="tag is-small is-info" href="<%- url_for(cat.path) %>">
        <%= cat.name %>&nbsp;<span class="fa fa-folder"></span>
      </a>&nbsp;
    <% }) %>
  </div>
</section>

The Loop

The loop is self explanatory.

    <% site.categories.forEach(function(cat){ %>
      ...
    <% }) %>

Render: Browser

Just open one of the quote post:

Hexo: Panel Categories

Layout: EJS Post

Consider make the code above a little bit more complex. We can pass argument to side panels. Here we can have a choice of tag icon to be shown, after or before.

  <aside class="sidebar column is-one-thirds is-paddingless">
    <%- partial('widget/tags', {view_tag_type: 'before'}) %>
  </aside>

Using Bulma’s tag class tag is-small is-light:

<!-- view_tag_type : 'before' or 'after' -->

<section class="panel is-light">
  <div class="panel-header">
    <p>Tags</p>
    <span class="fa fa-tag"></span>
  </div>
  <div class="panel-body has-background-white">
    <% if (view_tag_type == 'after'){ %>
      <% site.tags.forEach(function(tag){ %>
        <a class="tag is-small is-light has-background-white"
           href="<%- url_for(tag.path) %>">
          <%= tag.name %>&nbsp;<span class="fa fa-folder"></span>
        </a>&nbsp;
      <% }) %>
    <% } else { %>
      <% site.tags.forEach(function(tag){ %>
        <a class="tag is-small is-light has-background-white"
           href="<%- url_for(tag.path) %>">
          <span class="fa fa-folder"></span>&nbsp;<%= tag.name %>
        </a>&nbsp;
      <% }) %>
    <% } %>
  </div>
</section>

The Loop

The loop is still self explanatory.

      <% site.tags.forEach(function(tag){ %>
      ...
      <% }) %>

Render: Browser

Now you can see the result in the browser.

Hexo: Widget Tags


5: Archives

This archives widget is common in wordpress.

Layout: Widget: EJS Archives Simple

Hexo has built in list_archives helper.

<section class="panel is-light">
  <div class="panel-header">
    <p>Archives</p>
    <span class="fa fa-archive"></span>
  </div>
  <div class="panel-body has-background-white">
  <%- list_archives({show_count: theme.show_count, type: theme.archive_type}) %>
  </div>
</section>

Now you can see the result in the browser.

Hexo: Panel Archives Simple

We need something that we can style better.

Javascript: Widget: EJS Archives Grouped

Remember the Archives (section list) tutorial in a previous chapter ? We can also apply this in similar fashion, and of course with the same javascript.

  // Data Model

  // https://developer.mozilla.org/.../reduce
  
  function groupBy(objectArray, property) {
    return objectArray.reduce(function (acc, obj) {
      var key = obj[property];
      if (!acc[key]) {
        acc[key] = [];
      }
      acc[key].push(obj);
      return acc;
    }, {});
  }

  // grouping by date

  const posts = site.posts.map(post => ({ 
    ...post,
    year: date(post.date, "YYYY"),
    month: date(post.date, "MMMM")
  }));

  const postsByYear = groupBy(posts, 'year');
  
  // additional
  
  const pageYear  = date(page.date, 'YYYY');
  const pageMonth = date(page.date, 'MMMM');

Layout: Widget: EJS Archives Grouped

Now consider pour the javascript to HTML template in EJS artefact below:

<%
  ...
_%>
<section class="panel is-light">
  <div class="panel-header">
    <p>Archives</p>
    <span class="fa fa-archive"></span>
  </div>
  <div class="panel-body has-background-white">
  <% Object.keys(postsByYear).sort(date, -1).forEach(function (year){ %>
    <div class ="archive-year" id="<%= year %>">
      <a href="<%- url_for("/archives/") %>#<%= year %>">
      <%= year %></a>
    </div>
    
    <% if (year == pageYear) {%>
    <ul class="panel-archive">
    <%
      const postsByMonth = groupBy(postsByYear[year], 'month');
      Object.keys(postsByMonth).forEach(function (month){
    %>
      <li class="list-month">
        
        <span id="<%= year %>-<%= month %>">
              <a href="<%- url_for("/archives/") %>#<%= year %>-<%= month %>">
              <%= month %></a> - <%= year %></span>

        <% if (month == pageMonth) {%>
          <ul class="panel-list">
          <% postsByMonth[month].forEach(function(post){ %>

          <li>
            <a href="<%- url_for(post.path) %>">
              <%= post.title %>
            </a>
          </li>
          <% }) %>
          </ul>
        <% } %>

      </li>
      <% }) %>
    </ul>
    <% } %>
  <% }) %>
  </div>
</section>

The Loop

I know it is complex. So I write again here as below:

  <% Object.keys(postsByYear).sort(date, -1).forEach(function (year){ %>
    <%= year %>
    ...

    <% if (year == pageYear) {%>
      <%
        const postsByMonth = groupBy(postsByYear[year], 'month');
        Object.keys(postsByMonth).forEach(function (month){
      %>
      <%= month %> - <%= year %>
      ...

        <% if (month == pageMonth) {%>
          <% postsByMonth[month].forEach(function(post){ %>
          ...
              <%= post.title %>
          ...
          <% }) %>
        <% } %>

      <% }) %>
    <% } %>

  <% }) %>

Th effort is worthy.

Render: Browser

Now you can see the result in the browser.

Hexo: Widget Archives Grouped


What is Next ?

Consider continue reading [ Hexo - Data ]. There are, some interesting topic about Example Static Data with Hexo.

Thank you for reading.