Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add clips using annotations #145

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions pages/demo/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, {useState} from 'react'
import * as Form from "@radix-ui/react-form";

import Viewer from "../../docs/components/DynamicImports/Viewer"
import { styled } from "../../src/styles/stitches.config";


function Demo() {
const defaultIiifContent =
"https://seagate.whirl-i-gig.com/admin/service/IIIF/manifest/ca_objects:170";

const [iiifContent, setIiifContent] = useState(defaultIiifContent)
const [tempIiifContent, setTempIiifContent] = useState(defaultIiifContent)

const handleChange = (e) => {
e.preventDefault();
setTempIiifContent(e.target.value);
};

const handleSubmit = (e) => {
e.preventDefault();
setIiifContent(tempIiifContent)
};

const options = {
showIIIFBadge: false,
informationPanel: {
renderAbout: true,
renderSupplementing: true,
renderClips: true
}
}

return (
<StyledDiv>
<Form.Root onSubmit={handleSubmit}>
<Form.Field name="iiifContent" onChange={handleChange}>
<Form.Label>IIIF Manifest</Form.Label>
<Form.Control
defaultValue={ iiifContent}
placeholder="IIIF Manifest"
className="nx-block nx-w-full nx-appearance-none nx-rounded-lg nx-px-3 nx-py-2 nx-transition-colors nx-text-base nx-leading-tight md:nx-text-sm nx-bg-black/[.05] dark:nx-bg-gray-50/10 focus:nx-bg-white dark:focus:nx-bg-dark placeholder:nx-text-gray-500 dark:placeholder:nx-text-gray-400 contrast-more:nx-border contrast-more:nx-border-current"
/>
</Form.Field>

<Form.Submit >Submit</Form.Submit >
</Form.Root>
<Viewer iiifContent={iiifContent} options={options}/>
</StyledDiv>
);
}


const StyledDiv = styled("div", {
margin: "1rem auto",
input: {
padding: "0.5rem",
width: '90%',
marginBottom: '0.5rem'
},

button: {
padding: "0.25rem",
},

label: {
display: "none",
},
});

export default Demo
56 changes: 56 additions & 0 deletions src/components/Viewer/InformationPanel/Clip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { useEffect } from "react";
import useWebVtt, {
NodeWebVttCueNested,
} from "src/hooks/use-webvtt";

import { Group } from "src/components/Viewer/InformationPanel/Cue.styled";
import { InternationalString } from "@iiif/presentation-3";
import Menu from "src/components/Viewer/InformationPanel/Menu";
import { getLabel } from "src/hooks/use-iiif";

interface Clip {
clip: any;
}

const ResourceClip: React.FC<Clip> = ({ clip }) => {
const [cues, setCues] = React.useState<Array<NodeWebVttCueNested>>([]);
const { id, label } = clip;
const { createNestedCues, orderCuesByTime } = useWebVtt();

function convertHHMMSStoSeconds(hms: string) {
var parts = hms.split(":");
return +parts[0] * 60 * 60 + +parts[1] * 60 + +parts[2];
}

useEffect(() => {
if (id)
fetch(id, {
headers: {
"Content-Type": "text/plain",
Accept: "application/json",
},
})
.then((response) => response.json())
.then((data) => {
let flatCues = data.map((datum: any) => {
datum.start = convertHHMMSStoSeconds(datum.start);
datum.end = convertHHMMSStoSeconds(datum.end);
return datum;
});
const orderedCues = orderCuesByTime(flatCues);
const nestedCues = createNestedCues(orderedCues);
setCues(nestedCues);
})
.catch((error) => console.error(id, error.toString()));
}, [id]);

return (
<Group
aria-label={`navigate ${getLabel(label as InternationalString, "en")}`}
>
<Menu items={cues} />
</Group>
);
};

export default ResourceClip;
21 changes: 21 additions & 0 deletions src/components/Viewer/InformationPanel/InformationPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@ import React, { useEffect, useState } from "react";
import Information from "src/components/Viewer/InformationPanel/About/About";
import { Label } from "src/components/Primitives";
import { LabeledResource } from "src/hooks/use-iiif/getSupplementingResources";
import { LabeledClip} from "src/hooks/use-iiif/getSupplementingClips";
import Resource from "src/components/Viewer/InformationPanel/Resource";
import Clip from "src/components/Viewer/InformationPanel/Clip";
import { useViewerState } from "src/context/viewer-context";

