From ef93b55b19711f859ef96814b0c75380d1e5c2e2 Mon Sep 17 00:00:00 2001 From: Dave Lane Date: Tue, 24 Sep 2024 17:09:24 +1200 Subject: [PATCH] added filtering for categories, improved collapsing div look, tweaked filters, and other stuff --- src/lib/components/CollapsibleSection.svelte | 71 +++-- src/lib/components/Filterable.svelte | 40 ++- src/lib/components/Tile.svelte | 189 ++++++++++++ src/lib/components/filter.js | 10 +- src/routes/+page.svelte | 302 ++++++------------- 5 files changed, 352 insertions(+), 260 deletions(-) create mode 100644 src/lib/components/Tile.svelte diff --git a/src/lib/components/CollapsibleSection.svelte b/src/lib/components/CollapsibleSection.svelte index b6bfa66..62acc24 100644 --- a/src/lib/components/CollapsibleSection.svelte +++ b/src/lib/components/CollapsibleSection.svelte @@ -4,44 +4,67 @@ // Inclusive Components by Heydon Pickering https://inclusive-components.design/collapsible-sections/ export let headerText; - let expanded = false + let expanded = false;

- +
diff --git a/src/lib/components/Filterable.svelte b/src/lib/components/Filterable.svelte index 2e2d61a..059986f 100644 --- a/src/lib/components/Filterable.svelte +++ b/src/lib/components/Filterable.svelte @@ -6,7 +6,8 @@ export let tabindex = 0; export let context = 'none'; export let filterValues; - console.log('Filterable contextFilter: ', filterValues); + export let histogram; // an array with key = filter value, val = number of technologies + //console.log('Filterable contextFilter: ', filterValues); let activeFiltersString = 'None'; // @@ -20,8 +21,8 @@ }); function joinActive(isActive) { - console.log('joinActive'); - console.log('filterValues: ', filterValues); + //console.log('joinActive'); + //console.log('filterValues: ', filterValues); if (isActive.length) return formatter.format(isActive); return 'No'; } @@ -40,7 +41,7 @@ // active is an array of active filter filterValues export function filterVerbage(active) { - console.log('l = ', active.length); + //console.log('l = ', active.length); if (active.length == 1) return 'filter is'; else return 'filters are'; } @@ -48,9 +49,9 @@ // // managing filter values 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) { - console.log('b: ', b); + //console.log('b: ', b); if (b.id == filter.id) { $filterValues[i].active = filter.active; } }) return filterValues; @@ -73,6 +74,8 @@ activeFiltersString = joinActive(isActive); filterText = filterVerbage(isActive); } + + console.log('histogram: ', histogram);

Test Array

