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.
195 lines
4.4 KiB
195 lines
4.4 KiB
2 years ago
|
<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>
|