@knaw-huc/osd-iiif-viewer 0.0.0
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 +1 -0
- package/dist/ControlSlice.d.ts +15 -0
- package/dist/HighlightOverlay.d.ts +11 -0
- package/dist/Id.d.ts +1 -0
- package/dist/InstanceSlice.d.ts +12 -0
- package/dist/ViewerCanvas.d.ts +5 -0
- package/dist/ViewerContext.d.ts +3 -0
- package/dist/ViewerProvider.d.ts +3 -0
- package/dist/ViewerStore.d.ts +6 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +254 -0
- package/dist/manifest/CanvasSlice.d.ts +14 -0
- package/dist/manifest/ManifestSlice.d.ts +16 -0
- package/dist/manifest/findLanguage.d.ts +4 -0
- package/dist/manifest/findThumbnail.d.ts +2 -0
- package/dist/manifest/findTileSource.d.ts +2 -0
- package/dist/manifest/model.d.ts +29 -0
- package/dist/manifest/parseManifest.d.ts +2 -0
- package/dist/manifest/parseManifest.spec.d.ts +1 -0
- package/dist/manifest/useCanvas.d.ts +8 -0
- package/dist/manifest/useManifest.d.ts +2 -0
- package/dist/useViewer.d.ts +1 -0
- package/dist/useViewerControls.d.ts +19 -0
- package/dist/useViewerReady.d.ts +1 -0
- package/dist/useViewerStore.d.ts +1 -0
- package/dist/useViewerStoreSelector.d.ts +2 -0
- package/dist/util/assertSvgElement.d.ts +1 -0
- package/dist/util/orThrow.d.ts +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# osd-iiif-viewer
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { StateCreator } from 'zustand/vanilla';
|
|
2
|
+
import { ViewerStore } from './ViewerStore.ts';
|
|
3
|
+
export type ControlSlice = {
|
|
4
|
+
zoomLevel: number | null;
|
|
5
|
+
zoomMin: number | null;
|
|
6
|
+
zoomMax: number | null;
|
|
7
|
+
isFullPage: boolean;
|
|
8
|
+
setZoomLevel: (zoomLevel: number | null) => void;
|
|
9
|
+
setZoomMin: (zoomMin: number | null) => void;
|
|
10
|
+
setZoomMax: (zoomMax: number | null) => void;
|
|
11
|
+
setIsFullPage: (isFullPage: boolean) => void;
|
|
12
|
+
};
|
|
13
|
+
export declare const createControlSlice: StateCreator<ViewerStore, [
|
|
14
|
+
], [
|
|
15
|
+
], ControlSlice>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Id } from './Id.ts';
|
|
2
|
+
export type Highlight = {
|
|
3
|
+
id: Id;
|
|
4
|
+
path: string;
|
|
5
|
+
};
|
|
6
|
+
type HighlightOverlayProps = {
|
|
7
|
+
highlights: Highlight[];
|
|
8
|
+
onHover?: (highlightId: Id | null, event: MouseEvent) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function HighlightOverlay({ highlights, onHover }: HighlightOverlayProps): null;
|
|
11
|
+
export {};
|
package/dist/Id.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Id = string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { StateCreator } from 'zustand/vanilla';
|
|
2
|
+
import { default as OpenSeadragon } from 'openseadragon';
|
|
3
|
+
import { ViewerStore } from './ViewerStore.ts';
|
|
4
|
+
export type InstanceSlice = {
|
|
5
|
+
viewer: OpenSeadragon.Viewer | null;
|
|
6
|
+
viewerReady: boolean;
|
|
7
|
+
setViewer: (viewer: OpenSeadragon.Viewer | null) => void;
|
|
8
|
+
setViewerReady: (viewerReady: boolean) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare const createInstanceSlice: StateCreator<ViewerStore, [
|
|
11
|
+
], [
|
|
12
|
+
], InstanceSlice>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { InstanceSlice } from './InstanceSlice.ts';
|
|
2
|
+
import { ControlSlice } from './ControlSlice.ts';
|
|
3
|
+
import { ManifestSlice } from './manifest/ManifestSlice.ts';
|
|
4
|
+
import { CanvasSlice } from './manifest/CanvasSlice.ts';
|
|
5
|
+
export type ViewerStore = InstanceSlice & ControlSlice & ManifestSlice & CanvasSlice;
|
|
6
|
+
export declare const createViewerStore: () => import('zustand').StoreApi<ViewerStore>;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { ViewerCanvas } from './ViewerCanvas';
|
|
2
|
+
export type { ViewerStore } from './ViewerStore.ts';
|
|
3
|
+
export type { InstanceSlice } from './InstanceSlice.ts';
|
|
4
|
+
export type { ControlSlice } from './ControlSlice.ts';
|
|
5
|
+
export { useViewer } from './useViewer.tsx';
|
|
6
|
+
export { useViewerControls } from './useViewerControls.tsx';
|
|
7
|
+
export { ViewerProvider } from './ViewerProvider.tsx';
|
|
8
|
+
export { useViewerReady } from './useViewerReady.tsx';
|
|
9
|
+
export { useViewerStore } from './useViewerStore.tsx';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Viewer } from "openseadragon";
|
|
3
|
+
import { createContext, useContext, useRef, useEffect, useCallback, useState } from "react";
|
|
4
|
+
import "zustand";
|
|
5
|
+
import { createStore } from "zustand/vanilla";
|
|
6
|
+
import { useStore } from "zustand/index";
|
|
7
|
+
const createInstanceSlice = (set) => ({
|
|
8
|
+
viewer: null,
|
|
9
|
+
viewerReady: !1,
|
|
10
|
+
setViewer: (viewer) => set({ viewer }),
|
|
11
|
+
setViewerReady: (viewerReady) => set({ viewerReady })
|
|
12
|
+
}), createControlSlice = (set) => ({
|
|
13
|
+
zoomLevel: null,
|
|
14
|
+
zoomMin: null,
|
|
15
|
+
zoomMax: null,
|
|
16
|
+
isFullPage: !1,
|
|
17
|
+
setZoomLevel: (zoomLevel) => set({ zoomLevel }),
|
|
18
|
+
setZoomMin: (zoomMin) => set({ zoomMin }),
|
|
19
|
+
setZoomMax: (zoomMax) => set({ zoomMax }),
|
|
20
|
+
setIsFullPage: (isFullPage) => set({ isFullPage })
|
|
21
|
+
}), defaultLanguage = "en";
|
|
22
|
+
function findLanguage(translations, lang = defaultLanguage) {
|
|
23
|
+
if (!translations || typeof translations != "object")
|
|
24
|
+
return "";
|
|
25
|
+
const map = translations, tryLang = (lang2) => Array.isArray(map[lang2]) && map[lang2].length ? map[lang2][0] : null;
|
|
26
|
+
return tryLang(lang) ?? tryLang(defaultLanguage) ?? tryLang("none") ?? firstValue(map) ?? "";
|
|
27
|
+
}
|
|
28
|
+
function firstValue(map) {
|
|
29
|
+
for (const values of Object.values(map))
|
|
30
|
+
if (Array.isArray(values) && values.length)
|
|
31
|
+
return values[0];
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function orThrow(msg) {
|
|
35
|
+
throw new Error(msg);
|
|
36
|
+
}
|
|
37
|
+
function parseManifest(json) {
|
|
38
|
+
return assertManifest(json), {
|
|
39
|
+
id: json.id,
|
|
40
|
+
label: findLanguage(json.label),
|
|
41
|
+
canvases: json.items.map(parseCanvas),
|
|
42
|
+
structures: parseStructures(json.structures),
|
|
43
|
+
metadata: {
|
|
44
|
+
attribution: parseAttribution(json.requiredStatement),
|
|
45
|
+
rights: json.rights ?? null,
|
|
46
|
+
thumbnailUrl: parseThumbnailUrl(json.thumbnail)
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function parseCanvas(canvas) {
|
|
51
|
+
const painting = (canvas.items?.find(isAnnotationPage) ?? orThrow("No annotation page")).items?.find(isPaintingAnnotation) ?? orThrow("No painting annotation"), serviceId = (toBody(painting.body).service?.find(isImageService2) ?? orThrow("No ImageService2"))["@id"] ?? orThrow("No @id"), annotationPageIds = (canvas.annotations ?? []).filter(isAnnotationPage).map((a) => a.id).filter(isString);
|
|
52
|
+
return {
|
|
53
|
+
id: canvas.id,
|
|
54
|
+
label: findLanguage(canvas.label),
|
|
55
|
+
width: canvas.width,
|
|
56
|
+
height: canvas.height,
|
|
57
|
+
imageServiceId: serviceId,
|
|
58
|
+
annotationPageIds
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function parseStructures(ranges) {
|
|
62
|
+
if (!ranges?.length)
|
|
63
|
+
return [];
|
|
64
|
+
const top = ranges[0];
|
|
65
|
+
return top.items?.length ? top.items.filter(isRange).map(parseRange) : [];
|
|
66
|
+
}
|
|
67
|
+
function parseRange(range) {
|
|
68
|
+
const items = range.items ?? [];
|
|
69
|
+
return {
|
|
70
|
+
id: range.id,
|
|
71
|
+
label: findLanguage(range.label),
|
|
72
|
+
canvasIds: items.filter(isCanvasRef).map((c) => c.id),
|
|
73
|
+
children: items.filter(isRange).map(parseRange)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function parseAttribution(statement) {
|
|
77
|
+
return statement ? findLanguage(statement.value) : null;
|
|
78
|
+
}
|
|
79
|
+
function parseThumbnailUrl(thumbnails) {
|
|
80
|
+
return thumbnails?.length ? thumbnails[0].id : null;
|
|
81
|
+
}
|
|
82
|
+
function toBody(body) {
|
|
83
|
+
return Array.isArray(body) ? body[0] ?? orThrow("Empty body array") : body;
|
|
84
|
+
}
|
|
85
|
+
function assertManifest(json) {
|
|
86
|
+
if (!json || typeof json != "object")
|
|
87
|
+
throw new Error("No manifest found");
|
|
88
|
+
if (!("type" in json) || json.type !== "Manifest")
|
|
89
|
+
throw new Error("No manifest type found");
|
|
90
|
+
}
|
|
91
|
+
function isAnnotationPage(item) {
|
|
92
|
+
return typeof item == "object" && item !== null && "type" in item && item.type === "AnnotationPage";
|
|
93
|
+
}
|
|
94
|
+
function isPaintingAnnotation(item) {
|
|
95
|
+
return typeof item == "object" && item !== null && "motivation" in item && item.motivation === "painting";
|
|
96
|
+
}
|
|
97
|
+
function isImageService2(service) {
|
|
98
|
+
return service["@type"] === "ImageService2";
|
|
99
|
+
}
|
|
100
|
+
function isRange(item) {
|
|
101
|
+
return item.type === "Range";
|
|
102
|
+
}
|
|
103
|
+
function isCanvasRef(item) {
|
|
104
|
+
return item.type === "Canvas";
|
|
105
|
+
}
|
|
106
|
+
function isString(value) {
|
|
107
|
+
return typeof value == "string";
|
|
108
|
+
}
|
|
109
|
+
const defaultManifest = { data: null, url: "", isLoading: !1, error: null }, createManifestSlice = (set) => ({
|
|
110
|
+
manifest: { ...defaultManifest },
|
|
111
|
+
loadManifest: async (url) => {
|
|
112
|
+
const loading = { ...defaultManifest, url, loading: !0 };
|
|
113
|
+
set({ manifest: loading });
|
|
114
|
+
const done = { ...loading, loading: !1 };
|
|
115
|
+
try {
|
|
116
|
+
const response = await fetch(url);
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
const error = `Failed to fetch manifest: ${response.status}`;
|
|
119
|
+
set({ manifest: { ...done, error } });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const json = await response.json(), data = parseManifest(json);
|
|
123
|
+
set({ manifest: { ...done, data } });
|
|
124
|
+
} catch (e) {
|
|
125
|
+
const error = e instanceof Error ? e.message : "Unknown error";
|
|
126
|
+
set({ manifest: { ...done, error } });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}), createCanvasSlice = (set, get) => ({
|
|
130
|
+
canvas: {
|
|
131
|
+
currentIndex: 0
|
|
132
|
+
},
|
|
133
|
+
goToCanvas: (index) => {
|
|
134
|
+
const canvases = get().manifest.data?.canvases;
|
|
135
|
+
if (!canvases?.length)
|
|
136
|
+
return;
|
|
137
|
+
const clamped = Math.max(0, Math.min(index, canvases.length - 1));
|
|
138
|
+
set({ canvas: { currentIndex: clamped } });
|
|
139
|
+
},
|
|
140
|
+
nextCanvas: () => {
|
|
141
|
+
const { canvas, goToCanvas } = get();
|
|
142
|
+
goToCanvas(canvas.currentIndex + 1);
|
|
143
|
+
},
|
|
144
|
+
prevCanvas: () => {
|
|
145
|
+
const { canvas, goToCanvas } = get();
|
|
146
|
+
goToCanvas(canvas.currentIndex - 1);
|
|
147
|
+
}
|
|
148
|
+
}), createViewerStore = () => createStore((...a) => ({
|
|
149
|
+
...createInstanceSlice(...a),
|
|
150
|
+
...createControlSlice(...a),
|
|
151
|
+
...createManifestSlice(...a),
|
|
152
|
+
...createCanvasSlice(...a)
|
|
153
|
+
})), ViewerContext = createContext(null);
|
|
154
|
+
function useViewerStore() {
|
|
155
|
+
return useContext(ViewerContext) ?? orThrow("viewer hook must be used within viewer provider");
|
|
156
|
+
}
|
|
157
|
+
function findTileSource(canvas) {
|
|
158
|
+
return canvas.imageServiceId + "/info.json";
|
|
159
|
+
}
|
|
160
|
+
function useViewerStoreSelector(selector) {
|
|
161
|
+
const store = useViewerStore();
|
|
162
|
+
return useStore(store, selector);
|
|
163
|
+
}
|
|
164
|
+
function useCanvas() {
|
|
165
|
+
const { currentIndex } = useViewerStoreSelector((s) => s.canvas), goTo = useViewerStoreSelector((s) => s.goToCanvas), next = useViewerStoreSelector((s) => s.nextCanvas), prev = useViewerStoreSelector((s) => s.prevCanvas), current = useViewerStoreSelector(
|
|
166
|
+
(s) => s.manifest.data?.canvases[s.canvas.currentIndex] ?? null
|
|
167
|
+
), total = useViewerStoreSelector(
|
|
168
|
+
(s) => s.manifest.data?.canvases.length ?? 0
|
|
169
|
+
);
|
|
170
|
+
return { currentIndex, goTo, next, prev, current, total };
|
|
171
|
+
}
|
|
172
|
+
function ViewerCanvas({ showControls = !0 }) {
|
|
173
|
+
const containerRef = useRef(null), store = useViewerStore(), { current } = useCanvas(), tileSource = current ? findTileSource(current) : null;
|
|
174
|
+
return useEffect(() => {
|
|
175
|
+
if (!containerRef.current || !tileSource)
|
|
176
|
+
return;
|
|
177
|
+
const viewer = new Viewer({
|
|
178
|
+
element: containerRef.current,
|
|
179
|
+
prefixUrl: "https://openseadragon.github.io/openseadragon/images/",
|
|
180
|
+
crossOriginPolicy: "Anonymous",
|
|
181
|
+
tileSources: tileSource,
|
|
182
|
+
showNavigationControl: showControls
|
|
183
|
+
});
|
|
184
|
+
store.getState().setViewer(viewer), viewer.addOnceHandler("open", () => {
|
|
185
|
+
const state2 = store.getState();
|
|
186
|
+
state2.setViewerReady(!0), state2.setZoomLevel(viewer.viewport.getZoom()), state2.setZoomMin(viewer.viewport.getMinZoom()), state2.setZoomMax(viewer.viewport.getMaxZoom());
|
|
187
|
+
});
|
|
188
|
+
const onZoom = () => {
|
|
189
|
+
store.getState().setZoomLevel(viewer.viewport.getZoom());
|
|
190
|
+
};
|
|
191
|
+
return viewer.addHandler("animation-start", onZoom), viewer.addHandler("animation-finish", onZoom), () => {
|
|
192
|
+
viewer.removeHandler("animation-start", onZoom), viewer.removeHandler("animation-finish", onZoom), viewer.destroy();
|
|
193
|
+
const state2 = store.getState();
|
|
194
|
+
state2.setViewer(null), state2.setViewerReady(!1), state2.setZoomLevel(null), state2.setZoomMin(null), state2.setZoomMax(null), state2.setIsFullPage(!1);
|
|
195
|
+
};
|
|
196
|
+
}, [tileSource, showControls, store]), /* @__PURE__ */ jsx(
|
|
197
|
+
"div",
|
|
198
|
+
{
|
|
199
|
+
id: "viewer",
|
|
200
|
+
ref: containerRef,
|
|
201
|
+
style: { width: "100%", height: "100%" }
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
function useViewer() {
|
|
206
|
+
return useViewerStoreSelector((s) => s.viewer);
|
|
207
|
+
}
|
|
208
|
+
function useViewerControls(fullscreenRef) {
|
|
209
|
+
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);
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
const onFullscreenChange = () => {
|
|
212
|
+
store.getState().setIsFullPage(!!document.fullscreenElement);
|
|
213
|
+
};
|
|
214
|
+
return document.addEventListener("fullscreenchange", onFullscreenChange), () => document.removeEventListener(
|
|
215
|
+
"fullscreenchange",
|
|
216
|
+
onFullscreenChange
|
|
217
|
+
);
|
|
218
|
+
}, [store]);
|
|
219
|
+
const zoomIn = useCallback(() => {
|
|
220
|
+
viewer?.viewport.zoomBy(1.5);
|
|
221
|
+
}, [viewer]), zoomOut = useCallback(() => {
|
|
222
|
+
viewer?.viewport.zoomBy(0.667);
|
|
223
|
+
}, [viewer]), home = useCallback(() => {
|
|
224
|
+
viewer?.viewport.goHome();
|
|
225
|
+
}, [viewer]), toggleFullPage = useCallback(() => {
|
|
226
|
+
const container = fullscreenRef?.current;
|
|
227
|
+
container && (document.fullscreenElement ? document.exitFullscreen() : container.requestFullscreen());
|
|
228
|
+
}, [fullscreenRef]);
|
|
229
|
+
return {
|
|
230
|
+
zoomIn,
|
|
231
|
+
zoomOut,
|
|
232
|
+
home,
|
|
233
|
+
toggleFullPage,
|
|
234
|
+
zoomLevel,
|
|
235
|
+
zoomMin,
|
|
236
|
+
zoomMax,
|
|
237
|
+
isFullPage
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function ViewerProvider({ children }) {
|
|
241
|
+
const [store] = useState(() => createViewerStore());
|
|
242
|
+
return /* @__PURE__ */ jsx(ViewerContext.Provider, { value: store, children });
|
|
243
|
+
}
|
|
244
|
+
function useViewerReady() {
|
|
245
|
+
return useViewerStoreSelector((s) => s.viewerReady);
|
|
246
|
+
}
|
|
247
|
+
export {
|
|
248
|
+
ViewerCanvas,
|
|
249
|
+
ViewerProvider,
|
|
250
|
+
useViewer,
|
|
251
|
+
useViewerControls,
|
|
252
|
+
useViewerReady,
|
|
253
|
+
useViewerStore
|
|
254
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { StateCreator } from 'zustand/vanilla';
|
|
2
|
+
import { ViewerStore } from '../ViewerStore.ts';
|
|
3
|
+
export type CanvasState = {
|
|
4
|
+
currentIndex: number;
|
|
5
|
+
};
|
|
6
|
+
export type CanvasSlice = {
|
|
7
|
+
canvas: CanvasState;
|
|
8
|
+
goToCanvas: (index: number) => void;
|
|
9
|
+
nextCanvas: () => void;
|
|
10
|
+
prevCanvas: () => void;
|
|
11
|
+
};
|
|
12
|
+
export declare const createCanvasSlice: StateCreator<ViewerStore, [
|
|
13
|
+
], [
|
|
14
|
+
], CanvasSlice>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { StateCreator } from 'zustand/vanilla';
|
|
2
|
+
import { ViewerManifest } from './model.ts';
|
|
3
|
+
import { ViewerStore } from '../ViewerStore.ts';
|
|
4
|
+
export type ManifestState = {
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
data: ViewerManifest | null;
|
|
7
|
+
url: string | null;
|
|
8
|
+
error: string | null;
|
|
9
|
+
};
|
|
10
|
+
export type ManifestSlice = {
|
|
11
|
+
manifest: ManifestState;
|
|
12
|
+
loadManifest: (url: string) => Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
export declare const createManifestSlice: StateCreator<ViewerStore, [
|
|
15
|
+
], [
|
|
16
|
+
], ManifestSlice>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal model of parsed iiif manifests
|
|
3
|
+
*/
|
|
4
|
+
export type ViewerManifest = {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
canvases: ViewerCanvas[];
|
|
8
|
+
structures: ViewerRange[];
|
|
9
|
+
metadata: ViewerMetadata;
|
|
10
|
+
};
|
|
11
|
+
export type ViewerCanvas = {
|
|
12
|
+
id: string;
|
|
13
|
+
label: string;
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
imageServiceId: string;
|
|
17
|
+
annotationPageIds: string[];
|
|
18
|
+
};
|
|
19
|
+
export type ViewerRange = {
|
|
20
|
+
id: string;
|
|
21
|
+
label: string;
|
|
22
|
+
canvasIds: string[];
|
|
23
|
+
children: ViewerRange[];
|
|
24
|
+
};
|
|
25
|
+
export type ViewerMetadata = {
|
|
26
|
+
attribution: string | null;
|
|
27
|
+
rights: string | null;
|
|
28
|
+
thumbnailUrl: string | null;
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useViewer(): import('openseadragon').Viewer | null;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Expose zoom and full-screen controls and state
|
|
4
|
+
*
|
|
5
|
+
* Note: Fullscreen is handled via the native Fullscreen API,
|
|
6
|
+
* skipping setFullScreen/setFullPage which moves
|
|
7
|
+
* the viewer to <body> and destroys the React component tree.
|
|
8
|
+
* @see https://github.com/openseadragon/openseadragon/issues/449
|
|
9
|
+
*/
|
|
10
|
+
export declare function useViewerControls(fullscreenRef: RefObject<HTMLElement | null>): {
|
|
11
|
+
zoomIn: () => void;
|
|
12
|
+
zoomOut: () => void;
|
|
13
|
+
home: () => void;
|
|
14
|
+
toggleFullPage: () => void;
|
|
15
|
+
zoomLevel: number | null;
|
|
16
|
+
zoomMin: number | null;
|
|
17
|
+
zoomMax: number | null;
|
|
18
|
+
isFullPage: boolean;
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useViewerReady(): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useViewerStore(): import('zustand').StoreApi<import('./ViewerStore.ts').ViewerStore>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function assertSvgElement(element: unknown): asserts element is SVGElement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function orThrow(msg: string): never;
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@knaw-huc/osd-iiif-viewer",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"start": "vite",
|
|
18
|
+
"dev": "vite",
|
|
19
|
+
"build": "vite build",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"prepack": "npm run build",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"openseadragon": "^5.0.1",
|
|
28
|
+
"zustand": "^5.0.9"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"react": ">=18",
|
|
32
|
+
"react-dom": ">=18"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@eslint/js": "^9.39.1",
|
|
36
|
+
"@iiif/presentation-3": "^2.2.3",
|
|
37
|
+
"@stylistic/eslint-plugin": "^4.0.0",
|
|
38
|
+
"@types/node": "^24.10.1",
|
|
39
|
+
"@types/openseadragon": "^5.0.1",
|
|
40
|
+
"@types/react": "^19.2.5",
|
|
41
|
+
"@types/react-dom": "^19.2.3",
|
|
42
|
+
"@vitejs/plugin-react": "^5.1.1",
|
|
43
|
+
"eslint": "^9.39.1",
|
|
44
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
45
|
+
"eslint-plugin-react-refresh": "^0.4.24",
|
|
46
|
+
"globals": "^16.5.0",
|
|
47
|
+
"react": "^19.2.4",
|
|
48
|
+
"react-dom": "^19.2.4",
|
|
49
|
+
"typescript": "~5.9.3",
|
|
50
|
+
"typescript-eslint": "^8.46.4",
|
|
51
|
+
"vite": "^7.2.4",
|
|
52
|
+
"vite-plugin-dts": "^4.5.4",
|
|
53
|
+
"vitest": "^3.2.1"
|
|
54
|
+
}
|
|
55
|
+
}
|