@knaw-huc/osd-iiif-viewer 0.1.6 → 0.1.7

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
@@ -1,16 +1,17 @@
1
1
  # @knaw-huc/osd-iiif-viewer
2
2
 
3
- React wrapper around OpenSeadragon to display and browse image sources from iiif manifests.
3
+ Lightweight React wrapper around OpenSeadragon to display and browse image sources from IIIF manifests.
4
4
 
5
- Current features (WIP):
5
+ Features:
6
6
  - Canvas navigation
7
7
  - Highlight overlay
8
8
  - Zoom and fullscreen controls
9
- - Manifest parsing (iiif manifests v3, ImageService2)
9
+ - IIIF manifest loading
10
+
11
+ ## Example
10
12
 
11
- ## Minimal example
12
13
  ```tsx
13
- import { useEffect } from 'react';
14
+ import {useEffect} from 'react';
14
15
  import {
15
16
  Viewer,
16
17
  ViewerProvider,
@@ -20,27 +21,28 @@ import {
20
21
 
21
22
  export function Example() {
22
23
  return <ViewerProvider>
23
- <View/>
24
+ <MyOsdIiifView/>
24
25
  </ViewerProvider>
25
26
  }
26
27
 
27
- function View() {
28
+ function MyOsdIiifView() {
28
29
  const loadManifest = useLoadManifest();
29
- const manifest = useManifest();
30
+ const {isReady} = useManifest();
30
31
 
31
32
  useEffect(() => {
32
33
  loadManifest('/manifest.json');
33
34
  }, [loadManifest]);
34
35
 
35
- if (manifest.isLoading || manifest.error) {
36
+ if (!isReady) {
36
37
  return null;
37
38
  }
38
39
 
39
40
  return (
40
- <div style={{ width: '100%', height: '100vh' }}>
41
+ <div style={{width: '100%', height: '100vh'}}>
41
42
  <Viewer/>
42
43
  </div>
43
44
  );
44
45
  }
45
46
  ```
46
47
 
48
+ For more examples, run `npm i && npm start` and open http://localhost:5173
@@ -1,11 +1,14 @@
1
1
  import { StateCreator } from 'zustand/vanilla';
2
2
  import { ViewerStore } from './ViewerStore.ts';
