initial commit of new code to manage filters

This commit is contained in:
Dave Lane 2024-09-18 15:53:10 +12:00
parent 78dcd84341
commit a15ad889d1
3 changed files with 334 additions and 86 deletions

View file

@ -0,0 +1,145 @@
<script>
//import { setFilter, getFilter } from './filters';
// default selectAll value
let selectAll = true;
export let tabindex = 0;
export let context = 'none';
export let filterValues;
console.log('Filterable contextFilter: ', filterValues);
let activeFiltersString = 'None';
//
// printing output for filter
let isActive = [];
let filterText = 'none';
const formatter = new Intl.ListFormat('en', {
style: 'long',
type: 'conjunction',
});
function joinActive(isActive) {
console.log('joinActive');
console.log('filterValues: ', filterValues);
if (isActive.length) return formatter.format(isActive);
return 'None';
}
function getActive($f) {
let isActive = [];
//console.log('getActive type for filterValues: ', filterValues.constructor);
// use the global (to this component) variable
$f.forEach(function(filter, index) {
if (filter.active) {
isActive = [...isActive, filter.name];
}
});
return isActive;
}
// active is an array of active filter filterValues
export function filterVerbage(active) {
console.log('l = ', active.length);
if (active.length == 1) return 'filter is';
else return 'filters are';
}
//
// managing filter values
function updateFilterValues(filter) {
console.log('filter ('+ filter.id + ') has state ' + filter.active + '...');
$filterValues.forEach(function(b, i) {
console.log('b: ', b);
if (b.id == filter.id) { $filterValues[i].active = filter.active; }
})
return filterValues;
}
function allNoneActive() {
selectAll = !selectAll;
$filterValues.forEach(function(b, i) { $filterValues[i].active = selectAll; });
}
function invertActive() {
$filterValues.forEach(function(b, i) {
$filterValues[i].active = !$filterValues[i].active;
});
}
$: {
//console.log('doh! ', $filterValues);
isActive = getActive($filterValues);
activeFiltersString = joinActive(isActive);
filterText = filterVerbage(isActive);
}
</script>
<slot name="h2"><h2>Test Array</h2></slot>
<div class="actions">
<span class="allNone" role="button" tabindex={tabindex++} on:click={() => allNoneActive()}
on:keypress={() => allNoneActive()}>select all/none</span><wbr/>
<span class="invert" role="button" tabindex={tabindex++} on:click={() => invertActive()}
on:keypress={() => invertActive()}>invert selection</span>
</div>
<div class='container'>
{#each $filterValues as filter (filter.id)}
<span class="filter {context}" class:active={filter.active}
on:click="{() => { 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} ({filter.id})</span><wbr/>
{/each}
</div>
<p>{activeFiltersString} {filterText} active.</p>
<style>
h2 {
margin-bottom: 0.2em;
}
.actions {
display: inline;
color: #444;
/*width: 100%;*/
background-color: #eee;
border: 1px solid #ddd;
padding: 4px 8px;
}
.actions span {
margin-right: 1em;
margin-bottom: 1em;
cursor: pointer;
}
.actions span:hover { color: #000; }
.actions span:last { margin-right: 0px; }
.container {
line-height: 3em;
width: 90%;
padding: 1em 0 0 0;
/*display: grid;
grid-gap: 4px;
grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));*/
}
.filter {
width: 80px;
height: auto;
border: 1px #aaa solid;
background-color: #eee;
padding: 10px;
box-shadow: 4px 4px 3px #aaa;
border-radius: 6px;
cursor: pointer;
white-space: nowrap;
word-break: normal;
margin: 1.5em 1em 2em 0;
}
.filter:hover {
box-shadow: 6px 6px 5px #aaa;
border-color: #222;
}
.active {
background-color: lightblue;
border-color: blue;
}
</style>

View file

@ -6,11 +6,12 @@
import { references } from '$lib/references.js'; import { references } from '$lib/references.js';
import { colours } from '$lib/colours.js'; import { colours } from '$lib/colours.js';
import { setFilter, getFilter } from '$lib/components/filter.js';
import Filterable from '$lib/components/Filterable.svelte';
//console.log('colours: ', colours); //console.log('colours: ', colours);
// //
//function
// 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 instances = 0; let instances = 0;
@ -129,19 +130,26 @@
tech.hasOwnProperty('instances') && tech.hasOwnProperty('instances') &&
tech.instances.constructor === Array && tech.instances.constructor === Array &&
tech.instances.length tech.instances.length
) ) return true;
return true;
return false; return false;
} }
// return an array of keys from an object // return an object with ob's keys ordered alphabetically, with an idfrom an object
function getKeys(ob) { function getSortedFilter(ob) {
let keys = []; let keys = [];
let i = 0;
let key_array = [];
// first create a flat array.
for (let key in ob) { for (let key in ob) {
//keys.push(key.replace(/ /g, '\u00a0'));
keys.push(key); keys.push(key);
} }
return keys; // sort the array alphabetically
keys.sort();
// then create a dictionary of them
keys.forEach(function(name) {
key_array.push({ "id": i++, "name": name, "active": true });
});
return key_array;
} }
// 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
@ -173,22 +181,26 @@
// 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'
//
// host_list is in the form of
function hostColours(host_list, colours, hosts) { function hostColours(host_list, colours, hosts) {
let host_array = {}; let host_array = {};
let i = 0; let i = 0;
//console.log('hosts:', hosts); console.log('hosts:', hosts);
console.log('host_list:', host_list);
host_list.forEach(function (host) { for (let index in host_list) {
//console.log(host); console.log(host_list[index]);
if (hosts[host].hasOwnProperty('domain') && hosts[host].hasOwnProperty('affiliation')) { let hostname = host_list[index].name;
host_array[host] = { if (hosts[hostname].hasOwnProperty('domain') && hosts[hostname].hasOwnProperty('affiliation')) {
host_array[hostname] = {
colour: colours[i++], colour: colours[i++],
domain: hosts[host].domain, domain: hosts[hostname].domain,
affiliation: hosts[host].affiliation affiliation: hosts[hostname].affiliation
}; };
} }
}); }
return host_array; return host_array;
} }
@ -201,8 +213,9 @@
//console.log('affiliates:', affiliates); //console.log('affiliates:', affiliates);
console.log('affiliate_list:', affiliate_list); console.log('affiliate_list:', affiliate_list);
for (const affiliate of affiliate_list) { for (let index in affiliate_list) {
//console.log('affiliate:', affiliate); //console.log('affiliate:', affiliate);
let affiliate = affiliate_list[index].name;
if ( if (
affiliates[affiliate].hasOwnProperty('name') && affiliates[affiliate].hasOwnProperty('name') &&
affiliates[affiliate].hasOwnProperty('website') affiliates[affiliate].hasOwnProperty('website')
@ -229,7 +242,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;
} }
@ -241,17 +254,17 @@
//console.log('tech(' + index + '): ', tech); //console.log('tech(' + index + '): ', tech);
if (hasInstances(tech)) { if (hasInstances(tech)) {
const intersection = inCommon(tech.categories, list); const intersection = inCommon(tech.categories, list);
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('intersection length: '. intersection.length); //console.log('intersection length: '. intersection.length);
if (intersection.length > 0) { if (intersection.length > 0) {
console.log('including tech: ', tech); //console.log('including tech: ', tech);
included.push(tech); included.push(tech);
} }
} else { } else {
console.log('intersection not array'); //console.log('intersection not array');
} }
} }
}); });
@ -265,33 +278,50 @@
//const technologies = webservices.technologies; //const technologies = webservices.technologies;
//console.log(technologies); //console.log(technologies);
const category_list = getKeys(results.tech_lists.category_list).sort(); // set up filters for each type
const analogue_list = getKeys(results.tech_lists.analogue_list).sort(); setFilter('categories');
const license_list = getKeys(results.tech_lists.license_list).sort(); setFilter('analogues');
const status_list = getKeys(results.instance_lists.status_list).sort(); setFilter('licenses');
const affiliate_list = getKeys(results.instance_lists.affiliate_list).sort(); setFilter('statuses');
const host_list = getKeys(results.instance_lists.host_list).sort(); setFilter('affiliates');
setFilter('hosts');
// define the handle for each writable
const categoryFilter = getFilter('categories');
const analogueFilter = getFilter('analogues');
const licenseFilter = getFilter('licenses');
const statusFilter = getFilter('statuses');
const affiliateFilter = getFilter('affiliates');
const hostFilter = getFilter('hosts');
// populate the writable with actual data
$categoryFilter = getSortedFilter(results.tech_lists.category_list);
$analogueFilter = getSortedFilter(results.tech_lists.analogue_list);
$licenseFilter = getSortedFilter(results.tech_lists.license_list);
$statusFilter = getSortedFilter(results.instance_lists.status_list);
$affiliateFilter = getSortedFilter(results.instance_lists.affiliate_list);
$hostFilter = getSortedFilter(results.instance_lists.host_list);
const hosts = results.hosts; const hosts = results.hosts;
//console.log('host_data: ', results.hosts); console.log('host_data: ', results.hosts);
const affiliates = results.affiliates; const affiliates = results.affiliates;
//console.log('affiliate_data: ', results.affiliates); console.log('affiliate_data: ', results.affiliates);
const host_colours = hostColours(host_list, colours, hosts); const host_colours = hostColours($hostFilter, colours, hosts);
//console.log('host_colours:', host_colours); console.log('host_colours:', host_colours);
colours.sort(); colours.sort();
const affiliate_colours = affiliateColours(affiliate_list, colours, affiliates); const affiliate_colours = affiliateColours($affiliateFilter, colours, affiliates);
//console.log('affiliate_colours:',affiliate_colours); console.log('affiliate_colours:',affiliate_colours);
//console.log('categories array: ', results.tech_tags.categories); //console.log('categories array: ', results.tech_tags.categories);
//console.log('categories keys: ', categories); //console.log('categories keys: ', categories);
console.log('references: ', references); //console.log('references: ', references);
console.log('category_list: ', category_list); /*console.log('category_list: ', category_list);
//console.log('analogue_list: ', analogue_list); //console.log('analogue_list: ', analogue_list);
console.log('license_list: ', license_list); console.log('license_list: ', license_list);
console.log('status_list: ', status_list); console.log('status_list: ', status_list);
console.log('affiliate_list: ', affiliate_list); console.log('affiliate_list: ', affiliate_list);
console.log('hosts_list: ', host_list); console.log('hosts_list: ', host_list);*/
const filtered_technologies = filterTechnologiesByCategoryList( const filtered_technologies = filterTechnologiesByCategoryList(
@ -300,6 +330,12 @@
// references.category_filter_list // references.category_filter_list
); );
const technologies = sortTechnologies(filtered_technologies); const technologies = sortTechnologies(filtered_technologies);
function flipOn(list, id) {
console.log('list: ' + list + ', id: ' + id);
return true;
}
</script> </script>
<div class="webservices"> <div class="webservices">
@ -319,25 +355,35 @@
</ul> </ul>
</div> </div>
<div class="filters"> <div class="filters">
<div class="tag-list tech"> <div class="filter categories">
<div class="tags tech categories"> <Filterable filterValues={categoryFilter} context={'categories'}>
Categories<br/> {#each category_list as category}<span class="tag on category" style="white-space: nowrap; word-break: normal;">{category}</span><wbr/>{/each} <h2 slot="h2">Categories</h2>
</Filterable>
</div> </div>
<!--<div class="tags tech analogues">{#each analogues as analogue}<span class="tag analogue">{analogue}</span> {/each}</div>--> <div class="filter analogues">
<div class="tags tech licenses"> <Filterable filterValues={analogueFilter} context={'analogues'}>
Licenses<br/> {#each license_list as license}<span class="tag on license">{license}</span><wbr/>{/each} <h2 slot="h2">Analogues</h2>
</Filterable>
</div> </div>
<div class="filter licenses">
<Filterable filterValues={licenseFilter} context={'licenses'}>
<h2 slot="h2">Open Source & Copyleft Licenses</h2>
</Filterable>
</div> </div>
<div class="tag-list instance"> <div class="filter statuses">
<div class="tags instance statuses"> <Filterable filterValues={statusFilter} context={'statuses'}>
Statuses<br/> {#each status_list as status}<span class="tag on status">{status}</span><wbr/>{/each} <h2 slot="h2">Statuses</h2>
</Filterable>
</div> </div>
<div class="tags instance affiliates"> <div class="filter affilates">
Affiliates<br/> {#each affiliate_list as affiliate}<span class="tag on affiliate"><span class="marker circle" style="background-color: {affiliate_colours[affiliate].colour}"></span> {affiliate}</span><wbr/>{/each} <Filterable filterValues={affiliateFilter} context={'affiliates'}>
</div> <h2 slot="h2">Affiliates</h2>
<div class="tags instance hosts"> </Filterable>
Hosts<br/> {#each host_list as host}<span class="tag on host"><span class="marker square" style="background-color: {host_colours[host].colour}"></span> {host}</span><wbr/>{/each}
</div> </div>
<div class="filter hosts">
<Filterable filterValues={hostFilter} context={'hosts'}>
<h2 slot="h2">Hosts</h2>
</Filterable>
</div> </div>
</div> </div>
<div class="tiles"> <div class="tiles">
@ -356,12 +402,11 @@
>{/if} >{/if}
</div> </div>
<h2><a href={technology.website}>{technology.name}</a></h2> <h2><a href={technology.website}>{technology.name}</a></h2>
{#if technology.license}<p <p class="description">
class="license" {technology.description}{#if technology.extended_description}<span
title="The libre license for this project is {technology.license}" title={technology.extended_description}>i</span
> >{/if}
License: <span class="value">{technology.license}</span> </p>
</p>{/if}
{#if technology.categories}<p class="categories"> {#if technology.categories}<p class="categories">
{pluraliser('Category', 'Categories', technology.categories.length)}: {pluraliser('Category', 'Categories', technology.categories.length)}:
<span class="value">{toOxfordCommaString(technology.categories)}</span> <span class="value">{toOxfordCommaString(technology.categories)}</span>
@ -369,11 +414,12 @@
{#if technology.analogues}<p class="analogues"> {#if technology.analogues}<p class="analogues">
Alternative to <span class="value">{toOxfordCommaString(technology.analogues)}</span> Alternative to <span class="value">{toOxfordCommaString(technology.analogues)}</span>
</p>{/if} </p>{/if}
<p class="description"> {#if technology.license}<p
{technology.description}{#if technology.extended_description}<span class="license"
title={technology.extended_description}>i</span title="The libre license for this project is {technology.license}"
>{/if} >
</p> License: <span class="value">{technology.license}</span>
</p>{/if}
{#if hasInstances(technology)} {#if hasInstances(technology)}
<div class="instances"> <div class="instances">
<p> <p>
@ -482,7 +528,7 @@
/*width: 290px;*/ /*width: 290px;*/
overflow: hidden; overflow: hidden;
box-sizing: border-box; box-sizing: border-box;
box-shadow: 5px 5px 3px #71ba71; box-shadow: 5px 5px 3px #6a6d6a;
border: solid 3px #1e6831; border: solid 3px #1e6831;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
@ -497,7 +543,7 @@
font-size: 90%; font-size: 90%;
} }
.tile:hover { .tile:hover {
box-shadow: 10px 10px 6px #71ba71; box-shadow: 10px 10px 6px #727372;
} }
.tile .links h2 a { .tile .links h2 a {
color: #e6c4fc; color: #e6c4fc;

View file

@ -24,9 +24,9 @@
inkscape:pagecheckerboard="0" inkscape:pagecheckerboard="0"
inkscape:document-units="mm" inkscape:document-units="mm"
showgrid="false" showgrid="false"
inkscape:zoom="0.27496365" inkscape:zoom="1.0998546"
inkscape:cx="636.44776" inkscape:cx="623.2642"
inkscape:cy="1161.9718" inkscape:cy="345.04561"
inkscape:window-width="1547" inkscape:window-width="1547"
inkscape:window-height="1088" inkscape:window-height="1088"
inkscape:window-x="0" inkscape:window-x="0"
@ -35,6 +35,18 @@
inkscape:current-layer="layer1" /> inkscape:current-layer="layer1" />
<defs <defs
id="defs14"> id="defs14">
<linearGradient
inkscape:collect="always"
id="linearGradient48819">
<stop
style="stop-color:#edf300;stop-opacity:1;"
offset="0"
id="stop48815" />
<stop
style="stop-color:#edf300;stop-opacity:0;"
offset="1"
id="stop48817" />
</linearGradient>
<rect <rect
x="103.13863" x="103.13863"
y="378.72194" y="378.72194"
@ -120,20 +132,41 @@
id="linearGradient22687" id="linearGradient22687"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
gradientTransform="translate(28.373246,78.002834)" gradientTransform="translate(28.373246,78.002834)"
x1="11.545767" x1="0.16692716"
y1="43.178448" y1="12.035994"
x2="55.775768" x2="63.443279"
y2="43.178448" /> y2="71.808151" />
<linearGradient <linearGradient
inkscape:collect="always" inkscape:collect="always"
xlink:href="#linearGradient5458" xlink:href="#linearGradient5458"
id="linearGradient22710" id="linearGradient22710"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
gradientTransform="translate(89.161221,78.677533)" gradientTransform="translate(89.161221,78.677533)"
x1="11.545767" x1="1.2561908"
y1="43.178448" y1="35.261524"
x2="55.775768" x2="58.534"
y2="43.178448" /> y2="64.484146" />
<rect
x="103.13863"
y="378.72195"
width="328.13995"
height="308.35397"
id="rect19085-1" />
<rect
x="103.13863"
y="378.72195"
width="328.13995"
height="308.35397"
id="rect19085-1-7" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient48819"
id="linearGradient48821"
x1="108.90812"
y1="470.22269"
x2="221.20922"
y2="470.22269"
gradientUnits="userSpaceOnUse" />
</defs> </defs>
<g <g
inkscape:label="Layer 1" inkscape:label="Layer 1"
@ -156,7 +189,7 @@
y="28.054415" y="28.054415"
ry="3.9535661" /> ry="3.9535661" />
<rect <rect
style="fill:#0066ff;fill-opacity:0.65748;fill-rule:evenodd;stroke:url(#linearGradient22687);stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none" style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient22687);stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none"
id="rect43-7-5" id="rect43-7-5"
width="34.230003" width="34.230003"
height="32.348484" height="32.348484"
@ -170,9 +203,9 @@
style="font-style:normal;font-weight:normal;font-size:133.333px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect7333);fill:#f3f415;fill-opacity:1;stroke:#240d0c;stroke-opacity:0.95896482"><tspan style="font-style:normal;font-weight:normal;font-size:133.333px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect7333);fill:#f3f415;fill-opacity:1;stroke:#240d0c;stroke-opacity:0.95896482"><tspan
x="163.06445" x="163.06445"
y="498.98044" y="498.98044"
id="tspan43262">🗹</tspan></text> id="tspan60302">🗹</tspan></text>
<rect <rect
style="fill:#0066ff;fill-opacity:0.65748;fill-rule:evenodd;stroke:url(#linearGradient22710);stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none" style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient22710);stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none"
id="rect43-7-9" id="rect43-7-9"
width="34.230003" width="34.230003"
height="32.348484" height="32.348484"
@ -181,13 +214,37 @@
ry="3.9535661" /> ry="3.9535661" />
<text <text
xml:space="preserve" xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,83.231667,-9.193983)" transform="matrix(0.26458333,0,0,0.26458333,113.11267,-50.648638)"
id="text19083" id="text19083"
style="font-style:normal;font-weight:normal;font-size:160px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect19085);fill:#edf300;fill-opacity:1;stroke:none"><tspan style="font-style:normal;font-weight:normal;font-size:160px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect19085);fill:#edf300;fill-opacity:1;stroke:none"><tspan
x="103.13867" x="103.13867"
y="524.30269" y="524.30269"
id="tspan43266"><tspan id="tspan60306"><tspan
style="stroke:#140d0c;stroke-opacity:0.958965" style="stroke:#140d0c;stroke-opacity:0.958965"
id="tspan43264">🗸</tspan></tspan></text> id="tspan60304">🗸</tspan></tspan></text>
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,145.80878,-39.237512)"
id="text19083-2"
style="font-style:normal;font-weight:normal;font-size:160px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect19085-1);fill:#edf300;fill-opacity:1;stroke:none;stroke-width:4.7811;stroke-miterlimit:4;stroke-dasharray:none"
inkscape:export-filename="/home/dave/Projects/svelte/webservices-app/static/images/check-yellowblack.png"
inkscape:export-xdpi="200"
inkscape:export-ydpi="200"><tspan
x="103.13867"
y="524.30269"
id="tspan60310"><tspan
style="stroke:#140d0c;stroke-opacity:0.958965"
id="tspan60308">🗸</tspan></tspan></text>
<text
xml:space="preserve"
transform="matrix(0.26458333,0,0,0.26458333,146.54956,-3.8660822)"
id="text19083-2-0"
style="font-style:normal;font-weight:normal;font-size:160px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect19085-1-7);fill:#111111;fill-opacity:1;stroke:#edf3f8;stroke-width:4.7811;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:export-filename="/home/dave/Projects/svelte/webservices-app/static/images/check-black-in-white.png"
inkscape:export-xdpi="200"
inkscape:export-ydpi="200"><tspan
x="103.13867"
y="524.30269"
id="tspan60312">🗸</tspan></text>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB