diff --git a/.eleventy.js b/.eleventy.js index 26bda7a09..300f53926 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -1,9 +1,8 @@ const lodashGet = require("lodash/get"); +const yaml = require("js-yaml"); module.exports = function(eleventyConfig) { - // Support yaml data files - const yaml = require("js-yaml"); eleventyConfig.addDataExtension("yaml", contents => yaml.safeLoad(contents)) // pass images directly through to the output @@ -69,9 +68,37 @@ module.exports = function(eleventyConfig) { // filter a data array based on the value of a property eleventyConfig.addFilter('select', (array, clause) => { - const property = clause.split("=")[0]; - const value = clause.split("=")[1]; - return array.filter(item => item[property].includes(value)); + if(clause.indexOf("=") > -1) { + const property = clause.split("=")[0]; + const value = clause.split("=")[1]; + return array.filter(item => lodashGet(item, property).includes(value)); + } else { + return array.map(item => lodashGet(item, clause)); + } + }); + + eleventyConfig.addFilter('flatten', (array) => { + let results = []; + for(let result of array) { + if(result) { + if(Array.isArray(result)) { + results = [...results, ...result]; + } else { + results.push(result); + } + } + } + return results; + }); + + eleventyConfig.addFilter('unique', (array) => { + let caseInsensitive = {}; + for(let val of array) { + if(typeof val === "string") { + caseInsensitive[val.toLowerCase()] = val; + } + } + return Object.values(caseInsensitive); }); // Get a random selection of items from an array diff --git a/package.json b/package.json index 5f0e30a5b..945679297 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "postcss": "^8.1.10", "postcss-cli": "^8.3.0", "postcss-import": "^13.0.0", + "spdx-correct": "^3.1.1", "tailwindcss": "^2.0.1" }, "devDependencies": { diff --git a/src/11ty/normalizeLicense.js b/src/11ty/normalizeLicense.js new file mode 100644 index 000000000..1d873bdd7 --- /dev/null +++ b/src/11ty/normalizeLicense.js @@ -0,0 +1,10 @@ +const correct = require("spdx-correct"); + +module.exports = function(license, title) { + let correctedLicenseName = correct(license); + if(correctedLicenseName) { + return correctedLicenseName; + } + console.log( `WARN License name (${license}) for ${title} not found on the approved SPDX License List: https://spdx.org/licenses/` ); + return license; +}; \ No newline at end of file diff --git a/src/css/tailwind.css b/src/css/tailwind.css index 9e8288968..834955303 100644 --- a/src/css/tailwind.css +++ b/src/css/tailwind.css @@ -314,6 +314,15 @@ details[open] .summary-swap-open { .tool-content img { @apply my-8; } + +/* Filters */ +.filter-opensource--hide, +.filter-typeofcms--hide, +.filter-language--hide, +.filter-template--hide, +.filter-license--hide { + display: none !important; +} /* purgecss end ignore */ @tailwind utilities; diff --git a/src/js/filter-container.js b/src/js/filter-container.js new file mode 100644 index 000000000..77d11517a --- /dev/null +++ b/src/js/filter-container.js @@ -0,0 +1,114 @@ +class FilterContainer extends HTMLElement { + constructor() { + super(); + this.attrs = { + bind: "data-filter-bind", + delimiter: "data-filter-delimiter", + results: "data-filter-results", + }; + this.classes = { + hidden: "filter--hide", + } + } + + connectedCallback() { + this.results = this.querySelector(`[${this.attrs.results}]`); + let formElements = this.getAllFormElements(); + this.bindEvents(formElements); + this.filterAll(formElements); + } + + getAllFormElements() { + return this.querySelectorAll(`[${this.attrs.bind}]`); + } + + getAllKeys() { + let keys = new Set(); + for(let formEl of this.getAllFormElements()) { + keys.add(formEl.getAttribute(this.attrs.bind)); + } + return Array.from(keys); + } + + getElementSelector(key) { + return `data-filter-${key}` + } + + getAllFilterableElements() { + let keys = this.getAllKeys(); + let selector = keys.map(key => { + return `[${this.getElementSelector(key)}]`; + }).join(","); + return this.querySelectorAll(selector); + } + + bindEvents(formElements) { + for(let el of formElements) { + el.addEventListener("change", e => { + this.filter(e.target); + requestAnimationFrame(() => { + this.renderResultCount(); + }) + }, false); + } + } + + filterAll(formElements) { + for(let el of formElements) { + this.filter(el); + } + this.renderResultCount(); + } + + filter(formElement) { + let key = formElement.getAttribute(this.attrs.bind); + let delimiter = formElement.getAttribute(this.attrs.delimiter); + + let value = formElement.value; + let elementsSelectorAttr = this.getElementSelector(key); + + let elements = this.querySelectorAll(`[${elementsSelectorAttr}]`); + let count = 0; + let cls = `filter-${key}--hide`; + for(let element of Array.from(elements)) { + if(this.elementIsValid(element, elementsSelectorAttr, value, delimiter)) { + element.classList.remove(cls); + } else { + element.classList.add(cls); + } + } + } + + elementIsValid(element, attributeName, value, delimiter) { + if(!value && element.hasAttribute(attributeName)) { + return true; + } + let attrValue = element.getAttribute(attributeName); + if(delimiter && attrValue.split(delimiter).indexOf(value) > -1) { + return true; + } + if(!delimiter && attrValue === value) { + return true; + } + return false; + } + + elementIsVisible(element) { + for(let cls of element.classList) { + if(cls.startsWith("filter-") && cls.endsWith("--hide")) { + return false; + } + } + return true; + } + + renderResultCount() { + let count = Array.from(this.getAllFilterableElements()) + .filter(entry => this.elementIsVisible(entry)) + .length; + + this.results.innerHTML = `${count} Result${count !== 1 ? "s" : ""}`; + } +} + +window.customElements.define("filter-container", FilterContainer); \ No newline at end of file diff --git a/src/js/sort-container.js b/src/js/sort-container.js new file mode 100644 index 000000000..5428a372d --- /dev/null +++ b/src/js/sort-container.js @@ -0,0 +1,54 @@ +class SortContainer extends HTMLElement { + constructor() { + super(); + this.attrs = { + select: "data-sort", + children: "data-sort-children", + }; + } + + connectedCallback() { + this.select = this.querySelector(`[${this.attrs.select}]`); + this.bindEvents(); + } + + bindEvents() { + this.select.addEventListener("change", e => { + this.sort(e.target.value); + }, false); + } + + sort(key) { + let container = this.querySelector(`[${this.attrs.children}]`); + // Thanks https://github.com/component/sort (MIT License) + let arr = [].slice.call(container.children).sort((a, b) => { + let aVal = a.getAttribute(`data-sort-${key}`); + let bVal = b.getAttribute(`data-sort-${key}`); + + // numeric sorts + if(key.endsWith("-numeric") || key.endsWith("-numeric-ascending") || key.endsWith("-numeric-descending")) { + aVal = parseFloat(aVal) || 0; + bVal = parseFloat(bVal) || 0; + + if(key.endsWith("-numeric-descending")) { + [aVal, bVal] = [bVal, aVal]; + } + return aVal - bVal; + } + + if(bVal < aVal) { + return 1; + } else if(aVal < bVal) { + return -1; + } + return 0; + }); + let frag = document.createDocumentFragment(); + for (let i = 0; i < arr.length; i++) { + frag.appendChild(arr[i]); + } + container.appendChild(frag); + } +} + +window.customElements.define("sort-container", SortContainer); \ No newline at end of file diff --git a/src/site/_data/github.js b/src/site/_data/github.js index 4f7c7a44e..db1f962da 100644 --- a/src/site/_data/github.js +++ b/src/site/_data/github.js @@ -102,11 +102,13 @@ async function getReposFromMarkdown(glob) { let split = fullRepo.split("/"); let user = split[0]; let repo = split[1]; - if(!matter.data.repohost || matter.data.repohost === "github") - repos.push({ user, repo }); - } else { - // TODO maybe just log this in production? - // console.log( "GitHub full repo not found for", ssg ); + + if(!matter.data.repohost || matter.data.repohost === "github") { + if(matter.data.disabled) { + continue; + } + repos.push({ user, repo }); + } } } diff --git a/src/site/_includes/components/cards.njk b/src/site/_includes/components/cards.njk index 5294fb626..82588d2c6 100644 --- a/src/site/_includes/components/cards.njk +++ b/src/site/_includes/components/cards.njk @@ -1,7 +1,14 @@ {% macro repo(item, loopIndex, githubData) %} -
+
-
Language:
{{ item.data.language }}
{%- endif %} - {%- if item.data.templates %} + {%- if item.data.templates and item.data.templates.length > 0 %}
Templates:
{{ item.data.templates | join(", ") }}
{%- endif %} - {%- if item.data.license %} + {%- if item.data.license and item.data.license.length > 0 %}
License:
-
{{ item.data.license }}
+
{{ item.data.license | join(", ") }}
{%- endif %} {%- if item.data.typeofcms %}
Type:
@@ -69,7 +76,7 @@
{% if item.data.startertemplaterepo %} - + Deploy to Netlify diff --git a/src/site/_includes/layouts/base.njk b/src/site/_includes/layouts/base.njk index 4bd67a715..63676c05b 100644 --- a/src/site/_includes/layouts/base.njk +++ b/src/site/_includes/layouts/base.njk @@ -92,5 +92,7 @@ ogimage: "/img/og/default-og-image.png" + + diff --git a/src/site/_includes/layouts/tool.njk b/src/site/_includes/layouts/tool.njk index 2081990d2..6b648dc57 100644 --- a/src/site/_includes/layouts/tool.njk +++ b/src/site/_includes/layouts/tool.njk @@ -33,21 +33,21 @@ layout: layouts/base.njk
{%- if homepage %}
Home page
-
+
{{ homepage }}
{%- endif %} {%- if repo %}
Repository
-
+
{{ repo }}
{%- endif %} {%- if twitter %}
Twitter
-
+
@{{ twitter }}
@@ -62,11 +62,11 @@ layout: layouts/base.njk
Language:
{{ language }}
{%- endif %} - {%- if license %} + {%- if license and license.length > 0 %}
License:
-
{{ license }}
+
{{ license | join(", ") }}
{%- endif %} - {%- if templates %} + {%- if templates and templates.length > 0 %}
Templates:
{{ templates | join(", ") }}
{%- endif %} @@ -89,7 +89,7 @@ layout: layouts/base.njk {% if startertemplaterepo %} - + Deploy to Netlify diff --git a/src/site/generators.njk b/src/site/generators.njk index cd152378c..1253d5f83 100644 --- a/src/site/generators.njk +++ b/src/site/generators.njk @@ -13,11 +13,64 @@ layout: layouts/base.njk
-
- {% for item in collections.generators | sortTools(github) %} - {{ cards.repo(item, loop.index0, github) }} - {% endfor %} -
+ + +
+
+
+
+ Filter + +
+ + + +
+
+
+ Sort +
+ +
+
+
+
+ {% for item in collections.generators | sortTools(github) %} + {{ cards.repo(item, loop.index0, github) }} + {% endfor %} +
+
+
diff --git a/src/site/generators/ago.md b/src/site/generators/ago.md index 2fe2376d2..be110c2bb 100644 --- a/src/site/generators/ago.md +++ b/src/site/generators/ago.md @@ -5,9 +5,8 @@ homepage: https://github.com/dvwallin/ago language: - Go license: - - MIT License -templates: - - None + - MIT +templates: [] description: A static blog generator script without any fuzz --- diff --git a/src/site/generators/bashblog-ng.md b/src/site/generators/bashblog-ng.md index 1b4028e46..bc1a50023 100644 --- a/src/site/generators/bashblog-ng.md +++ b/src/site/generators/bashblog-ng.md @@ -6,8 +6,7 @@ language: - Bash license: - GPL-3.0-only -templates: - - None +templates: [] description: Bash script to create and manage blogs. --- diff --git a/src/site/generators/bashblog.md b/src/site/generators/bashblog.md index 52652f4a4..dfd827d9b 100644 --- a/src/site/generators/bashblog.md +++ b/src/site/generators/bashblog.md @@ -6,8 +6,7 @@ language: - Bash license: - GPL-3.0-only -templates: - - None +templates: [] description: A single Bash script to create blogs. --- diff --git a/src/site/generators/blogdown-r.md b/src/site/generators/blogdown-r.md index 143dd19ab..5e043bc9f 100644 --- a/src/site/generators/blogdown-r.md +++ b/src/site/generators/blogdown-r.md @@ -7,7 +7,8 @@ language: license: - GPL-3 templates: - - R, R Markdown + - R + - R Markdown description: Create Blogs and Websites with R Markdown --- diff --git a/src/site/generators/codedoc.md b/src/site/generators/codedoc.md index 7623ae0a2..eeb73193a 100644 --- a/src/site/generators/codedoc.md +++ b/src/site/generators/codedoc.md @@ -7,7 +7,9 @@ language: license: - MIT templates: - - Markdown, JSX/TSX + - Markdown + - JSX + - TSX description: Beautiful, component-based JAMStack sites for software docs/wiki --- diff --git a/src/site/generators/elderjs.md b/src/site/generators/elderjs.md index 794950eda..df75354d6 100644 --- a/src/site/generators/elderjs.md +++ b/src/site/generators/elderjs.md @@ -5,7 +5,7 @@ homepage: https://elderguide.com/tech/elderjs/ language: - JavaScript license: - - MIT License + - MIT templates: - Svelte description: An opinionated, SEO focused, static site generator for Svelte. diff --git a/src/site/generators/expansive.md b/src/site/generators/expansive.md index 0836d9cb0..96d4ec623 100644 --- a/src/site/generators/expansive.md +++ b/src/site/generators/expansive.md @@ -7,7 +7,7 @@ language: - Ejscript license: - GPL-2.0-only - - Commercial + - Embedthis Commercial templates: - Ejscript description: Expansive is a static site generator. It is written in Ejscript, and is blazing fast. diff --git a/src/site/generators/franklin.md b/src/site/generators/franklin.md index 1b475a893..66b5ad4d7 100644 --- a/src/site/generators/franklin.md +++ b/src/site/generators/franklin.md @@ -6,7 +6,7 @@ language: - Julia license: - MIT -templates: +templates: [] description: A simple, customisable static site generator oriented towards technical blogging and light, fast-loading pages. --- diff --git a/src/site/generators/generators.11tydata.js b/src/site/generators/generators.11tydata.js index d07e0d3a0..03d979c09 100644 --- a/src/site/generators/generators.11tydata.js +++ b/src/site/generators/generators.11tydata.js @@ -1,4 +1,11 @@ +const correctLicense = require("../../11ty/normalizeLicense"); + module.exports = { layout: "layouts/tool.njk", - tags: ["generators"] + tags: ["generators"], + eleventyComputed: { + license: (data) => { + return (data.license || []).map(license => correctLicense(license, data.title)); + } + } } \ No newline at end of file diff --git a/src/site/generators/pollen.md b/src/site/generators/pollen.md index 017ce0f4b..ff5957d9c 100644 --- a/src/site/generators/pollen.md +++ b/src/site/generators/pollen.md @@ -6,6 +6,7 @@ language: - Racket license: - LGPL-3.0-only +templates: [] description: A web book-publishing system written in Racket with static html as default output target. --- diff --git a/src/site/generators/qpage.md b/src/site/generators/qpage.md index 47bbc4a24..9b603d14b 100644 --- a/src/site/generators/qpage.md +++ b/src/site/generators/qpage.md @@ -6,8 +6,7 @@ language: - Python license: - MIT -templates: - - None +templates: [] description: 'QPage or QuickPage is a free project for creating academic homepage without any code' --- diff --git a/src/site/generators/rmarkdown.md b/src/site/generators/rmarkdown.md index 2bcf9a79d..41e3052ab 100644 --- a/src/site/generators/rmarkdown.md +++ b/src/site/generators/rmarkdown.md @@ -7,7 +7,8 @@ language: license: - GPL-3 templates: - - R, R Markdown + - R + - R Markdown description: Dynamic Documents for R --- diff --git a/src/site/generators/sblg.md b/src/site/generators/sblg.md index 1dd37c9fc..9ab62f261 100644 --- a/src/site/generators/sblg.md +++ b/src/site/generators/sblg.md @@ -6,8 +6,7 @@ language: - C license: - ISC -templates: - - None +templates: [] description: simple off-line blog utility --- diff --git a/src/site/generators/scalatic.md b/src/site/generators/scalatic.md index 48d8dd5e8..2073c77b9 100644 --- a/src/site/generators/scalatic.md +++ b/src/site/generators/scalatic.md @@ -6,6 +6,7 @@ language: - Scala license: - MIT +templates: [] description: Dead simple static blog generator written in Scala. --- diff --git a/src/site/generators/simple-website.md b/src/site/generators/simple-website.md index 3e9e69358..2d5c7b522 100644 --- a/src/site/generators/simple-website.md +++ b/src/site/generators/simple-website.md @@ -1,4 +1,7 @@ --- +disabled: true +# Repo no longer exists, overriding the tags will opt out of listing +tags: [] title: simple-website repo: alexanderte/simple-website homepage: https://github.com/alexanderte/simple-website diff --git a/src/site/generators/statamic.md b/src/site/generators/statamic.md index 77bbc4666..23aabcbc5 100644 --- a/src/site/generators/statamic.md +++ b/src/site/generators/statamic.md @@ -5,8 +5,7 @@ repo: statamic/ssg twitter: statamic language: - PHP -license: - - Custom +license: [] templates: - Antlers - HTML diff --git a/src/site/generators/statiq.md b/src/site/generators/statiq.md index d88ba6df3..bf399b269 100644 --- a/src/site/generators/statiq.md +++ b/src/site/generators/statiq.md @@ -5,7 +5,7 @@ homepage: https://statiq.dev/web/ language: - .Net license: - - Prosperity Public License 3.0.0 + - Prosperity Public License templates: - Razor - Markdown diff --git a/src/site/generators/systatic.md b/src/site/generators/systatic.md index 4e16067dc..a5f433665 100644 --- a/src/site/generators/systatic.md +++ b/src/site/generators/systatic.md @@ -1,4 +1,7 @@ --- +disabled: true +# Repo no longer exists, overriding the tags will opt out of listing +tags: [] title: Systatic repo: damcclean/systatic homepage: https://systatic.co diff --git a/src/site/generators/thumbsup.md b/src/site/generators/thumbsup.md index b27062ce0..ff665c1ce 100644 --- a/src/site/generators/thumbsup.md +++ b/src/site/generators/thumbsup.md @@ -6,8 +6,7 @@ language: - JavaScript license: - MIT -templates: - - None +templates: [] description: Static photo & video gallery generator --- diff --git a/src/site/generators/trio.md b/src/site/generators/trio.md index fdde7da89..186f0917b 100644 --- a/src/site/generators/trio.md +++ b/src/site/generators/trio.md @@ -6,8 +6,7 @@ language: - JavaScript license: - MIT -templates: - - None +templates: [] description: A fast, simple yet powerful JavaScript-driven static site generator. twitter: gettriossg --- diff --git a/src/site/generators/ulka.md b/src/site/generators/ulka.md index 604c8bc10..523f0efb3 100644 --- a/src/site/generators/ulka.md +++ b/src/site/generators/ulka.md @@ -3,7 +3,7 @@ title: Ulka repo: ulkajs/ulka homepage: https://ulka.js.org language: - - Javascript + - JavaScript license: - MIT templates: diff --git a/src/site/generators/weaver.md b/src/site/generators/weaver.md index 467bf67c3..c5bed1d32 100644 --- a/src/site/generators/weaver.md +++ b/src/site/generators/weaver.md @@ -1,13 +1,12 @@ --- title: Weaver repo: davidsiaw/weaver -homepage: http://davidsiaw.github.io/weaver-docs +homepage: https://davidsiaw.github.io/weaver-docs language: - Ruby license: - MIT templates: - - None description: A simple DSL-based web generator --- diff --git a/src/site/generators/wp2static.md b/src/site/generators/wp2static.md index ece9a6012..e1ece363a 100644 --- a/src/site/generators/wp2static.md +++ b/src/site/generators/wp2static.md @@ -6,6 +6,7 @@ language: - PHP license: - Unlicense +templates: [] description: Leverage WordPress as a great CMS, but benefit from the speed, security and portability that a static website provides --- diff --git a/src/site/generators/wpwmm4.md b/src/site/generators/wpwmm4.md index 7391c78fd..48192418c 100644 --- a/src/site/generators/wpwmm4.md +++ b/src/site/generators/wpwmm4.md @@ -7,6 +7,7 @@ language: - Make license: - BSD-2-Clause +templates: [] description: Web Page With Make and M4 --- diff --git a/src/site/headless-cms.njk b/src/site/headless-cms.njk index ebbdfc0fc..3e12038dd 100644 --- a/src/site/headless-cms.njk +++ b/src/site/headless-cms.njk @@ -13,11 +13,55 @@ layout: layouts/base.njk
-
- {% for item in collections.cms | sortTools(github) %} - {{ cards.repo(item, loop.index0, github) }} - {% endfor %} -
+ + +
+
+
+
+ Filter + +
+ + +
+
+
+ Sort +
+ +
+
+
+
+ {% for item in collections.cms | sortTools(github) %} + {{ cards.repo(item, loop.index0, github) }} + {% endfor %} +
+
+
diff --git a/src/site/headless-cms/gridly.md b/src/site/headless-cms/gridly.md index 426d04563..9833c4108 100644 --- a/src/site/headless-cms/gridly.md +++ b/src/site/headless-cms/gridly.md @@ -5,6 +5,7 @@ twitter: GridlyCMS typeofcms: "API Driven" supportedgenerators: - All +opensource: No description: A collaborative headless CMS for multilingual games-as-a-service projects with a powerful API, browser-based spreadsheet UI, and built-in functions to handle localization and frequent updates. --- diff --git a/src/site/headless-cms/statamic.md b/src/site/headless-cms/statamic.md index df7150331..9bb2553af 100644 --- a/src/site/headless-cms/statamic.md +++ b/src/site/headless-cms/statamic.md @@ -1,7 +1,7 @@ --- title: Statamic homepage: https://statamic.com/ -repo: https://github.com/statamic/cms +repo: statamic/cms twitter: statamic opensource: "Yes" typeofcms: "Git-based"