interface NavigatorProps {
activeCanvas: string;
resources?: Array<LabeledResource>;
clips?: Array<LabeledClip>;
}

export const InformationPanel: React.FC<NavigatorProps> = ({
activeCanvas,
resources,
clips,
}) => {
const viewerState: any = useViewerState();
const { configOptions } = viewerState;
Expand All @@ -31,6 +35,7 @@ export const InformationPanel: React.FC<NavigatorProps> = ({
const renderAbout =
informationPanel?.renderAbout || configOptions?.renderAbout;
const renderSupplementing = informationPanel?.renderSupplementing;
const renderClips = informationPanel?.renderClips;

useEffect(() => {
if (renderAbout) {
Expand Down Expand Up @@ -63,6 +68,13 @@ export const InformationPanel: React.FC<NavigatorProps> = ({
<Label label={label} />
</Trigger>
))}
{renderClips &&
clips &&
clips.map(({ id, label }) => (
<Trigger key={id} value={id as string}>
<Label label={label} />
</Trigger>
))}
</List>
<Scroll>
{renderAbout && (
Expand All @@ -79,6 +91,15 @@ export const InformationPanel: React.FC<NavigatorProps> = ({
</Content>
);
})}
{renderClips &&
clips &&
clips.map((clip) => {
return (
<Content key={clip.id} value={clip.id as string}>
<Clip clip={clip} />
</Content>
);
})}
</Scroll>
</Wrapper>
);
Expand Down
8 changes: 7 additions & 1 deletion src/components/Viewer/Viewer/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Canvas, IIIFExternalWebResource } from "@iiif/presentation-3";

import InformationPanel from "src/components/Viewer/InformationPanel/InformationPanel";
import { LabeledResource } from "src/hooks/use-iiif/getSupplementingResources";
import { LabeledClip } from "src/hooks/use-iiif/getSupplementingClips";
import Media from "src/components/Viewer/Media/Media";
import Painting from "../Painting/Painting";
import React from "react";
Expand All @@ -19,6 +20,7 @@ interface Props {
activeCanvas: string;
painting: IIIFExternalWebResource;
resources: LabeledResource[];
clips: LabeledClip[];
items: Canvas[];
isAudioVideo: boolean;
}
Expand All @@ -27,6 +29,7 @@ const ViewerContent: React.FC<Props> = ({
activeCanvas,
painting,
resources,
clips,
items,
isAudioVideo,
}) => {
Expand All @@ -40,7 +43,9 @@ const ViewerContent: React.FC<Props> = ({

const isAside =
informationPanel?.renderAbout ||
(informationPanel?.renderSupplementing && resources.length > 0);
(informationPanel?.renderSupplementing && resources.length > 0) ||
(informationPanel?.renderClips && clips.length > 0);
;

return (
<Content className="clover-content">
Expand Down Expand Up @@ -70,6 +75,7 @@ const ViewerContent: React.FC<Props> = ({
<InformationPanel
activeCanvas={activeCanvas}
resources={resources}
clips={clips}
/>
</CollapsibleContent>
</Aside>
Expand Down
11 changes: 11 additions & 0 deletions src/components/Viewer/Viewer/Viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import React, { useEffect, useState } from "react";
import {
getPaintingResource,
getSupplementingResources,
getSupplementingClips
} from "src/hooks/use-iiif";
import { useViewerDispatch, useViewerState } from "src/context/viewer-context";

import { ErrorBoundary } from "react-error-boundary";
import ErrorFallback from "src/components/Viewer/Viewer/ErrorFallback";
import { IIIFExternalWebResource } from "@iiif/presentation-3";
import { LabeledResource } from "src/hooks/use-iiif/getSupplementingResources";
import { LabeledClip } from "src/hooks/use-iiif/getSupplementingClips";
import ViewerContent from "src/components/Viewer/Viewer/Content";
import ViewerHeader from "src/components/Viewer/Viewer/Header";
import { Wrapper } from "src/components/Viewer/Viewer/Viewer.styled";
Expand Down Expand Up @@ -46,6 +48,7 @@ const Viewer: React.FC<ViewerProps> = ({ manifest, theme }) => {
undefined
);
const [resources, setResources] = useState<LabeledResource[]>([]);
const [clips, setClips] = useState<LabeledClip[]>([]);

const [isBodyLocked, setIsBodyLocked] = useBodyLocked(false);
const isSmallViewport = useMediaQuery(media.sm);
Expand Down Expand Up @@ -77,6 +80,12 @@ const Viewer: React.FC<ViewerProps> = ({ manifest, theme }) => {
activeCanvas,
"text/vtt"
);
const clips = getSupplementingClips(
vault,
activeCanvas,
"application/json"
);

if (painting) {
setIsAudioVideo(
["Sound", "Video"].indexOf(painting.type as ExternalResourceTypes) > -1
Expand All @@ -86,6 +95,7 @@ const Viewer: React.FC<ViewerProps> = ({ manifest, theme }) => {
setPainting({ ...painting });
}
setResources(resources);
setClips(clips);
setIsInformationPanel(resources.length !== 0);
}, [activeCanvas]);

Expand All @@ -110,6 +120,7 @@ const Viewer: React.FC<ViewerProps> = ({ manifest, theme }) => {
activeCanvas={activeCanvas}
painting={painting as IIIFExternalWebResource}
resources={resources}
clips={clips}
items={manifest.items}
isAudioVideo={isAudioVideo}
/>
Expand Down
2 changes: 2 additions & 0 deletions src/context/viewer-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type ViewerConfigOptions = {
open?: boolean;
renderAbout?: boolean;
renderSupplementing?: boolean;
renderClips?: boolean;
renderToggle?: boolean;
};
openSeadragon?: OpenSeadragonOptions;
Expand All @@ -33,6 +34,7 @@ const defaultConfigOptions = {
open: true,
renderAbout: true,
renderSupplementing: true,
renderClips: true,
renderToggle: true,
},
openSeadragon: {},
Expand Down
57 changes: 57 additions & 0 deletions src/hooks/use-iiif/getSupplementingClips.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {
Annotation,
AnnotationPage,
CanvasNormalized,
ContentResource,
IIIFExternalWebResource,
InternationalString,
} from "@iiif/presentation-3";

export interface LabeledClip {
id?: string;
type: "Dataset" | "Image" | "Video" | "Sound" | "Text";
format?: string;
label: InternationalString;
language?: string | string[];
processingLanguage?: string;
textDirection?: "ltr" | "rtl" | "auto";
}

// Get json annotations from activeCanvas
export const getSupplementingClips = (
vault: any,
activeCanvas: string,
format: string
): Array<LabeledClip> => {
const canvas: CanvasNormalized = vault.get({
id: activeCanvas,
type: "Canvas",
});

if (!canvas?.annotations || !canvas.annotations[0]) return [];

let clips: LabeledClip[] = [];
canvas.annotations.forEach((annotation) => {
const annotationPage: AnnotationPage = vault.get(annotation);
const annotations: Annotation[] = vault.get(annotationPage.items);
if (!Array.isArray(annotations)) return [];

annotations.filter((annotation) => {
if (!annotation.body) return;
if (annotation.motivation?.includes("supplementing")) {
let annotationBody = annotation.body as
| ContentResource
| ContentResource[];

if (Array.isArray(annotationBody)) annotationBody = annotationBody[0];

const clip: IIIFExternalWebResource = vault.get(annotationBody.id);
if (clip.format === format) {
clips.push(clip as LabeledClip);
}
}
});
});

return clips;
};
2 changes: 2 additions & 0 deletions src/hooks/use-iiif/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getCanvasByCriteria } from "src/hooks/use-iiif/getCanvasByCriteria";
import { getLabel } from "src/hooks/use-iiif/getLabel";
import { getPaintingResource } from "src/hooks/use-iiif/getPaintingResource";
import { getSupplementingResources } from "src/hooks/use-iiif/getSupplementingResources";
import { getSupplementingClips } from "src/hooks/use-iiif/getSupplementingClips";
import { getThumbnail } from "src/hooks/use-iiif/getThumbnail";

export {
Expand All @@ -11,5 +12,6 @@ export {
getLabel,
getPaintingResource,
getSupplementingResources,
getSupplementingClips,
getThumbnail,
};
Loading