@@ -88,7 +91,7 @@ {filter.name} + name="{filter.id}" role="button" tabindex={tabindex + filter.id}>{filter.name}{#if (histogram[filter.name] > 1) }  ({histogram[filter.name]}){/if} {/each} @@ -97,6 +100,7 @@ diff --git a/src/lib/components/Tile.svelte b/src/lib/components/Tile.svelte new file mode 100644 index 0000000..92e95ea --- /dev/null +++ b/src/lib/components/Tile.svelte @@ -0,0 +1,189 @@ + + +
+ +

{technology.name}

+

+ {technology.description}{#if technology.extended_description}i{/if} +

+ {#if technology.categories}

+ {pluraliser('Category', 'Categories', technology.categories.length)}: + {toOxfordCommaString(technology.categories)} +

{/if} + {#if technology.analogues}

+ Alternative to {toOxfordCommaString(technology.analogues)} +

{/if} + {#if technology.license}

+ License: {technology.license} +

{/if} + {#if hasInstances(technology)} +
+

+ {#each technology.instances as instance} + {/each} +

+
+ {:else} +
Nothing here yet...
+ {/if} +
+ + diff --git a/src/lib/components/filter.js b/src/lib/components/filter.js index e70cc4a..158410a 100644 --- a/src/lib/components/filter.js +++ b/src/lib/components/filter.js @@ -3,17 +3,17 @@ import { getContext, setContext } from 'svelte'; export function setFilter(context) { let contextFilter = writable(0); - console.log('setFilter: ', contextFilter); + //console.log('setFilter: ', contextFilter); contextFilter.subscribe((value) => { - console.log('initial value: ', value); + //console.log('initial value: ', value); }); setContext(context, contextFilter); - console.log('contextFilter (' + context + '): ', contextFilter); + //console.log('contextFilter (' + context + '): ', contextFilter); } export function getFilter(context) { - console.log('looking for writable for context ', context); + //console.log('looking for writable for context ', context); let f = getContext(context); - console.log('returning filter writable: ', f); + //console.log('returning filter writable: ', f); return f; } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 57d11e7..f436bcf 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -18,9 +18,10 @@ // // digest and return useful info from the Webservices JSON feed function processData(webservices) { - let instances = 0; - let current = []; - let future = []; + let stats = { 'instances': 0, 'technologies': 0, 'licenses': 0,'weak': 0, 'copyleft': 0 }; + let all = []; // all services + let current = []; // those with active instances + let future = []; // those that are planned // properties of technologies let category_list = []; let analogue_list = []; @@ -33,6 +34,7 @@ // actual objects let hosts = []; let affiliates = []; + // // just a trivial reassignment //let hosts = webservices.hosts; @@ -40,7 +42,10 @@ // pull out relevant info in useful chunks. for (let key in webservices.technologies) { 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) { + //console.log('tech '+key+' ('+tech.categories.length+'):', tech.categories); tech.categories.forEach(function (category, index) { if (category_list.hasOwnProperty(category)) category_list[category]++; else category_list[category] = 1; @@ -57,13 +62,11 @@ if (license_list.hasOwnProperty(license)) license_list[license]++; else license_list[license] = 1; } - if (hasInstances(tech)) { - tech['name'] = key; current.push(tech); //console.log(tech.name + ': ' + tech.instances.length + ' instances...'); tech.instances.forEach(function (instance, i) { - instances++; + stats.instances++; if (instance.hasOwnProperty('status')) { let tag = instance.status; if (status_list.hasOwnProperty(tag)) status_list[tag]++; @@ -84,6 +87,7 @@ } else { future[key] = tech; } + all[key] = tech; } for (let key in webservices.hosts) { //console.log('key: ', key); @@ -102,8 +106,15 @@ 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 { - total_instances: instances, + stats: stats, + all_services: all, active_services: current, candidate_services: future, tech_lists: { @@ -121,18 +132,7 @@ }; } - // 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 + // return an object with ob's keys ordered alphabetically, with an id from an object function getSortedFilter(ob) { let keys = []; let i = 0; @@ -150,6 +150,17 @@ 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 function getIntersection(a, b) { const set1 = new Set(a); @@ -160,22 +171,6 @@ 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... // this one is for 'hosts' @@ -240,7 +235,7 @@ const set2 = new Set(b); const intersection = [...set1].filter((element) => set2.has(element)); - console.log('intersection = ', intersection); + //console.log('intersection = ', intersection); return intersection; } @@ -255,13 +250,14 @@ // filter technologies based on a list of Categories function filterTechnologiesByCategoryList(technologies, list) { - console.log('looking for tech in categories: ', list); + //console.log('looking for tech in categories: ', list); const included = []; technologies.forEach(function (tech) { if (hasInstances(tech)) { const intersection = inCommon(tech.categories, list); - //console.log('categories: ', tech.categories); - //console.log('list: ', list); + console.log('for tech: ', tech.name); + console.log('categories: ', tech.categories); + console.log('list: ', list); console.log('intersection: ', intersection); if (intersection && intersection.constructor === Array) { //console.log('found intersection!', intersection.constructor); @@ -323,8 +319,9 @@ // reactive stuff... $: { - console.log('about to filterTechnologiesByCategoryList'); + //console.log('about to filterTechnologiesByCategoryList'); filteredTechnologies = filterTechnologiesByCategoryList(results.active_services, flattenFilter($categoryFilter)); + //filteredTechnologies = filterTechnologiesByCategoryList(results.all_services, flattenFilter($categoryFilter)); const technologies = sortTechnologies(filteredTechnologies); } @@ -336,61 +333,75 @@
-

Web Services

+
+
+

Web Services

+

This sites exists to provide an 'always-up-to-date', in-depth description of the libre software web services I have set + up and maintain. +

-
-

This sites exists to provide an 'always-up-to-date', in-depth description of the libre software web services I have set - up and maintain. -

- -
-
-
    -
  • Total number of services: {results.total_instances}
  • -
+
+
+

Statistics

+
    +
  • Number of individual services: {results.stats.instances}
  • +
  • Number of individual technologies: {results.stats.current}
  • +
  • Number of new technologies to be added: {results.stats.future}
  • +
  • Number of libre licenses used by these technologies: {results.stats.licenses}
  • +
  •   'weak' corporate-exploitation-friendly open source licenses : {results.stats.weak}
  • +
  •   'strong' user-protecting copyleft licenses: {results.stats.copyleft}
  • +
  • Total number of services: {results.stats.instances}
  • +
+
- +
- +

Categories

- +

Analogues

- +

Open Source & Copyleft Licenses

- +

Statuses

- +

Affiliates

- +

Hosts

@@ -399,56 +410,7 @@
{#each filteredTechnologies as technology} -
- -

{technology.name}

-

- {technology.description}{#if technology.extended_description}i{/if} -

- {#if technology.categories}

- {pluraliser('Category', 'Categories', technology.categories.length)}: - {toOxfordCommaString(technology.categories)} -

{/if} - {#if technology.analogues}

- Alternative to {toOxfordCommaString(technology.analogues)} -

{/if} - {#if technology.license}

- License: {technology.license} -

{/if} - {#if hasInstances(technology)} -
-

- {#each technology.instances as instance} - {/each} -

-
- {:else} -
Nothing here yet...
- {/if} -
+ {/each}
@@ -458,121 +420,31 @@ display: grid; margin: 0 auto 3em auto; padding: 0; + position: relative; + } + .introduction { + width: 50%; } .summary { + border-radius: 15px; + /*position: absolute; + right: 20px;*/ background-color: #ccc; - display: block; + /*display: block;*/ + width: 50%; + border: 2px #aaa solid; + margin: 0; + padding: 0 20px; } /* .filters { margin-bottom: 1em;} .filters .tags { margin: 1em 0; display: block; }*/ - .collapsible { - color: #eee !important; - background-color: #666 !important; - } - .tiles { - display: grid; + /*display: grid; grid-gap: 15px; - grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); - } - /* 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 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; + grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));*/ + /*grid-template-columns: repeat(auto-fit);*/ }