You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
bandolier-website/admin/bando/IconFinder.svelte

194 lines
4.4 KiB

<script>
import Icon from "$/client/components/Icon.svelte";
import { onMount } from "svelte";
import { log } from "$/client/logging.js";
import api from "$/client/api.js";
import { defer } from "$/client/helpers.js";
import Toast from "$/client/components/Toasts.svelte";
let all_icons = [];
let icons_by_letters = {"a": []};
let selected_letter = "a";
let inactive = false;
let icons = [];
export let size=48;
let search = "";
export let labels=true;
export let tight=false;
let load_promise = defer();
let send_toast;
const order_pages = (in_icons) => {
const letters = {};
for(let icon_name of in_icons) {
const first = icon_name[0];
letters[first] = letters[first] || [];
letters[first].push(icon_name);
}
return letters;
}
const search_icons = async (pattern) => {
await load_promise; // lord I hate Svelte's lifecycle
if(pattern.trim() === "") {
icons_by_letters = order_pages(all_icons);
icons = all_icons;
selected_letter = Object.keys(icons_by_letters)[0];
} else {
icons = all_icons.filter(i => i.includes(pattern));
if(icons.length > 0) {
icons_by_letters = order_pages(icons);
selected_letter = Object.keys(icons_by_letters)[0];
}
}
}
$: search_icons(search);
const gen_code = (name) => {
let results = `<Icon name="${name}" size="${size}" />`;
navigator.clipboard.writeText(results).then(() => {
send_toast(`${name} copied to clipboard.`);
}, () => {
send_toast(`${name} copy FAILED.`);
});
}
onMount(async () => {
const [status, data] = await api.get("/icons/index.json");
if(status === 200) {
all_icons = data;
icons_by_letters = order_pages(all_icons);
load_promise.resolve();
} else {
log.error("Invalid response", status, data);
load_promise.reject();
}
});
</script>
<style>
icons {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-template-rows: auto;
row-gap: 1rem;
}
icons.tight {
grid-template-columns: repeat(10, 1fr);
}
icons icon {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
search-bar {
display: flex;
justify-content: space-evenly;
flex-wrap: nowrap;
height: 5ch;
min-height: 5ch;
}
search-bar input#size {
width: 6ch;
}
search-bar input#search {
min-width: 30ch;
max-width: 30ch;
width: 30ch;
}
search-bar span {
padding-right: 1rem;
}
content {
display: flex;
flex-direction: column;
padding-top: 1rem;
}
display {
display: flex;
flex-direction: column;
align-items: stretch;
}
display letters {
display: grid;
grid-template-rows: 1fr 1fr;
grid-template-columns: repeat(13, 1fr);
margin-bottom: 1rem;
}
display letters letter {
font-size: 1.5em;
font-weight: 600;
text-align: center;
padding: 1rem;
border: 1px solid var(--value5);
}
display letters letter.selected {
background-color: var(--color-bg-secondary);
}
@media only screen and (max-width: 900px) {
icons {
grid-template-columns: repeat(4, 1fr);
}
}
@media only screen and (max-width: 600px) {
icons {
grid-template-columns: repeat(3, 1fr);
}
}
</style>
<content>
<search-bar>
<span on:click={ () => inactive = !inactive }>
<Icon tooltip="Toggle inactive look." name={ inactive ? 'eye' : 'eye-off'} size="24" />
</span>
<input placeholder="Search names..." bind:value={ search } id="search" >
<div>Pixel Size:</div>
<input bind:value={ size } id="size" >
</search-bar>
{#if icons.length > 0}
<display>
<letters>
{#each Object.keys(icons_by_letters) as letter}
<letter class:selected={ letter === selected_letter } on:click={ () => selected_letter = letter }>{ letter }</letter>
{/each}
</letters>
<icons class:tight={ tight }>
{#each icons_by_letters[selected_letter] as name}
<icon on:click={ () => gen_code(name) }>
<Icon name={ name } size={ size } inactive={inactive}/>
{#if labels}
<span>{ name }</span>
{/if}
</icon>
{/each}
</icons>
</display>
{:else}
<h1>No Icons match "{ search }"</h1>
{/if}
<Toast bind:send_toast orientation="bottom right" />
</content>