3
- export type ControlSlice = {
3
+ import { Resettable } from './Resettable.ts';
4
+ export type ControlState = {
4
5
  zoomLevel: number | null;
5
6
  zoomMin: number | null;
6
7
  zoomMax: number | null;
7
8
  isFullPage: boolean;
8
9
  rotation: number;
10
+ };
11
+ export type ControlSlice = Resettable & ControlState & {
9
12
  setZoomLevel: (zoomLevel: number | null) => void;
10
13
  setZoomMin: (zoomMin: number | null) => void;
11
14
  setZoomMax: (zoomMax: number | null) => void;
@@ -1,9 +1,12 @@
1
1
  import { StateCreator } from 'zustand/vanilla';
2
2
  import { default as OpenSeadragon } from 'openseadragon';
3
3
  import { ViewerStore } from './ViewerStore.ts';
4
- export type InstanceSlice = {
4
+ import { Resettable } from './Resettable.ts';
5
+ export type InstanceState = {
5
6
  viewer: OpenSeadragon.Viewer | null;
6
7
  viewerReady: boolean;
8
+ };
9
+ export type InstanceSlice = Resettable & InstanceState & {
7
10
  setViewer: (viewer: OpenSeadragon.Viewer | null) => void;
8
11
  setViewerReady: (viewerReady: boolean) => void;
9
12
  };
@@ -0,0 +1,3 @@
1
+ export type Resettable = {
2
+ reset: () => void;
3
+ };
@@ -2,5 +2,6 @@ import { InstanceSlice } from './InstanceSlice.ts';
2
2
  import { ControlSlice } from './ControlSlice.ts';
3
3
  import { ManifestSlice } from './manifest/ManifestSlice.ts';
4
4
  import { CanvasSlice } from './manifest/CanvasSlice.ts';
5
- export type ViewerStore = InstanceSlice & ControlSlice & ManifestSlice & CanvasSlice;
5
+ import { Resettable } from './Resettable.ts';
6
+ export type ViewerStore = InstanceSlice & ControlSlice & ManifestSlice & CanvasSlice & Resettable;
6
7
  export declare const createViewerStore: () => import('zustand').StoreApi<ViewerStore>;
package/dist/index.d.ts CHANGED
@@ -12,4 +12,3 @@ export { useVault } from './manifest/useVault';
12
12
  export { findTileSource } from './manifest/findTileSource';
13
13
  export { findThumbnail } from './manifest/findThumbnail';
14
14
  export { getImageServiceId } from './manifest/getImageServiceId';
15
- export { getAnnotationPageIds } from './manifest/getAnnotationPageIds.ts';
package/dist/index.js CHANGED
@@ -1774,60 +1774,73 @@ var E = class {
1774
1774
  this.dispatch(typeof r2 == "function" ? i2.setMetaValueDynamic({ id: e2, meta: t3, key: n2, updateValue: r2 }) : i2.setMetaValue({ id: e2, meta: t3, key: n2, value: r2 }));
1775
1775
  }
1776
1776
  };
1777
- const createInstanceSlice = (set) => ({
1777
+ const defaultState$1 = {
1778
1778
  viewer: null,
1779
- viewerReady: !1,
1779
+ viewerReady: !1
1780
+ }, createInstanceSlice = (set) => ({
1781
+ ...defaultState$1,
1780
1782
  setViewer: (viewer) => set({ viewer }),
1781
- setViewerReady: (viewerReady) => set({ viewerReady })
1782
- }), createControlSlice = (set) => ({
1783
+ setViewerReady: (viewerReady) => set({ viewerReady }),
1784
+ reset: () => set(defaultState$1)
1785
+ }), defaultState = {
1783
1786
  zoomLevel: null,
1784
1787
  zoomMin: null,
1785
1788
  zoomMax: null,
1786
1789
  isFullPage: !1,
1787
- rotation: 0,
1790
+ rotation: 0
1791
+ }, createControlSlice = (set) => ({
1792
+ ...defaultState,
1788
1793
  setZoomLevel: (zoomLevel) => set({ zoomLevel }),
1789
1794
  setZoomMin: (zoomMin) => set({ zoomMin }),
1790
1795
  setZoomMax: (zoomMax) => set({ zoomMax }),
1791
1796
  setIsFullPage: (isFullPage) => set({ isFullPage }),
1792
- setRotation: (rotation) => set({ rotation })
1793
- }), defaultState = { url: null, isLoading: !1, error: null }, createManifestSlice = (vault) => (set, get) => ({
1794
- manifest: { ...defaultState, vault },
1797
+ setRotation: (rotation) => set({ rotation }),
1798
+ reset: () => set(defaultState)
1799
+ }), defaultManifest = {
1800
+ url: null,
1801
+ isLoading: !1,
1802
+ error: null
1803
+ }, createManifestSlice = (vault) => (set) => ({
1804
+ vault,
1805
+ ...defaultManifest,
1795
1806
  loadManifest: async (url) => {
1796
- const { vault: vault2 } = get().manifest;
1797
- set({ manifest: { vault: vault2, url, isLoading: !0, error: null } });
1807
+ set({ url, isLoading: !0, error: null });
1798
1808
  try {
1799
- await vault2.loadManifest(url), set({ manifest: { vault: vault2, url, isLoading: !1, error: null } });
1809
+ await vault.loadManifest(url), set({ isLoading: !1, error: null });
1800
1810
  } catch (e2) {
1801
1811
  const error = e2 instanceof Error ? e2.message : "Unknown error";
1802
- set({ manifest: { vault: vault2, url, isLoading: !1, error } });
1812
+ set({ isLoading: !1, error });
1803
1813
  }
1804
- }
1805
- }), createCanvasSlice = (set, get) => ({
1806
- canvas: {
1807
- currentIndex: 0
1808
1814
  },
1815
+ reset: () => set(defaultManifest)
1816
+ }), defaultCanvas = {
1817
+ currentIndex: 0
1818
+ }, createCanvasSlice = (set, get) => ({
1819
+ ...defaultCanvas,
1809
1820
  goToCanvas: (index) => {
1810
- const { vault, url } = get().manifest;
1821
+ const { vault, url } = get();
1811
1822
  if (!url)
1812
1823
  return;
1813
1824
  const manifest = vault.get({ id: url, type: "Manifest" }), clamped = Math.max(0, Math.min(index, manifest.items.length - 1));
1814
- set({ canvas: { currentIndex: clamped } });
1825
+ set({ currentIndex: clamped });
1815
1826
  },
1816
1827
  nextCanvas: () => {
1817
- const { canvas, goToCanvas } = get();
1818
- goToCanvas(canvas.currentIndex + 1);
1828
+ const { currentIndex, goToCanvas } = get();
1829
+ goToCanvas(currentIndex + 1);
1819
1830
  },
1820
1831
  prevCanvas: () => {
1821
- const { canvas, goToCanvas } = get();
1822
- goToCanvas(canvas.currentIndex - 1);
1823
- }
1832
+ const { currentIndex, goToCanvas } = get();
1833
+ goToCanvas(currentIndex - 1);
1834
+ },
1835
+ reset: () => set(defaultCanvas)
1824
1836
  }), createViewerStore = () => {
1825
- const vault = new E();
1837
+ const vault = new E(), resets = [], withReset = (slice) => (resets.push(slice.reset), slice);
1826
1838
  return createStore((...a2) => ({
1827
- ...createInstanceSlice(...a2),
1828
- ...createControlSlice(...a2),
1829
- ...createManifestSlice(vault)(...a2),
1830
- ...createCanvasSlice(...a2)
1839
+ ...withReset(createInstanceSlice(...a2)),
1840
+ ...withReset(createControlSlice(...a2)),
1841
+ ...withReset(createManifestSlice(vault)(...a2)),
1842
+ ...withReset(createCanvasSlice(...a2)),
1843
+ reset: () => resets.forEach((fn) => fn())
1831
1844
  }));
1832
1845
  }, ViewerContext = createContext(null);
1833
1846
  function orThrow(msg) {
@@ -1920,7 +1933,7 @@ function useViewerStoreSelector(selector) {
1920
1933
  return useStore(store, selector);
1921
1934
  }
1922
1935
  function useCanvas() {
1923
- const { vault, url, isLoading } = useViewerStoreSelector((s2) => s2.manifest), currentIndex = useViewerStoreSelector((s2) => s2.canvas.currentIndex), goTo = useViewerStoreSelector((s2) => s2.goToCanvas), next = useViewerStoreSelector((s2) => s2.nextCanvas), prev = useViewerStoreSelector((s2) => s2.prevCanvas), { current, total } = useMemo(() => {
1936
+ const vault = useViewerStoreSelector((s2) => s2.vault), url = useViewerStoreSelector((s2) => s2.url), isLoading = useViewerStoreSelector((s2) => s2.isLoading), currentIndex = useViewerStoreSelector((s2) => s2.currentIndex), goTo = useViewerStoreSelector((s2) => s2.goToCanvas), next = useViewerStoreSelector((s2) => s2.nextCanvas), prev = useViewerStoreSelector((s2) => s2.prevCanvas), { current, total } = useMemo(() => {
1924
1937
  if (!url || isLoading)
1925
1938
  return { current: null, total: 0 };
1926
1939
  const manifest = vault.get({ id: url, type: "Manifest" }), canvasRef = manifest.items[currentIndex] ?? orThrow("No canvas ref for " + currentIndex), current2 = vault.get(canvasRef), total2 = manifest.items.length;
@@ -1929,21 +1942,20 @@ function useCanvas() {
1929
1942
  return { currentIndex, current, total, goTo, next, prev };
1930
1943
  }
1931
1944
  function useVault() {
1932
- return useViewerStoreSelector((s2) => s2.manifest.vault);
1945
+ return useViewerStoreSelector((s2) => s2.vault);
1933
1946
  }
1934
1947
  function Viewer({ showControls = !0 }) {
1935
1948
  const containerRef = useRef(null), store = useViewerStore(), vault = useVault(), { current } = useCanvas(), tileSource = current ? findTileSource(vault, current) : null;
1936
1949
  return useEffect(() => {
1937
- if (!containerRef.current || !tileSource)
1950
+ if (!containerRef.current)
1938
1951
  return;
1939
1952
  const viewer = new Viewer$1({
1940
1953
  element: containerRef.current,
1941
1954
  prefixUrl: "https://openseadragon.github.io/openseadragon/images/",
1942
1955
  crossOriginPolicy: "Anonymous",
1943
- tileSources: tileSource,
1944
1956
  showNavigationControl: showControls
1945
1957
  });
1946
- store.getState().setViewer(viewer), viewer.addOnceHandler("open", () => {
1958
+ store.getState().setViewer(viewer), viewer.addHandler("open", () => {
1947
1959
  const state2 = store.getState();
1948
1960
  state2.setViewerReady(!0), state2.setZoomLevel(viewer.viewport.getZoom()), state2.setZoomMin(viewer.viewport.getMinZoom()), state2.setZoomMax(viewer.viewport.getMaxZoom()), state2.setRotation(viewer.viewport.getRotation());
1949
1961
  });
@@ -1953,11 +1965,12 @@ function Viewer({ showControls = !0 }) {
1953
1965
  store.getState().setRotation(viewer.viewport.getRotation());
1954
1966
  };
1955
1967
  return viewer.addHandler("animation-start", onZoom), viewer.addHandler("animation-finish", onZoom), viewer.addHandler("rotate", onRotate), () => {
1956
- viewer.removeHandler("animation-start", onZoom), viewer.removeHandler("animation-finish", onZoom), viewer.removeHandler("rotate", onRotate), viewer.destroy();
1957
- const state2 = store.getState();
1958
- state2.setViewer(null), state2.setViewerReady(!1), state2.setZoomLevel(null), state2.setZoomMin(null), state2.setZoomMax(null), state2.setIsFullPage(!1), state2.setRotation(0);
1968
+ viewer.destroy(), store.getState().reset();
1959
1969
  };
1960
- }, [tileSource, showControls, store]), /* @__PURE__ */ jsx(
1970
+ }, [showControls, store]), useEffect(() => {
1971
+ const viewer = store.getState().viewer;
1972
+ !viewer || !tileSource || viewer.open(tileSource);
1973
+ }, [tileSource, store]), /* @__PURE__ */ jsx(
1961
1974
  "div",
1962
1975
  {
1963
1976
  id: "viewer",
@@ -2040,8 +2053,8 @@ function useViewerControls(fullscreenRef) {
2040
2053
  };
2041
2054
  }
2042
2055
  function useManifest() {
2043
- const manifest = useViewerStoreSelector((s2) => s2.manifest), isReady = !!manifest.url && !manifest.isLoading && !manifest.error;
2044
- return { ...manifest, isReady };
2056
+ const url = useViewerStoreSelector((s2) => s2.url), isLoading = useViewerStoreSelector((s2) => s2.isLoading), error = useViewerStoreSelector((s2) => s2.error);
2057
+ return { url, isLoading, error, isReady: !!url && !isLoading && !error };
2045
2058
  }
2046
2059
  function useLoadManifest() {
2047
2060
  return useViewerStoreSelector((s2) => s2.loadManifest);
@@ -2049,16 +2062,12 @@ function useLoadManifest() {
2049
2062
  function findThumbnail(vault, canvas, height = 120) {
2050
2063
  return getImageServiceId(vault, canvas) ? `/full/,${height}/0/default.jpg` : null;
2051
2064
  }
2052
- function getAnnotationPageIds(canvas) {
2053
- return canvas.annotations.map((ref) => ref.id).filter((id) => !!id);
2054
- }
2055
2065
  export {
2056
2066
  Overlay,
2057
2067
  Viewer,
2058
2068
  ViewerProvider,
2059
2069
  findThumbnail,
2060
2070
  findTileSource,
2061
- getAnnotationPageIds,
2062
2071
  getImageServiceId,
2063
2072
  useCanvas,
2064
2073
  useImageInfo,
@@ -1,10 +1,10 @@
1
1
  import { StateCreator } from 'zustand/vanilla';
2
2
  import { ViewerStore } from '../ViewerStore.ts';
3
+ import { Resettable } from '../Resettable.ts';
3
4
  export type CanvasState = {
4
5
  currentIndex: number;
5
6
  };
6
- export type CanvasSlice = {
7
- canvas: CanvasState;
7
+ export type CanvasSlice = Resettable & CanvasState & {
8
8
  goToCanvas: (index: number) => void;
9
9
  nextCanvas: () => void;
10
10
  prevCanvas: () => void;
@@ -1,14 +1,14 @@
1
1
  import { StateCreator } from 'zustand/vanilla';
2
2
  import { Vault } from '@iiif/helpers/vault';
3
3
  import { ViewerStore } from '../ViewerStore.ts';
4
+ import { Resettable } from '../Resettable.ts';
4
5
  export type ManifestState = {
5
6
  url: string | null;
6
- vault: Vault;
7
7
  isLoading: boolean;
8
8
  error: string | null;
9
9
  };
10
- export type ManifestSlice = {
11
- manifest: ManifestState;
10
+ export type ManifestSlice = Resettable & ManifestState & {
11
+ vault: Vault;
12
12
  loadManifest: (url: string) => Promise<void>;
13
13
  };
14
14
  export declare const createManifestSlice: (vault: Vault) => StateCreator<ViewerStore, [], [], ManifestSlice>;
@@ -1,8 +1,7 @@
1
1
  export declare function useManifest(): {
2
- isReady: boolean;
3
2
  url: string | null;
4
- vault: import('@iiif/helpers/vault').Vault;
5
3
  isLoading: boolean;
6
4
  error: string | null;
5
+ isReady: boolean;
7
6
  };
8
7
  export declare function useLoadManifest(): (url: string) => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knaw-huc/osd-iiif-viewer",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/knaw-huc/osd-iiif-viewer.git"
@@ -1,3 +0,0 @@
1
- import { CanvasNormalized } from '@iiif/presentation-3-normalized';
2
- import { Id } from '../Id.ts';
3
- export declare function getAnnotationPageIds(canvas: CanvasNormalized): Id[];