feat(ui): searchable language picker and light-mode keyword modals
This commit is contained in:
@@ -133,10 +133,10 @@
|
||||
|
||||
<div id="keyword-modal" class="hidden fixed inset-0 z-50 items-center justify-center">
|
||||
<div class="absolute inset-0 bg-black/70 backdrop-blur-sm"></div>
|
||||
<div class="relative z-10 w-full max-w-lg rounded-3xl glass-card p-6">
|
||||
<div class="relative z-10 w-full max-w-lg rounded-3xl glass-card theme-surface p-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-lg font-semibold text-white" id="keyword-modal-title">Add keyword</h3>
|
||||
<button id="keyword-modal-close" class="w-8 h-8 rounded-full bg-white/10 hover:bg-white/20 flex items-center justify-center text-gray-200">
|
||||
<h3 class="text-lg font-semibold text-slate-900 dark:text-white" id="keyword-modal-title">Add keyword</h3>
|
||||
<button id="keyword-modal-close" class="w-8 h-8 rounded-full bg-slate-100 hover:bg-slate-200 dark:bg-white/10 dark:hover:bg-white/20 flex items-center justify-center text-slate-600 dark:text-gray-200">
|
||||
<i data-lucide="x" class="w-4 h-4"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -144,20 +144,22 @@
|
||||
@csrf
|
||||
<input type="hidden" name="_method" id="keyword-form-method" value="POST">
|
||||
<div>
|
||||
<label class="text-xs uppercase tracking-[0.2em] text-gray-400">Emoji Slug</label>
|
||||
<input type="text" name="emoji_slug" id="keyword-emoji" class="mt-2 w-full rounded-xl bg-black/40 border border-white/10 px-4 py-3 text-sm text-gray-200 focus:outline-none focus:border-brand-ocean" placeholder="sparkles" required>
|
||||
<label class="text-xs uppercase tracking-[0.2em] text-slate-500 dark:text-gray-400">Emoji Slug</label>
|
||||
<input type="text" name="emoji_slug" id="keyword-emoji" class="mt-2 w-full rounded-xl bg-white border border-slate-300 px-4 py-3 text-sm text-slate-900 placeholder-slate-400 focus:outline-none focus:border-brand-ocean dark:bg-black/40 dark:border-white/10 dark:text-gray-200 dark:placeholder-gray-500" placeholder="sparkles" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs uppercase tracking-[0.2em] text-gray-400">Keyword</label>
|
||||
<input type="text" name="keyword" id="keyword-text" class="mt-2 w-full rounded-xl bg-black/40 border border-white/10 px-4 py-3 text-sm text-gray-200 focus:outline-none focus:border-brand-ocean" placeholder="magic" required>
|
||||
<label class="text-xs uppercase tracking-[0.2em] text-slate-500 dark:text-gray-400">Keyword</label>
|
||||
<input type="text" name="keyword" id="keyword-text" class="mt-2 w-full rounded-xl bg-white border border-slate-300 px-4 py-3 text-sm text-slate-900 placeholder-slate-400 focus:outline-none focus:border-brand-ocean dark:bg-black/40 dark:border-white/10 dark:text-gray-200 dark:placeholder-gray-500" placeholder="magic" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs uppercase tracking-[0.2em] text-gray-400">Language</label>
|
||||
<input type="text" name="lang" id="keyword-lang" class="mt-2 w-full rounded-xl bg-black/40 border border-white/10 px-4 py-3 text-sm text-gray-200 focus:outline-none focus:border-brand-ocean" placeholder="en">
|
||||
<label class="text-xs uppercase tracking-[0.2em] text-slate-500 dark:text-gray-400">Language</label>
|
||||
<input type="hidden" name="lang" id="keyword-lang" value="und">
|
||||
<input type="text" id="keyword-lang-display" list="keyword-language-options" class="mt-2 w-full rounded-xl bg-white border border-slate-300 px-4 py-3 text-sm text-slate-900 placeholder-slate-400 focus:outline-none focus:border-brand-ocean dark:bg-black/40 dark:border-white/10 dark:text-gray-200 dark:placeholder-gray-500" placeholder="Search language (e.g. English)">
|
||||
@include('partials.language-datalist', ['id' => 'keyword-language-options'])
|
||||
</div>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<button type="button" id="keyword-cancel" class="rounded-full border border-white/10 px-4 py-2 text-sm text-gray-200 hover:bg-white/5">Cancel</button>
|
||||
<button type="submit" class="rounded-full bg-brand-ocean text-white font-semibold px-5 py-2 text-sm">Save keyword</button>
|
||||
<button type="button" id="keyword-cancel" class="rounded-full border border-slate-300 px-4 py-2 text-sm text-slate-700 hover:bg-slate-100 dark:border-white/10 dark:text-gray-200 dark:hover:bg-white/5">Cancel</button>
|
||||
<button type="submit" class="rounded-full bg-brand-ocean text-white force-white font-semibold px-5 py-2 text-sm">Save keyword</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -179,11 +181,44 @@
|
||||
const emojiEl = document.getElementById('keyword-emoji');
|
||||
const textEl = document.getElementById('keyword-text');
|
||||
const langEl = document.getElementById('keyword-lang');
|
||||
const langDisplayEl = document.getElementById('keyword-lang-display');
|
||||
const langDataList = document.getElementById('keyword-language-options');
|
||||
const searchEl = document.getElementById('keyword-search');
|
||||
const importBtn = document.getElementById('import-btn');
|
||||
const importPanel = document.getElementById('import-panel');
|
||||
const importCancel = document.getElementById('import-cancel');
|
||||
|
||||
window.dewemojiPopulateLanguageOptions?.(langDataList);
|
||||
|
||||
const langDisplayToCode = new Map();
|
||||
const langCodeToDisplay = new Map();
|
||||
langDataList?.querySelectorAll('option').forEach((option) => {
|
||||
const code = (option.dataset.code || '').trim();
|
||||
const display = (option.value || '').trim();
|
||||
if (!code || !display) return;
|
||||
langDisplayToCode.set(display.toLowerCase(), code);
|
||||
langCodeToDisplay.set(code.toLowerCase(), display);
|
||||
});
|
||||
|
||||
const normalizeLang = (value) => {
|
||||
const v = (value || '').trim();
|
||||
if (!v) return 'und';
|
||||
const byDisplay = langDisplayToCode.get(v.toLowerCase());
|
||||
if (byDisplay) return byDisplay;
|
||||
|
||||
const bracket = v.match(/\(([A-Za-z]{2,3}(?:-[A-Za-z]{2})?)\)\s*$/);
|
||||
if (bracket?.[1]) return bracket[1];
|
||||
|
||||
if (/^[A-Za-z]{2,3}(?:-[A-Za-z]{2})?$/.test(v)) return v;
|
||||
return 'und';
|
||||
};
|
||||
|
||||
const displayLang = (code) => {
|
||||
const c = (code || '').trim().toLowerCase();
|
||||
if (!c) return '';
|
||||
return langCodeToDisplay.get(c) || code;
|
||||
};
|
||||
|
||||
const openModal = (mode, data = {}) => {
|
||||
if (mode === 'add' && limitReached) return;
|
||||
modal.classList.remove('hidden');
|
||||
@@ -193,7 +228,8 @@
|
||||
methodEl.value = mode === 'edit' ? 'PUT' : 'POST';
|
||||
emojiEl.value = data.emoji || '';
|
||||
textEl.value = data.keyword || '';
|
||||
langEl.value = data.lang || '';
|
||||
langEl.value = (data.lang || 'und').trim() || 'und';
|
||||
langDisplayEl.value = displayLang(data.lang || 'und');
|
||||
emojiEl.focus();
|
||||
};
|
||||
|
||||
@@ -209,6 +245,14 @@
|
||||
if (e.target === modal) closeModal();
|
||||
});
|
||||
|
||||
langDisplayEl?.addEventListener('input', () => {
|
||||
langEl.value = normalizeLang(langDisplayEl.value);
|
||||
});
|
||||
|
||||
form?.addEventListener('submit', () => {
|
||||
langEl.value = normalizeLang(langDisplayEl?.value || '');
|
||||
});
|
||||
|
||||
document.querySelectorAll('.edit-btn').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
openModal('edit', {
|
||||
|
||||
Reference in New Issue
Block a user