Skip to content

Commit c03487f

Browse files
authored
feat: image zoom/pan GUI, was in navigator, now in Thorium ... styling and l10n needs fixing, this is initial implementation (PR #2786)
1 parent ec81d73 commit c03487f

File tree

14 files changed

+282
-86
lines changed

14 files changed

+282
-86
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ external-assets/*
1212
.flox/lib
1313
.flox/NPM_PREFIX
1414
.flox/NPM_CACHE
15+
.env

package-lock.json

Lines changed: 53 additions & 38 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@
297297
"pdf.js": "github:edrlab/pdf.js#build",
298298
"proxy-agent": "^6.5.0",
299299
"r2-lcp-js": "^1.0.43",
300-
"r2-navigator-js": "^1.17.5",
300+
"r2-navigator-js": "^1.18.0",
301301
"r2-opds-js": "^1.0.46",
302302
"r2-shared-js": "^1.0.81",
303303
"r2-streamer-js": "^1.0.50",
@@ -312,6 +312,7 @@
312312
"react-router": "^6.29.0",
313313
"react-router-dom": "^6.29.0",
314314
"react-table": "^7.8.0",
315+
"react-zoom-pan-pinch": "^3.7.0",
315316
"readium-speech": "github:readium/speech#build",
316317
"redux": "^5.0.1",
317318
"redux-first-history": "^5.2.0",
@@ -327,7 +328,7 @@
327328
"typed-redux-saga": "^1.5.0",
328329
"urijs": "^1.19.11",
329330
"use-sync-external-store": "^1.4.0",
330-
"uuid": "^11.0.5",
331+
"uuid": "^11.1.0",
331332
"validator": "^13.12.0",
332333
"yargs": "^17.7.2",
333334
"yauzl": "^3.2.0",
@@ -397,7 +398,7 @@
397398
"mini-css-extract-plugin": "^2.9.2",
398399
"ncp": "^2.0.0",
399400
"npm-scripts-lifecycle": "^1.0.0",
400-
"prettier": "^3.5.1",
401+
"prettier": "^3.5.2",
401402
"remote-redux-devtools": "^0.5.16",
402403
"rimraf": "^6.0.1",
403404
"sass": "^1.85.0",

src/common/redux/states/renderer/readerRootState.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { ITTSState } from "readium-desktop/renderer/reader/redux/state/tts";
2424
import { IMediaOverlayState } from "readium-desktop/renderer/reader/redux/state/mediaOverlay";
2525
import { IAllowCustomConfigState } from "readium-desktop/renderer/reader/redux/state/allowCustom";
2626
import { ICacheDocument } from "./resourceCache";
27+
import { IImageClickState } from "readium-desktop/renderer/reader/redux/state/imageClick";
2728

2829
export interface IReaderRootState extends IRendererCommonRootState {
2930
reader: IReaderStateReader;
@@ -33,6 +34,7 @@ export interface IReaderRootState extends IRendererCommonRootState {
3334
mode: ReaderMode;
3435
annotation: IAnnotationModeState;
3536
annotationTagsIndex: TAnnotationTagsIndex;
37+
img: IImageClickState;
3638
}
3739

3840
export interface IReaderStateReader {

src/renderer/reader/components/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import Reader from "./Reader";
3131
import { getTranslator } from "readium-desktop/common/services/translator";
3232
import { getStore } from "../createStore";
3333
import { TranslatorContext } from "readium-desktop/renderer/common/translator.context";
34+
import { ImageClickManager } from "./ImageClickManager";
3435

3536
export default class App extends React.Component<{}, undefined> {
3637

@@ -210,6 +211,7 @@ url("${rcssPath}/fonts/iAWriterDuospace-Regular.ttf") format("truetype");
210211
<TranslatorContext.Provider value={getTranslator()}>
211212
<Reader />
212213
<ToastManager />
214+
<ImageClickManager />
213215
</TranslatorContext.Provider>
214216
</Provider>
215217
);
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// ==LICENSE-BEGIN==
2+
// Copyright 2017 European Digital Reading Lab. All rights reserved.
3+
// Licensed to the Readium Foundation under one or more contributor license agreements.
4+
// Use of this source code is governed by a BSD-style license
5+
// that can be found in the LICENSE file exposed on Github (readium) in the project repository.
6+
// ==LICENSE-END==
7+
8+
import * as React from "react";
9+
import { IReaderRootState } from "readium-desktop/common/redux/states/renderer/readerRootState";
10+
import { useSelector } from "readium-desktop/renderer/common/hooks/useSelector";
11+
12+
import * as stylesModals from "readium-desktop/renderer/assets/styles/components/modals.scss";
13+
import * as stylesButtons from "readium-desktop/renderer/assets/styles/components/buttons.scss";
14+
15+
import * as Dialog from "@radix-ui/react-dialog";
16+
import { useDispatch } from "readium-desktop/renderer/common/hooks/useDispatch";
17+
import { readerLocalActionSetImageClick } from "../redux/actions";
18+
import classNames from "classnames";
19+
20+
import SVG from "readium-desktop/renderer/common/components/SVG";
21+
import * as QuitIcon from "readium-desktop/renderer/assets/icons/baseline-close-24px.svg";
22+
23+
import { TransformWrapper, TransformComponent, useControls } from "react-zoom-pan-pinch";
24+
import { useTranslator } from "readium-desktop/renderer/common/hooks/useTranslator";
25+
26+
27+
const Controls = () => {
28+
const { zoomIn, zoomOut, resetTransform } = useControls();
29+
30+
return (
31+
<div style={{position: "absolute", display: "flex", flexDirection: "column", zIndex: 105, top: "42%", right: "10px"}}>
32+
<button className={stylesButtons.button_nav_primary} onClick={() => zoomIn()}>+</button>
33+
<button className={stylesButtons.button_nav_primary} onClick={() => zoomOut()}>-</button>
34+
<button className={stylesButtons.button_nav_primary} onClick={() => resetTransform()}>x</button>
35+
</div>
36+
);
37+
};
38+
39+
export const ImageClickManager: React.FC = () => {
40+
41+
const { open, isSVGFragment, HTMLImgSrc_SVGImageHref_SVGFragmentMarkup, altAttributeOf_HTMLImg_SVGImage_SVGFragment, titleAttributeOf_HTMLImg_SVGImage_SVGFragment, ariaLabelAttributeOf_HTMLImg_SVGImage_SVGFragment, naturalWidthOf_HTMLImg_SVGImage, naturalHeightOf_HTMLImg_SVGImage } = useSelector((state: IReaderRootState) => state.img);
42+
const dispatch = useDispatch();
43+
const [__] = useTranslator();
44+
45+
const scaleX = naturalWidthOf_HTMLImg_SVGImage ? ((window.innerHeight - 50) / naturalWidthOf_HTMLImg_SVGImage) : 1;
46+
const scaleY = naturalHeightOf_HTMLImg_SVGImage ? ((window.innerWidth - 50) / naturalHeightOf_HTMLImg_SVGImage) : 1;
47+
let scale = Math.min(scaleX, scaleY);
48+
if (scale > 1) scale = 1;
49+
50+
return (<>
51+
52+
<Dialog.Root open={open} onOpenChange={(openState: boolean) => {
53+
if (openState == false) {
54+
dispatch(readerLocalActionSetImageClick.build());
55+
}
56+
}}
57+
>
58+
<Dialog.Portal>
59+
<div className={stylesModals.modal_dialog_overlay}></div>
60+
<Dialog.Content style={{ width: "100%", height: "100%", backgroundColor: "unset", border: "unset" }} className={classNames(stylesModals.modal_dialog)} aria-describedby={undefined}>
61+
<Dialog.Close asChild>
62+
<button style={{position: "absolute", top: "10px", right: "10px", zIndex: 105}} className={stylesButtons.button_transparency_icon} aria-label={__("accessibility.closeDialog")}>
63+
<SVG ariaHidden={true} svg={QuitIcon} />
64+
</button>
65+
</Dialog.Close>
66+
<TransformWrapper initialScale={scale} minScale={scale / 2} maxScale={4 * scale}>
67+
<Controls />
68+
<TransformComponent wrapperStyle={{ width: "100%", height: "100%", minHeight: "inherit" }} >
69+
<img
70+
style={{ width: "100%", height: "100%", backgroundColor: "white", color: "black", fill: "currentcolor", stroke: "currentcolor" }}
71+
src={isSVGFragment ? ("data:image/svg+xml;base64," + Buffer.from(HTMLImgSrc_SVGImageHref_SVGFragmentMarkup).toString("base64")) : HTMLImgSrc_SVGImageHref_SVGFragmentMarkup}
72+
alt={altAttributeOf_HTMLImg_SVGImage_SVGFragment}
73+
title={titleAttributeOf_HTMLImg_SVGImage_SVGFragment}
74+
aria-label={ariaLabelAttributeOf_HTMLImg_SVGImage_SVGFragment}
75+
tabIndex={0}
76+
/>
77+
</TransformComponent>
78+
</TransformWrapper>
79+
</Dialog.Content>
80+
</Dialog.Portal>
81+
</Dialog.Root>
82+
</>);
83+
};

0 commit comments

Comments
 (0)