Skip to content

Commit

Permalink
COLUMBIA: rough out a NativeObjectViewer for PDFs
Browse files Browse the repository at this point in the history
- Rudimentary fullscreen resizing for NativeObjectViewer
- Refactor NativeObjectViewer to use a ResizeObserver effect directly
- Linter cleanup - NativeObjectViewer.js
- remove a console log in NativeObjectViewer.js
- linter fixes for src/components/NativeObjectViewer.js
- PrimaryWindowComponent passes textResources to TypeSpecificViewer
  • Loading branch information
barmintor committed Jan 28, 2025
1 parent 6883867 commit 51af516
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 28 deletions.
70 changes: 70 additions & 0 deletions src/components/NativeObjectViewer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
Fragment, useEffect, useRef, useState,
} from 'react';
import PropTypes from 'prop-types';
import { styled } from '@mui/material/styles';

const StyledContainer = styled('div')({
alignItems: 'center',
display: 'flex',
width: '100%',
});

const StyledObject = styled('object')({
width: '100%',
});

/** */
export function NativeObjectViewer({ nativeObjectOptions = {}, nativeObjectResources = [], windowId }) {
/** */
const defaultDimensions = (originalDimensions) => (originalDimensions || document.body.querySelector(`#${windowId} .mirador-primary-window`)?.getBoundingClientRect());

const eleRef = useRef(null);

const [currentDimensions, setCurrentDimensions] = useState({
current: eleRef.current?.getBoundingClientRect(),
original: defaultDimensions(null),
});

useEffect(() => {
const element = eleRef.current;

if (!element) return () => {};

const originalDimensions = element.getBoundingClientRect();
const observer = new ResizeObserver((entries) => {
if (entries.length === 0) return;
const current = (document.fullscreenElement) ? entries[0].contentRect : originalDimensions;

setCurrentDimensions({ current, original: originalDimensions });
});

observer.observe(element);
return () => {
// Cleanup the observer like any event handlers
observer.disconnect();
};
}, [eleRef]);

const dimensions = currentDimensions.current || currentDimensions.original;
/* eslint-disable jsx-a11y/alt-text */
return (
<StyledContainer ref={eleRef}>
<StyledObject {...nativeObjectOptions}>
{nativeObjectResources.map((nativeObject) => (
<Fragment key={nativeObject.id}>
<object data={`${nativeObject.id}`} type={nativeObject.getFormat()} width={`${dimensions?.width}px`} height={`${dimensions?.height}px`} />
</Fragment>
))}
</StyledObject>
</StyledContainer>
);
/* eslint-disable jsx-a11y/alt-text */
}
/* eslint-enable jsx-a11y/media-has-caption */

NativeObjectViewer.propTypes = {
nativeObjectOptions: PropTypes.object, // eslint-disable-line react/forbid-prop-types
nativeObjectResources: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
windowId: PropTypes.string.isRequired, // eslint-disable-line react/forbid-prop-types
};
24 changes: 18 additions & 6 deletions src/components/PrimaryWindow.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const GalleryView = lazy(() => import('../containers/GalleryView'));
const SelectCollection = lazy(() => import('../containers/SelectCollection'));
const WindowViewer = lazy(() => import('../containers/WindowViewer'));
const VideoViewer = lazy(() => import('../containers/VideoViewer'));
const NativeObjectViewer = lazy(() => import('../containers/NativeObjectViewer'));

