feat(web): iconify reader chrome, move 连读/预读 into 更多 menu with slider
This commit is contained in:
@@ -31,6 +31,9 @@ The format loosely follows Keep a Changelog and can be adapted to the team's hab
|
|||||||
|
|
||||||
### Changed / 变更
|
### Changed / 变更
|
||||||
|
|
||||||
|
- Reader chrome is no longer text-only: lucide icons accompany 导航/上一章/下一章/翻页/抽屉选项卡/书签操作, while 连读 and 预读 (now a 0–3 slider) collapse into a 更多 menu in the CBZ bar.
|
||||||
|
- 阅读器工具条告别纯文字:导航/上一章/下一章/翻页/抽屉选项卡/书签操作均配 lucide 图标;连读与预读(改为 0–3 滑条)收进 CBZ 工具条的「更多」菜单。
|
||||||
|
|
||||||
- Reader navigation tightened into one left-side drawer opened by a single 导航 toolbar button: 目录 and 书签 are now tabs of a sidebar shared by all four readers, following the reader theme's colors; the button lands on 目录 when a TOC exists, otherwise straight on bookmarks (flat archives, EPUB, PDF show no tab bar); the separate right-side bookmark panel and the per-reader TOC popovers are gone.
|
- Reader navigation tightened into one left-side drawer opened by a single 导航 toolbar button: 目录 and 书签 are now tabs of a sidebar shared by all four readers, following the reader theme's colors; the button lands on 目录 when a TOC exists, otherwise straight on bookmarks (flat archives, EPUB, PDF show no tab bar); the separate right-side bookmark panel and the per-reader TOC popovers are gone.
|
||||||
- 阅读器导航收紧为统一左侧抽屉,工具条只留一个「导航」按钮:目录与书签成为四个阅读器共用侧边栏内的两个选项卡,配色跟随阅读器主题;有目录时默认落在「目录」,否则直达书签(平铺包、EPUB、PDF 不显示选项卡条);原右侧书签面板与各阅读器自绘目录浮层移除。
|
- 阅读器导航收紧为统一左侧抽屉,工具条只留一个「导航」按钮:目录与书签成为四个阅读器共用侧边栏内的两个选项卡,配色跟随阅读器主题;有目录时默认落在「目录」,否则直达书签(平铺包、EPUB、PDF 不显示选项卡条);原右侧书签面板与各阅读器自绘目录浮层移除。
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { Check, Pencil, Plus, X } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { api } from "../api/client";
|
import { api } from "../api/client";
|
||||||
import type { Book } from "../api/types";
|
import type { Book } from "../api/types";
|
||||||
@@ -64,8 +65,13 @@ export function BookmarksTab({
|
|||||||
if (e.key === "Enter" && !add.isPending) add.mutate();
|
if (e.key === "Enter" && !add.isPending) add.mutate();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<button className="rd-btn shrink-0" disabled={add.isPending} onClick={() => add.mutate()}>
|
<button
|
||||||
加
|
className="rd-btn inline-flex shrink-0 items-center gap-1"
|
||||||
|
aria-label="加书签"
|
||||||
|
disabled={add.isPending}
|
||||||
|
onClick={() => add.mutate()}
|
||||||
|
>
|
||||||
|
<Plus size={14} /> 加
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ul className="space-y-0.5">
|
<ul className="space-y-0.5">
|
||||||
@@ -87,14 +93,15 @@ export function BookmarksTab({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="rd-btn shrink-0"
|
className="rd-btn inline-flex shrink-0 items-center"
|
||||||
|
aria-label="保存备注"
|
||||||
disabled={patch.isPending}
|
disabled={patch.isPending}
|
||||||
onClick={() => patch.mutate({ id: b.id, note: editVal.trim() })}
|
onClick={() => patch.mutate({ id: b.id, note: editVal.trim() })}
|
||||||
>
|
>
|
||||||
✓
|
<Check size={14} />
|
||||||
</button>
|
</button>
|
||||||
<button className="rd-btn shrink-0" onClick={() => setEditId(null)}>
|
<button className="rd-btn inline-flex shrink-0 items-center" aria-label="取消编辑" onClick={() => setEditId(null)}>
|
||||||
✕
|
<X size={14} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -115,14 +122,14 @@ export function BookmarksTab({
|
|||||||
setEditVal(b.note);
|
setEditVal(b.note);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
✎
|
<Pencil size={14} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="rd-btn m-0.5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100"
|
className="rd-btn m-0.5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100"
|
||||||
aria-label="删除书签"
|
aria-label="删除书签"
|
||||||
onClick={() => del.mutate(b.id)}
|
onClick={() => del.mutate(b.id)}
|
||||||
>
|
>
|
||||||
✕
|
<X size={14} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Bookmark, List, X } from "lucide-react";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
export type NavTab = "toc" | "bm";
|
export type NavTab = "toc" | "bm";
|
||||||
@@ -35,25 +36,27 @@ export function ReaderNav({
|
|||||||
<button
|
<button
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-selected={tab === "toc"}
|
aria-selected={tab === "toc"}
|
||||||
className={"rd-btn" + (tab === "toc" ? " rd-btn-on" : "")}
|
className={"rd-btn inline-flex items-center gap-1.5" + (tab === "toc" ? " rd-btn-on" : "")}
|
||||||
onClick={() => onTab?.("toc")}
|
onClick={() => onTab?.("toc")}
|
||||||
>
|
>
|
||||||
目录
|
<List size={14} /> 目录
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-selected={tab === "bm"}
|
aria-selected={tab === "bm"}
|
||||||
className={"rd-btn" + (tab === "bm" ? " rd-btn-on" : "")}
|
className={"rd-btn inline-flex items-center gap-1.5" + (tab === "bm" ? " rd-btn-on" : "")}
|
||||||
onClick={() => onTab?.("bm")}
|
onClick={() => onTab?.("bm")}
|
||||||
>
|
>
|
||||||
书签
|
<Bookmark size={14} /> 书签
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<span className="rd-btn rd-btn-on">书签</span>
|
<span className="rd-btn rd-btn-on inline-flex items-center gap-1.5">
|
||||||
|
<Bookmark size={14} /> 书签
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
<button className="rd-btn ml-auto" aria-label="关闭导航" onClick={onClose}>
|
<button className="rd-btn ml-auto" aria-label="关闭导航" onClick={onClose}>
|
||||||
✕
|
<X size={14} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="min-h-0 flex-1 overflow-y-auto p-2">{tab === "toc" ? toc : bookmarks}</div>
|
<div className="min-h-0 flex-1 overflow-y-auto p-2">{tab === "toc" ? toc : bookmarks}</div>
|
||||||
|
|||||||
@@ -131,6 +131,9 @@ body {
|
|||||||
.rd-row { @apply block w-full truncate rounded-md px-2.5 py-1.5 text-left text-sm transition-colors; }
|
.rd-row { @apply block w-full truncate rounded-md px-2.5 py-1.5 text-left text-sm transition-colors; }
|
||||||
.rd-row:hover { background: color-mix(in oklab, currentColor 12%, transparent); }
|
.rd-row:hover { background: color-mix(in oklab, currentColor 12%, transparent); }
|
||||||
.rd-btn-on { @apply font-semibold; background: color-mix(in oklab, currentColor 20%, transparent); }
|
.rd-btn-on { @apply font-semibold; background: color-mix(in oklab, currentColor 20%, transparent); }
|
||||||
|
/* Radix 菜单项高亮(指针/键盘) */
|
||||||
|
.rd-highlight { outline: none; }
|
||||||
|
.rd-highlight[data-highlighted] { background: color-mix(in oklab, currentColor 16%, transparent); }
|
||||||
.rd-divider { border-color: color-mix(in oklab, currentColor 14%, transparent); }
|
.rd-divider { border-color: color-mix(in oklab, currentColor 14%, transparent); }
|
||||||
|
|
||||||
/* 进度/页码滑条:主题色轨道 + 强调色拇指,触摸友好 */
|
/* 进度/页码滑条:主题色轨道 + 强调色拇指,触摸友好 */
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { BookOpenCheck, Check, ChevronLeft, ChevronRight, PanelLeft, Settings2 } from "lucide-react";
|
||||||
|
import { DropdownMenu } from "radix-ui";
|
||||||
import { useEffect, useMemo, useRef, useState, type MouseEvent } from "react";
|
import { useEffect, useMemo, useRef, useState, type MouseEvent } from "react";
|
||||||
import { api, formatPageUrl } from "../api/client";
|
import { api, formatPageUrl } from "../api/client";
|
||||||
import { btn } from "../components/ui";
|
import { btn } from "../components/ui";
|
||||||
@@ -296,7 +298,7 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome
|
|||||||
<span className="text-sm text-stone-500">— 全书完 —</span>
|
<span className="text-sm text-stone-500">— 全书完 —</span>
|
||||||
) : (
|
) : (
|
||||||
<button className={btn} onClick={() => goPage(chapters![ci + 1].start)}>
|
<button className={btn} onClick={() => goPage(chapters![ci + 1].start)}>
|
||||||
本章完 · 下一章 →
|
本章完 · 下一章 <ChevronRight size={14} className="inline" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -340,49 +342,79 @@ export default function CbzReader({ book, initialLocator, initialPercent, chrome
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1 flex items-center gap-2 pb-1 text-sm">
|
<div className="mt-1 flex items-center gap-2 pb-1 text-sm">
|
||||||
{chapters && chapters.length >= 2 && (
|
|
||||||
<button
|
|
||||||
className={"rd-btn" + (continuous ? " rd-btn-on" : "")}
|
|
||||||
aria-pressed={continuous}
|
|
||||||
onClick={() => setContinuous(!continuous)}
|
|
||||||
>
|
|
||||||
连读{continuous ? "·开" : "·关"}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{locked && (
|
|
||||||
<button
|
|
||||||
className="rd-btn"
|
|
||||||
aria-label="预读:锁定章节末尾时提前挂载并解码其后 N 章"
|
|
||||||
onClick={() => setPrefetch((p) => (p + 1) % 4)}
|
|
||||||
>
|
|
||||||
预读 {prefetch} 章
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
className="rd-btn"
|
className="rd-btn inline-flex items-center gap-1.5"
|
||||||
aria-expanded={!!nav}
|
aria-expanded={!!nav}
|
||||||
aria-controls="reader-nav"
|
aria-controls="reader-nav"
|
||||||
onClick={() => setNav(nav ? null : chapters ? "toc" : "bm")}
|
onClick={() => setNav(nav ? null : chapters ? "toc" : "bm")}
|
||||||
>
|
>
|
||||||
导航
|
<PanelLeft size={14} /> 导航
|
||||||
</button>
|
</button>
|
||||||
{chapters && (
|
{chapters && (
|
||||||
<>
|
<>
|
||||||
<span className="text-xs tabular-nums opacity-60">
|
<span className="text-xs tabular-nums opacity-60">
|
||||||
{ci + 1}/{chapters.length}
|
{ci + 1}/{chapters.length}
|
||||||
</span>
|
</span>
|
||||||
<button className="rd-btn" disabled={ci === 0} onClick={() => goPage(chapters[ci - 1].start)}>
|
<button
|
||||||
← 上一章
|
className="rd-btn inline-flex items-center gap-0.5"
|
||||||
|
disabled={ci === 0}
|
||||||
|
onClick={() => goPage(chapters[ci - 1].start)}
|
||||||
|
>
|
||||||
|
<ChevronLeft size={14} /> 上一章
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="rd-btn"
|
className="rd-btn inline-flex items-center gap-0.5"
|
||||||
disabled={ci === chapters.length - 1}
|
disabled={ci === chapters.length - 1}
|
||||||
onClick={() => goPage(chapters[ci + 1].start)}
|
onClick={() => goPage(chapters[ci + 1].start)}
|
||||||
>
|
>
|
||||||
下一章 →
|
下一章 <ChevronRight size={14} />
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{chapters && chapters.length >= 2 && (
|
||||||
|
<DropdownMenu.Root>
|
||||||
|
<DropdownMenu.Trigger className="rd-btn ml-auto inline-flex items-center gap-1.5" aria-label="更多阅读选项">
|
||||||
|
<Settings2 size={14} /> 更多
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
<DropdownMenu.Portal>
|
||||||
|
<DropdownMenu.Content
|
||||||
|
align="end"
|
||||||
|
side="top"
|
||||||
|
className="rd-divider z-50 min-w-44 rounded-md border bg-[var(--rd-bg,#171512)] p-1 shadow-2xl"
|
||||||
|
style={{ color: "var(--rd-fg,#c6c0b6)" }}
|
||||||
|
>
|
||||||
|
<DropdownMenu.CheckboxItem
|
||||||
|
className="rd-row rd-highlight cursor-pointer"
|
||||||
|
style={{ display: "flex", alignItems: "center", gap: 8 }}
|
||||||
|
checked={continuous}
|
||||||
|
onCheckedChange={(v) => setContinuous(!!v)}
|
||||||
|
>
|
||||||
|
<BookOpenCheck size={14} /> 连读(滑动跨章)
|
||||||
|
<DropdownMenu.ItemIndicator style={{ marginLeft: "auto" }}>
|
||||||
|
<Check size={14} />
|
||||||
|
</DropdownMenu.ItemIndicator>
|
||||||
|
</DropdownMenu.CheckboxItem>
|
||||||
|
{locked && (
|
||||||
|
<div className="px-2.5 pb-1.5 pt-2">
|
||||||
|
<div className="mb-1 text-xs opacity-60">
|
||||||
|
预读:锁章末尾时提前挂载其后 N 章 · 当前 {prefetch}
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min={0}
|
||||||
|
max={3}
|
||||||
|
step={1}
|
||||||
|
value={prefetch}
|
||||||
|
aria-label="预读章数"
|
||||||
|
className="rd-range"
|
||||||
|
onChange={(e) => setPrefetch(Number(e.target.value))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu.Root>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ChevronLeft, ChevronRight, PanelLeft } from "lucide-react";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { apiRaw } from "../api/client";
|
import { apiRaw } from "../api/client";
|
||||||
import { BookmarksTab } from "../components/Bookmarks";
|
import { BookmarksTab } from "../components/Bookmarks";
|
||||||
@@ -100,14 +101,19 @@ export default function EpubReader({ book, initialLocator, initialPercent }: Rea
|
|||||||
{!ready && !err && <div className="grid h-full place-items-center bg-stone-950 text-stone-500">EPUB 加载中…</div>}
|
{!ready && !err && <div className="grid h-full place-items-center bg-stone-950 text-stone-500">EPUB 加载中…</div>}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-center gap-3 border-t border-stone-800 px-4 py-2 text-sm">
|
<div className="flex items-center justify-center gap-3 border-t border-stone-800 px-4 py-2 text-sm">
|
||||||
<button className={btn} onClick={() => rendRef.current?.prev()}>
|
<button className={btn + " inline-flex items-center gap-0.5"} onClick={() => rendRef.current?.prev()}>
|
||||||
上一页
|
<ChevronLeft size={14} /> 上一页
|
||||||
</button>
|
</button>
|
||||||
<button className={btn} onClick={() => rendRef.current?.next()}>
|
<button className={btn + " inline-flex items-center gap-0.5"} onClick={() => rendRef.current?.next()}>
|
||||||
下一页
|
下一页 <ChevronRight size={14} />
|
||||||
</button>
|
</button>
|
||||||
<button className="rd-btn" aria-expanded={nav} aria-controls="reader-nav" onClick={() => setNav((v) => !v)}>
|
<button
|
||||||
导航
|
className="rd-btn inline-flex items-center gap-1.5"
|
||||||
|
aria-expanded={nav}
|
||||||
|
aria-controls="reader-nav"
|
||||||
|
onClick={() => setNav((v) => !v)}
|
||||||
|
>
|
||||||
|
<PanelLeft size={14} /> 导航
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{nav && (
|
{nav && (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, PanelLeft } from "lucide-react";
|
||||||
import { useEffect, useRef, useState, type KeyboardEvent } from "react";
|
import { useEffect, useRef, useState, type KeyboardEvent } from "react";
|
||||||
import * as pdfjs from "pdfjs-dist";
|
import * as pdfjs from "pdfjs-dist";
|
||||||
import { apiRaw } from "../api/client";
|
import { apiRaw } from "../api/client";
|
||||||
@@ -139,23 +140,28 @@ export default function PdfReader({ book, initialLocator, initialPercent }: Read
|
|||||||
<canvas ref={canvasRef} className="max-w-3xl rounded-md shadow-2xl ring-1 ring-stone-700/50" />
|
<canvas ref={canvasRef} className="max-w-3xl rounded-md shadow-2xl ring-1 ring-stone-700/50" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-center gap-2 border-t border-stone-800/80 px-4 py-2 text-sm">
|
<div className="flex items-center justify-center gap-2 border-t border-stone-800/80 px-4 py-2 text-sm">
|
||||||
<button className={btn} disabled={page === 0} onClick={() => setPage(0)} aria-label="第一页">
|
<button className={btn + " inline-flex items-center gap-0.5"} disabled={page === 0} onClick={() => setPage(0)} aria-label="第一页">
|
||||||
首页
|
<ChevronsLeft size={14} /> 首页
|
||||||
</button>
|
</button>
|
||||||
<button className={btn} disabled={page === 0} onClick={() => setPage((p) => Math.max(0, p - 1))}>
|
<button className={btn + " inline-flex items-center gap-0.5"} disabled={page === 0} onClick={() => setPage((p) => Math.max(0, p - 1))}>
|
||||||
← 上一页
|
<ChevronLeft size={14} /> 上一页
|
||||||
</button>
|
</button>
|
||||||
<span className="min-w-16 text-center tabular-nums text-stone-400">
|
<span className="min-w-16 text-center tabular-nums text-stone-400">
|
||||||
{page + 1}/{num}
|
{page + 1}/{num}
|
||||||
</span>
|
</span>
|
||||||
<button className={btn} disabled={page >= num - 1} onClick={() => setPage((p) => Math.min(num - 1, p + 1))}>
|
<button className={btn + " inline-flex items-center gap-0.5"} disabled={page >= num - 1} onClick={() => setPage((p) => Math.min(num - 1, p + 1))}>
|
||||||
下一页 →
|
下一页 <ChevronRight size={14} />
|
||||||
</button>
|
</button>
|
||||||
<button className={btn} disabled={page >= num - 1} onClick={() => setPage(num - 1)} aria-label="最后一页">
|
<button className={btn + " inline-flex items-center gap-0.5"} disabled={page >= num - 1} onClick={() => setPage(num - 1)} aria-label="最后一页">
|
||||||
末页
|
<ChevronsRight size={14} /> 末页
|
||||||
</button>
|
</button>
|
||||||
<button className="rd-btn" aria-expanded={nav} aria-controls="reader-nav" onClick={() => setNav((v) => !v)}>
|
<button
|
||||||
导航
|
className="rd-btn inline-flex items-center gap-1.5"
|
||||||
|
aria-expanded={nav}
|
||||||
|
aria-controls="reader-nav"
|
||||||
|
onClick={() => setNav((v) => !v)}
|
||||||
|
>
|
||||||
|
<PanelLeft size={14} /> 导航
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{nav && (
|
{nav && (
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import DOMPurify from "dompurify";
|
import DOMPurify from "dompurify";
|
||||||
import { marked } from "marked";
|
import { marked } from "marked";
|
||||||
|
import { ChevronLeft, ChevronRight, PanelLeft } from "lucide-react";
|
||||||
import { useEffect, useMemo, useRef, useState, type MouseEvent, type ReactNode } from "react";
|
import { useEffect, useMemo, useRef, useState, type MouseEvent, type ReactNode } from "react";
|
||||||
import { apiRaw } from "../api/client";
|
import { apiRaw } from "../api/client";
|
||||||
import { BookmarksTab } from "../components/Bookmarks";
|
import { BookmarksTab } from "../components/Bookmarks";
|
||||||
@@ -187,12 +188,12 @@ function MdView({ book, initialLocator, initialPercent, chrome }: ReaderProps) {
|
|||||||
{chrome.on && (
|
{chrome.on && (
|
||||||
<ReaderSheet pr={pr} frac={frac} onSeek={seek}>
|
<ReaderSheet pr={pr} frac={frac} onSeek={seek}>
|
||||||
<button
|
<button
|
||||||
className="rd-btn"
|
className="rd-btn inline-flex items-center gap-1.5"
|
||||||
aria-expanded={nav}
|
aria-expanded={nav}
|
||||||
aria-controls="reader-nav"
|
aria-controls="reader-nav"
|
||||||
onClick={() => setNav((v) => !v)}
|
onClick={() => setNav((v) => !v)}
|
||||||
>
|
>
|
||||||
导航
|
<PanelLeft size={14} /> 导航
|
||||||
</button>
|
</button>
|
||||||
</ReaderSheet>
|
</ReaderSheet>
|
||||||
)}
|
)}
|
||||||
@@ -310,23 +311,31 @@ function TxtView({ book, initialLocator, initialPercent, chrome }: ReaderProps)
|
|||||||
{chrome.on && (
|
{chrome.on && (
|
||||||
<ReaderSheet pr={pr} frac={total} onSeek={seek}>
|
<ReaderSheet pr={pr} frac={total} onSeek={seek}>
|
||||||
<button
|
<button
|
||||||
className="rd-btn"
|
className="rd-btn inline-flex items-center gap-1.5"
|
||||||
aria-expanded={!!nav}
|
aria-expanded={!!nav}
|
||||||
aria-controls="reader-nav"
|
aria-controls="reader-nav"
|
||||||
onClick={() => setNav(nav ? null : chapters.length > 1 ? "toc" : "bm")}
|
onClick={() => setNav(nav ? null : chapters.length > 1 ? "toc" : "bm")}
|
||||||
>
|
>
|
||||||
导航
|
<PanelLeft size={14} /> 导航
|
||||||
</button>
|
</button>
|
||||||
{chapters.length > 1 && (
|
{chapters.length > 1 && (
|
||||||
<>
|
<>
|
||||||
<span className="text-xs opacity-60 tabular-nums">
|
<span className="text-xs opacity-60 tabular-nums">
|
||||||
{ci + 1}/{chapters.length}
|
{ci + 1}/{chapters.length}
|
||||||
</span>
|
</span>
|
||||||
<button className="rd-btn" disabled={ci === 0} onClick={() => goChapter(ci - 1)}>
|
<button
|
||||||
← 上一章
|
className="rd-btn inline-flex items-center gap-0.5"
|
||||||
|
disabled={ci === 0}
|
||||||
|
onClick={() => goChapter(ci - 1)}
|
||||||
|
>
|
||||||
|
<ChevronLeft size={14} /> 上一章
|
||||||
</button>
|
</button>
|
||||||
<button className="rd-btn" disabled={ci === chapters.length - 1} onClick={() => goChapter(ci + 1)}>
|
<button
|
||||||
下一章 →
|
className="rd-btn inline-flex items-center gap-0.5"
|
||||||
|
disabled={ci === chapters.length - 1}
|
||||||
|
onClick={() => goChapter(ci + 1)}
|
||||||
|
>
|
||||||
|
下一章 <ChevronRight size={14} />
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user