diff --git a/src/renderer/src/stories/FullScreenToggle.ts b/src/renderer/src/stories/FullScreenToggle.ts new file mode 100644 index 000000000..ff1e1557f --- /dev/null +++ b/src/renderer/src/stories/FullScreenToggle.ts @@ -0,0 +1,72 @@ +import { LitElement, css, html } from "lit"; + +import fullScreenIcon from './assets/fullscreen.svg?raw' +import fullScreenExitIcon from './assets/fullscreen_exit.svg?raw' + +import { unsafeHTML } from "lit/directives/unsafe-html.js"; + +type FullScreenToggleProps = { + target: HTMLElement | (() => HTMLElement); +} + +export class FullScreenToggle extends LitElement { + static get styles() { + return css` + :host { + display: flex; + position: absolute; + top: 10px; + right: 10px; + padding: 10px; + color: white; + background-color: gainsboro; + border: 1px solid gray; + border-radius: 10px; + cursor: pointer; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.3); + z-index: 1000; + opacity: 0.5; + transition: opacity 0.5s; + } + + :host(:hover) { + opacity: 1; + } + `; + } + + static get properties() { + return { + icon: { type: String }, + }; + } + + declare icon: string; + declare target: FullScreenToggleProps['target']; + + constructor({ target }: FullScreenToggleProps) { + + super(); + this.target = target; + this.icon = fullScreenIcon + + const fullscreenchanged = () => this.icon = document.fullscreenElement ? fullScreenExitIcon : fullScreenIcon + + this.addEventListener('click', () => { + const target = (typeof this.target === 'function' ? this.target() : this.target) + + target.addEventListener("fullscreenchange", fullscreenchanged); + + const fullScreenEl = document.fullscreenElement + if (!fullScreenEl) target.requestFullscreen() + else document.exitFullscreen() + + }) + } + + render() { + return html`${unsafeHTML(this.icon)}`; + } +} + +customElements.get("fullscreen-toggle") || customElements.define("fullscreen-toggle", FullScreenToggle); diff --git a/src/renderer/src/stories/List.ts b/src/renderer/src/stories/List.ts index 69c57b1b8..7ffb24b26 100644 --- a/src/renderer/src/stories/List.ts +++ b/src/renderer/src/stories/List.ts @@ -240,10 +240,10 @@ export class List extends LitElement { const { items, emptyMessage} = this - return items.length || !emptyMessage ? html` + return html`
    - ${items.map(this.#renderListItem)} -
` : html`
${emptyMessage}
` + ${(items.length || !emptyMessage) ? items.map(this.#renderListItem) : html`
${emptyMessage}
`} + ` } } diff --git a/src/renderer/src/stories/Modal.ts b/src/renderer/src/stories/Modal.ts index 62e19f008..83363090f 100644 --- a/src/renderer/src/stories/Modal.ts +++ b/src/renderer/src/stories/Modal.ts @@ -51,6 +51,7 @@ export class Modal extends LitElement { /* Modal Body */ .modal-body { + position: relative; overflow-y: auto; width: 100%; flex-grow: 1; diff --git a/src/renderer/src/stories/assets/fullscreen.svg b/src/renderer/src/stories/assets/fullscreen.svg new file mode 100644 index 000000000..f1d258a7d --- /dev/null +++ b/src/renderer/src/stories/assets/fullscreen.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/src/stories/assets/fullscreen_exit.svg b/src/renderer/src/stories/assets/fullscreen_exit.svg new file mode 100644 index 000000000..9a3189294 --- /dev/null +++ b/src/renderer/src/stories/assets/fullscreen_exit.svg @@ -0,0 +1 @@ + diff --git a/src/renderer/src/stories/preview/NWBFilePreview.js b/src/renderer/src/stories/preview/NWBFilePreview.js index 3a5de8286..cf3a1e66b 100644 --- a/src/renderer/src/stories/preview/NWBFilePreview.js +++ b/src/renderer/src/stories/preview/NWBFilePreview.js @@ -6,6 +6,7 @@ import { run } from "../pages/guided-mode/options/utils"; import { until } from "lit/directives/until.js"; import { InstanceManager } from "../InstanceManager"; import { path } from "../../electron"; +import { FullScreenToggle } from "../FullScreenToggle"; export function getSharedPath(array) { array = array.map((str) => str.replace(/\\/g, "/")); // Convert to Mac-style path @@ -55,7 +56,7 @@ class NWBPreviewInstance extends LitElement { const isOnline = navigator.onLine; return isOnline - ? new Neurosift({ url: getURLFromFilePath(this.file, this.project) }) + ? new Neurosift({ url: getURLFromFilePath(this.file, this.project), fullscreen: false }) : until( (async () => { const htmlRep = await run("html", { nwbfile_path: this.file }, { swal: false }); @@ -71,11 +72,27 @@ customElements.get("nwb-preview-instance") || customElements.define("nwb-preview export class NWBFilePreview extends LitElement { static get styles() { return css` + :host { + display: block; + width: 100%; + height: 100%; + background: white; + position: relative; + } + iframe { width: 100%; height: 100%; border: 0; } + + #inspect { + display: flex; + flex-direction: column; + border-left: 1px solid gray; + box-shadow: -5px 0 5px -5px rgba(0, 0, 0, 0.5); + z-index: 1; + } `; } @@ -105,58 +122,60 @@ export class NWBFilePreview extends LitElement { const onlyFirstFile = fileArr.length <= 1; - return html`
-
- ${(() => { - if (onlyFirstFile) return new NWBPreviewInstance(fileArr[0].info, this.project); - else { - const _instances = fileArr.map(this.createInstance); - - const instances = _instances.reduce((acc, { subject, session, display }) => { - if (!acc[`sub-${subject}`]) acc[`sub-${subject}`] = {}; - acc[`sub-${subject}`][`ses-${session}`] = display; - return acc; - }, {}); - - return new InstanceManager({ instances }); - } - })()} -
- ${this.inspect - ? html`
-

Inspector Report

- ${until( - (async () => { - const opts = {}; // NOTE: Currently options are handled on the Python end until exposed to the user - - const title = "Inspecting your file"; - - const items = onlyFirstFile - ? removeFilePaths( - await run( - "inspect_file", - { nwbfile_path: fileArr[0].info.file, ...opts }, - { title } - ) - ) // Inspect the first file - : await (async () => - truncateFilePaths( - await run("inspect_folder", { path, ...opts }, { title: title + "s" }), - getSharedPath(fileArr.map((o) => o.info.file)) - ))(); - - const list = new InspectorList({ - items: items, - listStyles: { maxWidth: "350px" }, - }); - list.style.padding = "10px"; - return list; - })(), - html`Loading inspector report...` - )} -
` - : ""} -
`; + return html` ${new FullScreenToggle({ target: this })} +
+
+ ${(() => { + if (onlyFirstFile) return new NWBPreviewInstance(fileArr[0].info, this.project); + else { + const _instances = fileArr.map(this.createInstance); + + const instances = _instances.reduce((acc, { subject, session, display }) => { + if (!acc[`sub-${subject}`]) acc[`sub-${subject}`] = {}; + acc[`sub-${subject}`][`ses-${session}`] = display; + return acc; + }, {}); + + return new InstanceManager({ instances }); + } + })()} +
+ ${this.inspect + ? html`
+

Inspector Report

+ ${until( + (async () => { + const opts = {}; // NOTE: Currently options are handled on the Python end until exposed to the user + + const title = "Inspecting your file"; + + const items = onlyFirstFile + ? removeFilePaths( + await run( + "inspect_file", + { nwbfile_path: fileArr[0].info.file, ...opts }, + { title } + ) + ) // Inspect the first file + : await (async () => + truncateFilePaths( + await run("inspect_folder", { path, ...opts }, { title: title + "s" }), + getSharedPath(fileArr.map((o) => o.info.file)) + ))(); + + const list = new InspectorList({ + items: items, + listStyles: { minWidth: "300px", maxWidth: "350px" }, + emptyMessage: "No issues found.", + }); + list.style.padding = "10px"; + return list; + })(), + html`Loading inspector report...` + )} +
` + : ""} +
`; } } diff --git a/src/renderer/src/stories/preview/Neurosift.js b/src/renderer/src/stories/preview/Neurosift.js index 622148da9..aaf626bb4 100644 --- a/src/renderer/src/stories/preview/Neurosift.js +++ b/src/renderer/src/stories/preview/Neurosift.js @@ -2,6 +2,7 @@ import { LitElement, css, html } from "lit"; import { baseUrl } from "../../globals"; import { Loader } from "../Loader"; +import { FullScreenToggle } from "../FullScreenToggle"; export function getURLFromFilePath(file, projectName) { const regexp = new RegExp(`.+(${projectName}.+)`); @@ -12,6 +13,7 @@ export class Neurosift extends LitElement { static get styles() { return css` :host { + background: white; width: 100%; height: 100%; display: grid; @@ -21,7 +23,8 @@ export class Neurosift extends LitElement { --loader-color: hsl(200, 80%, 50%); } - :host > * { + iframe, + .loader-container { width: 100%; height: 100%; } @@ -35,6 +38,19 @@ export class Neurosift extends LitElement { left: 0; } + .fullscreen-toggle { + display: flex; + position: absolute; + top: 10px; + right: 10px; + padding: 10px; + color: white; + background-color: gainsboro; + border: 1px solid gray; + border-radius: 10px; + cursor: pointer; + } + span { font-size: 14px; } @@ -55,9 +71,10 @@ export class Neurosift extends LitElement { }; } - constructor({ url } = {}) { + constructor({ url, fullscreen = true } = {}) { super(); this.url = url; + this.fullscreen = fullscreen; } render() { @@ -65,11 +82,12 @@ export class Neurosift extends LitElement { ? html`
${new Loader({ message: `Loading Neurosift view...
${this.url}` })}
+ ${this.fullscreen ? new FullScreenToggle({ target: this }) : ""} ` : ``;