diff --git a/apps/commons/src/pages/imageviewer/imageViewer.css b/apps/commons/src/pages/imageviewer/imageViewer.css new file mode 100644 index 0000000000000000000000000000000000000000..745003246b0cebcc716dc0a95f6bba841b3a5d00 --- /dev/null +++ b/apps/commons/src/pages/imageviewer/imageViewer.css @@ -0,0 +1,63 @@ +.imageViewer { + display: table; + height: 100%; + width: 100%; +} + +.imageViewer .toolbar { + display: flex; + justify-content: space-between; + align-items: center; + height: 32px; + padding: 8px; + margin-top: 4px; +} + +.imageViewer .image { + display: table-row; + height: 100%; +} + +.imageViewer .toolbar .navigation { + display: flex; + align-items: center; + gap: 8px; +} + +.imageViewer .toolbar .zoom { + display: flex; + align-items: center; + gap: 8px; +} + +.imageViewer .lw-button { + display: flex !important; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border-radius: 3px; +} + +.imageViewer .lw-button:hover { + cursor: pointer; + background-color: rgba(0, 0, 0, .1); + /* border: 1px solid #aaa; */ +} + +.imageViewer .pageInfo { + /* display: block; + position: relative; + float: right; + margin: 5px 3px; */ +} + +.imageViewer input.currPage { + height: 20px; + width: 20px; + text-align: right; +} + +.imageViewer .openseadragon-message { + white-space: pre; +} \ No newline at end of file diff --git a/apps/commons/src/pages/imageviewer/index.tsx b/apps/commons/src/pages/imageviewer/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..47313a8443ad828a0eb1fa9d9d14cde6fc9c38b8 --- /dev/null +++ b/apps/commons/src/pages/imageviewer/index.tsx @@ -0,0 +1,95 @@ +import { Box, Link, Stack, Typography } from '@mui/material'; +import { Logo } from '@src/components'; +import { Page } from '@src/layouts'; +import { Trans, useTranslation } from 'react-i18next'; +import { Link as RouterLink } from 'react-router'; +import { BroadcastChannel } from 'broadcast-channel'; +import { useEffect, useState } from 'react'; +import OpenSeaDragon from 'openseadragon'; +import './imageViewer.css' + +export const ImageViewerPage = () => { + const { t } = useTranslation(); + const channel = new BroadcastChannel('imageViewer-channel'); + const [images, setImages] = useState("") + const id: string = "LEAF-Wwriter-SepImageViewer" + //var viewer: any; + + useEffect(() => { + const handleMessage = (event) => { + console.log(event) + if (event.tileSources) { + setImages(event.tileSources); + } + }; + + channel.onmessage = handleMessage; + + // Clean up the channel when the component unmounts + return () => { + channel.close(); + }; + }, [channel]); + + useEffect(() => { + channel.postMessage({imageViewerWindowReady: true}) + console.log("HIU") + }, []) + + useEffect(() => { + console.log(images) + }, [images]) + + useEffect(() => { + + let viewer = OpenSeaDragon({ + id: 'seadragon-viewer', + sequenceMode: true, + autoHideControls: false, + showFullPageControl: false, + previousButton: `${id}_prev`, + nextButton: `${id}_next`, + zoomInButton: `${id}_zoomIn`, + zoomOutButton: `${id}_zoomOut`, + homeButton: `${id}_home`, + }); + console.log(images) + viewer.open(images) + + // Cleanup (equal to componentWillUnmount) + return () => { + viewer.destroy(); + viewer = null; + }; + }, [images]); + + return ( + <div id={id} className="imageViewer"> + <div className="toolbar"> + <div className="navigation"> + <span id={id + "_prev"} className="lw-button"> + <i className="fas fa-arrow-left"></i> + </span> + <span id={id + "_next"} className="lw-button"> + <i className="fas fa-arrow-right"></i> + </span> + <span className="pageInfo"> + <input type="text" className="currPage" /> / <span className="totalPages"/> + </span> + </div> + <div className="zoom"> + <span id={id + "_zoomIn"} className="lw-button"> + <i className="fas fa-search-plus"></i> + </span> + <span id={id + "_zoomOut"} className="lw-button"> + <i className="fas fa-search-minus"></i> + </span> + <span id={id + "_home"} className="lw-button"> + <i className="fas fa-compress"></i> + </span> + </div> + </div> + <div id="seadragon-viewer" className="seadragon-viewer" /> + </div> + ); +}; diff --git a/apps/commons/src/pages/index.ts b/apps/commons/src/pages/index.ts index 56bd97c36732c3704840685f1f7eb165ce72bd78..9d36d32d32590a011b2ee59a9a66057f9916388f 100644 --- a/apps/commons/src/pages/index.ts +++ b/apps/commons/src/pages/index.ts @@ -2,3 +2,4 @@ export * from './LinkAccounts'; export * from './edit'; export * from './error/NotFoundView'; export * from './home'; +export * from './imageviewer'; diff --git a/apps/commons/src/routes.tsx b/apps/commons/src/routes.tsx index 071dd8a80c619b80185dfc70ca2173565dfaa06c..550fef7391b61ff3dbd72e96c0418c005f390630 100644 --- a/apps/commons/src/routes.tsx +++ b/apps/commons/src/routes.tsx @@ -1,5 +1,5 @@ import { BasicLayout } from './layouts'; -import { EditPage, HomePage, LinkAccountsPage, NotFoundPage } from './pages'; +import { EditPage, HomePage, LinkAccountsPage, NotFoundPage, ImageViewerPage } from './pages'; export const routes = [ { @@ -10,6 +10,7 @@ export const routes = [ { path: '/link-accounts', element: <LinkAccountsPage /> }, { path: '/edit', element: <EditPage /> }, { path: '/view', element: <EditPage /> }, + { path: '/imageviewer', element: <ImageViewerPage /> }, { index: true, element: <HomePage /> }, ], }, diff --git a/packages/cwrc-leafwriter/src/components/editorToolbar/index.tsx b/packages/cwrc-leafwriter/src/components/editorToolbar/index.tsx index 9eee8a96d27ce8c4f3fecfa0aa5230943d09d37d..c133ed4b60907ebee9c72752639520fe11a14193 100644 --- a/packages/cwrc-leafwriter/src/components/editorToolbar/index.tsx +++ b/packages/cwrc-leafwriter/src/components/editorToolbar/index.tsx @@ -8,6 +8,9 @@ import { Button } from './Button'; import { IconButton } from './IconButton'; import { Toggle } from './Toggle'; import { Trans, useTranslation } from 'react-i18next'; +//import PortalComponent from './PortalComponent'; +import { BroadcastChannel } from 'broadcast-channel'; +import { useEffect, useState } from 'react'; type ItemType = 'button' | 'divider' | 'iconButton' | 'toggle'; type ItemGroup = 'action' | 'ui' | 'panel' | 'general'; @@ -42,6 +45,28 @@ export const EditorToolbar = () => { const container = useRef<HTMLDivElement>(null); + /* + const [showPortal, setShowPortal] = useState(false); + + const channel = new BroadcastChannel('imageViewer-channel'); + const [message, setMessage] = useState("") + + useEffect(() => { + const handleMessage = (event) => { + console.log(event) + if (event.imageViewerWindowReady && event.imageViewerWindowReady == true) { + channel.postMessage({dataIn: "Hi, I am Broadcast!"}) + } + }; + + channel.onmessage = handleMessage; + + // Clean up the channel when the component unmounts + return () => { + channel.close(); + }; +}, [channel]);*/ + const isSupported = useCallback( (name: EntityType) => window.writer.schemaManager.mapper.getEntitiesMapping().has(name), [schemaId], @@ -227,6 +252,15 @@ export const EditorToolbar = () => { title: t('Validate').toString(), type: 'iconButton', }, + { + group: 'ui', + icon: 'validate', + onClick: () => { + const externalWindow = window.open(window.location.origin + '/imageviewer', '', 'width=600,height=400,left=200,top=200'); + }, + title: t('Second Window Image Viewer').toString(), + type: 'iconButton', + }, { group: 'ui', type: 'divider', hide: isReadonly }, { group: 'ui', diff --git a/packages/cwrc-leafwriter/src/js/layout/panels/imageViewer/index.ts b/packages/cwrc-leafwriter/src/js/layout/panels/imageViewer/index.ts index d72fc5d826df3427c14f1a2e24ca3a33d6108562..8740534c7ae26ce4ef137c208c94f516dab71ca6 100644 --- a/packages/cwrc-leafwriter/src/js/layout/panels/imageViewer/index.ts +++ b/packages/cwrc-leafwriter/src/js/layout/panels/imageViewer/index.ts @@ -6,6 +6,7 @@ import { Octokit } from '@octokit/rest'; import { Buffer } from 'buffer/'; import axios, { AxiosInstance, AxiosResponse } from 'axios'; import i18next from '../../../../i18n'; +import { BroadcastChannel } from 'broadcast-channel'; const { t } = i18next; @@ -25,17 +26,23 @@ class ImageViewer { readonly osd: ReturnType<typeof OpenSeaDragon>; + readonly channel: any; + $pageBreaks: any; $oldPageBreaks: any; currentIndex = -1; ignoreScroll = false; inhibitScroll = false // Marker to inhibit scrolling when user has zoomed in on a particular image + globalTileSources: any; + constructor({ attribute, parentId, tag, writer }: ImageViewerProps) { this.writer = writer; this.id = `${parentId}_imageViewer`; this.tagName = tag ?? 'pb'; // page break element name this.attrName = attribute ?? 'facs'; // attribute that stores the image URL + this.channel = new BroadcastChannel('imageViewer-channel'); + this.channel.onmessage = this.handleMessage; const _this = this; @@ -149,6 +156,14 @@ class ImageViewer { }); } + + private handleMessage = (event) => { + console.log(event) + if (event.imageViewerWindowReady && event.imageViewerWindowReady == true) { + this.channel.postMessage({tileSources: this.globalTileSources}) + } + }; + // ensure page break tags are display block private cssHack() { if (!this.writer.editor) return; @@ -451,6 +466,8 @@ class ImageViewer { } } + this.globalTileSources = tileSources + this.osd.open(tileSources); // tileSources.length === 0