GalleryView.displayName = 'GalleryView';
SelectCollection.displayName = 'SelectCollection';
Expand All @@ -25,8 +26,8 @@ const Root = styled('div', { name: 'PrimaryWindow', slot: 'root' })(() => ({

/** */
const TypeSpecificViewer = ({
audioResources = [], isCollection = false,
isFetching = false, videoResources = [], view = undefined, windowId,
audioResources = [], isCollection = false, isFetching = false, textResources = [],
videoResources = [], view = undefined, windowId,
}) => {
if (isCollection) {
return (
Expand All @@ -43,20 +44,27 @@ const TypeSpecificViewer = ({
/>
);
}
if (videoResources.length > 0) {
if (videoResources && videoResources.length > 0) {
return (
<VideoViewer
windowId={windowId}
/>
);
}
if (audioResources.length > 0) {
if (audioResources && audioResources.length > 0) {
return (
<AudioViewer
windowId={windowId}
/>
);
}
if (textResources && textResources.length > 0) {
return (
<NativeObjectViewer
windowId={windowId}
/>
);
}
return (
<WindowViewer
windowId={windowId}
Expand All @@ -70,6 +78,7 @@ TypeSpecificViewer.propTypes = {
audioResources: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
isCollection: PropTypes.bool,
isFetching: PropTypes.bool,
textResources: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
videoResources: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
view: PropTypes.string,
windowId: PropTypes.string.isRequired,
Expand All @@ -80,13 +89,15 @@ TypeSpecificViewer.propTypes = {
* window. Right now this differentiates between a Image, Video, or Audio viewer.
*/
export function PrimaryWindow({
audioResources = undefined, isCollection = false, isFetching = false, videoResources = undefined,
view = undefined, windowId, isCollectionDialogVisible = false, children = null, className = undefined,
audioResources = undefined, children = null, className = undefined, isCollection = false,
isCollectionDialogVisible = false, isFetching = false, textResources = undefined, videoResources = undefined,
view = undefined, windowId,
}) {
const viewerProps = {
audioResources,
isCollection,
isFetching,
textResources,
videoResources,
view,
windowId,
Expand All @@ -111,6 +122,7 @@ PrimaryWindow.propTypes = {
isCollection: PropTypes.bool,
isCollectionDialogVisible: PropTypes.bool,
isFetching: PropTypes.bool,
textResources: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
videoResources: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types
view: PropTypes.string,
windowId: PropTypes.string.isRequired,
Expand Down
23 changes: 23 additions & 0 deletions src/containers/NativeObjectViewer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { connect } from 'react-redux';
import { compose } from 'redux';
import { withTranslation } from 'react-i18next';
import { withPlugins } from '../extend/withPlugins';
import { NativeObjectViewer } from '../components/NativeObjectViewer';
import { getConfig, getVisibleCanvasTextResources } from '../state/selectors';

/** */
const mapStateToProps = (state, { windowId }) => (
{
nativeObjectOptions: getConfig(state).nativeObjectOptions,
nativeObjectResources: getVisibleCanvasTextResources(state, { windowId }) || [],
windowId,
}
);

const enhance = compose(
withTranslation(),
connect(mapStateToProps, null),
withPlugins('NativeObjectViewer'),
);

export default enhance(NativeObjectViewer);
4 changes: 3 additions & 1 deletion src/containers/PrimaryWindow.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { compose } from 'redux';
import { connect } from 'react-redux';
import { withPlugins } from '../extend/withPlugins';
import {
getManifestoInstance, getVisibleCanvasAudioResources, getVisibleCanvasVideoResources, getWindow,
getManifestoInstance, getVisibleCanvasAudioResources, getVisibleCanvasTextResources,
getVisibleCanvasVideoResources, getWindow,
} from '../state/selectors';
import { PrimaryWindow } from '../components/PrimaryWindow';

Expand All @@ -13,6 +14,7 @@ const mapStateToProps = (state, { windowId }) => {
audioResources: getVisibleCanvasAudioResources(state, { windowId }) || [],
isCollection: manifestoInstance && manifestoInstance.isCollection(),
isCollectionDialogVisible: getWindow(state, { windowId }).collectionDialogOn,
textResources: getVisibleCanvasTextResources(state, { windowId }) || [],
videoResources: getVisibleCanvasVideoResources(state, { windowId }) || [],
};
};
Expand Down
9 changes: 8 additions & 1 deletion src/lib/MiradorCanvas.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import flatten from 'lodash/flatten';
import flattenDeep from 'lodash/flattenDeep';
import { Canvas, AnnotationPage, Annotation } from 'manifesto.js';
import { audioResourcesFrom, iiifImageResourcesFrom, videoResourcesFrom } from './typeFilters';
import {
audioResourcesFrom, iiifImageResourcesFrom, textResourcesFrom, videoResourcesFrom,
} from './typeFilters';
import canvasTypes from './canvasTypes';

/**
Expand Down Expand Up @@ -84,6 +86,11 @@ export default class MiradorCanvas {
}));
}

/** */
get textResources() {
return textResourcesFrom(this.imageResources);
}

/** */
get videoResources() {
return videoResourcesFrom(this.imageResources);
Expand Down
29 changes: 9 additions & 20 deletions src/state/selectors/canvases.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,26 +228,6 @@ const probeReplacements = (resources, probeResponses) => {
});
};

/** */
const probeReplacements = (resources, probeResponses) => {
if (!probeResponses) return resources;

return resources.map((r) => {
const probeService = getProbeService(r);
const probeServiceId = probeService && probeService.id;
const probeResponse = probeServiceId && probeResponses[probeServiceId];
if (!probeResponse || probeResponse.isFetching) return r;

const probeContentUrl = probeResponse.json && (probeResponse.json.location || probeResponse.json.substitute);
const probeReplacedProperties = {};
if (probeContentUrl) {
probeReplacedProperties.id = probeContentUrl;
if (probeResponse.json.format) probeReplacedProperties.format = probeResponse.json.format;
}
return new Resource({ ...r.__jsonld, ...probeReplacedProperties }, r.options);
});
};

/**
* Return visible non tiled canvas resources.
* @param {object}
Expand Down Expand Up @@ -311,6 +291,15 @@ export const getVisibleCanvasAudioResources = createSelector(
.map(canvas => probeReplacements(new MiradorCanvas(canvas).audioResources, probeResponses))),
);

export const getVisibleCanvasTextResources = createSelector(
[
getVisibleCanvases,
selectProbeResponses,
],
(canvases, probeResponses) => flatten(canvases
.map(canvas => probeReplacements(new MiradorCanvas(canvas).textResources, probeResponses))),
);

/**
* Returns info response.
* @param {object} state
Expand Down

0 comments on commit 51af516

Please sign in to comment.