292 lines
6.0 KiB
Svelte
292 lines
6.0 KiB
Svelte
<script>
|
|
import { DropdownMenu, Button, Input } from "@budibase/bbui"
|
|
import { createEventDispatcher, tick } from "svelte"
|
|
|
|
import icons from "./icons.js"
|
|
|
|
const dispatch = createEventDispatcher()
|
|
|
|
export let value = ""
|
|
export let maxIconsPerPage = 30
|
|
|
|
let searchTerm = ""
|
|
let selectedLetter = "A"
|
|
|
|
let currentPage = 1
|
|
let filteredIcons = findIconByTerm(selectedLetter)
|
|
|
|
$: dispatch("change", value)
|
|
|
|
const alphabet = [
|
|
"A",
|
|
"B",
|
|
"C",
|
|
"D",
|
|
"E",
|
|
"F",
|
|
"G",
|
|
"H",
|
|
"I",
|
|
"J",
|
|
"K",
|
|
"L",
|
|
"M",
|
|
"N",
|
|
"O",
|
|
"P",
|
|
"Q",
|
|
"R",
|
|
"S",
|
|
"T",
|
|
"U",
|
|
"V",
|
|
"W",
|
|
"X",
|
|
"Y",
|
|
"Z",
|
|
]
|
|
let buttonAnchor, dropdown
|
|
let loading = false
|
|
|
|
function findIconByTerm(term) {
|
|
const r = new RegExp(`\^${term}`, "i")
|
|
return icons.filter(i => r.test(i.label))
|
|
}
|
|
|
|
async function switchLetter(letter) {
|
|
currentPage = 1
|
|
searchTerm = ""
|
|
loading = true
|
|
selectedLetter = letter
|
|
filteredIcons = findIconByTerm(letter)
|
|
await tick() //svg icons do not update without tick
|
|
loading = false
|
|
}
|
|
|
|
async function findIconOnPage() {
|
|
loading = true
|
|
const iconIdx = filteredIcons.findIndex(i => i.value === value)
|
|
if (iconIdx !== -1) {
|
|
currentPage = Math.ceil(iconIdx / maxIconsPerPage)
|
|
}
|
|
await tick() //svg icons do not update without tick
|
|
loading = false
|
|
}
|
|
|
|
async function setSelectedUI() {
|
|
if (value) {
|
|
const letter = displayValue.substring(0, 1)
|
|
await switchLetter(letter)
|
|
await findIconOnPage()
|
|
}
|
|
}
|
|
|
|
async function pageClick(next) {
|
|
loading = true
|
|
if (next && currentPage < totalPages) {
|
|
currentPage++
|
|
} else if (!next && currentPage > 1) {
|
|
currentPage--
|
|
}
|
|
await tick() //svg icons do not update without tick
|
|
loading = false
|
|
}
|
|
|
|
async function searchForIcon(e) {
|
|
currentPage = 1
|
|
loading = true
|
|
filteredIcons = findIconByTerm(searchTerm)
|
|
await tick() //svg icons do not update without tick
|
|
loading = false
|
|
}
|
|
|
|
$: displayValue = value ? value.substring(7) : "Pick Icon"
|
|
|
|
$: totalPages = Math.ceil(filteredIcons.length / maxIconsPerPage)
|
|
$: pageEndIdx = maxIconsPerPage * currentPage
|
|
$: pagedIcons = filteredIcons.slice(pageEndIdx - maxIconsPerPage, pageEndIdx)
|
|
|
|
$: pagerText = `Page ${currentPage} of ${totalPages}`
|
|
</script>
|
|
|
|
<div bind:this={buttonAnchor}>
|
|
<Button secondary small on:click={dropdown.show}>{displayValue}</Button>
|
|
</div>
|
|
<DropdownMenu
|
|
bind:this={dropdown}
|
|
on:open={setSelectedUI}
|
|
anchor={buttonAnchor}>
|
|
<div class="container">
|
|
<div class="search-area">
|
|
<div class="alphabet-area">
|
|
{#each alphabet as letter, idx}
|
|
<span
|
|
class="letter"
|
|
class:letter-selected={letter === selectedLetter}
|
|
on:click={() => switchLetter(letter)}>
|
|
{letter}
|
|
</span>
|
|
{#if idx !== alphabet.length - 1}<span>-</span>{/if}
|
|
{/each}
|
|
</div>
|
|
<div class="search-input">
|
|
<div class="input-wrapper">
|
|
<Input bind:value={searchTerm} thin placeholder="Search Icon" />
|
|
</div>
|
|
<Button secondary on:click={searchForIcon}>Search</Button>
|
|
</div>
|
|
<div class="page-area">
|
|
<div class="pager">
|
|
<span on:click={() => pageClick(false)}>
|
|
<i class="page-btn fas fa-chevron-left" />
|
|
</span>
|
|
<span>{pagerText}</span>
|
|
<span on:click={() => pageClick(true)}>
|
|
<i class="page-btn fas fa-chevron-right" />
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{#if pagedIcons.length > 0}
|
|
<div class="icon-area">
|
|
{#if !loading}
|
|
{#each pagedIcons as icon}
|
|
<div
|
|
class="icon-container"
|
|
class:selected={value === icon.value}
|
|
on:click={() => (value = icon.value)}>
|
|
<div class="icon-preview">
|
|
<i class={`${icon.value} fa-3x`} />
|
|
</div>
|
|
<div class="icon-label">{icon.label}</div>
|
|
</div>
|
|
{/each}
|
|
{/if}
|
|
</div>
|
|
{:else}
|
|
<div class="no-icons">
|
|
<h5>
|
|
{`There is no icons for this ${searchTerm ? 'search' : 'page'}`}
|
|
</h5>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</DropdownMenu>
|
|
|
|
<style>
|
|
.container {
|
|
width: 610px;
|
|
height: 350px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 10px 0px 10px 15px;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
.search-area {
|
|
flex: 0 0 80px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.icon-area {
|
|
flex: 1;
|
|
display: grid;
|
|
grid-template-columns: repeat(5, 1fr);
|
|
grid-gap: 5px;
|
|
justify-content: flex-start;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
padding-right: 10px;
|
|
}
|
|
|
|
.no-icons {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.alphabet-area {
|
|
display: flex;
|
|
flex-flow: row wrap;
|
|
padding-bottom: 10px;
|
|
padding-right: 15px;
|
|
justify-content: space-around;
|
|
}
|
|
|
|
.loading-container {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.search-input {
|
|
display: flex;
|
|
flex-flow: row nowrap;
|
|
width: 100%;
|
|
padding-right: 15px;
|
|
}
|
|
|
|
.input-wrapper {
|
|
width: 510px;
|
|
margin-right: 5px;
|
|
}
|
|
|
|
.page-area {
|
|
padding: 10px;
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.letter {
|
|
color: var(--blue);
|
|
}
|
|
|
|
.letter:hover {
|
|
cursor: pointer;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.letter-selected {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.icon-container {
|
|
height: 100px;
|
|
display: flex;
|
|
justify-content: center;
|
|
flex-direction: column;
|
|
border: var(--border-dark);
|
|
}
|
|
|
|
.icon-container:hover {
|
|
cursor: pointer;
|
|
background: var(--grey-2);
|
|
}
|
|
|
|
.selected {
|
|
background: var(--grey-3);
|
|
}
|
|
|
|
.icon-preview {
|
|
flex: 1;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.icon-label {
|
|
flex: 0 0 20px;
|
|
text-align: center;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.page-btn {
|
|
color: var(--blue);
|
|
}
|
|
|
|
.page-btn:hover {
|
|
cursor: pointer;
|
|
}
|
|
</style>
|