File size: 3,229 Bytes
cf9842a
eec7490
80f9a97
eec7490
 
6736e17
 
 
 
eec7490
 
 
6736e17
eec7490
 
1d5cfba
 
eec7490
 
 
 
 
 
 
 
 
 
 
 
6736e17
eec7490
 
 
 
 
 
 
 
 
 
6736e17
cf9842a
 
 
 
6736e17
eec7490
 
6736e17
 
17c4592
 
 
 
6736e17
 
 
 
 
 
 
eec7490
 
1d5cfba
eec7490
17c4592
 
 
 
6736e17
eec7490
 
80f9a97
eec7490
80f9a97
 
 
eec7490
 
 
 
80f9a97
 
 
eec7490
80f9a97
eec7490
80f9a97
 
 
 
eec7490
80f9a97
eec7490
80f9a97
 
 
 
eec7490
80f9a97
eec7490
80f9a97
 
eec7490
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { useEffect, useMemo, useRef, type FC } from 'react'
import OpenSeadragon from 'openseadragon'
import { RetroButton } from './retro'

interface Props {
  /** URL du IIIF Image Service (zoom tuilé natif) */
  iiifServiceUrl?: string | null
  /** URL image statique (fallback si pas de service IIIF) */
  fallbackImageUrl?: string | null
  onViewerReady?: (viewer: OpenSeadragon.Viewer) => void
}

const Viewer: FC<Props> = ({ iiifServiceUrl, fallbackImageUrl, onViewerReady }) => {
  const containerRef = useRef<HTMLDivElement>(null)
  const viewerRef = useRef<OpenSeadragon.Viewer | null>(null)
  const onViewerReadyRef = useRef(onViewerReady)
  onViewerReadyRef.current = onViewerReady

  useEffect(() => {
    if (!containerRef.current) return

    const viewer = OpenSeadragon({
      element: containerRef.current,
      showNavigationControl: false,
      gestureSettingsMouse: { clickToZoom: false, scrollToZoom: true, dragToPan: true },
      gestureSettingsTouch: { scrollToZoom: true, dragToPan: true, pinchToZoom: true },
      animationTime: 0.3,
      minZoomLevel: 0.1,
      maxZoomLevel: 20,
      crossOriginPolicy: 'Anonymous',
    })

    viewerRef.current = viewer

    return () => {
      viewer.destroy()
      viewerRef.current = null
    }
  }, [])

  // Source à ouvrir : préférer le service IIIF (zoom tuilé), sinon image statique
  const source = useMemo(
    () => iiifServiceUrl || fallbackImageUrl || '',
    [iiifServiceUrl, fallbackImageUrl]
  )

  useEffect(() => {
    const viewer = viewerRef.current
    if (!viewer || !source) return

    // Remove previous handlers to avoid accumulation on rapid page changes
    viewer.removeAllHandlers('open')
    viewer.removeAllHandlers('open-failed')

    if (iiifServiceUrl) {
      // Zoom tuilé natif — OpenSeadragon fetch info.json et configure les tuiles
      viewer.open(iiifServiceUrl + '/info.json')
    } else {
      // Image statique simple (pas de zoom tuilé)
      viewer.open({ type: 'image', url: source })
    }

    viewer.addOnceHandler('open', () => {
      onViewerReadyRef.current?.(viewer)
    })

    viewer.addOnceHandler('open-failed', () => {
      console.warn('[Viewer] Failed to open image source:', source)
    })
  }, [source, iiifServiceUrl])

  return (
    <div className="relative w-full h-full bg-retro-black">
      <div ref={containerRef} className="w-full h-full" />
      <div className="absolute bottom-2 right-2 flex gap-[2px]">
        <RetroButton
          size="sm"
          onClick={() => viewerRef.current?.viewport.zoomBy(1.5)}
          title="Zoom +"
        >
          +
        </RetroButton>
        <RetroButton
          size="sm"
          onClick={() => viewerRef.current?.viewport.zoomBy(0.67)}
          title="Zoom -"
        >
          -
        </RetroButton>
        <RetroButton
          size="sm"
          onClick={() => viewerRef.current?.viewport.goHome()}
          title="Reset"
        >
          o
        </RetroButton>
        <RetroButton
          size="sm"
          onClick={() => viewerRef.current?.setFullScreen(true)}
          title="Plein ecran"
        >
          []
        </RetroButton>
      </div>
    </div>
  )
}

export default Viewer