@knaw-huc/osd-iiif-viewer 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/ControlSlice.d.ts +2 -0
- package/dist/Viewer.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +55 -44
- package/dist/useViewerControls.d.ts +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,19 +12,19 @@ Current features (WIP):
|
|
|
12
12
|
```tsx
|
|
13
13
|
import {useEffect} from 'react';
|
|
14
14
|
import {
|
|
15
|
+
Viewer,
|
|
15
16
|
ViewerProvider,
|
|
16
|
-
ViewerCanvas,
|
|
17
17
|
useLoadManifest,
|
|
18
18
|
useManifest,
|
|
19
19
|
} from '@knaw-huc/osd-iiif-viewer';
|
|
20
20
|
|
|
21
21
|
export function Example() {
|
|
22
22
|
return <ViewerProvider>
|
|
23
|
-
<
|
|
23
|
+
<View/>
|
|
24
24
|
</ViewerProvider>
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
function
|
|
27
|
+
function View() {
|
|
28
28
|
const loadManifest = useLoadManifest();
|
|
29
29
|
const manifest = useManifest();
|
|
30
30
|
|
|
@@ -38,7 +38,7 @@ function Viewer() {
|
|
|
38
38
|
|
|
39
39
|
return (
|
|
40
40
|
<div style={{width: '100%', height: '100vh'}}>
|
|
41
|
-
<
|
|
41
|
+
<Viewerer/>
|
|
42
42
|
</div>
|
|
43
43
|
);
|
|
44
44
|
}
|
package/dist/ControlSlice.d.ts
CHANGED
|
@@ -5,10 +5,12 @@ export type ControlSlice = {
|
|
|
5
5
|
zoomMin: number | null;
|
|
6
6
|
zoomMax: number | null;
|
|
7
7
|
isFullPage: boolean;
|
|
8
|
+
rotation: number;
|
|
8
9
|
setZoomLevel: (zoomLevel: number | null) => void;
|
|
9
10
|
setZoomMin: (zoomMin: number | null) => void;
|
|
10
11
|
setZoomMax: (zoomMax: number | null) => void;
|
|
11
12
|
setIsFullPage: (isFullPage: boolean) => void;
|
|
13
|
+
setRotation: (rotation: number) => void;
|
|
12
14
|
};
|
|
13
15
|
export declare const createControlSlice: StateCreator<ViewerStore, [
|
|
14
16
|
], [
|
package/dist/Viewer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
type
|
|
1
|
+
type ViewerProps = {
|
|
2
2
|
showControls?: boolean;
|
|
3
3
|
};
|
|
4
|
-
export declare function Viewer({ showControls }:
|
|
4
|
+
export declare function Viewer({ showControls }: ViewerProps): import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
export { Viewer } from './Viewer.tsx';
|
|
1
2
|
export { useImageInfo } from './useImageInfo.tsx';
|
|
2
3
|
export { Overlay } from './Overlay.tsx';
|
|
3
|
-
export { Viewer } from './Viewer.tsx';
|
|
4
4
|
export { ViewerProvider } from './ViewerProvider';
|
|
5
5
|
export { useViewer } from './useViewer';
|
|
6
6
|
export { useViewerControls } from './useViewerControls';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useStore } from "zustand/index";
|
|
2
|
-
import { createContext, useContext, useMemo, useState, useEffect, useRef, useCallback } from "react";
|
|
3
|
-
import { createStore } from "zustand/vanilla";
|
|
4
|
-
import { createPortal } from "react-dom";
|
|
5
1
|
import { jsx } from "react/jsx-runtime";
|
|
6
2
|
import { Viewer as Viewer$1 } from "openseadragon";
|
|
3
|
+
import { createContext, useContext, useRef, useEffect, useMemo, useState, useCallback } from "react";
|
|
4
|
+
import { createStore } from "zustand/vanilla";
|
|
5
|
+
import { useStore } from "zustand/index";
|
|
6
|
+
import { createPortal } from "react-dom";
|
|
7
7
|
const createInstanceSlice = (set) => ({
|
|
8
8
|
viewer: null,
|
|
9
9
|
viewerReady: !1,
|
|
@@ -14,10 +14,12 @@ const createInstanceSlice = (set) => ({
|
|
|
14
14
|
zoomMin: null,
|
|
15
15
|
zoomMax: null,
|
|
16
16
|
isFullPage: !1,
|
|
17
|
+
rotation: 0,
|
|
17
18
|
setZoomLevel: (zoomLevel) => set({ zoomLevel }),
|
|
18
19
|
setZoomMin: (zoomMin) => set({ zoomMin }),
|
|
19
20
|
setZoomMax: (zoomMax) => set({ zoomMax }),
|
|
20
|
-
setIsFullPage: (isFullPage) => set({ isFullPage })
|
|
21
|
+
setIsFullPage: (isFullPage) => set({ isFullPage }),
|
|
22
|
+
setRotation: (rotation) => set({ rotation })
|
|
21
23
|
}), defaultLanguage = "en";
|
|
22
24
|
function findLanguage(translations, lang = defaultLanguage) {
|
|
23
25
|
if (!translations || typeof translations != "object")
|
|
@@ -154,43 +156,13 @@ const defaultManifest = { data: null, url: "", isLoading: !1, error: null }, cre
|
|
|
154
156
|
function useViewerStore() {
|
|
155
157
|
return useContext(ViewerContext) ?? orThrow("viewer hook must be used within viewer provider");
|
|
156
158
|
}
|
|
159
|
+
function findTileSource(canvas) {
|
|
160
|
+
return canvas.imageServiceId + "/info.json";
|
|
161
|
+
}
|
|
157
162
|
function useViewerStoreSelector(selector) {
|
|
158
163
|
const store = useViewerStore();
|
|
159
164
|
return useStore(store, selector);
|
|
160
165
|
}
|
|
161
|
-
function useViewer() {
|
|
162
|
-
return useViewerStoreSelector((s) => s.viewer);
|
|
163
|
-
}
|
|
164
|
-
function useViewerReady() {
|
|
165
|
-
return useViewerStoreSelector((s) => s.viewerReady);
|
|
166
|
-
}
|
|
167
|
-
function useImageInfo() {
|
|
168
|
-
const viewer = useViewer(), ready = useViewerReady();
|
|
169
|
-
return useMemo(() => {
|
|
170
|
-
if (!viewer || !ready)
|
|
171
|
-
return null;
|
|
172
|
-
const item = viewer.world.getItemAt(0);
|
|
173
|
-
if (!item)
|
|
174
|
-
return null;
|
|
175
|
-
const size = item.getContentSize();
|
|
176
|
-
return { location: item.imageToViewportRectangle(0, 0, size.x, size.y), size };
|
|
177
|
-
}, [viewer, ready]);
|
|
178
|
-
}
|
|
179
|
-
function Overlay({ location, children }) {
|
|
180
|
-
const viewer = useViewer(), [container] = useState(() => {
|
|
181
|
-
const el = document.createElement("div");
|
|
182
|
-
return el.style.position = "absolute", el.style.pointerEvents = "none", el;
|
|
183
|
-
});
|
|
184
|
-
return useEffect(() => {
|
|
185
|
-
if (viewer)
|
|
186
|
-
return viewer.addOverlay({ element: container, location }), () => {
|
|
187
|
-
viewer.removeOverlay(container);
|
|
188
|
-
};
|
|
189
|
-
}, [viewer, location, container]), viewer ? createPortal(children, container) : null;
|
|
190
|
-
}
|
|
191
|
-
function findTileSource(canvas) {
|
|
192
|
-
return canvas.imageServiceId + "/info.json";
|
|
193
|
-
}
|
|
194
166
|
function useCanvas() {
|
|
195
167
|
const { currentIndex } = useViewerStoreSelector((s) => s.canvas), goTo = useViewerStoreSelector((s) => s.goToCanvas), next = useViewerStoreSelector((s) => s.nextCanvas), prev = useViewerStoreSelector((s) => s.prevCanvas), current = useViewerStoreSelector(
|
|
196
168
|
(s) => s.manifest.data?.canvases[s.canvas.currentIndex] ?? null
|
|
@@ -213,15 +185,17 @@ function Viewer({ showControls = !0 }) {
|
|
|
213
185
|
});
|
|
214
186
|
store.getState().setViewer(viewer), viewer.addOnceHandler("open", () => {
|
|
215
187
|
const state2 = store.getState();
|
|
216
|
-
state2.setViewerReady(!0), state2.setZoomLevel(viewer.viewport.getZoom()), state2.setZoomMin(viewer.viewport.getMinZoom()), state2.setZoomMax(viewer.viewport.getMaxZoom());
|
|
188
|
+
state2.setViewerReady(!0), state2.setZoomLevel(viewer.viewport.getZoom()), state2.setZoomMin(viewer.viewport.getMinZoom()), state2.setZoomMax(viewer.viewport.getMaxZoom()), state2.setRotation(viewer.viewport.getRotation());
|
|
217
189
|
});
|
|
218
190
|
const onZoom = () => {
|
|
219
191
|
store.getState().setZoomLevel(viewer.viewport.getZoom());
|
|
192
|
+
}, onRotate = () => {
|
|
193
|
+
store.getState().setRotation(viewer.viewport.getRotation());
|
|
220
194
|
};
|
|
221
|
-
return viewer.addHandler("animation-start", onZoom), viewer.addHandler("animation-finish", onZoom), () => {
|
|
222
|
-
viewer.removeHandler("animation-start", onZoom), viewer.removeHandler("animation-finish", onZoom), viewer.destroy();
|
|
195
|
+
return viewer.addHandler("animation-start", onZoom), viewer.addHandler("animation-finish", onZoom), viewer.addHandler("rotate", onRotate), () => {
|
|
196
|
+
viewer.removeHandler("animation-start", onZoom), viewer.removeHandler("animation-finish", onZoom), viewer.removeHandler("rotate", onRotate), viewer.destroy();
|
|
223
197
|
const state2 = store.getState();
|
|
224
|
-
state2.setViewer(null), state2.setViewerReady(!1), state2.setZoomLevel(null), state2.setZoomMin(null), state2.setZoomMax(null), state2.setIsFullPage(!1);
|
|
198
|
+
state2.setViewer(null), state2.setViewerReady(!1), state2.setZoomLevel(null), state2.setZoomMin(null), state2.setZoomMax(null), state2.setIsFullPage(!1), state2.setRotation(0);
|
|
225
199
|
};
|
|
226
200
|
}, [tileSource, showControls, store]), /* @__PURE__ */ jsx(
|
|
227
201
|
"div",
|
|
@@ -232,12 +206,42 @@ function Viewer({ showControls = !0 }) {
|
|
|
232
206
|
}
|
|
233
207
|
);
|
|
234
208
|
}
|
|
209
|
+
function useViewer() {
|
|
210
|
+
return useViewerStoreSelector((s) => s.viewer);
|
|
211
|
+
}
|
|
212
|
+
function useViewerReady() {
|
|
213
|
+
return useViewerStoreSelector((s) => s.viewerReady);
|
|
214
|
+
}
|
|
215
|
+
function useImageInfo() {
|
|
216
|
+
const viewer = useViewer(), ready = useViewerReady();
|
|
217
|
+
return useMemo(() => {
|
|
218
|
+
if (!viewer || !ready)
|
|
219
|
+
return null;
|
|
220
|
+
const item = viewer.world.getItemAt(0);
|
|
221
|
+
if (!item)
|
|
222
|
+
return null;
|
|
223
|
+
const size = item.getContentSize();
|
|
224
|
+
return { location: item.imageToViewportRectangle(0, 0, size.x, size.y), size };
|
|
225
|
+
}, [viewer, ready]);
|
|
226
|
+
}
|
|
227
|
+
function Overlay({ location, children }) {
|
|
228
|
+
const viewer = useViewer(), [container] = useState(() => {
|
|
229
|
+
const el = document.createElement("div");
|
|
230
|
+
return el.style.position = "absolute", el.style.pointerEvents = "none", el;
|
|
231
|
+
});
|
|
232
|
+
return useEffect(() => {
|
|
233
|
+
if (viewer)
|
|
234
|
+
return viewer.addOverlay({ element: container, location }), () => {
|
|
235
|
+
viewer.removeOverlay(container);
|
|
236
|
+
};
|
|
237
|
+
}, [viewer, location, container]), viewer ? createPortal(children, container) : null;
|
|
238
|
+
}
|
|
235
239
|
function ViewerProvider({ children }) {
|
|
236
240
|
const [store] = useState(() => createViewerStore());
|
|
237
241
|
return /* @__PURE__ */ jsx(ViewerContext.Provider, { value: store, children });
|
|
238
242
|
}
|
|
239
243
|
function useViewerControls(fullscreenRef) {
|
|
240
|
-
const viewer = useViewer(), store = useViewerStore(), zoomLevel = useViewerStoreSelector((s) => s.zoomLevel), zoomMin = useViewerStoreSelector((s) => s.zoomMin), zoomMax = useViewerStoreSelector((s) => s.zoomMax), isFullPage = useViewerStoreSelector((s) => s.isFullPage);
|
|
244
|
+
const viewer = useViewer(), store = useViewerStore(), zoomLevel = useViewerStoreSelector((s) => s.zoomLevel), zoomMin = useViewerStoreSelector((s) => s.zoomMin), zoomMax = useViewerStoreSelector((s) => s.zoomMax), isFullPage = useViewerStoreSelector((s) => s.isFullPage), rotation = useViewerStoreSelector((s) => s.rotation);
|
|
241
245
|
useEffect(() => {
|
|
242
246
|
const onFullscreenChange = () => {
|
|
243
247
|
store.getState().setIsFullPage(!!document.fullscreenElement);
|
|
@@ -253,6 +257,11 @@ function useViewerControls(fullscreenRef) {
|
|
|
253
257
|
viewer?.viewport.zoomBy(0.667);
|
|
254
258
|
}, [viewer]), home = useCallback(() => {
|
|
255
259
|
viewer?.viewport.goHome();
|
|
260
|
+
}, [viewer]), rotateRight = useCallback(() => {
|
|
261
|
+
if (!viewer)
|
|
262
|
+
return;
|
|
263
|
+
const rotationUpdate = viewer.viewport.getRotation() + 90;
|
|
264
|
+
viewer.viewport.setRotation(rotationUpdate);
|
|
256
265
|
}, [viewer]), toggleFullPage = useCallback(() => {
|
|
257
266
|
const container = fullscreenRef?.current;
|
|
258
267
|
container && (document.fullscreenElement ? document.exitFullscreen() : container.requestFullscreen());
|
|
@@ -261,11 +270,13 @@ function useViewerControls(fullscreenRef) {
|
|
|
261
270
|
zoomIn,
|
|
262
271
|
zoomOut,
|
|
263
272
|
home,
|
|
273
|
+
rotateRight,
|
|
264
274
|
toggleFullPage,
|
|
265
275
|
zoomLevel,
|
|
266
276
|
zoomMin,
|
|
267
277
|
zoomMax,
|
|
268
|
-
isFullPage
|
|
278
|
+
isFullPage,
|
|
279
|
+
rotation
|
|
269
280
|
};
|
|
270
281
|
}
|
|
271
282
|
function useManifest() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
2
|
/**
|
|
3
|
-
* Expose zoom and full-screen controls and state
|
|
3
|
+
* Expose zoom, rotation and full-screen controls and state
|
|
4
4
|
*
|
|
5
5
|
* Note: Fullscreen is handled via the native Fullscreen API,
|
|
6
6
|
* skipping setFullScreen/setFullPage which moves
|
|
@@ -11,9 +11,11 @@ export declare function useViewerControls(fullscreenRef: RefObject<HTMLElement |
|
|
|
11
11
|
zoomIn: () => void;
|
|
12
12
|
zoomOut: () => void;
|
|
13
13
|
home: () => void;
|
|
14
|
+
rotateRight: () => void;
|
|
14
15
|
toggleFullPage: () => void;
|
|
15
16
|
zoomLevel: number | null;
|
|
16
17
|
zoomMin: number | null;
|
|
17
18
|
zoomMax: number | null;
|
|
18
19
|
isFullPage: boolean;
|
|
20
|
+
rotation: number;
|
|
19
21
|
};
|