+
+ {this.state.showNavigator && this.state.viewerReady && (
+
+ )}
{
this.state.selectedAnno && this.state.selectedAnno.body &&
diff --git a/src/components/AdnoNavigator/AdnoNavigator.css b/src/components/AdnoNavigator/AdnoNavigator.css
new file mode 100644
index 0000000..16a315e
--- /dev/null
+++ b/src/components/AdnoNavigator/AdnoNavigator.css
@@ -0,0 +1,27 @@
+/* AdnoNavigator.css */
+.adno-navigator {
+ position: absolute;
+ cursor: crosshair;
+ border-radius: 4px;
+ border: 1px solid rgba(255, 255, 255, 0.25);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
+ z-index: 100;
+ background: #111;
+}
+
+.adno-navigator--bottom-right {
+ bottom: 12px;
+ right: 12px;
+}
+
+.adno-navigator--bottom-center {
+ bottom: 0;
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.adno-navigator--right-vertical {
+ right: 12px;
+ top: calc(50% + 58px);
+ transform: translateY(-50%);
+}
\ No newline at end of file
diff --git a/src/components/AdnoNavigator/AdnoNavigator.js b/src/components/AdnoNavigator/AdnoNavigator.js
new file mode 100644
index 0000000..fb4d374
--- /dev/null
+++ b/src/components/AdnoNavigator/AdnoNavigator.js
@@ -0,0 +1,161 @@
+import { Component, createRef } from "react";
+import "./AdnoNavigator.css";
+
+class AdnoNavigator extends Component {
+ constructor(props) {
+ super(props);
+ this.canvasRef = createRef();
+ this.isDragging = false;
+ this._bgImage = null;
+ this._timer = null;
+ }
+
+ componentDidMount() {
+ const { viewer } = this.props;
+
+ this.loadThumbnail();
+ this._timer = setTimeout(() => this.draw(), 500);
+ viewer.addHandler('animation', this.draw);
+ viewer.addHandler('resize', this.draw);
+ }
+
+ componentWillUnmount() {
+ const { viewer } = this.props;
+
+ clearTimeout(this._timer);
+ viewer.removeHandler('animation', this.draw);
+ viewer.removeHandler('resize', this.draw);
+ }
+
+ loadThumbnail = () => {
+ const { imgUrl } = this.props;
+ if (!imgUrl) return;
+
+ this._bgImage = new Image();
+ this._bgImage.crossOrigin = 'anonymous';
+ this._bgImage.src = imgUrl.endsWith('/info.json')
+ ? `${imgUrl.replace('/info.json', '')}/full/!512,512/0/default.jpg`
+ : imgUrl;
+ this._bgImage.onload = () => this.draw();
+ }
+
+ draw = () => {
+ const { viewer } = this.props;
+ const canvas = this.canvasRef.current;
+ if (!canvas) return;
+
+ const ctx = canvas.getContext('2d');
+ const W = canvas.width;
+ const H = canvas.height;
+
+ ctx.clearRect(0, 0, W, H);
+
+ const item = viewer.world.getItemAt(0);
+ if (!item) return;
+
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.85)';
+ ctx.fillRect(0, 0, W, H);
+
+ if (this._bgImage?.complete && this._bgImage.naturalWidth) {
+ ctx.drawImage(this._bgImage, 0, 0, W, H);
+ }
+
+ this.drawViewportRect(ctx, item, W, H);
+ }
+
+ drawViewportRect = (ctx, item, W, H) => {
+ const { viewer } = this.props;
+ const imgBounds = item.getBounds();
+ const viewBounds = viewer.viewport.getBounds(true);
+
+ const toCanvas = (x, y) => ({
+ x: ((x - imgBounds.x) / imgBounds.width) * W,
+ y: ((y - imgBounds.y) / imgBounds.height) * H,
+ });
+
+ const tl = toCanvas(viewBounds.x, viewBounds.y);
+ const br = toCanvas(
+ viewBounds.x + viewBounds.width,
+ viewBounds.y + viewBounds.height
+ );
+
+ const rx = Math.max(0, tl.x);
+ const ry = Math.max(0, tl.y);
+ const rw = Math.min(W, br.x) - rx;
+ const rh = Math.min(H, br.y) - ry;
+
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
+ ctx.fillRect(0, 0, W, ry);
+ ctx.fillRect(0, ry + rh, W, H - ry - rh);
+ ctx.fillRect(0, ry, rx, rh);
+ ctx.fillRect(rx + rw, ry, W - rx - rw, rh);
+
+ ctx.strokeStyle = 'rgba(255, 220, 50, 0.9)';
+ ctx.lineWidth = 2;
+ ctx.strokeRect(rx, ry, rw, rh);
+ }
+
+ panTo = (e) => {
+ const { viewer } = this.props;
+ const canvas = this.canvasRef.current;
+ const item = viewer.world.getItemAt(0);
+ if (!canvas || !item) return;
+
+ const rect = canvas.getBoundingClientRect();
+ const imgBounds = item.getBounds();
+
+ const vpX = imgBounds.x + ((e.clientX - rect.left) / canvas.width) * imgBounds.width;
+ const vpY = imgBounds.y + ((e.clientY - rect.top) / canvas.height) * imgBounds.height;
+
+ viewer.viewport.panTo(new OpenSeadragon.Point(vpX, vpY), false);
+ }
+
+ handleMouseDown = (e) => { this.isDragging = true; this.panTo(e); }
+ handleMouseMove = (e) => { if (this.isDragging) this.panTo(e); }
+ handleMouseUp = () => { this.isDragging = false; }
+
+ computeSize = () => {
+ const { viewer, imageRatio, layout } = this.props;
+ const container = viewer?.container;
+ const cw = container?.clientWidth || 800;
+ const ch = container?.clientHeight || 600;
+
+ const fit = (w, h, maxW, maxH) => {
+ if (h > maxH) { h = Math.round(maxH); w = Math.round(h / imageRatio); }
+ if (w > maxW) { w = Math.round(maxW); h = Math.round(w * imageRatio); }
+ return { width: w, height: h };
+ };
+
+ if (layout === 'bottom-center') {
+ const w = Math.round(cw * 0.5);
+ return fit(w, Math.round(w * imageRatio), cw * 0.5, ch * 0.4);
+ }
+ if (layout === 'right-vertical') {
+ const h = Math.round(ch * 0.8);
+ return fit(Math.round(h / imageRatio), h, cw * 0.2, ch * 0.8);
+ }
+ const w = Math.round(cw * 0.25);
+ return fit(w, Math.round(w * imageRatio), cw * 0.25, ch * 0.3);
+ }
+
+ render() {
+ const { layout } = this.props;
+ const { width, height } = this.computeSize();
+
+ return (
+
+ );
+ }
+}
+
+export default AdnoNavigator;
+
diff --git a/src/components/OpenView/OpenView.js b/src/components/OpenView/OpenView.js
index f3bdddc..ca86aef 100644
--- a/src/components/OpenView/OpenView.js
+++ b/src/components/OpenView/OpenView.js
@@ -2,18 +2,15 @@ import { Component } from "react";
import { withRouter } from "react-router-dom";
import parse from 'html-react-parser';
-// Import FontAwesome
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-
import { faMagnifyingGlassMinus, faPlay, faPause, faEye, faEyeSlash, faArrowRight, faArrowLeft, faExpand, faRotate, faQuestion, faVolumeOff, faVolumeHigh, faCircleInfo, faExternalLink } from "@fortawesome/free-solid-svg-icons";
-
-// Import utils
import { getEye } from "../../Utils/utils";
-// Import CSS
import "./OpenView.css";
import { withTranslation } from "react-i18next";
+import AdnoNavigator from '../AdnoNavigator/AdnoNavigator'
+
class OpenView extends Component {
constructor(props) {
super(props);
@@ -25,7 +22,11 @@ class OpenView extends Component {
isAnnotationsVisible: true,
currentTrack: undefined,
soundMode: this.props.soundMode,
- audioContexts: []
+ audioContexts: [],
+
+ imageRatio: null,
+ navigatorLayout: null,
+ viewerReady: false
}
}
@@ -53,13 +54,31 @@ class OpenView extends Component {
this.openSeadragon = OpenSeadragon({
id: 'adno-osd',
homeButton: "home-button",
- showNavigator: this.props.showNavigator,
+ // showNavigator: this.props.showNavigator,
+ showNavigator: false,
tileSources: tileSources,
prefixUrl: 'https://openseadragon.github.io/openseadragon/images/',
crossOriginPolicy: 'Anonymous',
ajaxWithCredentials: false
})
+ this.openSeadragon.addOnceHandler('open', () => {
+ const item = this.openSeadragon.world.getItemAt(0);
+ if (item) {
+ const size = item.getContentSize();
+ const ratio = size.y / size.x;
+ let layout = 'bottom-right';
+ if (ratio < 0.30) layout = 'bottom-center';
+ else if (ratio > 3.33) layout = 'right-vertical';
+
+ this.setState({
+ imageRatio: ratio,
+ navigatorLayout: layout,
+ viewerReady: true // 👈 maintenant OSD existe vraiment
+ });
+ }
+ });
+
OpenSeadragon.setString("Tooltips.FullPage", this.props.t('editor.fullpage'));
OpenSeadragon.setString("Tooltips.Home", this.props.t('editor.home'));
OpenSeadragon.setString("Tooltips.ZoomIn", this.props.t('editor.zoom_in'));
@@ -624,15 +643,6 @@ class OpenView extends Component {
if (prevProps.showEyes !== this.props.showEyes)
setTimeout(this.freeMode, 1000)
- // Check if the user toggled the navigator on/off
- if (this.props.showNavigator !== prevProps.showNavigator && this.openSeadragon?.navigator) {
- if (this.props.showNavigator) {
- document.getElementById(this.openSeadragon.navigator.id).style.display = 'block';
- } else {
- document.getElementById(this.openSeadragon.navigator.id).style.display = 'none';
- }
-
- }
}
}
@@ -746,6 +756,14 @@ class OpenView extends Component {
return (
+ {this.props.showNavigator && this.openSeadragon && this.state.viewerReady && (
+
+ )}
{
this.state.fullScreenEnabled && this.props.selectedAnno && this.props.selectedAnno.body &&
this.getAnnotationHTMLBody(this.props.selectedAnno)
diff --git a/src/components/Project/Project.js b/src/components/Project/Project.js
index 55fb53a..c7eac95 100644
--- a/src/components/Project/Project.js
+++ b/src/components/Project/Project.js
@@ -128,6 +128,8 @@ const Project = ({ editMode }) => {
})
: annotations;
+ console.log(settings)
+
if (!state.selectedProject)
return
{
}))}
changeSelectedAnno={(anno) => setState(prev => ({ ...prev, selectedAnnotation: anno }))}
rotation={settings.rotation}
+ showNavigator={settings.showNavigator}
/>
) : (