@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 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
- <Viewer/>
23
+ <View/>
24
24
  </ViewerProvider>
25
25
  }
26
26
 
27
- function Viewer() {
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
- <ViewerCanvas/>
41
+ <Viewerer/>
42
42
  </div>
43
43
  );
44
44
  }
@@ -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 ViewerCanvasProps = {
1
+ type ViewerProps = {
2
2
  showControls?: boolean;
3
3
  };
4
- export declare function Viewer({ showControls }: ViewerCanvasProps): import("react/jsx-runtime").JSX.Element;
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knaw-huc/osd-iiif-viewer",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/knaw-huc/osd-iiif-viewer.git"