Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions .eleventy.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
10 changes: 10 additions & 0 deletions src/11ty/normalizeLicense.js
Original file line number Diff line number Diff line change
@@ -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;
};
9 changes: 9 additions & 0 deletions src/css/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
114 changes: 114 additions & 0 deletions src/js/filter-container.js
Original file line number Diff line number Diff line change
@@ -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);
54 changes: 54 additions & 0 deletions src/js/sort-container.js
Original file line number Diff line number Diff line change
@@ -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);
12 changes: 7 additions & 5 deletions src/site/_data/github.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
}
}
}

Expand Down
19 changes: 13 additions & 6 deletions src/site/_includes/components/cards.njk
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
{% macro repo(item, loopIndex, githubData) %}
<div id="{{item.id}}" class="generator-card flex flex-col h-full">
<div id="{{item.id}}" class="generator-card flex flex-col h-full"
data-sort-name="{{ item.data.title | lower }}"
{%- if githubData[item.data.repo].stars !== "" %} data-sort-githubstars-numeric-descending="{{ githubData[item.data.repo].stars }}"{% endif %}
{%- if item.data.typeofcms %} data-filter-typeofcms="{{ item.data.typeofcms | lower }}"{% endif %}
{%- if item.data.opensource %} data-filter-opensource="{{ item.data.opensource | lower }}"{% endif %}
data-filter-template="{{ (item.data.templates or []) | join(",") | lower }}"
{%- if item.data.language %} data-filter-language="{{ item.data.language | join(",") | lower }}"{% endif %}
{%- if item.data.license %} data-filter-license="{{ item.data.license | join(",") | lower }}"{% endif %}>
<a href="{{ item.url }}" class="flex-grow block border-0 bg-white text-gray-700 rounded-t-lg last:rounded-b-lg">
<div class="text-xl md:text-2xl lg:text-3xl font-bold text-ellipsis text-black bg-gradient-to-b border-b rounded-t-lg p-4
<div class="text-xl md:text-2xl lg:text-3xl font-bold text-ellipsis text-black bg-gradient-to-b border-b border-black rounded-t-lg p-4
{%- if loopIndex % 4 === 0 %} bg-gradient-card-sunrise
{%- elseif loopIndex % 4 === 1 %} bg-gradient-card-blue
{%- elseif loopIndex % 4 === 2 %} bg-gradient-card-seafoam
Expand Down Expand Up @@ -49,13 +56,13 @@
<dt class="col-span-2 font-bold">Language:</dt>
<dd class="col-span-3">{{ item.data.language }}</dd>
{%- endif %}
{%- if item.data.templates %}
{%- if item.data.templates and item.data.templates.length > 0 %}
<dt class="col-span-2 font-bold">Templates:</dt>
<dd class="col-span-3">{{ item.data.templates | join(", ") }}</dd>
{%- endif %}
{%- if item.data.license %}
{%- if item.data.license and item.data.license.length > 0 %}
<dt class="col-span-2 font-bold">License:</dt>
<dd class="col-span-3">{{ item.data.license }}</dd>
<dd class="col-span-3">{{ item.data.license | join(", ") }}</dd>
{%- endif %}
{%- if item.data.typeofcms %}
<dt class="col-span-2 font-bold">Type:</dt>
Expand All @@ -69,7 +76,7 @@
</div>
</a>
{% if item.data.startertemplaterepo %}
<a href="https://app.netlify.com/start/deploy?repository={{ item.data.startertemplaterepo}}" class="block px-4 py-3 text-gray-700 bg-white rounded-b-lg text-center md:text-lg whitespace-no-wrap border-t">
<a href="https://app.netlify.com/start/deploy?repository={{ item.data.startertemplaterepo}}" class="block px-4 py-3 text-gray-700 bg-white rounded-b-lg text-center md:text-lg whitespace-nowrap border-t">
<svg role="img" aria-hidden="true" focusable="false" width="30" height="30" viewBox="0 0 40 40" class="inline-block mr-1" fill="#36b0bb"><use xlink:href="#logo-netlify-gem"/></svg>
Deploy to Netlify
</a>
Expand Down
2 changes: 2 additions & 0 deletions src/site/_includes/layouts/base.njk
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,7 @@ ogimage: "/img/og/default-og-image.png"
</defs>
</svg>
<script type="module" src="/js/details-force-state.js"></script>
<script type="module" src="/js/filter-container.js"></script>
<script type="module" src="/js/sort-container.js"></script>
</body>
</html>
14 changes: 7 additions & 7 deletions src/site/_includes/layouts/tool.njk
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,21 @@ layout: layouts/base.njk
<dl class="inline-block mt-4 mb-4">
{%- if homepage %}
<dt class="sr-only">Home page</dt>
<dd class="inline-block mr-6 whitespace-no-wrap">
<dd class="inline-block mr-6 whitespace-nowrap">
<svg role="img" aria-hidden="true" focusable="false" width="20" height="20" class="fill-current inline-block mr-1"><use xlink:href="#icon-home"/></svg>
<a href="{{ homepage }}" target="_blank" rel="noopener">{{ homepage }}</a>
</dd>
{%- endif %}
{%- if repo %}
<dt class="sr-only">Repository</dt>
<dd class="inline-block mr-6 whitespace-no-wrap">
<dd class="inline-block mr-6 whitespace-nowrap">
<svg role="img" aria-hidden="true" focusable="false" width="20" height="20" class="fill-current inline-block mr-1"><use xlink:href="#icon-github"/></svg>
<a href="{% if not repohost or repohost == "github" %}https://github.com/{% endif %}{% if repohost == "gitlab" %}https://gitlab.com/{% endif %}{{ repo }}" target="_blank" rel="noopener">{{ repo }}</a>
</dd>
{%- endif %}
{%- if twitter %}
<dt class="sr-only">Twitter</dt>
<dd class="inline-block mr-6 whitespace-no-wrap">
<dd class="inline-block mr-6 whitespace-nowrap">
<svg role="img" aria-hidden="true" focusable="false" width="20" height="20" class="fill-current inline-block mr-1"><use xlink:href="#icon-twitter"/></svg>
<a href="https://twitter.com/{{ twitter }}/" target="_blank" rel="noopener">@{{ twitter }}</a>
</dd>
Expand All @@ -62,11 +62,11 @@ layout: layouts/base.njk
<dt class="inline-block font-bold">Language:</dt>
<dd class="inline-block mr-6">{{ language }}</dd>
{%- endif %}
{%- if license %}
{%- if license and license.length > 0 %}
<dt class="inline-block font-bold">License:</dt>
<dd class="inline-block mr-6">{{ license }}</dd>
<dd class="inline-block mr-6">{{ license | join(", ") }}</dd>
{%- endif %}
{%- if templates %}
{%- if templates and templates.length > 0 %}
<dt class="inline-block font-bold">Templates:</dt>
<dd class="inline mr-6">{{ templates | join(", ") }}</dd>
{%- endif %}
Expand All @@ -89,7 +89,7 @@ layout: layouts/base.njk


{% if startertemplaterepo %}
<a href="https://app.netlify.com/start/deploy?repository={{ startertemplaterepo }}" class="inline-block my-8 px-4 py-3 text-gray-700 bg-white rounded-lg text-center md:text-lg whitespace-no-wrap border-t">
<a href="https://app.netlify.com/start/deploy?repository={{ startertemplaterepo }}" class="inline-block my-8 px-4 py-3 text-gray-700 bg-white rounded-lg text-center md:text-lg whitespace-nowrap border-t">
<svg role="img" aria-hidden="true" focusable="false" width="30" height="30" viewBox="0 0 40 40" class="inline-block mr-1" fill="#36b0bb"><use xlink:href="#logo-netlify-gem"/></svg>
Deploy to Netlify
</a>
Expand Down
Loading