Files
dewemoji/app/resources/views/site/home.blade.php
2026-02-03 22:37:52 +07:00

170 lines
5.8 KiB
PHP

@extends('site.layout')
@section('title', 'Dewemoji - Emoji Browser')
@section('content')
<section class="card" style="padding: 18px;">
<h1 style="margin: 0 0 6px 0;">Emoji Browser</h1>
<p style="margin: 0; color: var(--muted);">Rebuilt website powered by <code>/v1</code> APIs.</p>
<div style="display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 10px; margin-top: 16px;" id="filters">
<input id="q" type="text" placeholder="Search (e.g. love, senyum)" style="padding: 10px; border: 1px solid var(--border); border-radius: 10px;">
<select id="category" style="padding: 10px; border: 1px solid var(--border); border-radius: 10px;">
<option value="">All categories</option>
</select>
<select id="subcategory" style="padding: 10px; border: 1px solid var(--border); border-radius: 10px;" disabled>
<option value="">All subcategories</option>
</select>
</div>
</section>
<section class="card" style="margin-top: 14px; padding: 14px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<strong>Results</strong>
<span id="count" style="color: var(--muted); font-size: 13px;">0 / 0</span>
</div>
<div id="grid" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(94px, 1fr)); gap: 10px;"></div>
<div style="text-align: center; margin-top: 14px;">
<button id="more" style="display:none; border: 1px solid var(--border); background: white; border-radius: 10px; padding: 10px 14px; cursor: pointer;">
Load more
</button>
</div>
</section>
@endsection
@push('scripts')
<script>
(() => {
const state = {
page: 1,
limit: 20,
total: 0,
items: [],
categories: {},
};
const qEl = document.getElementById('q');
const catEl = document.getElementById('category');
const subEl = document.getElementById('subcategory');
const grid = document.getElementById('grid');
const count = document.getElementById('count');
const more = document.getElementById('more');
function esc(s) {
return String(s || '').replace(/[&<>"']/g, (c) => ({
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;'
}[c]));
}
async function loadCategories() {
const res = await fetch('/v1/categories');
const data = await res.json();
state.categories = data || {};
Object.keys(state.categories).forEach((name) => {
const opt = document.createElement('option');
opt.value = name;
opt.textContent = name;
catEl.appendChild(opt);
});
}
function renderSubcategories() {
const category = catEl.value;
const subs = state.categories[category] || [];
subEl.innerHTML = '<option value="">All subcategories</option>';
subs.forEach((s) => {
const opt = document.createElement('option');
opt.value = s;
opt.textContent = s;
subEl.appendChild(opt);
});
subEl.disabled = subs.length === 0;
}
async function fetchEmojis(reset = false) {
if (reset) {
state.page = 1;
state.items = [];
grid.innerHTML = '';
}
const params = new URLSearchParams({
page: String(state.page),
limit: String(state.limit),
});
if (qEl.value.trim()) params.set('q', qEl.value.trim());
if (catEl.value) params.set('category', catEl.value);
if (subEl.value) params.set('subcategory', subEl.value);
const res = await fetch('/v1/emojis?' + params.toString());
const data = await res.json();
state.total = data.total || 0;
(data.items || []).forEach((item) => state.items.push(item));
renderGrid(data.items || [], reset);
updateCount();
updateMoreButton();
}
function renderGrid(items, reset) {
if (reset && items.length === 0) {
grid.innerHTML = '<p style="color:var(--muted);grid-column:1/-1;">No emojis found.</p>';
return;
}
items.forEach((item) => {
const card = document.createElement('a');
card.href = '/emoji/' + encodeURIComponent(item.slug);
card.className = 'card';
card.style.cssText = 'padding:10px;text-decoration:none;color:inherit;display:flex;flex-direction:column;align-items:center;gap:6px;';
card.innerHTML = `
<div style="font-size:30px;">${esc(item.emoji)}</div>
<div style="font-size:12px;text-align:center;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%">${esc(item.name)}</div>
`;
grid.appendChild(card);
});
}
function updateCount() {
count.textContent = `${state.items.length} / ${state.total}`;
}
function updateMoreButton() {
const canLoad = state.items.length < state.total;
more.style.display = canLoad ? 'inline-block' : 'none';
}
let timer = null;
qEl.addEventListener('input', () => {
clearTimeout(timer);
timer = setTimeout(() => fetchEmojis(true), 220);
});
catEl.addEventListener('change', async () => {
renderSubcategories();
await fetchEmojis(true);
});
subEl.addEventListener('change', () => fetchEmojis(true));
more.addEventListener('click', async () => {
state.page += 1;
await fetchEmojis(false);
});
(async () => {
await loadCategories();
await fetchEmojis(true);
})();
})();
</script>
@endpush