kopia lustrzana https://github.com/learn-awesome/learndb
Combobox fix, map fix, tag search
rodzic
b420ed03ca
commit
7c1ab1afb7
|
@ -10,28 +10,27 @@
|
||||||
let query = {
|
let query = {
|
||||||
text: "",
|
text: "",
|
||||||
topic: "",
|
topic: "",
|
||||||
format: "",
|
|
||||||
level: "",
|
level: "",
|
||||||
tags: "",
|
tag: "",
|
||||||
sortby: "rating"
|
sortby: "rating"
|
||||||
};
|
};
|
||||||
|
|
||||||
$: fetch(`/learn/items.json?_shape=array&links__contains=${format}|`)
|
$: query && fetch(`/learn/items.json?_shape=array&_size=100&links__contains=${format}|&topics__contains=${query.topic}`)
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
items = data;
|
items = data;
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleQueryChanged(event){
|
function handleQueryChanged(event){
|
||||||
console.log("queryChanged: ", event.detail);
|
// console.log("queryChanged: ", event.detail);
|
||||||
query = event.detail;
|
query = event.detail;
|
||||||
}
|
}
|
||||||
|
|
||||||
$: filteredItems = items.filter(item => {
|
$: filteredItems = items.filter(item => {
|
||||||
if(query.text && !item.name.toLowerCase().includes(query.text.toLowerCase())){ return false; }
|
if(query.text && !item.name.toLowerCase().includes(query.text.toLowerCase())){ return false; }
|
||||||
if(query.format && !item.links.includes(query.format)) { return false; }
|
if(query.topic && !item.topics.includes(query.topic)){ return false; }
|
||||||
if(query.level && item.difficulty != query.level){ return false; }
|
if(query.level && item.difficulty != query.level){ return false; }
|
||||||
// TODO Apply tags filter
|
if(query.tag && !item.tags.includes(query.tag)){ return false; }
|
||||||
return true;
|
return true;
|
||||||
}).sort((a,b) => {
|
}).sort((a,b) => {
|
||||||
if(query.sortby == 'rating') { return (a.rating - b.rating) };
|
if(query.sortby == 'rating') { return (a.rating - b.rating) };
|
||||||
|
|
|
@ -3,16 +3,12 @@
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
export let alltopics;
|
export let alltopics;
|
||||||
export let hideFormat = false;
|
|
||||||
export let hideTopic = false;
|
export let hideTopic = false;
|
||||||
export let hideQuality = true;
|
|
||||||
|
|
||||||
let query = {
|
let query = {
|
||||||
text: "",
|
text: "",
|
||||||
topic: "",
|
topic: "",
|
||||||
format: "",
|
|
||||||
level: "",
|
level: "",
|
||||||
quality: "",
|
|
||||||
sortby: "rating",
|
sortby: "rating",
|
||||||
tag: ""
|
tag: ""
|
||||||
};
|
};
|
||||||
|
@ -24,24 +20,19 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form class="w-full p-2 inline-flex flex-wrap" on:submit|preventDefault>
|
<form class="w-full p-2 inline-flex flex-wrap" on:submit|preventDefault>
|
||||||
<sl-input type="search" placeholder="Type something to search items by keywords" size="medium" clearable class="flex-1 border-0 p-0 focus:ring-0" value={query.text} on:sl-input="{e => query.text = e.target.value}">
|
<sl-input type="search" placeholder="Search by keywords" size="medium" clearable class="flex-1 border-0 p-0 focus:ring-0" value={query.text} on:sl-input="{e => query.text = e.target.value}">
|
||||||
<sl-icon name="search" slot="prefix"></sl-icon>
|
<sl-icon name="search" slot="prefix"></sl-icon>
|
||||||
</sl-input>
|
</sl-input>
|
||||||
|
|
||||||
{#if !hideTopic}
|
{#if !hideTopic}
|
||||||
<ComboBox options={alltopics.map(t => { return {label: t.display_name, value: t.name}; }).sort((a,b) => a.label.localeCompare(b.label)).slice(0,10)} selected={null}/>
|
<fluent-combobox autocomplete="both" placeholder="Any topic" class="ml-2 mt-1 outline-none border-2 border-grey-600" on:change="{e => query.topic = e.target.value}">
|
||||||
|
{#each alltopics.sort((a,b) => a.display_name.localeCompare(b.display_name)) as topic}
|
||||||
|
<fluent-option value={topic.name}>{topic.display_name}</fluent-option>
|
||||||
|
{/each}
|
||||||
|
</fluent-combobox>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !hideFormat}
|
<sl-select class="ml-2 w-52" on:sl-change="{e => query.tag = e.target.value}" value={query.tag}>
|
||||||
<sl-select class="ml-2 w-44" on:sl-change="{e => query.format = e.target.value}" value={query.format}>
|
|
||||||
<sl-menu-item value="">Any format</sl-menu-item>
|
|
||||||
<sl-menu-item value="book">Books</sl-menu-item>
|
|
||||||
<sl-menu-item value="video">Videos</sl-menu-item>
|
|
||||||
<sl-menu-item value="audio">Podcasts</sl-menu-item>
|
|
||||||
</sl-select>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<sl-select class="ml-2 w-44" on:sl-change="{e => query.tag = e.target.value}" value={query.tag}>
|
|
||||||
<sl-menu-item value="">Any tag</sl-menu-item>
|
<sl-menu-item value="">Any tag</sl-menu-item>
|
||||||
<sl-menu-item value="inspirational">Inspirational</sl-menu-item>
|
<sl-menu-item value="inspirational">Inspirational</sl-menu-item>
|
||||||
<sl-menu-item value="educational">Educational</sl-menu-item>
|
<sl-menu-item value="educational">Educational</sl-menu-item>
|
||||||
|
@ -49,6 +40,7 @@
|
||||||
<sl-menu-item value="entertaining">Entertaining</sl-menu-item>
|
<sl-menu-item value="entertaining">Entertaining</sl-menu-item>
|
||||||
<sl-menu-item value="visual">Visual</sl-menu-item>
|
<sl-menu-item value="visual">Visual</sl-menu-item>
|
||||||
<sl-menu-item value="interactive">Interactive</sl-menu-item>
|
<sl-menu-item value="interactive">Interactive</sl-menu-item>
|
||||||
|
<sl-menu-item value="oer">Open (no login or pay)</sl-menu-item>
|
||||||
</sl-select>
|
</sl-select>
|
||||||
|
|
||||||
<sl-select class="ml-2 w-44" on:sl-change="{e => query.level = e.target.value}" value={query.level}>
|
<sl-select class="ml-2 w-44" on:sl-change="{e => query.level = e.target.value}" value={query.level}>
|
||||||
|
@ -60,15 +52,6 @@
|
||||||
<sl-menu-item value="research">Research</sl-menu-item>
|
<sl-menu-item value="research">Research</sl-menu-item>
|
||||||
</sl-select>
|
</sl-select>
|
||||||
|
|
||||||
{#if !hideQuality}
|
|
||||||
<sl-select class="ml-2 w-44" on:sl-change="{e => query.quality = e.target.value}" value={query.quality}>
|
|
||||||
<sl-menu-item value="">Any quality</sl-menu-item>
|
|
||||||
<sl-menu-item value="visual">Visual</sl-menu-item>
|
|
||||||
<sl-menu-item value="interactive">Interactive</sl-menu-item>
|
|
||||||
<sl-menu-item value="entertaining">Entertaining</sl-menu-item>
|
|
||||||
</sl-select>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<sl-select class="ml-2 w-52" on:sl-change="{e => query.sortby = e.target.value}" value={query.sortby}>
|
<sl-select class="ml-2 w-52" on:sl-change="{e => query.sortby = e.target.value}" value={query.sortby}>
|
||||||
<sl-icon name="sort-down-alt" slot="prefix"></sl-icon>
|
<sl-icon name="sort-down-alt" slot="prefix"></sl-icon>
|
||||||
<sl-menu-item value="rating">Sort by Rating</sl-menu-item>
|
<sl-menu-item value="rating">Sort by Rating</sl-menu-item>
|
||||||
|
|
|
@ -14,9 +14,8 @@
|
||||||
let query = {
|
let query = {
|
||||||
text: "",
|
text: "",
|
||||||
topic: "",
|
topic: "",
|
||||||
format: "",
|
|
||||||
level: "",
|
level: "",
|
||||||
tags: "",
|
tag: "",
|
||||||
sortby: "rating"
|
sortby: "rating"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,15 +27,14 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleQueryChanged(event){
|
function handleQueryChanged(event){
|
||||||
console.log("queryChanged: ", event.detail);
|
// console.log("queryChanged: ", event.detail);
|
||||||
query = event.detail;
|
query = event.detail;
|
||||||
}
|
}
|
||||||
|
|
||||||
$: filteredItems = items.filter(item => {
|
$: filteredItems = items.filter(item => {
|
||||||
if(query.text && !item.name.toLowerCase().includes(query.text.toLowerCase())){ return false; }
|
if(query.text && !item.name.toLowerCase().includes(query.text.toLowerCase())){ return false; }
|
||||||
if(query.format && !item.links.includes(query.format)) { return false; }
|
|
||||||
if(query.level && item.difficulty != query.level){ return false; }
|
if(query.level && item.difficulty != query.level){ return false; }
|
||||||
// TODO: apply tags filter
|
if(query.tag && !item.tags.includes(query.tag)){ return false; }
|
||||||
return true;
|
return true;
|
||||||
}).sort((a,b) => {
|
}).sort((a,b) => {
|
||||||
if(query.sortby == 'rating') { return (a.rating - b.rating) };
|
if(query.sortby == 'rating') { return (a.rating - b.rating) };
|
||||||
|
|
|
@ -5,6 +5,6 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<a href="#/map" class="underline hover:scale-110 px-3 py-2 hover:ease-in-out transition duration-200">Try mapview (beta)</a>
|
<a href="#/map" class="font-semibold bg-lightTertiary text-lightBg rounded-lg hover:scale-110 px-3 py-2 hover:ease-in-out transition duration-200">Explore Map</a>
|
||||||
</div>
|
</div>
|
||||||
<TopicMasonryGrid {alltopics}/>
|
<TopicMasonryGrid {alltopics}/>
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
</sl-breadcrumb>
|
</sl-breadcrumb>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gap-8 columns-1 sm:columns-2 lg:columns-3 xl:columns-4 2xl:columns-5 mb-8">
|
<div class="gap-8 columns-1 sm:columns-2 lg:columns-3 xl:columns-4 3xl:columns-5 mb-8">
|
||||||
{#each [...map.entries()].sort((t1,t2) => (t1[0].sort_index || 100) - (t2[0].sort_index || 100)) as parent}
|
{#each [...map.entries()].sort((t1,t2) => (t1[0].sort_index || 100) - (t2[0].sort_index || 100)) as parent}
|
||||||
<a href={"#/topic/" + parent[0].name}>
|
<a href={"#/topic/" + parent[0].name}>
|
||||||
<div class="rounded-lg shadow-md p-4 break-inside-avoid mb-4 border-8 border-lightPrimCont dark:border-darkPrimCont hover:bg-lightPrimCont dark:bg-darkPrimCont ">
|
<div class="rounded-lg shadow-md p-4 break-inside-avoid mb-4 border-8 border-lightPrimCont dark:border-darkPrimCont hover:bg-lightPrimCont dark:bg-darkPrimCont ">
|
||||||
|
|
|
@ -1,15 +1,24 @@
|
||||||
<script>
|
<script>
|
||||||
export let options = [];
|
export let options = [];
|
||||||
let userInput = '3';
|
export let userInput = '';
|
||||||
$: filteredOptions = options.filter(x => x.label.startsWith(userInput))
|
$: filteredOptions = options.filter(x => x.label.startsWith(userInput))
|
||||||
$: console.log(userInput)
|
$: console.log({options})
|
||||||
|
$: console.log(filteredOptions)
|
||||||
|
$: console.log({userInput})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<sl-dropdown>
|
<sl-dropdown class="ml-2">
|
||||||
<sl-input slot="trigger" on:change="{e => userInput = e.target.value}" value={userInput}></sl-input>
|
<sl-input
|
||||||
|
slot="trigger"
|
||||||
|
class="p-0 m-0 border-0"
|
||||||
|
on:sl-input="{e => userInput = e.target.value}"
|
||||||
|
value={userInput}
|
||||||
|
placeholder="select topic">
|
||||||
|
</sl-input>
|
||||||
|
|
||||||
<sl-menu>
|
<sl-menu>
|
||||||
{#each filteredOptions as opt (opt.value)}
|
{#each filteredOptions as opt (opt.value)}
|
||||||
<sl-menu-item>{opt.label}</sl-menu-item>
|
<sl-menu-item on:click="{e => userInput = opt.value}">{opt.label}</sl-menu-item>
|
||||||
{/each}
|
{/each}
|
||||||
</sl-menu>
|
</sl-menu>
|
||||||
</sl-dropdown>
|
</sl-dropdown>
|
||||||
|
|
|
@ -23,6 +23,8 @@ body {
|
||||||
|
|
||||||
text {
|
text {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
fill: #7a28cb;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grandparent text {
|
.grandparent text {
|
||||||
|
@ -40,11 +42,11 @@ rect.parent,
|
||||||
}
|
}
|
||||||
|
|
||||||
.grandparent rect {
|
.grandparent rect {
|
||||||
fill: rgb(232, 255, 0);
|
fill: #f4b393;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grandparent:hover rect {
|
.grandparent:hover rect {
|
||||||
fill: rgb(202, 225, 0);
|
fill: #f4d393;
|
||||||
}
|
}
|
||||||
|
|
||||||
.children rect.parent,
|
.children rect.parent,
|
||||||
|
@ -54,21 +56,21 @@ rect.parent,
|
||||||
|
|
||||||
.leaf rect.parent {
|
.leaf rect.parent {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
fill: #c9d9ff;
|
fill: #ffdbd1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.leaf rect.parent:hover {
|
.leaf rect.parent:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
fill: #d9e9ff;
|
fill: #ffcfd4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.children rect.parent {
|
.children rect.parent {
|
||||||
fill: #99F6E4;
|
fill: #ceec97;
|
||||||
fill-opacity: .5;
|
fill-opacity: .5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.children:hover rect.child {
|
.children:hover rect.child {
|
||||||
fill: #D7CBFA;
|
fill: #deec97;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -88,8 +90,8 @@ function hierarchy(topic_array, parent_id){
|
||||||
.map(item => ({ ...item, children: nest(items, item.name), value: 100 }))
|
.map(item => ({ ...item, children: nest(items, item.name), value: 100 }))
|
||||||
.map(item => {
|
.map(item => {
|
||||||
return item.children.length == 0 ?
|
return item.children.length == 0 ?
|
||||||
{id: item.name, name: item.name.split("/").reverse()[0], value: 100} :
|
{id: item.name, name: item.name, value: 100} :
|
||||||
{id: item.name, name: item.name.split("/").reverse()[0], children: item.children}
|
{id: item.name, name: item.name, children: item.children}
|
||||||
});
|
});
|
||||||
|
|
||||||
let topnodes = nest(topic_array, ''); // includes childless nodes
|
let topnodes = nest(topic_array, ''); // includes childless nodes
|
||||||
|
@ -98,7 +100,7 @@ function hierarchy(topic_array, parent_id){
|
||||||
let misc = {id: 'misc', name: 'Misc', children: topnodes.filter(t => t.value).slice(0,15)};
|
let misc = {id: 'misc', name: 'Misc', children: topnodes.filter(t => t.value).slice(0,15)};
|
||||||
|
|
||||||
|
|
||||||
return {name: "·", children: [...topnodes.filter(t => t.children), misc]};
|
return {name: "", children: [...topnodes.filter(t => t.children), misc]};
|
||||||
}
|
}
|
||||||
|
|
||||||
d3.json("/learn/topics.json?_shape=array&_size=5000", function(alltopics){
|
d3.json("/learn/topics.json?_shape=array&_size=5000", function(alltopics){
|
||||||
|
@ -232,7 +234,7 @@ grandparent.append("text")
|
||||||
g.append("text")
|
g.append("text")
|
||||||
.attr("dy", ".75em")
|
.attr("dy", ".75em")
|
||||||
.on("click", (n) => { window.parent.location.href = "/#/topic/" + n.name;})
|
.on("click", (n) => { window.parent.location.href = "/#/topic/" + n.name;})
|
||||||
.text(function(d) { return d.name.split('{')[0].split('(')[0]
|
.text(function(d) { return d.name.split('/').reverse()[0].split('{')[0].split('(')[0]
|
||||||
.split('[')[0]; })
|
.split('[')[0]; })
|
||||||
.call(text);
|
.call(text);
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
<link href="/static/bundle.css" rel="stylesheet" />
|
<link href="/static/bundle.css" rel="stylesheet" />
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.73/dist/themes/light.css" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.73/dist/themes/light.css" />
|
||||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.73/dist/shoelace.js"></script>
|
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.73/dist/shoelace.js"></script>
|
||||||
|
<script type="module" src="https://unpkg.com/@fluentui/web-components"></script>
|
||||||
<title>LearnAwesome</title>
|
<title>LearnAwesome</title>
|
||||||
|
|
||||||
<!-- fonts -->
|
<!-- fonts -->
|
||||||
|
|
Ładowanie…
Reference in New Issue