138 lines
3.4 KiB
Vue
138 lines
3.4 KiB
Vue
<script setup>
|
|
import iconJson from "@/assets/json/iconamoon.json";
|
|
|
|
definePageMeta({
|
|
title: "Set Ikons",
|
|
middleware: ["auth"],
|
|
requiresAuth: true,
|
|
breadcrumb: [
|
|
{
|
|
name: "Set Ikons",
|
|
type: "current",
|
|
},
|
|
],
|
|
});
|
|
|
|
const copyToClipboard = (text) => {
|
|
let tagHtml = '<Icon name="' + text + '" />';
|
|
navigator.clipboard.writeText(tagHtml).then(() => {
|
|
showTooltip(text, "Kod disalin!");
|
|
});
|
|
};
|
|
|
|
const searchQuery = ref("");
|
|
const displayedIcons = ref([]);
|
|
const containerRef = ref(null);
|
|
const tooltips = reactive({});
|
|
const batchSize = 50;
|
|
let currentIndex = 0;
|
|
|
|
const filteredIcons = computed(() => {
|
|
if (!searchQuery.value) return iconJson;
|
|
return iconJson.filter((icon) =>
|
|
icon.name.toLowerCase().includes(searchQuery.value.toLowerCase())
|
|
);
|
|
});
|
|
|
|
const loadMoreIcons = () => {
|
|
const newIcons = filteredIcons.value.slice(
|
|
currentIndex,
|
|
currentIndex + batchSize
|
|
);
|
|
displayedIcons.value.push(...newIcons);
|
|
currentIndex += batchSize;
|
|
};
|
|
|
|
const resetIconDisplay = () => {
|
|
displayedIcons.value = [];
|
|
currentIndex = 0;
|
|
loadMoreIcons();
|
|
};
|
|
|
|
const showTooltip = (iconName, message) => {
|
|
tooltips[iconName] = message;
|
|
setTimeout(() => {
|
|
tooltips[iconName] = null;
|
|
}, 2000);
|
|
};
|
|
|
|
watch(searchQuery, () => {
|
|
resetIconDisplay();
|
|
nextTick(() => {
|
|
if (containerRef.value) {
|
|
containerRef.value.scrollTop = 0;
|
|
}
|
|
});
|
|
});
|
|
|
|
onMounted(() => {
|
|
resetIconDisplay();
|
|
});
|
|
|
|
const handleScroll = () => {
|
|
if (!containerRef.value) return;
|
|
|
|
const { scrollTop, scrollHeight, clientHeight } = containerRef.value;
|
|
if (scrollHeight - scrollTop <= clientHeight + 1) {
|
|
loadMoreIcons();
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<LayoutsBreadcrumb />
|
|
|
|
<rs-card>
|
|
<template #header>
|
|
Set Ikon Icomoon (Jumlah: {{ filteredIcons.length }})
|
|
</template>
|
|
<template #body>
|
|
<FormKit
|
|
type="text"
|
|
v-model="searchQuery"
|
|
placeholder="Search icons..."
|
|
class="mb-4"
|
|
/>
|
|
|
|
<div
|
|
ref="containerRef"
|
|
class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-8 max-h-[70vh] overflow-y-auto"
|
|
@scroll="handleScroll"
|
|
>
|
|
<div
|
|
v-for="icon in displayedIcons"
|
|
:key="icon.name"
|
|
class="flex items-center relative"
|
|
>
|
|
<Icon :name="icon.name" class="!w-10 !h-10 mr-4"></Icon>
|
|
<div class="flex flex-col">
|
|
<div class="text-sm">{{ icon.name }}</div>
|
|
<div class="relative">
|
|
<rs-button
|
|
@click="copyToClipboard(icon.name)"
|
|
class="!text-xs !py-1 !px-2 !mt-1 relative"
|
|
>
|
|
<Icon name="ph:copy" class="!w-4 !h-4 mr-1"></Icon>
|
|
Salin Kod
|
|
</rs-button>
|
|
<transition name="fade">
|
|
<span
|
|
v-if="tooltips[icon.name]"
|
|
class="absolute top-4 left-0 mt-4 bg-black text-white text-xs rounded py-1 px-2 z-10"
|
|
>
|
|
{{ tooltips[icon.name] }}
|
|
</span>
|
|
</transition>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-if="displayedIcons.length === 0" class="text-center mt-4">
|
|
No icons found matching your search.
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</div>
|
|
</template>
|