From 0aa71c92b9c2891149caec1a65120dae096295df Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Mon, 17 Jul 2023 15:06:18 +0200 Subject: [PATCH 1/7] add keyboard shortcuts to move slides in presentation mode Left Arrow goes back Right Arrow goes forward --- frontend/components/SlideControls.js | 46 +++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/frontend/components/SlideControls.js b/frontend/components/SlideControls.js index 60a6380eb0..a195612300 100644 --- a/frontend/components/SlideControls.js +++ b/frontend/components/SlideControls.js @@ -1,6 +1,32 @@ -import { html } from "../imports/Preact.js" +import { html, useRef, useEffect, useState, useLayoutEffect } from "../imports/Preact.js" export const SlideControls = () => { + const button_prev_ref = useRef(/** @type {HTMLButtonElement?} */ (null)) + const button_next_ref = useRef(/** @type {HTMLButtonElement?} */ (null)) + + const [presenting, set_presenting] = useState(false) + + const move_slides_with_arrows = (/** @type {KeyboardEvent} */ e) => { + const activeElement = document.activeElement + if ( + activeElement != null && + activeElement !== document.body && + activeElement !== button_prev_ref.current && + activeElement !== button_next_ref.current + ) { + // We do not move slides with arrow if we have an active element + return + } + if (e.key === "ArrowLeft") { + button_prev_ref.current?.click() + } else if (e.key === "ArrowRight") { + button_next_ref.current?.click() + } else { + return + } + e.preventDefault() + } + const calculate_slide_positions = (/** @type {Event} */ e) => { const notebook_node = /** @type {HTMLElement?} */ (e.target)?.closest("pluto-editor")?.querySelector("pluto-notebook") if (!notebook_node) return [] @@ -40,15 +66,27 @@ export const SlideControls = () => { if (pos) window.scrollTo(window.pageXOffset, pos) } + const presenting_ref = useRef(false) + presenting_ref.current = presenting // @ts-ignore window.present = () => { - document.body.classList.toggle("presentation") + set_presenting(!presenting_ref.current) } + useLayoutEffect(() => { + document.body.classList.toggle("presentation", presenting) + + window.addEventListener("keydown", move_slides_with_arrows) + + return () => { + window.removeEventListener("keydown", move_slides_with_arrows) + } + }, [presenting]) + return html` ` } From 985d98e8cf59ee581deb0346a1e69d1ccc44b1aa Mon Sep 17 00:00:00 2001 From: Alberto Mengali Date: Mon, 17 Jul 2023 15:08:48 +0200 Subject: [PATCH 2/7] remove useEffect import --- frontend/components/SlideControls.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/SlideControls.js b/frontend/components/SlideControls.js index a195612300..290fd52405 100644 --- a/frontend/components/SlideControls.js +++ b/frontend/components/SlideControls.js @@ -1,4 +1,4 @@ -import { html, useRef, useEffect, useState, useLayoutEffect } from "../imports/Preact.js" +import { html, useRef, useState, useLayoutEffect } from "../imports/Preact.js" export const SlideControls = () => { const button_prev_ref = useRef(/** @type {HTMLButtonElement?} */ (null)) From 9f3e07f95c0722e2f797c9fdb1983c145d829124 Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 18 Jul 2023 14:51:53 +0200 Subject: [PATCH 3/7] avoid adding event listeners when not presenting --- frontend/components/SlideControls.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/components/SlideControls.js b/frontend/components/SlideControls.js index 290fd52405..62aa9bc7ba 100644 --- a/frontend/components/SlideControls.js +++ b/frontend/components/SlideControls.js @@ -76,6 +76,8 @@ export const SlideControls = () => { useLayoutEffect(() => { document.body.classList.toggle("presentation", presenting) + if (!presenting) return // We do not add listeners if not presenting + window.addEventListener("keydown", move_slides_with_arrows) return () => { From 91e41a9eedbf8aa4d6377b567059e67dad34296a Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 18 Jul 2023 14:52:15 +0200 Subject: [PATCH 4/7] add presentation popup when detecting full screen --- frontend/components/SlideControls.js | 86 +++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/frontend/components/SlideControls.js b/frontend/components/SlideControls.js index 62aa9bc7ba..35553f7a08 100644 --- a/frontend/components/SlideControls.js +++ b/frontend/components/SlideControls.js @@ -1,10 +1,12 @@ -import { html, useRef, useState, useLayoutEffect } from "../imports/Preact.js" +import { html, useRef, useState, useLayoutEffect, useEffect } from "../imports/Preact.js" +import { open_pluto_popup } from "./Popup.js" export const SlideControls = () => { const button_prev_ref = useRef(/** @type {HTMLButtonElement?} */ (null)) const button_next_ref = useRef(/** @type {HTMLButtonElement?} */ (null)) const [presenting, set_presenting] = useState(false) + const [fullscreen, set_fullscreen] = useState(false) const move_slides_with_arrows = (/** @type {KeyboardEvent} */ e) => { const activeElement = document.activeElement @@ -73,6 +75,25 @@ export const SlideControls = () => { set_presenting(!presenting_ref.current) } + const fullscreen_ref = useRef(false) + fullscreen_ref.current = fullscreen + const check_fullscreen_status = (/** @type {UIEvent} */ e) => { + // This will detect full screen if the window height becomes equivalent to the screen height + // In firefox, screen.height is adapted based on OS DPI settings (at least on windows) and browser zoom, while on chrome the screen is not reflecting the browser zoom level. This means that the current approach will only work on chrome when the browser zoom is 100%. + let maxHeight = window.screen.height + let curHeight = window.innerHeight + + if (!fullscreen_ref.current && maxHeight === curHeight) { + // We just got into FullScreen + set_fullscreen(true) + } else if (fullscreen_ref.current && maxHeight !== curHeight) { + // We just got out of FullScreen + set_fullscreen(false) + } else { + return + } + } + useLayoutEffect(() => { document.body.classList.toggle("presentation", presenting) @@ -85,6 +106,69 @@ export const SlideControls = () => { } }, [presenting]) + useLayoutEffect(() => { + console.log("presenting") + if (!presenting_ref.current && fullscreen) { + open_pluto_popup({ + type: "info", + source_element: null, + body: html`It seems you have just entered fullscreen mode. +

Would you like to activate Pluto presentation mode?

+ { + e.preventDefault() + set_presenting(true) + window.dispatchEvent(new CustomEvent("close pluto popup")) + }} + >Yes +
+ { + e.preventDefault() + window.dispatchEvent(new CustomEvent("close pluto popup")) + }} + >No`, + }) + } else if (presenting_ref.current && !fullscreen) { + open_pluto_popup({ + type: "info", + source_element: null, + body: html`It seems you have just exited fullscreen mode. +

Would you like to deactivate Pluto presentation mode?

+ { + e.preventDefault() + set_presenting(false) + window.dispatchEvent(new CustomEvent("close pluto popup")) + }} + >Yes +
+ { + e.preventDefault() + window.dispatchEvent(new CustomEvent("close pluto popup")) + }} + >No`, + }) + } + }, [fullscreen]) + + useEffect(() => { + window.addEventListener("resize", check_fullscreen_status) + + return () => { + window.removeEventListener("resize", check_fullscreen_status) + } + }, []) + return html`