170 lines
5.8 KiB
PHP
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) => ({
|
|
'&': '&',
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": '''
|
|
}[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
|
|
|