mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 17:27:08 +08:00
This commit is contained in:
1 parent
dd6818178c
commit
a29fbbd31b
3 files changed
+100
-29
No files matched your search
@@ -16,24 +16,24 @@
|
||||
<input id="fontInput" class="file-input" type="file" accept=".ttf,.otf,.ttc,font/*" multiple>
|
||||
<input id="fontDirectoryInput" class="file-input" type="file" webkitdirectory multiple>
|
||||
<div class="tool-group">
|
||||
<button id="ofdButton" class="icon-button" type="button" title="打开" aria-label="打开"><span class="open-icon" aria-hidden="true"></span></button>
|
||||
<button id="ofdButton" class="icon-button" type="button" title="打开" aria-label="打开" aria-keyshortcuts="Control+O Meta+O"><span class="open-icon" aria-hidden="true"></span></button>
|
||||
<button id="togglePagesButton" class="icon-button" type="button" title="页面栏" aria-label="页面栏" aria-pressed="true">☰</button>
|
||||
<button id="toggleMetaButton" class="icon-button" type="button" title="信息栏" aria-label="信息栏" aria-pressed="true">ⓘ</button>
|
||||
</div>
|
||||
<div class="toolbar-space"></div>
|
||||
<div class="tool-group">
|
||||
<button id="prevButton" class="icon-button arrow-prev" type="button" title="上一页" aria-label="上一页" disabled></button>
|
||||
<button id="prevButton" class="icon-button arrow-prev" type="button" title="上一页" aria-label="上一页" aria-keyshortcuts="ArrowLeft" disabled></button>
|
||||
<label class="page-control">
|
||||
<input id="pageInput" type="number" min="1" value="0" disabled>
|
||||
<span>/</span>
|
||||
<span id="pageTotal">0</span>
|
||||
</label>
|
||||
<button id="nextButton" class="icon-button arrow-next" type="button" title="下一页" aria-label="下一页" disabled></button>
|
||||
<button id="nextButton" class="icon-button arrow-next" type="button" title="下一页" aria-label="下一页" aria-keyshortcuts="ArrowRight" disabled></button>
|
||||
</div>
|
||||
<div class="tool-group">
|
||||
<button id="zoomOutButton" class="icon-button" type="button" title="缩小" disabled>-</button>
|
||||
<button id="zoomOutButton" class="icon-button" type="button" title="缩小" aria-label="缩小" aria-keyshortcuts="-" disabled>-</button>
|
||||
<span id="zoomLabel" class="zoom-label">100%</span>
|
||||
<button id="zoomInButton" class="icon-button" type="button" title="放大" disabled>+</button>
|
||||
<button id="zoomInButton" class="icon-button" type="button" title="放大" aria-label="放大" aria-keyshortcuts="= Shift+= Plus" disabled>+</button>
|
||||
<button id="fitButton" class="button" type="button" data-short="宽" aria-label="适宽" disabled>适宽</button>
|
||||
<button id="fitHeightButton" class="button" type="button" data-short="高" aria-label="适高" disabled>适高</button>
|
||||
<button id="rotateButton" class="button" type="button" data-short="转" title="顺时针旋转90°" aria-label="旋转" disabled>旋转</button>
|
||||
@@ -60,7 +60,7 @@
|
||||
<div id="navigationTabs" class="navigation-tabs" role="tablist" aria-label="文档导航" hidden>
|
||||
<button id="pagesTab" type="button" role="tab" aria-selected="true" aria-controls="pageList">页面</button>
|
||||
<button id="outlinesTab" type="button" role="tab" aria-selected="false" aria-controls="outlineList" tabindex="-1">目录</button>
|
||||
<button id="searchTab" type="button" role="tab" aria-selected="false" aria-controls="searchPanel" tabindex="-1">搜索</button>
|
||||
<button id="searchTab" type="button" role="tab" aria-keyshortcuts="Control+F Meta+F" aria-selected="false" aria-controls="searchPanel" tabindex="-1">搜索</button>
|
||||
</div>
|
||||
<div id="pageList" class="page-list" role="tabpanel" aria-label="页面"></div>
|
||||
<div id="outlineList" role="tabpanel" aria-label="目录" hidden></div>
|
||||
@@ -72,14 +72,14 @@
|
||||
<div class="search-tools">
|
||||
<span id="searchStatus" role="status" aria-live="polite">输入文字</span>
|
||||
<span id="searchCount">0 / 0</span>
|
||||
<button id="searchPrev" type="button" class="icon-button arrow-prev" title="上一处" aria-label="上一处" disabled></button>
|
||||
<button id="searchNext" type="button" class="icon-button arrow-next" title="下一处" aria-label="下一处" disabled></button>
|
||||
<button id="searchPrev" type="button" class="icon-button arrow-prev" title="上一处" aria-label="上一处" aria-keyshortcuts="Shift+F3" disabled></button>
|
||||
<button id="searchNext" type="button" class="icon-button arrow-next" title="下一处" aria-label="下一处" aria-keyshortcuts="F3" disabled></button>
|
||||
</div>
|
||||
<div id="searchResults" class="search-results"></div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<section class="viewer-panel" tabindex="0">
|
||||
<section class="viewer-panel" tabindex="0" aria-label="阅读区">
|
||||
<div id="emptyState" class="empty-state">选择 OFD 文件</div>
|
||||
<div id="progressPanel" class="progress-panel" role="status" aria-live="polite" hidden>
|
||||
<div class="progress-spinner"></div>
|
||||
|
||||
@@ -659,6 +659,11 @@ button.outline-link:hover {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.viewer-panel:focus-visible {
|
||||
outline: 2px solid var(--focus);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.viewer-panel.pan-mode,
|
||||
.pan-mode .text-run > span {
|
||||
cursor: grab;
|
||||
|
||||
+86
-20
@@ -154,11 +154,11 @@ el.togglePagesButton.addEventListener("click", () => toggleSidebar("pages"));
|
||||
el.toggleMetaButton.addEventListener("click", () => toggleSidebar("meta"));
|
||||
el.pagesTab.addEventListener("click", () => showNavigation(el.pagesTab));
|
||||
el.outlinesTab.addEventListener("click", () => showNavigation(el.outlinesTab));
|
||||
el.searchTab.addEventListener("click", () => {
|
||||
showNavigation(el.searchTab);
|
||||
el.searchInput.focus({ preventScroll: true });
|
||||
});
|
||||
el.searchTab.addEventListener("click", focusSearch);
|
||||
el.navigationTabs.addEventListener("keydown", (event) => {
|
||||
if (event.isComposing || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
|
||||
return;
|
||||
}
|
||||
if (["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) {
|
||||
event.preventDefault();
|
||||
const tabs = [el.pagesTab, el.outlinesTab, el.searchTab].filter((tab) => !tab.hidden);
|
||||
@@ -176,21 +176,7 @@ el.searchForm.addEventListener("submit", (event) => {
|
||||
el.searchInput.addEventListener("input", () => resetSearch(false));
|
||||
el.searchPrev.addEventListener("click", () => selectSearchMatch(Math.max(0, state.searchIndex) - 1));
|
||||
el.searchNext.addEventListener("click", () => selectSearchMatch(state.searchIndex + 1));
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "f" && state.doc) {
|
||||
event.preventDefault();
|
||||
if (!state.showPages) {
|
||||
toggleSidebar("pages");
|
||||
}
|
||||
showNavigation(el.searchTab);
|
||||
el.searchInput.focus({ preventScroll: true });
|
||||
el.searchInput.select();
|
||||
} else if (event.key === "Escape" && !el.searchPanel.hidden) {
|
||||
resetSearch();
|
||||
showNavigation(el.pagesTab);
|
||||
el.pagesTab.focus();
|
||||
}
|
||||
});
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
el.fontAddButton.addEventListener("click", () => openFontFile(el.fontInput));
|
||||
el.fontDirectoryButton.addEventListener("click", () => openFontFile(el.fontDirectoryInput));
|
||||
el.localFontButton.addEventListener("click", loadLocalFonts);
|
||||
@@ -267,6 +253,74 @@ el.fontDirectoryButton.disabled = !("webkitdirectory" in el.fontDirectoryInput);
|
||||
updateSidebarState();
|
||||
boot();
|
||||
|
||||
function handleKeyDown(event) {
|
||||
if (event.defaultPrevented || event.isComposing || event.altKey || document.body.hasAttribute("aria-busy")) {
|
||||
return;
|
||||
}
|
||||
const key = event.key;
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
if (!event.shiftKey && key.toLowerCase() === "o") {
|
||||
event.preventDefault();
|
||||
openOFDFile();
|
||||
} else if (!event.shiftKey && key.toLowerCase() === "f" && state.doc) {
|
||||
event.preventDefault();
|
||||
focusSearch();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!state.doc) {
|
||||
return;
|
||||
}
|
||||
const target = event.target;
|
||||
if (key === "F3") {
|
||||
event.preventDefault();
|
||||
if (state.searchMatches.length) {
|
||||
selectSearchMatch(event.shiftKey ? Math.max(0, state.searchIndex) - 1 : state.searchIndex + 1);
|
||||
} else {
|
||||
focusSearch();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (key === "Escape" && !event.shiftKey && !el.searchPanel.hidden && (el.pageListPanel.contains(target) || el.viewerPanel.contains(target))) {
|
||||
event.preventDefault();
|
||||
resetSearch();
|
||||
showNavigation(el.pagesTab);
|
||||
if (COMPACT_LAYOUT.matches && state.showPages) {
|
||||
state.showPages = false;
|
||||
updateSidebarState();
|
||||
}
|
||||
el.viewerPanel.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
if (key === "Enter" && target === el.pageInput && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
el.viewerPanel.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
if (key === "Enter" && target === el.searchInput && event.shiftKey && state.searchMatches.length) {
|
||||
event.preventDefault();
|
||||
selectSearchMatch(Math.max(0, state.searchIndex) - 1);
|
||||
return;
|
||||
}
|
||||
if (!el.viewerPanel.contains(target) || target.closest("input, textarea, select, button, a, [contenteditable]")) {
|
||||
return;
|
||||
}
|
||||
let button;
|
||||
if (key === "+" || (key === "=" && !event.shiftKey)) {
|
||||
button = el.zoomInButton;
|
||||
} else if (key === "-" && !event.shiftKey) {
|
||||
button = el.zoomOutButton;
|
||||
} else if (!event.shiftKey && (key === "ArrowLeft" || key === "ArrowRight")) {
|
||||
if (document.getSelection().isCollapsed && el.viewerPanel.scrollWidth <= el.viewerPanel.clientWidth) {
|
||||
button = key === "ArrowLeft" ? el.prevButton : el.nextButton;
|
||||
}
|
||||
}
|
||||
if (button) {
|
||||
event.preventDefault();
|
||||
button.click();
|
||||
}
|
||||
}
|
||||
|
||||
async function openOFDFile() {
|
||||
if (document.body.hasAttribute("aria-busy")) {
|
||||
return;
|
||||
@@ -1320,7 +1374,10 @@ async function openDocument(options = {}) {
|
||||
}
|
||||
applyFit(false);
|
||||
await nextFrame();
|
||||
await renderPage(pageIndex, { keepBusy: true, scroll: false, openSeq });
|
||||
const page = await renderPage(pageIndex, { keepBusy: true, scroll: false, openSeq });
|
||||
if (page && options.resetScroll) {
|
||||
el.viewerPanel.focus({ preventScroll: true });
|
||||
}
|
||||
queueNearbyPages(pageIndex, openSeq);
|
||||
loadDocumentDetails(openSeq);
|
||||
} catch (err) {
|
||||
@@ -2042,6 +2099,15 @@ function prefixSVGIds(svg, prefix) {
|
||||
}
|
||||
}
|
||||
|
||||
function focusSearch() {
|
||||
if (!state.showPages) {
|
||||
toggleSidebar("pages");
|
||||
}
|
||||
showNavigation(el.searchTab);
|
||||
el.searchInput.focus({ preventScroll: true });
|
||||
el.searchInput.select();
|
||||
}
|
||||
|
||||
function showNavigation(selected) {
|
||||
for (const [tab, panel] of [[el.pagesTab, el.pageList], [el.outlinesTab, el.outlineList], [el.searchTab, el.searchPanel]]) {
|
||||
const active = tab === selected;
|
||||
|
||||
Reference in new issue
Block a user