Merge pull request 'Home sections editor: Urdu keyboard and Roman typing in its boxes' (#64) from feature/site-editor-keyboard into main
Reviewed-on: #64
This commit is contained in:
commit
1494c28776
@ -113,8 +113,11 @@ const fullTitle = title ? `${title} · دیوان` : 'دیوان · اردو ک
|
||||
// (e.g. the author filter): whichever was focused last
|
||||
let target = q;
|
||||
document.addEventListener('focusin', (e) => { if (e.target.matches?.('[data-urdu]')) target = e.target; });
|
||||
// a page may redraw its boxes (e.g. the home sections editor): find the box again by its id
|
||||
const live = () => (target.isConnected ? target : (target = (target.id && document.getElementById(target.id)) || q));
|
||||
const typed = () => target.dispatchEvent(new Event('input', { bubbles: true })); // so suggestions update
|
||||
const insert = (text) => {
|
||||
live();
|
||||
// rich editors (the content editor) take typed text through the browser's own editing command
|
||||
if (target.isContentEditable) { target.focus(); document.execCommand('insertText', false, text); return; }
|
||||
const s = target.selectionStart ?? target.value.length, e = target.selectionEnd ?? target.value.length;
|
||||
@ -125,6 +128,7 @@ const fullTitle = title ? `${title} · دیوان` : 'دیوان · اردو ک
|
||||
const b = ev.target.closest('button'); if (!b) return;
|
||||
if (b.dataset.act === 'shift') { shift = !shift; render(); return; }
|
||||
if (b.dataset.act === 'bs') {
|
||||
live();
|
||||
if (target.isContentEditable) { target.focus(); document.execCommand('delete'); return; }
|
||||
const s = target.selectionStart, e = target.selectionEnd;
|
||||
target.setRangeText('', s === e ? Math.max(0, s - 1) : s, e, 'end'); target.focus(); typed();
|
||||
|
||||
@ -59,6 +59,22 @@ const centuries = Object.fromEntries(Array.from({ length: 16 }, (_, c) => [c, ce
|
||||
const b = document.createElement('button'); b.type = 'button'; b.className = 'tool'; b.textContent = label; b.title = title;
|
||||
b.onclick = () => { fn(); draw(); }; return b;
|
||||
};
|
||||
// a box with the Urdu keyboard and Roman typing buttons inside it, as in search (Base.astro handles them)
|
||||
const KB = '<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><rect x="2" y="6" width="20" height="12" rx="2"/><path d="M6 10h.01M10 10h.01M14 10h.01M18 10h.01M7 14h10" stroke-linecap="round" stroke-width="2"/></svg>';
|
||||
// a redrawn box shows the keyboard/Roman state only if it is the box being typed in
|
||||
let typing = '';
|
||||
host.addEventListener('focusin', (e) => { const t = e.target as HTMLElement; if (t.matches('input[data-urdu]')) typing = t.id; });
|
||||
const pressed = (header: string, id: string) => (id === typing && document.getElementById(header)?.getAttribute('aria-pressed')) || 'false';
|
||||
const keyed = (input: HTMLInputElement, id: string, cls = '') => {
|
||||
input.id = id; input.setAttribute('data-urdu', '');
|
||||
const wrap = document.createElement('div'); wrap.className = `in-box-field ${cls}`;
|
||||
const rom = document.createElement('button'); rom.type = 'button'; rom.className = 'in-box rom'; rom.textContent = 'اب';
|
||||
rom.title = 'رومن حروف سے اردو'; rom.dataset.urduRom = id; rom.setAttribute('aria-pressed', pressed('rom-toggle', id));
|
||||
const kb = document.createElement('button'); kb.type = 'button'; kb.className = 'in-box'; kb.innerHTML = KB;
|
||||
kb.title = 'اردو کی بورڈ'; kb.setAttribute('aria-label', 'اردو کی بورڈ'); kb.dataset.urduKb = id; kb.setAttribute('aria-pressed', pressed('kb-toggle', id));
|
||||
wrap.append(input, rom, kb);
|
||||
return wrap;
|
||||
};
|
||||
const move = (xs: any[], i: number, d: number) => { const j = i + d; if (j >= 0 && j < xs.length) [xs[i], xs[j]] = [xs[j], xs[i]]; };
|
||||
const SORTS: [string, string][] = [['manual', 'دستی'], ['alpha', 'حروفِ تہجی'], ['timeline', 'زمانی']];
|
||||
|
||||
@ -66,7 +82,7 @@ const centuries = Object.fromEntries(Array.from({ length: 16 }, (_, c) => [c, ce
|
||||
host.replaceChildren(...state.map((s, i) => {
|
||||
const box = document.createElement('fieldset'); box.className = 'site-section';
|
||||
const head = document.createElement('div'); head.className = 'site-head';
|
||||
const title = document.createElement('input'); title.value = s.title; title.setAttribute('data-urdu', '');
|
||||
const title = document.createElement('input'); title.value = s.title;
|
||||
title.placeholder = s.century === null ? 'حصے کا نام' : data.centuries[s.century]; title.setAttribute('aria-label', 'حصے کا نام');
|
||||
title.oninput = () => (s.title = title.value);
|
||||
const sort = document.createElement('select'); sort.className = 'tool'; sort.setAttribute('aria-label', 'ترتیب');
|
||||
@ -74,17 +90,17 @@ const centuries = Object.fromEntries(Array.from({ length: 16 }, (_, c) => [c, ce
|
||||
sort.onchange = () => { s.sort = sort.value; draw(); };
|
||||
const kind = document.createElement('small'); kind.className = 'muted';
|
||||
kind.textContent = s.century === null ? `منتخب شعرا (${s.poets.length})` : `صدی: ${data.centuries[s.century]}`;
|
||||
head.append(title, sort, kind,
|
||||
head.append(keyed(title, `site-title-${i}`, 'site-title'), sort, kind,
|
||||
btn('↑', 'حصہ اوپر', () => move(state, i, -1)), btn('↓', 'حصہ نیچے', () => move(state, i, 1)),
|
||||
btn('ہٹائیں', 'یہ حصہ ہٹائیں', () => { if (confirm('یہ حصہ ہٹا دیں؟')) state.splice(i, 1); }));
|
||||
box.append(head);
|
||||
if (s.century === null) box.append(poetList(s));
|
||||
if (s.century === null) box.append(poetList(s, i));
|
||||
return box;
|
||||
}));
|
||||
}
|
||||
|
||||
// a chosen-poets section: its poets (↑ ↓ ✕ when sorted by hand) and a name box to add one
|
||||
function poetList(s: any) {
|
||||
function poetList(s: any, i: number) {
|
||||
const wrap = document.createElement('div');
|
||||
const ol = document.createElement('ol'); ol.className = 'site-poets';
|
||||
s.poets.forEach((id: number, j: number) => {
|
||||
@ -94,16 +110,17 @@ const centuries = Object.fromEntries(Array.from({ length: 16 }, (_, c) => [c, ce
|
||||
ol.append(li);
|
||||
});
|
||||
const find = document.createElement('div'); find.className = 'author-find';
|
||||
const box = document.createElement('input'); box.setAttribute('data-urdu', ''); box.placeholder = 'شاعر یا ادیب کا نام لکھ کر شامل کریں'; box.autocomplete = 'off';
|
||||
const box = document.createElement('input'); box.placeholder = 'شاعر یا ادیب کا نام لکھ کر شامل کریں'; box.autocomplete = 'off';
|
||||
const list = document.createElement('div'); list.className = 'author-suggest'; list.hidden = true;
|
||||
box.oninput = () => {
|
||||
const t = norm(box.value.trim());
|
||||
const hits = t ? data.poets.filter((p: any) => !s.poets.includes(p.id) && norm(p.name).includes(t)).slice(0, 10) : [];
|
||||
list.replaceChildren(...hits.map((p: any) => btn(p.name, 'شامل کریں', () => s.poets.push(p.id))));
|
||||
// after adding, the (redrawn) name box keeps the focus for the next name
|
||||
list.replaceChildren(...hits.map((p: any) => btn(p.name, 'شامل کریں', () => { s.poets.push(p.id); setTimeout(() => document.getElementById(`site-find-${i}`)?.focus()); })));
|
||||
list.hidden = !hits.length;
|
||||
};
|
||||
box.onkeydown = (e) => { if (e.key === 'Enter') { e.preventDefault(); (list.querySelector('button') as HTMLButtonElement | null)?.click(); } };
|
||||
find.append(box, list);
|
||||
find.append(keyed(box, `site-find-${i}`), list);
|
||||
wrap.append(ol, find);
|
||||
return wrap;
|
||||
}
|
||||
|
||||
@ -397,7 +397,8 @@ h1 + .muted { text-align: center; margin-top: 0; }
|
||||
/* home page sections editor (/mod/site) */
|
||||
.site-section { border: 1px solid var(--border); border-radius: 10px; background: var(--paper); margin: 10px 0; padding: 8px 12px; }
|
||||
.site-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
|
||||
.site-head input { flex: 1 1 14em; font: inherit; padding: 2px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--inner); color: var(--ink); }
|
||||
.site-head .site-title { flex: 1 1 14em; }
|
||||
.site-head input { width: 100%; font: inherit; padding: 2px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--inner); color: var(--ink); }
|
||||
.site-poets { list-style: urdu; margin: 8px 0; padding-inline-start: 2.5em; columns: 2 16em; }
|
||||
.site-poets li { break-inside: avoid; }
|
||||
.site-poets .tool { min-width: 1.6em; padding: 0 4px; margin-inline-start: 4px; font-size: .85rem; }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user