Compare commits

..

No commits in common. "ef93b55b19711f859ef96814b0c75380d1e5c2e2" and "d2aab294bdd8c3fbcf092195a3f78303e055f32b" have entirely different histories.

5 changed files with 317 additions and 402 deletions

View file

@ -4,67 +4,40 @@
// Inclusive Components by Heydon Pickering https://inclusive-components.design/collapsible-sections/ // Inclusive Components by Heydon Pickering https://inclusive-components.design/collapsible-sections/
export let headerText; export let headerText;
let expanded = false; let expanded = false
</script> </script>
<div class="collapsible"> <div class="collapsible">
<h3> <h3>
<button aria-expanded={expanded} on:click={() => expanded = !expanded}>{headerText} <button aria-expanded={expanded} on:click={() => expanded = !expanded}>{headerText}
<svg class="indicator" viewBox="0 0 20 20" fill="none" > <svg viewBox="0 0 20 20" fill="none" >
<path class="vert" d="M10 1V19" stroke="black" stroke-width="2"/> <path class="vert" d="M10 1V19" stroke="black" stroke-width="2"/>
<path d="M1 10L19 10" stroke="black" stroke-width="2"/> <path d="M1 10L19 10" stroke="black" stroke-width="2"/>
</svg> </svg>
</button> </button>
</h3> </h3>
<!-- <div class='tree' hidden={!expanded}>
<svg viewBox="0 0 60 60" fill="#ccc">
<path class="open" d="M40 1 V69" stroke="#444" stroke-width="4" />
</svg>
</div> -->
<div class='contents' hidden={!expanded}> <div class='contents' hidden={!expanded}>
<slot></slot> <slot></slot>
</div> </div>
</div> </div>
<style> <style>
.collapsible { .collapsible { border-bottom: 1px solid var(--gray-light, #eee); }
margin: 8px 0 4px 0; h3 { margin: 0; }
} button {
.collapsible .collapsible h3 button { background-color: var(--background, #fff);
padding-left: 20px; color: var(--gray-darkest, #282828);
} display: flex;
.tree { float: left; } justify-content: space-between;
.contents { width: 100%;
float: left; border: none;
clear: both; margin: 0;
width: 100%; padding: 1em 0.5em;
border-bottom: dashed #aaa 1px; }
padding-bottom: 1em; button[aria-expanded="true"] { border-bottom: 1px solid var(--gray-light, #eee); }
margin-bottom: 1em; button[aria-expanded="true"] .vert { display: none; }
} button:focus svg { outline: 2px solid; }
h3 { button [aria-expanded="true"] rect { fill: currentColor; }
margin: 0; svg { height: 0.7em; width: 0.7em; }
}
button {
background-color: var(--background, #fff);
color: var(--gray-darkest, #282828);
display: flex;
justify-content: space-between;
border: 1px #aaa solid;
margin-left: 0px;
padding: 0.7em 0.7em;
}
button[aria-expanded="true"] { border-bottom: 2px solid var(--gray-light, #eee); }
button[aria-expanded="true"] .vert { display: none; }
button:focus svg { outline: 1firtpx solid; }
button[aria-expanded="true"] rect { fill: currentColor; }
svg.indicator {
height: 1.0em;
width: 1.0em;
margin-left: 6px;
margin-right: 6px;
margin-top: 4px;;
}
.tree:hover svg { outline: 1firtpx solid; }
</style> </style>

View file

@ -6,8 +6,7 @@
export let tabindex = 0; export let tabindex = 0;
export let context = 'none'; export let context = 'none';
export let filterValues; export let filterValues;
export let histogram; // an array with key = filter value, val = number of technologies console.log('Filterable contextFilter: ', filterValues);
//console.log('Filterable contextFilter: ', filterValues);
let activeFiltersString = 'None'; let activeFiltersString = 'None';
// //
@ -21,8 +20,8 @@
}); });
function joinActive(isActive) { function joinActive(isActive) {
//console.log('joinActive'); console.log('joinActive');
//console.log('filterValues: ', filterValues); console.log('filterValues: ', filterValues);
if (isActive.length) return formatter.format(isActive); if (isActive.length) return formatter.format(isActive);
return 'No'; return 'No';
} }
@ -41,7 +40,7 @@
// active is an array of active filter filterValues // active is an array of active filter filterValues
export function filterVerbage(active) { export function filterVerbage(active) {
//console.log('l = ', active.length); console.log('l = ', active.length);
if (active.length == 1) return 'filter is'; if (active.length == 1) return 'filter is';
else return 'filters are'; else return 'filters are';
} }
@ -49,9 +48,9 @@
// //
// managing filter values // managing filter values
function updateFilterValues(filter) { function updateFilterValues(filter) {
//console.log('filter ('+ filter.id + ') has state ' + filter.active + '...'); console.log('filter ('+ filter.id + ') has state ' + filter.active + '...');
$filterValues.forEach(function(b, i) { $filterValues.forEach(function(b, i) {
//console.log('b: ', b); console.log('b: ', b);
if (b.id == filter.id) { $filterValues[i].active = filter.active; } if (b.id == filter.id) { $filterValues[i].active = filter.active; }
}) })
return filterValues; return filterValues;
@ -74,8 +73,6 @@
activeFiltersString = joinActive(isActive); activeFiltersString = joinActive(isActive);
filterText = filterVerbage(isActive); filterText = filterVerbage(isActive);
} }
console.log('histogram: ', histogram);
</script> </script>
<slot name="h2"><h2>Test Array</h2></slot> <slot name="h2"><h2>Test Array</h2></slot>
@ -91,7 +88,7 @@
<span class="filter {context}" class:active={filter.active} <span class="filter {context}" class:active={filter.active}
on:click="{() => { filter.active = !filter.active; updateFilterValues(filter);}}" on:click="{() => { filter.active = !filter.active; updateFilterValues(filter);}}"
on:keypress="{() => {filter.active = !filter.active; updateFilterValues(filter);}}" on:keypress="{() => {filter.active = !filter.active; updateFilterValues(filter);}}"
name="{filter.id}" role="button" tabindex={tabindex + filter.id}>{filter.name}{#if (histogram[filter.name] > 1) } <span class='histogram' title="There are {histogram[filter.name]} webservices in this category">&nbsp;({histogram[filter.name]})</span>{/if}</span><wbr/> name="{filter.id}" role="button" tabindex={tabindex + filter.id}>{filter.name}</span><wbr/>
{/each} {/each}
</div> </div>
@ -100,7 +97,6 @@
<style> <style>
h2 { h2 {
margin-bottom: 0.2em; margin-bottom: 0.2em;
color: #444;
} }
.actions { .actions {
display: inline; display: inline;
@ -111,7 +107,6 @@
padding: 4px 8px; padding: 4px 8px;
} }
.actions span { .actions span {
font-size: 90%;
margin-right: 1em; margin-right: 1em;
margin-bottom: 1em; margin-bottom: 1em;
cursor: pointer; cursor: pointer;
@ -119,13 +114,15 @@
.actions span:hover { color: #000; } .actions span:hover { color: #000; }
.actions span:last { margin-right: 0px; } .actions span:last { margin-right: 0px; }
.container { .container {
line-height: 1.5em; line-height: 3em;
width: 90%; width: 90%;
padding: 1em 0 0 0; padding: 1em 0 0 0;
margin-bottom: 20px; /*display: grid;
grid-gap: 4px;
grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));*/
} }
.filter { .filter {
font-size: 80%; font-size: 80%;
width: 80px; width: 80px;
height: auto; height: auto;
border: 1px #aaa solid; border: 1px #aaa solid;
@ -144,48 +141,6 @@
} }
.active { .active {
background-color: lightblue; background-color: lightblue;
/*border-color: blue;*/ border-color: blue;
}
/* instance-specific styles */
.filter {
font-size: 80%;
margin-right: 0.5em;
line-height: 2.5;
padding: 6px 8px;
border-radius: 10px;
white-space: nowrap;
word-break: normal;
box-shadow: 3px 3px 3px #6a6d6a;
color: #222;
}
.filter:hover { box-shadow: 4px 4px 3px #727372; color: #000; }
.filter:hover.active { color: #fff;}
.filter.categories.active {
background-color: #9aa34d;
color: #eee;
}
.filter.analogues.active {
background-color: #a34b62;
color: #eee;
}
.filter.licenses.active {
background-color: #6498a3;
color: #eee;
}
.filter.statuses.active {
background-color: #a369a2;
color: #eee;
}
.filter.affiliates.active {
background-color: #fff;
color: #000;
}
.filter.hosts.active {
background-color: #fff;
color: #000;
}
.filter .histogram {
color: #aaa;
} }
</style> </style>

View file

@ -1,189 +0,0 @@
<script>
// a 'technology' object
export let technology;
export let affiliate_colours;
const formatter = new Intl.ListFormat('en', {
style: 'long',
type: 'conjunction',
});
// combine an array of terms into a sentence with proper Oxford commas, dealing with the special cases
// of one or two elements.
function toOxfordCommaString(arr) {
//console.log('arr: ', arr);
return formatter.format(arr);
}
// return a singular or plural term depending on the value of 'count'
function pluraliser(singular, plural, count) {
if (count > 1) return plural;
return singular;
}
// console.log(technologies);
// return true if a tech object includes valid instances
function hasInstances(tech) {
if (
tech.hasOwnProperty('instances') &&
tech.instances.constructor === Array &&
tech.instances.length
) return true;
return false;
}
</script>
<div class="tile technology">
<div class="links">
{#if technology.repository}<span class="repository"
><a
href={technology.repository}
title="The source code repository for {technology.name}">R</a
></span
>{/if}
{#if technology.wikipedia}<span class="wikipedia"
><a href={technology.wikipedia} title="Wikipedia Page for {technology.name}">W</a
></span
>{/if}
</div>
<h2><a href={technology.website}>{technology.name}</a></h2>
<p class="description">
{technology.description}{#if technology.extended_description}<span
title={technology.extended_description}>i</span
>{/if}
</p>
{#if technology.categories}<p class="categories">
{pluraliser('Category', 'Categories', technology.categories.length)}:
<span class="value">{toOxfordCommaString(technology.categories)}</span>
</p>{/if}
{#if technology.analogues}<p class="analogues">
Alternative to <span class="value">{toOxfordCommaString(technology.analogues)}</span>
</p>{/if}
{#if technology.license}<p
class="license"
title="The libre license for this project is {technology.license}"
>
License: <span class="value">{technology.license}</span>
</p>{/if}
{#if hasInstances(technology)}
<div class="instances">
<p>
{#each technology.instances as instance}
<a
href="https://{instance.domain}"
title="{technology.name} instance {instance.domain} hosted on '{instance.host}' by {instance.affiliation}"
><span
class="marker circle"
style="background-color: {affiliate_colours[instance.affiliation].colour}"
></span></a
>{/each}
</p>
</div>
{:else}
<div class="instances">Nothing here yet...</div>
{/if}
</div>
<style>
.tile {
display: inline-block;
height: 515px;
/*min-width: 240px;
max-width: 300px;*/
width: 290px;
overflow: hidden;
box-sizing: border-box;
box-shadow: 5px 5px 3px #6a6d6a;
border: solid 3px #516855;
border-radius: 15px;
text-overflow: ellipsis;
overflow: hidden;
position: relative;
background-color: #eee;
margin-top: 8px;
margin-right: 18px;
margin-bottom: 10px;
}
.tile .links {
position: absolute;
top: 0;
right: 0;
padding: 2px 6px;
font-size: 90%;
}
.tile:hover {
box-shadow: 10px 10px 6px #727372;
}
.tile p.description {
height: 200px;
overflow: scroll;
}
.tile .links h2 a {
color: #e6c4fc;
}
.tile .links a:visited {
color: #ced0ff;
}
.tile .links a:hover,
.tile:hover h2 a {
color: #fff !important;
}
.tile h2 {
background-color: #999;
text-align: center;
padding: 0.5em;
margin: 0;
}
.tile h2 a {
color: #e6c4fc;
}
.tile h2 a:visited {
color: #ced0ff;
}
.tile h2 a:hover {
color: #fff;
}
.tile .description {
height: 260px;
text-overflow: ellipsis;
overflow: hidden;
}
.tile p {
padding: 0 1em 0.2em 1em;
font-size: 80%;
color: #555;
}
.instances {
background-color: #ddd;
position: absolute;
bottom: 0;
height: 3em;
width: 100%;
}
.instances .marker {
margin-right: 3px;
}
.webservices li {
list-style-type: none;
}
.value {
font-weight: bold;
color: #000;
}
.marker {
vertical-align: middle;
}
.circle {
height: 20px;
width: 20px;
background-color: #555;
border-radius: 50%;
display: inline-block;
}
.square {
height: 20px;
width: 20px;
background-color: #555;
display: inline-block;
}
</style>

View file

@ -3,17 +3,17 @@ import { getContext, setContext } from 'svelte';
export function setFilter(context) { export function setFilter(context) {
let contextFilter = writable(0); let contextFilter = writable(0);
//console.log('setFilter: ', contextFilter); console.log('setFilter: ', contextFilter);
contextFilter.subscribe((value) => { contextFilter.subscribe((value) => {
//console.log('initial value: ', value); console.log('initial value: ', value);
}); });
setContext(context, contextFilter); setContext(context, contextFilter);
//console.log('contextFilter (' + context + '): ', contextFilter); console.log('contextFilter (' + context + '): ', contextFilter);
} }
export function getFilter(context) { export function getFilter(context) {
//console.log('looking for writable for context ', context); console.log('looking for writable for context ', context);
let f = getContext(context); let f = getContext(context);
//console.log('returning filter writable: ', f); console.log('returning filter writable: ', f);
return f; return f;
} }

View file

@ -10,18 +10,16 @@
import { setFilter, getFilter } from '$lib/components/filter.js'; import { setFilter, getFilter } from '$lib/components/filter.js';
import Filterable from '$lib/components/Filterable.svelte'; import Filterable from '$lib/components/Filterable.svelte';
// collapsible sections // collapsible sections
import CollapsibleSection from '$lib/components/CollapsibleSection.svelte'; import CollapsibleSection from '$lib/components/CollapsibleSection.svelte'
import Tile from '$lib/components/Tile.svelte';
//console.log('colours: ', colours); //console.log('colours: ', colours);
// //
// digest and return useful info from the Webservices JSON feed // digest and return useful info from the Webservices JSON feed
function processData(webservices) { function processData(webservices) {
let stats = { 'instances': 0, 'technologies': 0, 'licenses': 0,'weak': 0, 'copyleft': 0 }; let instances = 0;
let all = []; // all services let current = [];
let current = []; // those with active instances let future = [];
let future = []; // those that are planned
// properties of technologies // properties of technologies
let category_list = []; let category_list = [];
let analogue_list = []; let analogue_list = [];
@ -34,7 +32,6 @@
// actual objects // actual objects
let hosts = []; let hosts = [];
let affiliates = []; let affiliates = [];
// //
// just a trivial reassignment // just a trivial reassignment
//let hosts = webservices.hosts; //let hosts = webservices.hosts;
@ -42,10 +39,7 @@
// pull out relevant info in useful chunks. // pull out relevant info in useful chunks.
for (let key in webservices.technologies) { for (let key in webservices.technologies) {
let tech = webservices.technologies[key]; let tech = webservices.technologies[key];
tech['name'] = key;
console.log('tech '+key+' ('+tech.categories.length+'):', tech.categories);
if (tech.hasOwnProperty('categories') && tech.categories.constructor === Array) { if (tech.hasOwnProperty('categories') && tech.categories.constructor === Array) {
//console.log('tech '+key+' ('+tech.categories.length+'):', tech.categories);
tech.categories.forEach(function (category, index) { tech.categories.forEach(function (category, index) {
if (category_list.hasOwnProperty(category)) category_list[category]++; if (category_list.hasOwnProperty(category)) category_list[category]++;
else category_list[category] = 1; else category_list[category] = 1;
@ -62,11 +56,13 @@
if (license_list.hasOwnProperty(license)) license_list[license]++; if (license_list.hasOwnProperty(license)) license_list[license]++;
else license_list[license] = 1; else license_list[license] = 1;
} }
if (hasInstances(tech)) { if (hasInstances(tech)) {
tech['name'] = key;
current.push(tech); current.push(tech);
//console.log(tech.name + ': ' + tech.instances.length + ' instances...'); //console.log(tech.name + ': ' + tech.instances.length + ' instances...');
tech.instances.forEach(function (instance, i) { tech.instances.forEach(function (instance, i) {
stats.instances++; instances++;
if (instance.hasOwnProperty('status')) { if (instance.hasOwnProperty('status')) {
let tag = instance.status; let tag = instance.status;
if (status_list.hasOwnProperty(tag)) status_list[tag]++; if (status_list.hasOwnProperty(tag)) status_list[tag]++;
@ -87,7 +83,6 @@
} else { } else {
future[key] = tech; future[key] = tech;
} }
all[key] = tech;
} }
for (let key in webservices.hosts) { for (let key in webservices.hosts) {
//console.log('key: ', key); //console.log('key: ', key);
@ -106,15 +101,8 @@
affiliates[key] = affiliate; affiliates[key] = affiliate;
} }
// assign stats based on gathered data...
stats.current = current.length;
stats.future = future.length;
console.log('license_list: ', license_list);
stats.licenses = license_list.length;
return { return {
stats: stats, total_instances: instances,
all_services: all,
active_services: current, active_services: current,
candidate_services: future, candidate_services: future,
tech_lists: { tech_lists: {
@ -132,7 +120,18 @@
}; };
} }
// return an object with ob's keys ordered alphabetically, with an id from an object // console.log(technologies);
// return true if a tech object includes valid instances
function hasInstances(tech) {
if (
tech.hasOwnProperty('instances') &&
tech.instances.constructor === Array &&
tech.instances.length
) return true;
return false;
}
// return an object with ob's keys ordered alphabetically, with an idfrom an object
function getSortedFilter(ob) { function getSortedFilter(ob) {
let keys = []; let keys = [];
let i = 0; let i = 0;
@ -150,17 +149,6 @@
return key_array; return key_array;
} }
// return true if a tech object includes valid instances
function hasInstances(tech) {
if (
tech.hasOwnProperty('instances') &&
tech.instances.constructor === Array &&
tech.instances.length
) return true;
return false;
}
// get intersection: ref https://bobbyhadz.com/blog/javascript-get-intersection-of-two-arrays // get intersection: ref https://bobbyhadz.com/blog/javascript-get-intersection-of-two-arrays
function getIntersection(a, b) { function getIntersection(a, b) {
const set1 = new Set(a); const set1 = new Set(a);
@ -171,6 +159,22 @@
return intersection; return intersection;
} }
// combine an array of terms into a sentence with proper Oxford commas, dealing with the special cases
// of one or two elements.
function toOxfordCommaString(arr) {
if (arr.length == 1) return arr;
if (arr.length == 2) return arr.join(' and ');
else {
var last = arr.pop();
return arr.join(', ') + ', and ' + last;
}
}
// return a singular or plural term depending on the value of 'count'
function pluraliser(singular, plural, count) {
if (count > 1) return plural;
return singular;
}
// assign colours from a set of differentiated colours to a list of tags... // assign colours from a set of differentiated colours to a list of tags...
// this one is for 'hosts' // this one is for 'hosts'
@ -235,7 +239,7 @@
const set2 = new Set(b); const set2 = new Set(b);
const intersection = [...set1].filter((element) => set2.has(element)); const intersection = [...set1].filter((element) => set2.has(element));
//console.log('intersection = ', intersection); console.log('intersection = ', intersection);
return intersection; return intersection;
} }
@ -250,14 +254,13 @@
// filter technologies based on a list of Categories // filter technologies based on a list of Categories
function filterTechnologiesByCategoryList(technologies, list) { function filterTechnologiesByCategoryList(technologies, list) {
//console.log('looking for tech in categories: ', list); console.log('looking for tech in categories: ', list);
const included = []; const included = [];
technologies.forEach(function (tech) { technologies.forEach(function (tech) {
if (hasInstances(tech)) { if (hasInstances(tech)) {
const intersection = inCommon(tech.categories, list); const intersection = inCommon(tech.categories, list);
console.log('for tech: ', tech.name); //console.log('categories: ', tech.categories);
console.log('categories: ', tech.categories); //console.log('list: ', list);
console.log('list: ', list);
console.log('intersection: ', intersection); console.log('intersection: ', intersection);
if (intersection && intersection.constructor === Array) { if (intersection && intersection.constructor === Array) {
//console.log('found intersection!', intersection.constructor); //console.log('found intersection!', intersection.constructor);
@ -319,9 +322,8 @@
// reactive stuff... // reactive stuff...
$: { $: {
//console.log('about to filterTechnologiesByCategoryList'); console.log('about to filterTechnologiesByCategoryList');
filteredTechnologies = filterTechnologiesByCategoryList(results.active_services, flattenFilter($categoryFilter)); filteredTechnologies = filterTechnologiesByCategoryList(results.active_services, flattenFilter($categoryFilter));
//filteredTechnologies = filterTechnologiesByCategoryList(results.all_services, flattenFilter($categoryFilter));
const technologies = sortTechnologies(filteredTechnologies); const technologies = sortTechnologies(filteredTechnologies);
} }
@ -333,75 +335,61 @@
</script> </script>
<div class="webservices"> <div class="webservices">
<div class="header"> <h1>Web Services</h1>
<div class="introduction">
<h1>Web Services</h1> <div class="introduction">
<p>This sites exists to provide an 'always-up-to-date', in-depth description of the <a <p>This sites exists to provide an 'always-up-to-date', in-depth description of the <a
href="https://tech.oeru.org/foss-libresoftware-its-about-clarity-and-values" href="https://tech.oeru.org/foss-libresoftware-its-about-clarity-and-values"
title="What do I mean by 'libre' software?">libre software</a> web services <a href="https://davelane.nz" title="Who is this Dave Lane character?">I</a> have set title="What do I mean by 'libre' software?">libre software</a> web services <a href="https://davelane.nz" title="Who is this Dave Lane character?">I</a> have set
up and maintain. up and maintain.
</p> </p>
</div>
<div class="summary">
<h2>Statistics</h2>
<ul>
<li>Number of individual services: {results.stats.instances}</li>
<li>Number of individual technologies: {results.stats.current}</li>
<li>Number of new technologies to be added: {results.stats.future}</li>
<li>Number of libre licenses used by these technologies: {results.stats.licenses}</li>
<li>&nbsp;&nbsp;'weak' corporate-exploitation-friendly open source licenses : {results.stats.weak}</li>
<li>&nbsp;&nbsp;'strong' user-protecting copyleft licenses: {results.stats.copyleft}</li>
<li>Total number of services: {results.stats.instances}</li>
</ul>
</div>
</div> </div>
<CollapsibleSection headerText={'Filters'}> <div class="summary">
<ul>
<li>Total number of services: {results.total_instances}</li>
</ul>
</div>
<CollapsibleSection headerText={'Show or Hide Filters'}>
<div class="filters"> <div class="filters">
<CollapsibleSection headerText={'Software Catergory Filter'}> <CollapsibleSection headerText={'Show or Hide Software Catergory Filter'}>
<div class="filter-group categories"> <div class="filter categories">
<Filterable filterValues={categoryFilter} <Filterable filterValues={categoryFilter} context={'categories'}>
histogram={results.tech_lists.category_list} context={'categories'}>
<h2 slot="h2">Categories</h2> <h2 slot="h2">Categories</h2>
</Filterable> </Filterable>
</div> </div>
</CollapsibleSection> </CollapsibleSection>
<CollapsibleSection headerText={'Software Analogue Filter'}> <CollapsibleSection headerText={'Show or Hide Software Analogue Filter'}>
<div class="filter-group analogues"> <div class="filter analogues">
<Filterable filterValues={analogueFilter} <Filterable filterValues={analogueFilter} context={'analogues'}>
histogram={results.tech_lists.analogue_list} context={'analogues'}>
<h2 slot="h2">Analogues</h2> <h2 slot="h2">Analogues</h2>
</Filterable> </Filterable>
</div> </div>
</CollapsibleSection> </CollapsibleSection>
<CollapsibleSection headerText={'License Filter'}> <CollapsibleSection headerText={'Show or Hide License Filter'}>
<div class="filter-group licenses"> <div class="filter licenses">
<Filterable filterValues={licenseFilter} <Filterable filterValues={licenseFilter} context={'licenses'}>
histogram={results.tech_lists.license_list} context={'licenses'}>
<h2 slot="h2">Open Source & Copyleft Licenses</h2> <h2 slot="h2">Open Source & Copyleft Licenses</h2>
</Filterable> </Filterable>
</div> </div>
</CollapsibleSection> </CollapsibleSection>
<CollapsibleSection headerText={'Service Status Filter'}> <CollapsibleSection headerText={'Show or Hide Service Status Filter'}>
<div class="filter-group statuses"> <div class="filter statuses">
<Filterable filterValues={statusFilter} <Filterable filterValues={statusFilter} context={'statuses'}>
histogram={results.instance_lists.status_list} context={'statuses'}>
<h2 slot="h2">Statuses</h2> <h2 slot="h2">Statuses</h2>
</Filterable> </Filterable>
</div> </div>
</CollapsibleSection> </CollapsibleSection>
<CollapsibleSection headerText={'Service Affiliates Filter'}> <CollapsibleSection headerText={'Show or Hide Service Affiliates Filter'}>
<div class="filter-group affilates"> <div class="filter affilates">
<Filterable filterValues={affiliateFilter} <Filterable filterValues={affiliateFilter} context={'affiliates'}>
histogram={results.instance_lists.affiliate_list} context={'affiliates'}>
<h2 slot="h2">Affiliates</h2> <h2 slot="h2">Affiliates</h2>
</Filterable> </Filterable>
</div> </div>
</CollapsibleSection> </CollapsibleSection>
<CollapsibleSection headerText={'Service Host filter'}> <CollapsibleSection headerText={'Show or Hide Service Host filter'}>
<div class="filter-group hosts"> <div class="filter hosts">
<Filterable filterValues={hostFilter} <Filterable filterValues={hostFilter} context={'hosts'}>
histogram={results.instance_lists.host_list} context={'hosts'}>
<h2 slot="h2">Hosts</h2> <h2 slot="h2">Hosts</h2>
</Filterable> </Filterable>
</div> </div>
@ -410,7 +398,56 @@
</CollapsibleSection> </CollapsibleSection>
<div class="tiles"> <div class="tiles">
{#each filteredTechnologies as technology} {#each filteredTechnologies as technology}
<Tile technology={technology} affiliate_colours={affiliate_colours} /> <div class="tile technology">
<div class="links">
{#if technology.repository}<span class="repository"
><a
href={technology.repository}
title="The source code repository for {technology.name}">R</a
></span
>{/if}
{#if technology.wikipedia}<span class="wikipedia"
><a href={technology.wikipedia} title="Wikipedia Page for {technology.name}">W</a
></span
>{/if}
</div>
<h2><a href={technology.website}>{technology.name}</a></h2>
<p class="description">
{technology.description}{#if technology.extended_description}<span
title={technology.extended_description}>i</span
>{/if}
</p>
{#if technology.categories}<p class="categories">
{pluraliser('Category', 'Categories', technology.categories.length)}:
<span class="value">{toOxfordCommaString(technology.categories)}</span>
</p>{/if}
{#if technology.analogues}<p class="analogues">
Alternative to <span class="value">{toOxfordCommaString(technology.analogues)}</span>
</p>{/if}
{#if technology.license}<p
class="license"
title="The libre license for this project is {technology.license}"
>
License: <span class="value">{technology.license}</span>
</p>{/if}
{#if hasInstances(technology)}
<div class="instances">
<p>
{#each technology.instances as instance}
<a
href="https://{instance.domain}"
title="{technology.name} instance {instance.domain} hosted on '{instance.host}' by {instance.affiliation}"
><span
class="marker circle"
style="background-color: {affiliate_colours[instance.affiliation].colour}"
></span></a
>{/each}
</p>
</div>
{:else}
<div class="instances">Nothing here yet...</div>
{/if}
</div>
{/each} {/each}
</div> </div>
</div> </div>
@ -420,31 +457,170 @@
display: grid; display: grid;
margin: 0 auto 3em auto; margin: 0 auto 3em auto;
padding: 0; padding: 0;
position: relative;
}
.introduction {
width: 50%;
} }
.summary { .summary {
border-radius: 15px;
/*position: absolute;
right: 20px;*/
background-color: #ccc; background-color: #ccc;
/*display: block;*/ display: block;
width: 50%; }
border: 2px #aaa solid; .filters {
margin: 0; /*background-color: #f1ff94;
padding: 0 20px; padding: 0.5em;
border: 3px solid #cbea77;
margin: 1em 0;*/
margin-bottom: 1em;
} }
/* .filters { margin-bottom: 1em;}
.filters .tags { .filters .tags {
/*padding: 0.5em;*/
margin: 1em 0; margin: 1em 0;
display: block; display: block;
}*/ }
.tag-list {
/* white-space: normal;
word-break: normal;
display: inline;*/
}
.tags {
/* white-space: normal;
word-break: normal;
display: inline;*/
}
.tag {
font-size: 80%;
margin-right: 0.5em;
line-height: 2.5;
padding: 6px 8px;
border-radius: 10px;
white-space: nowrap;
word-break: normal;
box-shadow: 3px 3px 3px #6a6d6a;
}
.tag:hover { box-shadow: 4px 4px 3px #727372;}
.tag.category {
background-color: #9aa34d;
color: #fff;
}
.tag.license {
background-color: #6498a3;
color: #fff;
}
.tag.status {
background-color: #a369a2;
color: #fff;
}
.tag.affiliate {
background-color: #fff;
color: #000;
}
.tag.host {
background-color: #fff;
color: #000;
}
.tag.on {
padding-right: 25px;
background-image: url('/images/check-black-in-white-20px.png');
background-position: right center;
background-repeat: no-repeat;
}
.tag.off {
color: #ccc;
opacity: 0.5;
}
.tiles { .tiles {
/*display: grid; display: grid;
grid-gap: 15px; grid-gap: 15px;
grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));*/ grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
/*grid-template-columns: repeat(auto-fit);*/ }
/* flip card stuff: https://www.w3schools.com/howto/howto_css_flip_card.asp */
.tile {
height: 515px;
min-width: 240px;
max-width: 300px;
/*width: 290px;*/
overflow: hidden;
box-sizing: border-box;
box-shadow: 5px 5px 3px #6a6d6a;
border: solid 3px #1e6831;
text-overflow: ellipsis;
overflow: hidden;
position: relative;
background-color: #eee;
}
.tile .links {
position: absolute;
top: 0;
right: 0;
padding: 2px 6px;
font-size: 90%;
}
.tile:hover {
box-shadow: 10px 10px 6px #727372;
}
.tile .links h2 a {
color: #e6c4fc;
}
.tile .links a:visited {
color: #ced0ff;
}
.tile .links a:hover,
.tile:hover h2 a {
color: #fff !important;
}
.tile h2 {
background-color: #999;
text-align: center;
padding: 0.5em;
margin: 0;
}
.tile h2 a {
color: #e6c4fc;
}
.tile h2 a:visited {
color: #ced0ff;
}
.tile h2 a:hover {
color: #fff;
}
.tile .description {
height: 260px;
text-overflow: ellipsis;
overflow: hidden;
}
.tile p {
padding: 0 1em 0.2em 1em;
font-size: 80%;
color: #555;
}
.instances {
background-color: #ddd;
position: absolute;
bottom: 0;
height: 3em;
width: 100%;
}
.instances .marker {
margin-right: 3px;
}
.webservices li {
list-style-type: none;
}
.value {
font-weight: bold;
color: #000;
}
.marker {
vertical-align: middle;
}
.circle {
height: 20px;
width: 20px;
background-color: #555;
border-radius: 50%;
display: inline-block;
}
.square {
height: 20px;
width: 20px;
background-color: #555;
display: inline-block;
} }
</style> </style>