Bookmark icon like Android apps: grey outline when not set, filled brand colour when set
Material Design 'bookmark' / 'bookmark_border' (Apache-2.0) drawn as a CSS mask so it follows the
theme; replaces the 🔖 emoji on bookmark buttons, couplet bookmarks, bookmarked phrases, contents
lists and cards, and the نشانات tab.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2b558ea494
commit
cd404710f0
@ -3,7 +3,7 @@
|
|||||||
const { current } = Astro.props as { current: 'bookmarks' | 'words' };
|
const { current } = Astro.props as { current: 'bookmarks' | 'words' };
|
||||||
---
|
---
|
||||||
<nav class="admin-nav">
|
<nav class="admin-nav">
|
||||||
<a href="/library" aria-current={current === 'bookmarks' ? 'page' : undefined}>🔖 نشانات</a>
|
<a href="/library" aria-current={current === 'bookmarks' ? 'page' : undefined}><span class="bm-icon on" aria-hidden="true"></span> نشانات</a>
|
||||||
<a href="/words" aria-current={current === 'words' ? 'page' : undefined}>ذخیرۂ الفاظ</a>
|
<a href="/words" aria-current={current === 'words' ? 'page' : undefined}>ذخیرۂ الفاظ</a>
|
||||||
<a href="/account">پروفائل</a>
|
<a href="/account">پروفائل</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@ -145,7 +145,7 @@ const fullTitle = title ? `${title} · دیوان` : 'دیوان · اردو ک
|
|||||||
return e;
|
return e;
|
||||||
};
|
};
|
||||||
let current = '';
|
let current = '';
|
||||||
// reader's library: 🔖 bookmarks (poets, books/chapters, poems, couplets, phrases) and the word book (+);
|
// reader's library: bookmarks (poets, books/chapters, poems, couplets, phrases) and the word book (+);
|
||||||
// signed-out readers go to sign-in
|
// signed-out readers go to sign-in
|
||||||
let source = {}; // the couplet the selected word came from
|
let source = {}; // the couplet the selected word came from
|
||||||
async function save(item) {
|
async function save(item) {
|
||||||
|
|||||||
@ -47,13 +47,13 @@ for (const p of page.poems ?? []) {
|
|||||||
groups.at(-1)!.poems.push(p);
|
groups.at(-1)!.poems.push(p);
|
||||||
}
|
}
|
||||||
const crumbs = page.breadcrumbs ?? [];
|
const crumbs = page.breadcrumbs ?? [];
|
||||||
// what the signed-in reader has bookmarked on this page (🔖 poet, book/chapter, poem, couplets, phrases)
|
// what the signed-in reader has bookmarked on this page (poet, book/chapter, poem, couplets, phrases)
|
||||||
const token = Astro.locals.user ? Astro.cookies.get(COOKIE)?.value : undefined;
|
const token = Astro.locals.user ? Astro.cookies.get(COOKIE)?.value : undefined;
|
||||||
const poetId = page.poet?.id, poemId = page.type === 'poem' ? page.poem.id : undefined;
|
const poetId = page.poet?.id, poemId = page.type === 'poem' ? page.poem.id : undefined;
|
||||||
const categoryId = page.type === 'category' ? page.category.id : undefined;
|
const categoryId = page.type === 'category' ? page.category.id : undefined;
|
||||||
const saved = token ? (await asUser(token, `/api/library/state?poet=${poetId ?? ''}&category=${categoryId ?? ''}&poem=${poemId ?? ''}`)).data
|
const saved = token ? (await asUser(token, `/api/library/state?poet=${poetId ?? ''}&category=${categoryId ?? ''}&poem=${poemId ?? ''}`)).data
|
||||||
: { poet: false, category: false, poem: false, couplets: [], phrases: [] };
|
: { poet: false, category: false, poem: false, couplets: [], phrases: [] };
|
||||||
// on contents pages: which works are bookmarked or hold bookmarks (🔖), and which books/chapters do
|
// on contents pages: which works are bookmarked or hold bookmarks, and which books/chapters do
|
||||||
const marks = token && page.type !== 'poem' ? (await asUser(token, '/api/library/marks')).data : { poems: {}, categories: [] };
|
const marks = token && page.type !== 'poem' ? (await asUser(token, '/api/library/marks')).data : { poems: {}, categories: [] };
|
||||||
const glyph = (poemId: number) => (marks.poems[poemId]?.fav ? ' mark-fav' : '') + (marks.poems[poemId]?.bm ? ' mark-bm' : '');
|
const glyph = (poemId: number) => (marks.poems[poemId]?.fav ? ' mark-fav' : '') + (marks.poems[poemId]?.bm ? ' mark-bm' : '');
|
||||||
// the marked part of each line of a block, for its bookmarked phrases
|
// the marked part of each line of a block, for its bookmarked phrases
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// Browser -> API for the 🔖 bookmark buttons and the word book: the reader's session cookie becomes the Bearer token.
|
// Browser -> API for the bookmark buttons and the word book: the reader's session cookie becomes the Bearer token.
|
||||||
// JSON posts from other sites are refused: the Origin must be this site (and SameSite cookies are not sent).
|
// JSON posts from other sites are refused: the Origin must be this site (and SameSite cookies are not sent).
|
||||||
import type { APIRoute } from 'astro';
|
import type { APIRoute } from 'astro';
|
||||||
import { asUser, COOKIE } from '../../lib/auth';
|
import { asUser, COOKIE } from '../../lib/auth';
|
||||||
|
|||||||
@ -33,7 +33,7 @@ const at = (item: any) => (item.poem ? `${item.poem.url}${item.poem.couplet != n
|
|||||||
<p class="muted center">{ud(total)} نشانات</p>
|
<p class="muted center">{ud(total)} نشانات</p>
|
||||||
|
|
||||||
{total === 0 && (
|
{total === 0 && (
|
||||||
<p class="muted center">ابھی کوئی نشان نہیں۔ کسی شاعر، کتاب، باب یا کلام پر 🔖 "نشان" دبائیں، کسی شعر کے ساتھ 🔖 لگائیں، یا کوئی عبارت منتخب کر کے "نشان" لگائیں۔</p>
|
<p class="muted center">ابھی کوئی نشان نہیں۔ کسی شاعر، کتاب، باب یا کلام پر "نشان" دبائیں، کسی شعر کے ساتھ نشان لگائیں، یا کوئی عبارت منتخب کر کے "نشان" لگائیں۔</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{lib.poets.length > 0 && (
|
{lib.poets.length > 0 && (
|
||||||
|
|||||||
@ -214,11 +214,17 @@ h1 + .muted { text-align: center; margin-top: 0; }
|
|||||||
.grant-form legend { font-size: .85rem; padding: 0 6px; }
|
.grant-form legend { font-size: .85rem; padding: 0 6px; }
|
||||||
.grant-form label.check { flex-direction: row; align-items: center; gap: 6px; }
|
.grant-form label.check { flex-direction: row; align-items: center; gap: 6px; }
|
||||||
|
|
||||||
/* bookmarks (🔖): poets, books/chapters, poems, couplets; the word book is separate (+) */
|
/* bookmarks: an Android-style bookmark icon (Material Design 'bookmark', Apache-2.0), grey outline when not set,
|
||||||
|
filled in the brand colour when set; drawn as a mask so it follows the theme. The word book is separate (+). */
|
||||||
|
:root { --bm-outline: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M17 3H7c-1.1 0-2 .9-2 2v16l7-3 7 3V5c0-1.1-.9-2-2-2zm0 15-5-2.18L7 18V5h10v13z'/%3E%3C/svg%3E"); --bm-filled: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M17 3H7c-1.1 0-2 .9-2 2v16l7-3 7 3V5c0-1.1-.9-2-2-2z'/%3E%3C/svg%3E"); }
|
||||||
|
.bm-icon, .save::before, .bm::before, .bmk::after, .mark-fav::after, .mark-bm::after {
|
||||||
|
content: ''; display: inline-block; width: 1.05em; height: 1.05em; vertical-align: -.18em; background-color: var(--muted);
|
||||||
|
-webkit-mask: var(--bm-outline) center / contain no-repeat; mask: var(--bm-outline) center / contain no-repeat; user-select: none; }
|
||||||
|
.save[aria-pressed='true']::before, .bm[aria-pressed='true']::before, .bmk::after, .mark-fav::after, .mark-bm::after, .bm-icon.on {
|
||||||
|
background-color: var(--brand); -webkit-mask-image: var(--bm-filled); mask-image: var(--bm-filled); }
|
||||||
.save { font: inherit; font-size: .85rem; padding: 1px 14px; border: 1.5px solid var(--border); border-radius: 999px; background: var(--inner); color: var(--ink); cursor: pointer; }
|
.save { font: inherit; font-size: .85rem; padding: 1px 14px; border: 1.5px solid var(--border); border-radius: 999px; background: var(--inner); color: var(--ink); cursor: pointer; }
|
||||||
.save::before { content: '🔖 '; filter: grayscale(1); opacity: .6; }
|
.save::before { margin-inline-end: 4px; }
|
||||||
.save[aria-pressed='true'] { border-color: var(--brand); color: var(--brand); }
|
.save[aria-pressed='true'] { border-color: var(--brand); color: var(--brand); }
|
||||||
.save[aria-pressed='true']::before { filter: none; opacity: 1; }
|
|
||||||
/* word book (ذخیرۂ الفاظ): + to add, ✓ when added (hearts are for poets and works) */
|
/* word book (ذخیرۂ الفاظ): + to add, ✓ when added (hearts are for poets and works) */
|
||||||
.wordbook { font: inherit; font-size: .85rem; padding: 1px 14px; margin-bottom: 6px; border: 1.5px solid var(--border); border-radius: 999px; background: var(--inner); color: var(--ink); cursor: pointer; }
|
.wordbook { font: inherit; font-size: .85rem; padding: 1px 14px; margin-bottom: 6px; border: 1.5px solid var(--border); border-radius: 999px; background: var(--inner); color: var(--ink); cursor: pointer; }
|
||||||
.wordbook::before { content: '+ '; font-weight: 700; color: var(--gold); }
|
.wordbook::before { content: '+ '; font-weight: 700; color: var(--gold); }
|
||||||
@ -227,9 +233,8 @@ h1 + .muted { text-align: center; margin-top: 0; }
|
|||||||
.couplet, .poem .para, .poem .single { position: relative; }
|
.couplet, .poem .para, .poem .single { position: relative; }
|
||||||
.bm { position: absolute; inset-inline-end: -26px; top: .5em; width: 20px; height: 22px; padding: 0; border: 0; background: none; cursor: pointer;
|
.bm { position: absolute; inset-inline-end: -26px; top: .5em; width: 20px; height: 22px; padding: 0; border: 0; background: none; cursor: pointer;
|
||||||
opacity: 0; transition: opacity .15s; user-select: none; }
|
opacity: 0; transition: opacity .15s; user-select: none; }
|
||||||
.bm::before { content: '🔖'; font-size: .85rem; filter: grayscale(1); opacity: .6; }
|
.bm::before { width: 18px; height: 18px; }
|
||||||
.bm[aria-pressed='true'] { opacity: 1; }
|
.bm[aria-pressed='true'] { opacity: 1; }
|
||||||
.bm[aria-pressed='true']::before { filter: none; opacity: 1; }
|
|
||||||
.couplet:hover .bm, .poem .para:hover .bm, .poem .single:hover .bm, .bm:focus-visible { opacity: 1; }
|
.couplet:hover .bm, .poem .para:hover .bm, .poem .single:hover .bm, .bm:focus-visible { opacity: 1; }
|
||||||
@media (hover: none) { .bm { opacity: 1; } }
|
@media (hover: none) { .bm { opacity: 1; } }
|
||||||
@media (max-width: 700px) { .bm { inset-inline-end: -4px; top: 0; } }
|
@media (max-width: 700px) { .bm { inset-inline-end: -4px; top: 0; } }
|
||||||
@ -251,8 +256,7 @@ h1 + .muted { text-align: center; margin-top: 0; }
|
|||||||
.lib-remove { position: absolute; left: 8px; top: 8px; margin: 0; }
|
.lib-remove { position: absolute; left: 8px; top: 8px; margin: 0; }
|
||||||
.lib-remove button { color: var(--brand); }
|
.lib-remove button { color: var(--brand); }
|
||||||
|
|
||||||
/* bookmarked phrases, and 🔖 where the reader's bookmarks are */
|
/* bookmarked phrases, and the bookmark icon where the reader's bookmarks are */
|
||||||
.bmk { text-decoration: underline dotted var(--brand) 2px; text-underline-offset: 6px; cursor: pointer; background: var(--gold-subtle); border-radius: 4px; }
|
.bmk { text-decoration: underline dotted var(--brand) 2px; text-underline-offset: 6px; cursor: pointer; background: var(--gold-subtle); border-radius: 4px; }
|
||||||
.bmk::after { content: '🔖'; font-size: .55em; vertical-align: super; margin-inline-start: 1px; user-select: none; }
|
.bmk::after { width: .7em; height: .7em; vertical-align: super; margin-inline-start: 2px; }
|
||||||
.mark-fav::after, .mark-bm::after { font-size: .65em; margin-inline-start: 6px; vertical-align: middle; user-select: none; }
|
.mark-fav::after, .mark-bm::after { width: .85em; height: .85em; margin-inline-start: 6px; vertical-align: -.1em; }
|
||||||
.mark-fav::after, .mark-bm::after { content: '🔖'; }
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user