26 | {% endfor %}
27 |
28 |
29 | {# This can still be customized, but then there's markup for basic pagination #}
30 | {% pagination category %}
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Bryan Robinson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.eleventy.js:
--------------------------------------------------------------------------------
1 | const slugify = require('slugify');
2 | let _ = require("lodash");
3 | const getCategoryKeys = require('./src/getCategoryKeys.js')
4 |
5 | /*
6 | List all options from this plugin
7 | categoryVar
8 | categoryCollection
9 | perPageCount
10 | itemsCollection
11 |
12 | */
13 |
14 | module.exports = function(eleventyConfig, options={
15 | categoryVar: "categories",
16 | itemCollection: "posts",
17 | }) {
18 | const categoryCollection = options.categoryCollection || options.categoryVar;
19 | const perPageCount = options.perPageCount || 5
20 |
21 | // Creates the collection
22 | eleventyConfig.addCollection(categoryCollection, function(collections) {
23 |
24 | const posts = collections.getFilteredByTag(options.itemsCollection)
25 | let tagArray = getCategoryKeys(posts, options);
26 |
27 | const categoriesWithPosts = tagArray.map(category => {
28 | let filteredPosts = posts.filter(post => {
29 | if (!post.data[categoryCollection]) return false
30 | return post.data[categoryCollection].includes(category)}
31 | ).flat();
32 | console.log(slugify(category))
33 | return {
34 | 'title': category,
35 | 'slug': slugify(category),
36 | 'posts': [ ...filteredPosts ],
37 | };
38 | })
39 | console.log(`\x1b[32m[Dynamic Categories] Created Collection ${categoryCollection} with ${categoriesWithPosts.length} items`, '\x1b[0m')
40 | return categoriesWithPosts;
41 | })
42 |
43 | eleventyConfig.addCollection(`${categoryCollection}ByPage`, function(collection) {
44 | // Get unique list of all tags currently in use
45 | const posts = collection.getFilteredByTag(options.itemsCollection)
46 |
47 | // Get each item that matches the tag and add it to the tag's array, chunked by paginationSize
48 | let paginationSize = perPageCount;
49 | let tagMap = [];
50 | let tagArray = getCategoryKeys(posts, options);
51 |
52 | for(let tagName of tagArray) {
53 | const filteredPosts = posts.filter(post => {
54 | if (!post.data[categoryCollection]) return false
55 | return post.data[categoryCollection].includes(tagName)}
56 | ).flat();
57 |
58 | let tagItems = filteredPosts.reverse();
59 | let pagedItems = _.chunk(tagItems, paginationSize);
60 | const totalPages = Math.ceil(filteredPosts.length / perPageCount)
61 | for( let pageNumber = 0, max = pagedItems.length; pageNumber < max; pageNumber++) {
62 | const currentNumber = pageNumber + 1
63 | const slug = slugify(tagName)
64 | tagMap.push({
65 | slug,
66 | title: tagName,
67 | totalPages,
68 | posts: pagedItems[pageNumber],
69 | permalinkScheme: `${slug}${currentNumber > 1 ? `/${currentNumber}` : ''}/index.html`,
70 | pages: {
71 | current: currentNumber,
72 | next: currentNumber != totalPages && currentNumber + 1,
73 | previous: currentNumber > 1 && currentNumber - 1
74 | }
75 | });
76 | }
77 | }
78 | // Return a two-dimensional array of items, chunked by paginationSize
79 | return tagMap;
80 | });
81 |
82 | eleventyConfig.addShortcode('pagination', function(page) {
83 | const {pages} = page
84 | const {current, next, previous} = pages
85 | const allNumbers = Array.from(Array(page.totalPages).keys())
86 |
87 | const pageList = allNumbers.map(number => {
88 | const pageNumber = number+1;
89 | const urlBase = current == 1 ? './' : '../'
90 | const url = pageNumber === 1 ? `${urlBase}` : `${urlBase}${pageNumber}`
91 | const isCurrent = pageNumber === current;
92 |
93 | return `
94 |
95 | ${isCurrent ? pageNumber : `${pageNumber}`}
96 |
97 | `}).join('')
98 |
99 | const nextHref = next ? `Next Page`: 'Next Page'
100 | const previousHref = previous ? `Previous Page`: 'Previous Page'
101 | const markup = ``
102 | return markup
103 | })
104 |
105 |
106 |
107 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Eleventy Dynamic Categories
2 |
3 | This plugin is super alpha!
4 |
5 | This plugin will accept a category name and a collection name and create data that can be used to create or display category lists for content.
6 |
7 | It creates two collections. One is named either the `categoryVar` or `categoryCollection` configuration string. This has all posts in the category. The other is named that same string with `ByPage` appended to create a category collection that is paginated.
8 |
9 | Example:
10 |
11 | When you initialize with a `categoryVar` or `categoryCollection` of `categories`, the plugin will create two collections: `categories` and `categoriesByPage`. ``categories` is a collection where each item has the following data:
12 |
13 | ```js
14 | {
15 | "title": "Category Name",
16 | "slug": "category-name",
17 | "posts": [ /* full array of posts in the category */ ]
18 | }
19 | ```
20 |
21 | This is great for simple loops or for categories with small amounts of content.
22 |
23 | You also get `categoriesByPage` which allows you to use 11ty's Pagination functionality to go deeper and paginate posts per category, as well, with more data and helper functions. Each category page has the following information (required for making the pages)
24 |
25 | ```js
26 | {
27 | "title": "Category Name",
28 | "slug": "category-name",
29 | "posts": [ /* array of posts in the category on this page */ ],
30 | "permalinkScheme": "category-name/:num/",
31 | "totalPages": 4,
32 | "pages": {
33 | "current": 1,
34 | "next": 2,
35 | "previous": false // (or num)
36 | }
37 | }
38 |
39 | ```
40 | This is pretty abstract. There are examples below that should hopefully clarify.
41 |
42 | ## Installation
43 |
44 | Install the plugin with
45 |
46 | ```sh
47 | npm install eleventy-plugin-dynamic-categories
48 | ```
49 | ### Configure
50 |
51 | Add the plugin to your `.eleventy.js` config file. Provide the plugin with the name of the variable that you use in your frontmatter to assign categories to your content. Use `itemsCollection` to specify the key for which collection you want to use.
52 |
53 | |property|description|type|default|
54 | |---|---|---|---|
55 | |`categoryVar`|The name of the variable in your frontmatter that you use to assign categories to your content.|`string`|`categories`|
56 | |`itemsCollection`|The name of the collection you want to categorize.|`string`|`posts`|
57 | |`perPageCount`|The number of items to display per page.|`int`|`5`|
58 | |`categoryCollection`|The name of the collection that will be created by the plugin (must be unique).|`string`|`categories`|
59 |
60 |
61 | ```js
62 | const dynamicCategories = require('eleventy-plugin-dynamic-categories');
63 |
64 | module.exports = function(eleventyConfig) {
65 | eleventyConfig.addPlugin(dynamicCategories, {
66 | categoryVar: "categories", // Name of your category variable from your frontmatter (default: categories)
67 | itemsCollection: "posts", // Name of your collection to use for the items (default: posts)
68 | categoryCollection: "categories", // Name of the new collection to use for the categories (default: value in categoryVar)
69 | // categoryCollection MUST be unique currently
70 | perPageCount: 5 // Number of items to display per page of categoriesByPage (default: 5)
71 | })
72 | }
73 | ```
74 |
75 | ## Build or loop through your categories
76 |
77 | The plugin creates a data structure of an array of categories that contain a title (based on the string for each category), a slug to be used for URLs (slugified from the category name), and an array of items that are assigned to that category. The data is stored as an 11ty Collection with the key of the `categoryVar` you specified or overridden by the `categoryCollection` you specified. The collection name must be unique.
78 |
79 |
80 | ### Usage for pagination:
81 |
82 | ```html
83 | ---
84 | pagination:
85 | data: collections.categories
86 | alias: category
87 | size: 1
88 | permalink: /blog/category/{{ category.slug }}/
89 | ---
90 |
91 | {% for post in category.posts %}
92 |
134 | {% endfor %}
135 |
136 |
137 | {# This can still be customized, but then there's markup for basic pagination #}
138 | {% pagination category %}
139 | ```
140 |
141 |
142 | ## Multiple Category usage
143 | If you need to create multiple categories out of multiple collections, you can add the plugin multiple times with different configruations.
144 |
145 | ```js
146 | const dynamicCategories = require('eleventy-plugin-dynamic-categories');
147 |
148 | module.exports = function(eleventyConfig) {
149 | eleventyConfig.addPlugin(dynamicCategories, {
150 | categoryVar: "categories", // Name of your category variable from your frontmatter (default: categories)
151 | itemsCollection: "posts", // Name of your collection to use for the items (default: posts)
152 | categoryCollection: "categories" // Name of the new collection to use for the categories (default: value in categoryVar)
153 | // categoryCollection MUST be unique currently
154 | })
155 | eleventyConfig.addPlugin(dynamicCategories, {
156 | categoryVar: "categories2", // Name of your category variable from your frontmatter (default: categories)
157 | itemsCollection: "posts", // Name of your collection to use for the items (default: posts)
158 | categoryCollection: "categories2" // Name of the new collection to use for the categories (default: value in categoryVar)
159 | // categoryCollection MUST be unique currently
160 | })
161 | }
162 | ```
163 |
164 | ## Pagination template tag
165 | The pagination template tag is a helper tag that generates markup for basic pagination to save template overhead. It accepts the page information from the pagination item (usually aliased to something like `category`).
166 |
167 | For each page, this will generate pagination that includes next and previous links as well as a list of page numbers. The current page will be styled as active.
168 |
169 | ### Usage
170 | ```html
171 | {% pagination category %}
172 | ```
173 |
174 |
--------------------------------------------------------------------------------