Compare commits

..

6 commits

Author SHA1 Message Date
Dave Lane
2cea5f44fb WIP just need to fix the filtering of technologies based on instance properties 2024-11-19 10:59:01 +13:00
Dave Lane
37cd4fd17a Working modals 2024-11-11 15:01:13 +13:00
Dave Lane
b5cdfa0a09 working modals now 2024-10-31 10:52:18 +13:00
Dave Lane
0052c00f03 working modal but still munted styles 2024-10-30 17:20:32 +13:00
Dave Lane
26869a837b wip commit 2024-10-26 16:02:35 +13:00
Dave Lane
3068f5980d Styled accordions, sorted coloured dots with filter tags, got stats working and styled 2024-10-22 16:50:28 +13:00
8 changed files with 644 additions and 501 deletions

View file

@ -1,8 +1,24 @@
# Plans for Webservices App # Plans for Webservices App
High priority: I've got quite a few plans for this app... here's a rough sketch of what I'm planning.
First release:
* filtering of all properties like Categories, Analogues, Licenses, Statuses, Hosts, and Affiliations. * filtering of all properties like Categories, Analogues, Licenses, Statuses, Hosts, and Affiliations.
* modal showing each technology in more detail. DONE
* split out all constants (arrays) into separate files. ~DONE
* split out functions into svelte components.
Future versions:
* upgrade to Svelte 5
* a 'Technology' view (overlay), addressable by URL (i.e. going to, say, http://localhost:5173/ServiceName brings up the site with ServiceName shown in 'Technology' view, allowing people to reference a specific technology of interest to others). * a 'Technology' view (overlay), addressable by URL (i.e. going to, say, http://localhost:5173/ServiceName brings up the site with ServiceName shown in 'Technology' view, allowing people to reference a specific technology of interest to others).
* split out all constants (arrays) into separate files. * ability to go 'prev' or 'next' in modals within filtered set of technologies.
* split out functions into svelte modules. * ability to select a Hosts and view all Technologies with instances on that Host
* ability to see the status (via UptimeKuma API) of each instance.
Possibly, eventually:
* move data into a database (rather than a fragile hand-crafted JSON file) with integrity checking.
* Authentication for administration
* CRUD interface for Technologies, Licenses, Hosts, and Affiliations.

687
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@ body {
min-height: 100%; min-height: 100%;
padding: 1em; padding: 1em;
width: 100%; width: 100%;
font-size: 1.2em; font-size: 1.15em;
border: double 3px #ddd; border: double 3px #ddd;
border-top: none; border-top: none;
border-bottom: none; border-bottom: none;
@ -28,7 +28,7 @@ a:hover {
text-decoration: underline; text-decoration: underline;
} }
a:visited { a:visited {
color: #437fad; color: #325f81;
} }
.y-accordions, .y-accordions,
.y-accordion-header-button { .y-accordion-header-button {
@ -37,3 +37,59 @@ a:visited {
.yaccordion .y-accordians .y-accordion-header-button { .yaccordion .y-accordians .y-accordion-header-button {
background-color: #eee; background-color: #eee;
} }
/* modal styles */
.y-modal .y-modal-container {
background-color: #eee;
border: solid 3px #3a8ea5;
border-radius: 15px;
box-shadow: 5px 5px 3px #6a6d6a;
width: 90%;
margin: 1em auto;
max-width: 40em;
height: auto;
padding: 0;
}
.y-modal .y-modal-container .y-modal-content {
width: 100%;
border-width: 0;
}
.y-modal .y-modal-container .y-modal-header {
text-align: center;
width: 100%;
background-color: #999;
}
.y-modal .y-modal-container .y-modal-header .y-modal-title {
font-size: 1.5em;
margin: auto;
}
.y-modal .y-modal-container .y-modal-body {
background-color: #eee;
}
.y-modal .y-modal-container div.instances {
background-color: #ddd;
width: 100%;
padding: 20px;
margin-bottom: 0;
}
.y-modal .y-modal-container p.instances,
.y-modal .y-modal-container li p {
padding: 0;
margin-bottom: 0;
}
/* end modals */
/* popover styles */
.y-popover {
z-index: 10000;
}
.y-popover a {
font-size: 110%;
}
.y-popover a:hover {
color: #000;
text-decoration: underline;
}
.y-popover a:visited {
color: #325f81;
}

View file

@ -1,70 +0,0 @@
<script>
// Ref: https://svelte.dev/repl/a5f4d395b15a44d48a6b2239ef705fc4?version=3.35.0
// based on suggestions from:
// Inclusive Components by Heydon Pickering https://inclusive-components.design/collapsible-sections/
export let headerText;
let expanded = false;
</script>
<div class="collapsible">
<h3>
<button aria-expanded={expanded} on:click={() => expanded = !expanded}>{headerText}
<svg class="indicator" viewBox="0 0 20 20" fill="none" >
<path class="vert" d="M10 1V19" stroke="black" stroke-width="2"/>
<path d="M1 10L19 10" stroke="black" stroke-width="2"/>
</svg>
</button>
</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}>
<slot></slot>
</div>
</div>
<style>
.collapsible {
margin: 8px 0 4px 0;
}
.collapsible .collapsible h3 button {
padding-left: 20px;
}
.tree { float: left; }
.contents {
float: left;
clear: both;
width: 100%;
border-bottom: dashed #aaa 1px;
padding-bottom: 1em;
margin-bottom: 1em;
}
h3 {
margin: 0;
}
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>

View file

@ -152,7 +152,7 @@
/* instance-specific styles */ /* instance-specific styles */
.filter { .filter {
font-size: 80%; font-size: 0.8em;
margin-right: 0.5em; margin-right: 0.5em;
line-height: 2.5; line-height: 2.5;
padding: 6px 8px; padding: 6px 8px;

View file

@ -1,10 +1,12 @@
<script> <script>
// a 'technology' object // a 'technology' object
export let technology; export let technology;
export let licenses;
export let affiliate_colours; export let affiliate_colours;
import { Button, Modal, ModalBody, ModalFooter, Popover } from 'yesvelte'; import { Button, ButtonGroup, El, Modal, ModalBody, ModalFooter,
ModalHeader, ModalTitle, Popover, PopoverBody, PopoverHeader } from 'yesvelte';
let showModal = false; export let showModal = false;
const formatter = new Intl.ListFormat('en', { const formatter = new Intl.ListFormat('en', {
style: 'long', style: 'long',
@ -31,58 +33,42 @@
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;
} }
function handleClick() {
console.log('clicked!');
}
/*document.addEventListener('keydown', (event) => {
const { activeElement } = document;
const hasButtonRole = activeElement?.getAttribute('role') === 'button';
if (hasButtonRole) {
// prevent default behaviour, including scrolling on spacebar
if (['Spacebar', ' ', 'Enter'].includes(event.key)) {
event.preventDefault();
}
if (event.key === 'Enter') {
activeElement.click();
}
}
});
document.addEventListener('keyup', (event) => {
const { activeElement } = document;
const hasButtonRole = activeElement?.getAttribute('role') === 'button';
if (hasButtonRole && ['Spacebar', ' '].includes(event.key)) {
event.preventDefault();
activeElement.click();
}
});*/
//onclick="handleClick()"
</script> </script>
<!-- making a dive act like a button: https://kvack.dev/blog/make-that-div-behave-like-a-button --> <!-- making a dive act like a button: https://kvack.dev/blog/make-that-div-behave-like-a-button -->
<div on:click={() => (showModal = !showModal)} onclick="handleClick()" role="button" tabindex=0 class="tile technology"> <button on:click={() => (showModal = !showModal)} tabindex=0 class="tile technology" col>
<div class="links">
<span class="website"><a href="{technology.website}" title="Project site for {technology.name}">P</a></span>
{#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>{technology.name}</h2> <h2>{technology.name}</h2>
<div class="links">
<span class="website">P</span>
<Popover trigger="hover">
<PopoverHeader><strong>Project website</strong> for {technology.name}</PopoverHeader>
<PopoverBody><a href="{technology.website}">{technology.website}</a></PopoverBody>
</Popover>
{#if technology.repository}<span class="repository">S</span>
<Popover trigger="hover">
<PopoverHeader><strong>Source code</strong> repository for {technology.name}</PopoverHeader>
<PopoverBody><a href={technology.repository}>{technology.repository}</a></PopoverBody>
</Popover>
{/if}
{#if technology.wikipedia}<span class="wikipedia">W</span>
<Popover trigger="hover">
<PopoverHeader><strong>Wikipedia entry</strong> for {technology.name}</PopoverHeader>
<PopoverBody><a href={technology.wikipedia}>{technology.wikipedia}</a></PopoverBody>
</Popover>
{/if}
</div>
<p class="description">{technology.description}{#if technology.extended_description}<span <p class="description">{technology.description}{#if technology.extended_description}<span
title={technology.extended_description}>i</span>{/if} title={technology.extended_description}>i</span>{/if}
</p> </p>
<div class="details">
{#if technology.categories}<p class="property categories">{pluraliser('Category', {#if technology.categories}<p class="property categories">{pluraliser('Category',
'Categories', technology.categories.length)}: <span class="value">{toOxfordCommaString(technology.categories)}</span></p>{/if} 'Categories', technology.categories.length)}: <span class="value">{toOxfordCommaString(technology.categories)}</span></p>{/if}
{#if technology.analogues}<p class="property analogues">Alternative to <span {#if technology.analogues}<p class="property analogues">Alternative to <span
@ -90,114 +76,171 @@
{#if technology.license}<p class="property license" {#if technology.license}<p class="property license"
title="The libre license for this project is {technology.license}">License: <span title="The libre license for this project is {technology.license}">License: <span
class="value">{technology.license}</span></p>{/if} class="value">{technology.license}</span></p>{/if}
</div>
{#if hasInstances(technology)} {#if hasInstances(technology)}
<div class="instances"> <div class="instances">
<p>{#each technology.instances as instance}<a <p>
href="https://{instance.domain}" {#each technology.instances as instance}
title="{technology.name} instance {instance.domain} hosted on '{instance.host}' by {instance.affiliation}"><span class="marker circle" <span class="marker circle"
style="background-color: {affiliate_colours[instance.affiliation].colour}" style="background-color: {affiliate_colours[instance.affiliation].colour}"
></span></a>{/each} ></span>
<Popover trigger="hover">
<PopoverHeader>{technology.name} Instance</PopoverHeader>
<PopoverBody><a href="https://{instance.domain}">https://{instance.domain}</a> hosted on '{instance.host}' by {instance.affiliation}</PopoverBody>
</Popover>
{/each}
</p> </p>
</div> </div>
{:else} {:else}
<div class="instances">Nothing here yet...</div> <div class="instances">Nothing here yet...</div>
{/if} {/if}
</div> </button>
<Modal title="{technology.name}" bind:showModal> <Modal scrollable bind:show={showModal} autoClose dismissible>
<ModalHeader>
<ModalTitle>{technology.name}</ModalTitle>
</ModalHeader>
<ModalBody> <ModalBody>
Modal stuff... <p class="description">{technology.description}{#if technology.extended_description}{technology.extended_description}{/if}
</p>
<div class="links">
<p class="link"><span class="label">Project website</span>: <span class="website"><a href="{technology.website}" title="Project site for {technology.name}">{technology.website}</a></span></p>
{#if technology.repository}<p class="link"><span class="label">Source code repository</span>: <span class="repository"><a
href={technology.repository} title="The source code repository for {technology.name}">{technology.repository}</a></span></p>{/if}
{#if technology.wikipedia}<p class="link"><span class="label">Wikipedia entry</span>: <span class="wikipedia"><a
href={technology.wikipedia}
title="Wikipedia Page for {technology.name}">{technology.wikipedia}</a></span></p>{/if}
</div>
<div class="details">
{#if technology.categories}<p class="property categories">{pluraliser('Category',
'Categories', technology.categories.length)}: <span class="value">{toOxfordCommaString(technology.categories)}</span></p>{/if}
{#if technology.analogues}<p class="property analogues">Alternative to <span
class="value">{toOxfordCommaString(technology.analogues)}</span></p>{/if}
{#if technology.license}<p class="property license"
title="The libre license for this project is {technology.license}">License: <span
class="value"><a href="{licenses[technology.license].url}">{licenses[technology.license].name}</a></span></p>{/if}
</div>
</ModalBody> </ModalBody>
{#if hasInstances(technology)}
<div class="instances">
<p class="instances">Instances of {technology.name}:</p>
<ol>
{#each technology.instances as instance}
<li>
<p class="instance"><a href="https://{instance.domain}">{instance.domain}</a> hosted on '{instance.host}' <span class="marker circle" style="background-color: {affiliate_colours[instance.affiliation].colour}"/> by {instance.affiliation}".
</p>
</li>
{/each}
</ol>
</div>
{:else}
<div class="instances">Nothing here yet...</div>
{/if}
<!--<ModalFooter>
Previous and Next buttons to go here.
</ModalFooter>-->
</Modal> </Modal>
<style> <style>
.tile { .tile.technology {
display: inline-block;
height: 515px; background-color: #eee;
/*min-width: 240px;
max-width: 300px;*/
width: 290px;
overflow: hidden;
box-sizing: border-box;
box-shadow: 5px 5px 3px #6a6d6a;
border: solid 3px #3a8ea5; border: solid 3px #3a8ea5;
border-radius: 15px; border-radius: 15px;
text-overflow: ellipsis; box-shadow: 5px 5px 3px #6a6d6a;
overflow: hidden; box-sizing: border-box;
position: relative; display: inline-block;
background-color: #eee; height: 515px;
margin-top: 8px; line-height: 1.2;
margin-right: 18px;
margin-bottom: 10px; margin-bottom: 10px;
margin-right: 18px;
margin-top: 8px;
overflow: hidden;
padding: 0;
position: relative;
text-align: left;
text-overflow: ellipsis;
vertical-align: top !important;
width: 290px;
} }
.tile .links { .tile.technology .links {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
padding: 2px 6px; padding: 2px 6px;
font-size: 90%; font-size: 90%;
} }
.tile:hover { .tile.technology:hover {
box-shadow: 10px 10px 6px #727372; box-shadow: 6px 6px 5px #727372;
} }
.tile:active { .tile.technology:active {
box-shadow: 0 0 0; box-shadow: 0 0 0;
border-color: #000; border-color: #000;
} }
.tile .links h2 a { .tile.technology .links h2 a {
color: #e6c4fc; color: #e6c4fc;
} }
.tile .links a:visited { .tile.technology .links a:visited {
color: #ced0ff; color: #ced0ff;
} }
.tile .links a:hover, .tile.technology .links a:hover,
.tile:hover h2 a { .tile.technology:hover h2 a {
color: #fff !important; color: #fff !important;
} }
.tile h2 { .tile.technology h2 {
position: absolute;
top: 0;
right: 0;
width: 100%;
background-color: #999; background-color: #999;
text-align: center; text-align: center;
padding: 0.5em; padding: 0.5em;
margin: 0; margin: 0;
} }
.tile h2 a { .tile.technology h2 a {
color: #e6c4fc; color: #e6c4fc;
} }
.tile h2 a:visited { .tile.technology h2 a:visited {
color: #ced0ff; color: #ced0ff;
} }
.tile h2 a:hover { .tile.technology h2 a:hover {
color: #fff; color: #fff;
} }
.tile .description { .tile.technology .description {
height: 200px; height: 200px;
line-height: 1.2em;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
padding-top: 0.5em; padding-top: 0.5em;
vertical-align: top;
} }
.tile p { .tile.technology p {
padding: 0 0.7em 0.2em 0.7em; padding: 0 0.7em 0.2em 0.7em;
font-size: 80%; font-size: 80%;
color: #555; color: #555;
} }
.tile p.property { .tile.technology p.property {
padding: 0.2em 0.7em; padding: 0.2em 0.7em;
margin: 0; margin: 0;
} }
.instances { .tile.technology .details {
vertical-align: bottom;
}
.tile.technology .instances {
background-color: #ddd; background-color: #ddd;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
height: 2.5em; height: 2.5em;
width: 100%; width: 100% !important;
vertical-align: middle; vertical-align: middle;
} }
.instances p { padding-top: 0.7em; vertical-align: middle; } .tile.technology .instances p {
.instances .marker { width: 100%;
margin-right: 3px; padding-top: 0.7em;
vertical-align: middle;
} }
.webservices li { .tile.technology .instances .marker {
list-style-type: none; margin-right: 3px;
} }
.value { .value {
font-weight: bold; font-weight: bold;
@ -217,10 +260,4 @@
.circle:hover { .circle:hover {
border: 1px solid #000; border: 1px solid #000;
} }
.square {
height: 20px;
width: 20px;
background-color: #555;
display: inline-block;
}
</style> </style>

View file

@ -1,15 +1,17 @@
// for multiple async fetches: https://youtu.be/EQy-AYhZIlE?si=FwyAPUjbixSUlc9q&t=490 // for multiple async fetches: https://youtu.be/EQy-AYhZIlE?si=FwyAPUjbixSUlc9q&t=490
export const load = async ({ fetch }) => { export const load = async ({ fetch }) => {
const webservicesResult = await fetch('https://static.magnificent.nz/webservices/webservices.json'); const webservicesResult = await fetch(
const webservicesData = await webservicesResult.json(); 'https://static.magnificent.nz/webservices/webservices.json'
);
//console.log(webservicesData); const webservicesData = await webservicesResult.json();
return {
webservices: {
technologies: webservicesData.technologies,
affiliates: webservicesData.affiliates,
hosts: webservicesData.hosts
}
};
}
//console.log(webservicesData);
return {
webservices: {
technologies: webservicesData.technologies,
affiliates: webservicesData.affiliates,
hosts: webservicesData.hosts,
licenses: webservicesData.licenses
}
};
};

View file

@ -8,14 +8,16 @@
// the filters // the filters
import { setFilter, getFilter } from '$lib/components/filter.js'; import { setFilter, getFilter } from '$lib/components/filter.js';
import { Accordions, Accordion, AccordionBody } from 'yesvelte'; import { Accordions, Accordion, AccordionBody, El } from 'yesvelte';
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 TechnologyTile from '$lib/components/TechnologyTile.svelte'; import TechnologyTile from '$lib/components/TechnologyTile.svelte';
// URL-related stuff
import { page } from '$app/stores';
let tabindex = 0; // a counter which facilitates keyboard navigation. let tabindex = 0; // a counter which facilitates keyboard navigation.
let showModal = true; // use to make a technology modal show when rendered
// return the % a is of b // return the % a is of b
function percent(a, b) { function percent(a, b) {
@ -41,6 +43,7 @@
// actual objects // actual objects
let hosts = []; let hosts = [];
let affiliates = []; let affiliates = [];
let licenses = [];
// //
// just a trivial reassignment // just a trivial reassignment
@ -117,6 +120,10 @@
let affiliate = webservices.affiliates[key]; let affiliate = webservices.affiliates[key];
affiliates[key] = affiliate; affiliates[key] = affiliate;
} }
for (let key in webservices.licenses) {
let license = webservices.licenses[key];
licenses[key] = license;
}
// assign stats based on gathered data... // assign stats based on gathered data...
stats.current = current.length; stats.current = current.length;
@ -148,7 +155,8 @@
host_list: host_list host_list: host_list
}, },
hosts: hosts, hosts: hosts,
affiliates: affiliates affiliates: affiliates,
licenses: licenses
}; };
} }
@ -279,6 +287,8 @@
return technologies.sort((a, b) => a.name.localeCompare(b.name)); return technologies.sort((a, b) => a.name.localeCompare(b.name));
} }
// set previous and next on each technology
// generic function to find the intersection of two arrays // generic function to find the intersection of two arrays
// approach ref: https://bobbyhadz.com/blog/javascript-get-intersection-of-two-arrays // approach ref: https://bobbyhadz.com/blog/javascript-get-intersection-of-two-arrays
function inCommon(a, b) { function inCommon(a, b) {
@ -389,6 +399,7 @@
// //
// process the data received by the web request! // process the data received by the web request!
const results = processData(webservices); const results = processData(webservices);
const licenses = results.licenses;
//const technologies = webservices.technologies; //const technologies = webservices.technologies;
//console.log(technologies); //console.log(technologies);
@ -422,10 +433,12 @@
const affiliates = results.affiliates; const affiliates = results.affiliates;
//console.log('affiliate_data: ', results.affiliates); //console.log('affiliate_data: ', results.affiliates);
const host_colours = hostColours($hostFilter, 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($affiliateFilter, colours, affiliates); const affiliate_colours = affiliateColours($affiliateFilter, colours, affiliates);
console.log('affiliate_colours:',affiliate_colours); //console.log('affiliate_colours:',affiliate_colours);
//console.log('licenses:',licenses );
let filteredTechnologies; let filteredTechnologies;
let technologies; let technologies;
@ -464,16 +477,16 @@
'license', 'license',
flattenFilter($licenseFilter) flattenFilter($licenseFilter)
); );
/*console.log( console.log(
'about to sort technologies alphabetically - starting with ' + 'about to filter technologies by instance status - starting with ' +
filteredTechnologies.length + filteredTechnologies.length +
' technologies...' ' technologies...'
);*/ );
/*filteredTechnologies = filterTechnologiesByInstanceValue( /*filteredTechnologies = filterTechnologiesByInstanceValue(
filteredTechnologies, filteredTechnologies,
'status', 'status',
flattenFilter($statusFilter) flattenFilter($statusFilter)
)*/ )
/*filteredTechnologies = filterTechnologiesByInstanceValue( /*filteredTechnologies = filterTechnologiesByInstanceValue(
filteredTechnologies, filteredTechnologies,
'affiliate', 'affiliate',
@ -486,8 +499,13 @@
)*/ )*/
//filteredTechnologies = filterTechnologiesByCategoryList(results.all_services, flattenFilter($categoryFilter)); //filteredTechnologies = filterTechnologiesByCategoryList(results.all_services, flattenFilter($categoryFilter));
console.log(
'about to sort technologies alphabetically - starting with ' +
filteredTechnologies.length +
' technologies...'
);
const technologies = sortTechnologies(filteredTechnologies); const technologies = sortTechnologies(filteredTechnologies);
console.log('finally have ' + technologies.length + ' technologies left to show...'); //console.log('finally have ' + technologies.length + ' technologies left to show...');
} }
</script> </script>
@ -595,9 +613,7 @@
context={'categories'}> context={'categories'}>
<h2 slot="title">Categories</h2> <h2 slot="title">Categories</h2>
<p slot="description"> <p slot="description">
You can These are general categories of software which help you understand the These are general categories of software which help you understand the sort of software you're looking at, as well as identifying functionally similar software for comparison.
sort of software you're looking at, as well as identifying functionally similar
software for comparison.
</p> </p>
</Filterable> </Filterable>
</div> </div>
@ -689,9 +705,16 @@
</Accordion> </Accordion>
</Accordions> </Accordions>
<p>Showing {filteredTechnologies.length} technology tiles.</p> <p>Showing {filteredTechnologies.length} technology tiles.</p>
<!--<p>Current URL: {$page.url}...</p>-->
<div class="tiles"> <div class="tiles">
{#each filteredTechnologies as technology} {#each filteredTechnologies as technology, index}
<TechnologyTile {technology} {affiliate_colours} /> <!--{#if (index != 1) }-->
<TechnologyTile {technology} {affiliate_colours} {licenses}/>
<!--{:else}
<TechnologyTile {technology} {affiliate_colours} {licenses} {showModal}/>
{/if}-->
{:else}
<p class='no-tiles'>No technologies are selected</p>
{/each} {/each}
</div> </div>
</div> </div>
@ -724,7 +747,7 @@
min-width: 30em; min-width: 30em;
border: 2px #aaa solid; border: 2px #aaa solid;
margin: 0 15px 20px 6px; margin: 0 15px 20px 6px;
padding: 0 20px; padding: 10px 20px;
box-shadow: 6px 6px 20px #999; box-shadow: 6px 6px 20px #999;
} }
.summary .label { .summary .label {