'State of the art' responsive design
This commit is contained in:
parent
12e1344e23
commit
376880c0c1
3 changed files with 194 additions and 98 deletions
102
ui/index.html
102
ui/index.html
|
@ -7,58 +7,7 @@
|
|||
<meta charset="utf-8" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Fira+Sans&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* { font-size: 14px; }
|
||||
body {
|
||||
font-family: 'Fira Sans', sans-serif;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
max-height: 100vh;
|
||||
flex-direction: column;
|
||||
}
|
||||
nav { flex: 0 0; }
|
||||
main { flex: 1; display: flex; flex-grow: 1; min-height: 0 }
|
||||
#visualization {
|
||||
width: 15rem;
|
||||
padding: .25rem;
|
||||
border-radius: .125rem;
|
||||
margin-right: .5rem;
|
||||
background: #AAA;
|
||||
}
|
||||
#docs {
|
||||
flex-grow: 1;
|
||||
flex: 2;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4,1fr);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.title {
|
||||
background: #333;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
.title h1 { margin: 0; font-size: 1.4rem; }
|
||||
.searchbox {
|
||||
padding: 1em;
|
||||
background: #666;
|
||||
}
|
||||
.searchbox input {
|
||||
width: calc(100% - 2em);
|
||||
}
|
||||
.document {
|
||||
float: left;
|
||||
margin: .25rem;
|
||||
background: #CCC;
|
||||
border-radius: .125rem;
|
||||
}
|
||||
.document img { width: calc(100% - 1rem); }
|
||||
.document h2 { font-size: 1.2rem; }
|
||||
.document h3 { font-size: 1.1rem; }
|
||||
.document img { margin: .5rem .5rem 0 .5rem; }
|
||||
.document h2, .document h3, .document p { margin: .25rem; };
|
||||
</style>
|
||||
<link rel="stylesheet" href="./style.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -83,7 +32,8 @@ main { flex: 1; display: flex; flex-grow: 1; min-height: 0 }
|
|||
</div>
|
||||
<template id="document">
|
||||
<div class="document" id="doc{{ id }}">
|
||||
<img src="{{ url }}"/>
|
||||
<a class="img_box" href="{{ url }}" target="_blank"
|
||||
style="background-image: url({{ url }})"/></a>
|
||||
<h2><a href="{{ site_url }}">{{ title }}</a></h2>
|
||||
<h3>By {{ author }}</h3>
|
||||
{{ description }}
|
||||
|
@ -91,50 +41,6 @@ main { flex: 1; display: flex; flex-grow: 1; min-height: 0 }
|
|||
</template>
|
||||
</div>
|
||||
<script src="./carrotsearch.foamtree.js"></script>
|
||||
<script>
|
||||
const solr = "http://localhost:8983/solr/photo";
|
||||
const q = document.querySelector("#q");
|
||||
let foamtree = null;
|
||||
|
||||
document.querySelector("#form").addEventListener("submit", async function(e) {
|
||||
e.preventDefault();
|
||||
let templateHTML = document.querySelector("#document").innerHTML;
|
||||
let docs = document.querySelector("#docs");
|
||||
let docMap = {}
|
||||
|
||||
const doc = (docData) => {
|
||||
docMap[docData.id] = docData;
|
||||
console.log(templateHTML);
|
||||
return templateHTML
|
||||
.replaceAll("{{ id }}", docData.id)
|
||||
.replaceAll("{{ title }}", docData.t_title)
|
||||
.replaceAll("{{ author }}", docData.t_author)
|
||||
.replaceAll("{{ description }}", docData.t_description)
|
||||
.replaceAll("{{ url }}", docData.img_url)
|
||||
.replaceAll("{{ site_url }}", "#");
|
||||
};
|
||||
|
||||
let list = await fetch(solr + "/clustering?q=" + q.value);
|
||||
let body = await list.json();
|
||||
console.log(body);
|
||||
docs.innerHTML = body.response.docs.map(doc).reduce((a, b) => a + b, "");
|
||||
|
||||
let clusters = body.clusters.map(e => { return { label: e.labels[0], weight:
|
||||
e.score, groups: e.docs.map(id => { return { id, label: docMap[id].t_title }; }) }; });
|
||||
|
||||
if (foamtree === null) {
|
||||
foamtree = new CarrotSearchFoamTree({
|
||||
id: "visualization",
|
||||
layout: "squarified",
|
||||
groupLabelFontFamily: "Fira Sans",
|
||||
dataObject: {
|
||||
groups: clusters
|
||||
}
|
||||
});
|
||||
} else {
|
||||
foamtree.set({ dataObject: { groups: clusters }});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script src="./search.js"></script>
|
||||
</body>
|
||||
</html>`
|
||||
|
|
71
ui/search.js
Normal file
71
ui/search.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
// vim: set ts=2 sw=2 et tw=80:
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const query = urlParams.get('q');
|
||||
const solr = "http://localhost:8983/solr/photo";
|
||||
const q = document.querySelector("#q");
|
||||
const templateHTML = document.querySelector("#document").innerHTML;
|
||||
const docs = document.querySelector("#docs");
|
||||
let foamtree = null;
|
||||
let queryResult = null;
|
||||
|
||||
function buildCard(docData) {
|
||||
const emptyOr = s => s ? s : "";
|
||||
return templateHTML
|
||||
.replaceAll("{{ id }}", emptyOr(docData.id))
|
||||
.replaceAll("{{ title }}", emptyOr(docData.t_title))
|
||||
.replaceAll("{{ author }}", emptyOr(docData.t_author))
|
||||
.replaceAll("{{ description }}", emptyOr(docData.t_description))
|
||||
.replaceAll("{{ url }}", emptyOr(docData.img_url))
|
||||
.replaceAll("{{ site_url }}", "#");
|
||||
}
|
||||
|
||||
function updateResults() {
|
||||
docs.innerHTML = queryResult.response.docs.map(d => {
|
||||
return buildCard(d);
|
||||
}).reduce((a, b) => a + b, "");
|
||||
}
|
||||
|
||||
async function buildQuery(e) {
|
||||
if (e) e.preventDefault();
|
||||
let docMap = {}
|
||||
|
||||
let list = await fetch(solr + "/clustering?q=" + q.value);
|
||||
queryResult = await list.json();
|
||||
console.log(queryResult);
|
||||
|
||||
updateResults();
|
||||
for (const e of queryResult.response.docs) {
|
||||
docMap[e.id] = e;
|
||||
}
|
||||
|
||||
let clusters = queryResult.clusters.map(e => {
|
||||
return {
|
||||
label: e.labels[0],
|
||||
weight: e.score,
|
||||
groups: e.docs.map(id => {
|
||||
return { id, label: docMap[id].t_title };
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
if (foamtree === null) {
|
||||
foamtree = new CarrotSearchFoamTree({
|
||||
id: "visualization",
|
||||
layout: "squarified",
|
||||
groupLabelFontFamily: "Fira Sans",
|
||||
dataObject: {
|
||||
groups: clusters
|
||||
}
|
||||
});
|
||||
} else {
|
||||
foamtree.set({ dataObject: { groups: clusters }});
|
||||
}
|
||||
};
|
||||
|
||||
if (query) {
|
||||
q.value = query;
|
||||
buildQuery().catch(console.error);
|
||||
}
|
||||
|
||||
document.querySelector("#form").addEventListener("submit", buildQuery);
|
119
ui/style.css
Normal file
119
ui/style.css
Normal file
|
@ -0,0 +1,119 @@
|
|||
/* vim: set ts=2 sw=2 et tw=80: */
|
||||
|
||||
:root { --columns: 14; }
|
||||
@media screen and (max-width: 3000px) { :root { --columns: 13; } }
|
||||
@media screen and (max-width: 2800px) { :root { --columns: 12; } }
|
||||
@media screen and (max-width: 2600px) { :root { --columns: 11; } }
|
||||
@media screen and (max-width: 2400px) { :root { --columns: 10; } }
|
||||
@media screen and (max-width: 2200px) { :root { --columns: 9; } }
|
||||
@media screen and (max-width: 2000px) { :root { --columns: 8; } }
|
||||
@media screen and (max-width: 1800px) { :root { --columns: 7; } }
|
||||
@media screen and (max-width: 1600px) { :root { --columns: 6; } }
|
||||
@media screen and (max-width: 1400px) { :root { --columns: 5; } }
|
||||
@media screen and (max-width: 1200px) { :root { --columns: 4; } }
|
||||
@media screen and (max-width: 1000px) { :root { --columns: 3; } }
|
||||
@media screen and (max-width: 800px) { :root { --columns: 2; } }
|
||||
@media screen and (max-width: 600px) { :root { --columns: 1; } }
|
||||
|
||||
* {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Fira Sans', sans-serif;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
max-height: 100vh;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
nav {
|
||||
flex: 0 0;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
#visualization {
|
||||
width: 15rem;
|
||||
padding: .25rem;
|
||||
border-radius: .125rem;
|
||||
margin-right: .5rem;
|
||||
background: #AAA;
|
||||
}
|
||||
|
||||
#docs {
|
||||
flex-grow: 1;
|
||||
flex: 2;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--columns),1fr);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.title {
|
||||
background: #333;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.title h1 {
|
||||
margin: 0;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.searchbox {
|
||||
padding: 1em;
|
||||
background: #666;
|
||||
}
|
||||
|
||||
.searchbox input {
|
||||
width: calc(100% - 2em);
|
||||
}
|
||||
|
||||
.document {
|
||||
float: left;
|
||||
margin: .25rem;
|
||||
background: #CCC;
|
||||
border-radius: .125rem;
|
||||
text-align: center;
|
||||
max-width: calc((100vw - 16rem - 20px) / var(--columns) + 1rem);
|
||||
max-width: calc((100vw - 16rem - 20px) / var(--columns) + 1rem);
|
||||
}
|
||||
|
||||
.document .img_box {
|
||||
background: #222;
|
||||
padding: .5rem;
|
||||
padding-bottom: 0;
|
||||
display: inline-block;
|
||||
width: calc((100vw - 16rem - 20px) / var(--columns) - 3rem);
|
||||
height: calc((100vw - 16rem - 20px) / var(--columns) - 3rem);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.document p {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.document h2 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.document h3 {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.document img {
|
||||
margin: .5rem .5rem 0 .5rem;
|
||||
}
|
||||
|
||||
.document h2, .document h3, .document p {
|
||||
margin: .25rem;
|
||||
};
|
Reference in a new issue