Merge pull request 'UI: poet cards (corner bookmark, one size, years on two lines); centred previous/next' (#59) from feature/poet-card-layout into main
Reviewed-on: #59
This commit is contained in:
commit
f8517aeb85
@ -11,8 +11,11 @@ export const centuryName = (c: number | null) => (c ? `${ORDINALS[c] ?? ud(c)}
|
|||||||
const span = (a: number | null, b: number | null, mark: string) =>
|
const span = (a: number | null, b: number | null, mark: string) =>
|
||||||
a || b ? `${a ? ud(a) : '؟'} – ${b ? ud(b) : '؟'} ${mark}` : '';
|
a || b ? `${a ? ud(a) : '؟'} – ${b ? ud(b) : '؟'} ${mark}` : '';
|
||||||
// Hijri and Gregorian (عیسوی): "۱۲۹۴ – ۱۳۵۷ ھ · ۱۸۷۷ – ۱۹۳۸ ء"
|
// Hijri and Gregorian (عیسوی): "۱۲۹۴ – ۱۳۵۷ ھ · ۱۸۷۷ – ۱۹۳۸ ء"
|
||||||
export const years = (p: { birth_year_ah: number | null; death_year_ah: number | null; birth_year_ce?: number | null; death_year_ce?: number | null }) =>
|
type Years = { birth_year_ah: number | null; death_year_ah: number | null; birth_year_ce?: number | null; death_year_ce?: number | null };
|
||||||
[span(p.birth_year_ah, p.death_year_ah, 'ھ'), span(p.birth_year_ce ?? null, p.death_year_ce ?? null, 'ء')].filter(Boolean).join(' · ');
|
// Hijri and Gregorian spans, each on its own (poet cards show them as two lines)
|
||||||
|
export const yearLines = (p: Years) =>
|
||||||
|
[span(p.birth_year_ah, p.death_year_ah, 'ھ'), span(p.birth_year_ce ?? null, p.death_year_ce ?? null, 'ء')].filter(Boolean) as string[];
|
||||||
|
export const years = (p: Years) => yearLines(p).join(' · ');
|
||||||
|
|
||||||
// search highlighting (same matching rules as the API's search)
|
// search highlighting (same matching rules as the API's search)
|
||||||
export { highlighter } from '../../../api/src/urdu.ts';
|
export { highlighter } from '../../../api/src/urdu.ts';
|
||||||
|
|||||||
@ -133,8 +133,8 @@ const title = page.type === 'poem' ? page.poem.title : page.type === 'poet' ? pa
|
|||||||
</article>
|
</article>
|
||||||
<p class="muted hint">کسی لفظ کو منتخب کریں تو اس کے معنی اور تلفظ ساتھ نظر آئیں گے۔</p>
|
<p class="muted hint">کسی لفظ کو منتخب کریں تو اس کے معنی اور تلفظ ساتھ نظر آئیں گے۔</p>
|
||||||
<nav class="poem-nav">
|
<nav class="poem-nav">
|
||||||
{page.next ? <a href={page.next}>اگلا ›</a> : <span />}
|
{page.prev && <a href={page.prev} rel="prev">‹ پچھلا</a>}
|
||||||
{page.prev ? <a href={page.prev}>‹ پچھلا</a> : <span />}
|
{page.next && <a href={page.next} rel="next">اگلا ›</a>}
|
||||||
</nav>
|
</nav>
|
||||||
{page.divan && (
|
{page.divan && (
|
||||||
<p class="source divan-edit">
|
<p class="source divan-edit">
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
import { api } from '../lib/api';
|
import { api } from '../lib/api';
|
||||||
import { asUser, COOKIE } from '../lib/auth';
|
import { asUser, COOKIE } from '../lib/auth';
|
||||||
import { centuryOf, centuryName, years, ud } from '../lib/urdu';
|
import { centuryOf, centuryName, yearLines, ud } from '../lib/urdu';
|
||||||
|
|
||||||
type Poet = { id: number; url: string; nickname: string; birth_year_ah: number | null; death_year_ah: number | null; pin_order: number | null };
|
type Poet = { id: number; url: string; nickname: string; birth_year_ah: number | null; death_year_ah: number | null; pin_order: number | null };
|
||||||
const poets = (await api<Poet[]>('/api/poets')) ?? [];
|
const poets = (await api<Poet[]>('/api/poets')) ?? [];
|
||||||
@ -34,7 +34,7 @@ const card = (p: Poet) => `poet-card${marked.includes(p.id) ? ' mark-bm' : ''}`;
|
|||||||
<h2>مقبول شعرا</h2>
|
<h2>مقبول شعرا</h2>
|
||||||
<div class="poets">
|
<div class="poets">
|
||||||
{pinned.map((p) => (
|
{pinned.map((p) => (
|
||||||
<a class={card(p)} href={p.url}>{p.nickname}<small>{years(p)}</small></a>
|
<a class={card(p)} href={p.url} title={p.nickname}><span class="name">{p.nickname}</span>{yearLines(p).map((y) => <small>{y}</small>)}</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -45,9 +45,9 @@ const card = (p: Poet) => `poet-card${marked.includes(p.id) ? ' mark-bm' : ''}`;
|
|||||||
<h2>{centuryName(century)}</h2>
|
<h2>{centuryName(century)}</h2>
|
||||||
<div class="poets">
|
<div class="poets">
|
||||||
{list.map((p) => (
|
{list.map((p) => (
|
||||||
<a class={card(p)} href={p.url}>
|
<a class={card(p)} href={p.url} title={p.nickname}>
|
||||||
{p.nickname}
|
<span class="name">{p.nickname}</span>
|
||||||
<small>{years(p)}</small>
|
{yearLines(p).map((y) => <small>{y}</small>)}
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -63,11 +63,17 @@ h1::after { content: ''; display: block; width: 140px; height: 2px; background:
|
|||||||
/* poet tiles (captions only, photos hidden for now) */
|
/* poet tiles (captions only, photos hidden for now) */
|
||||||
.poets { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
|
.poets { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
|
||||||
.poet-card {
|
.poet-card {
|
||||||
min-width: 120px; text-align: center; background: var(--paper); border: 1.5px solid var(--border);
|
/* one size for every card: long names end in … (full name on hover) */
|
||||||
border-radius: 999px; padding: 2px 14px; color: var(--ink); transition: transform .25s;
|
width: 210px; max-width: calc(50% - 4px); min-height: 5.125rem; text-align: center;
|
||||||
|
display: flex; flex-direction: column; justify-content: center; background: var(--paper); border: 1.5px solid var(--border);
|
||||||
|
border-radius: 999px; padding: 6px 36px 8px; color: var(--ink); transition: transform .25s; position: relative;
|
||||||
}
|
}
|
||||||
|
/* the bookmark sits in the top-left corner, inside the side padding (36px), so it never covers the name and a saved card keeps its size */
|
||||||
|
.poet-card.mark-bm::after { position: absolute; top: 12px; left: 16px; margin: 0; }
|
||||||
.poet-card:hover { transform: translateY(-3px); border-color: var(--brand); color: var(--ink); }
|
.poet-card:hover { transform: translateY(-3px); border-color: var(--brand); color: var(--ink); }
|
||||||
.poet-card small { display: block; color: var(--muted); font-size: .75rem; line-height: 1.4; }
|
.poet-card .name { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||||
|
/* years in Naskh: compact lines whatever the reading font (Nastaliq's tall line boxes made cards grow in some browsers) */
|
||||||
|
.poet-card small { display: block; color: var(--muted); font-family: 'Noto Naskh Arabic', serif; font-size: .75rem; line-height: 1.6; white-space: nowrap; }
|
||||||
.hero { text-align: center; }
|
.hero { text-align: center; }
|
||||||
.hero p { margin: 0; }
|
.hero p { margin: 0; }
|
||||||
|
|
||||||
@ -99,7 +105,7 @@ h1::after { content: ''; display: block; width: 140px; height: 2px; background:
|
|||||||
.couplet p, .couplet p + p { text-align: center; }
|
.couplet p, .couplet p + p { text-align: center; }
|
||||||
.couplet::after { display: none; }
|
.couplet::after { display: none; }
|
||||||
}
|
}
|
||||||
.poem-nav { display: flex; justify-content: space-between; }
|
.poem-nav { display: flex; justify-content: center; gap: 12px; } /* RTL: پچھلا on the right, اگلا on the left */
|
||||||
.poem-nav a { border: 1.5px solid var(--border); background: var(--inner); border-radius: 10px; padding: 0 14px; }
|
.poem-nav a { border: 1.5px solid var(--border); background: var(--inner); border-radius: 10px; padding: 0 14px; }
|
||||||
.source { text-align: center; font-size: .85rem; color: var(--muted); }
|
.source { text-align: center; font-size: .85rem; color: var(--muted); }
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user