view/widget/archives.jsx

/**
 * Archives widget JSX component.
 * @module view/widget/archives
 */
const { Component } = require('inferno');
const { cacheComponent } = require('../../util/cache');
const { translate } = require('../../util/i18n');
const { resolveArchiveOptions, groupArchives } = require('../../util/archives');

/**
 * Archives widget JSX component.
 *
 * @example
 * <Archives
 *     title="Widget title"
 *     showCount={true}
 *     items={[
 *         {
 *             url: '/path/to/archive/page',
 *             name: 'Archive name',
 *             count: 1
 *         }
 *     ]} />
 */
class Archives extends Component {
  render() {
    const { items, title, showCount, moreUrl, moreTitle } = this.props;

    return (
      <div class="card widget" data-type="archives">
        <div class="card-content">
          <div class="menu">
            <h3 class="menu-label">{title}</h3>
            <ul class="menu-list">
              {items.map((archive) => (
                <li>
                  <a class="level is-mobile" href={archive.url}>
                    <span class="level-start">
                      <span class="level-item">{archive.name}</span>
                    </span>
                    {showCount ? (
                      <span class="level-end">
                        <span class="level-item tag">{archive.count}</span>
                      </span>
                    ) : null}
                  </a>
                </li>
              ))}
              {moreUrl ? (
                <li>
                  <a class="level is-mobile" href={moreUrl}>
                    <span class="level-start">
                      <span class="level-item">{moreTitle}</span>
                    </span>
                    <span class="level-end">
                      <span class="level-item icon is-small" aria-hidden="true">
                        <i class="fas fa-chevron-right"></i>
                      </span>
                    </span>
                  </a>
                </li>
              ) : null}
            </ul>
          </div>
        </div>
      </div>
    );
  }
}

/**
 * Cacheable archives widget JSX component.
 * <p>
 * This class is supposed to be used in combination with the <code>locals</code> hexo filter
 * ({@link module:hexo/filter/locals}).
 *
 * @see module:util/cache.cacheComponent
 * @see https://github.com/hexojs/hexo/blob/4.2.0/lib/plugins/helper/list_archives.js
 * @example
 * <Archives.Cacheable
 *     site={{ posts: {...} }}
 *     config={{
 *         language: 'en_US',
 *         timezone: 'UTC',
 *         archive_dir: '/path/to/archive'
 *     }}
 *     page={{
 *         lang: 'en_US',
 *         language: 'en_US'
 *     }}
 *     helper={{
 *         url_for: function() {...},
 *         _p: function() {...}
 *     }}
 *     group_by="monthly"
 *     order={-1}
 *     showCount={true}
 *     format="MMMM YYYY" />
 */
Archives.Cacheable = cacheComponent(Archives, 'widget.archives', (props) => {
  const { site, config, page, helper, widget } = props;
  const { groupBy, order, format, amount, showCount } = resolveArchiveOptions(widget);

  const { url_for, _p, __ } = helper;
  const posts = site.posts.sort('date', order);
  if (!posts.length) {
    return null;
  }

  const { groups, hasMore } = groupArchives(posts, {
    groupBy,
    format,
    amount,
    language: page.lang || page.language || config.language,
    timezone: config.timezone,
  });

  const link = (item) => {
    let url = `${config.archive_dir}/${item.year}/`;

    if (groupBy === 'monthly') {
      if (item.month < 10) url += '0';
      url += `${item.month}/`;
    }

    return url_for(url);
  };

  return {
    items: groups.map((item) => ({
      name: item.name,
      count: item.count,
      url: link(item),
    })),
    title: _p('common.archive', Infinity),
    showCount,
    moreUrl: hasMore ? url_for(`${config.archive_dir}/`) : null,
    moreTitle: translate(__, 'widget.more', 'More'),
  };
});

module.exports = Archives;