mirador
Version:
An open-source, web-based 'multi-up' viewer that supports zoom-pan-rotate functionality, ability to display/compare simple images, and images with annotations.
130 lines (114 loc) • 4.01 kB
JSX
import { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { FullScreen, useFullScreenHandle } from 'react-full-screen';
import { I18nextProvider } from 'react-i18next';
import { ThemeProvider, StyledEngineProvider, createTheme } from '@mui/material/styles';
import { DndContext, DndProvider } from 'react-dnd';
import { MultiBackend } from 'react-dnd-multi-backend';
import { HTML5toTouch } from 'rdndmb-html5-to-touch';
import rtlPlugin from 'stylis-plugin-rtl';
import { prefixer } from 'stylis';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';
import createI18nInstance from '../i18n';
import FailedImageProvider from '../contexts/FailedImageProvider';
import FullScreenContext from '../contexts/FullScreenContext';
import LocaleContext from '../contexts/LocaleContext';
/**
* Allow applications to opt-out of (or provide their own) drag and drop context
*/
const MaybeDndProvider = ({ dndManager = undefined, children }) => {
if (dndManager === false) {
return children;
}
if (dndManager === undefined) {
return (
<DndProvider backend={MultiBackend} options={HTML5toTouch}>
{children}
</DndProvider>
);
}
return <DndContext.Provider value={dndManager}>{children}</DndContext.Provider>;
};
MaybeDndProvider.propTypes = {
children: PropTypes.node.isRequired,
dndManager: PropTypes.oneOf([undefined, false, PropTypes.object]),
};
/**
* Shim to inject the full screen handle into a context
*/
const FullScreenShim = ({ children }) => {
const handle = useFullScreenHandle();
return (
<FullScreen handle={handle}>
<FullScreenContext.Provider value={handle}>{children}</FullScreenContext.Provider>
</FullScreen>
);
};
FullScreenShim.propTypes = {
children: PropTypes.node.isRequired,
};
/**
* Hook up the I18next provider to the configuration in redux to allow
* plugins + config to inject additional translations.
*/
const StoreAwareI18nextProvider = ({ children, language, translations }) => {
const [i18n] = useState(createI18nInstance({ lng: language }));
useEffect(() => {
if (i18n && i18n.language !== language) i18n.changeLanguage(language);
}, [i18n, language]);
useEffect(() => {
Object.keys(translations).forEach((lng) => {
i18n.addResourceBundle(lng, 'translation', translations[lng], true, true);
});
}, [i18n, translations]);
return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
};
StoreAwareI18nextProvider.propTypes = {
children: PropTypes.node.isRequired,
language: PropTypes.string.isRequired,
translations: PropTypes.object.isRequired,
};
/**
* Create rtl emotion cache
*/
const cacheRtl = createCache({
key: 'muirtl',
stylisPlugins: [prefixer, rtlPlugin],
});
/**
* Create default emotion cache
*/
const cacheDefault = createCache({
key: 'mui',
});
/**
* This component adds viewer-specific providers.
* @prop {Object} manifests
*/
export function AppProviders({ children = null, language, theme, translations, dndManager = undefined }) {
return (
<FullScreenShim>
<StoreAwareI18nextProvider language={language} translations={translations}>
<LocaleContext.Provider value={language}>
<StyledEngineProvider injectFirst>
<CacheProvider value={theme.direction === 'rtl' ? cacheRtl : cacheDefault}>
<ThemeProvider theme={createTheme(theme)}>
<MaybeDndProvider dndManager={dndManager}>
<FailedImageProvider>{children}</FailedImageProvider>
</MaybeDndProvider>
</ThemeProvider>
</CacheProvider>
</StyledEngineProvider>
</LocaleContext.Provider>
</StoreAwareI18nextProvider>
</FullScreenShim>
);
}
AppProviders.propTypes = {
children: PropTypes.node,
dndManager: PropTypes.object,
language: PropTypes.string.isRequired,
theme: PropTypes.object.isRequired,
translations: PropTypes.object.isRequired,
};