Skip to content

Commit

Permalink
fix popups not reopening on if you focus them while they're closing
Browse files Browse the repository at this point in the history
  • Loading branch information
plt-amy committed Dec 22, 2024
1 parent cfad6bc commit c846bd6
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 27 deletions.
8 changes: 7 additions & 1 deletion support/shake/app/Shake/KaTeX.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ module Shake.KaTeX
( katexRules
, getDisplayMath
, getInlineMath
, prerenderMaths

, getParsedPreamble
, getPreambleFor
) where

import Control.Monad

import qualified Data.ByteString.Lazy as LazyBS
import qualified Data.Text.Encoding as Text
import qualified Data.Text.IO as Text
Expand Down Expand Up @@ -44,6 +47,9 @@ getDisplayMath contents = askOracle (LatexEquation (True, contents))
getInlineMath :: Text -> Action Text
getInlineMath contents = askOracle (LatexEquation (False, contents))

prerenderMaths :: [Text] -> [Text] -> Action ()
prerenderMaths display inline = void . askOracles $ [LatexEquation (True, c) | c <- display] <> [LatexEquation (False, c) | c <- inline]

-- | Get the preamble for a given build ('True' = dark mode)
getPreambleFor :: Bool -> Action Text
getPreambleFor = askOracle . LatexPreamble
Expand All @@ -67,7 +73,7 @@ katexRules = versioned 2 do
let dark = if bool then darkSettings else mempty
pure (preambleToLatex pre <> Text.pack "\n" <> dark)

_ <- addOracleCache \(LatexEquation (display, tex)) -> do
_ <- versioned 2 $ addOracleCache \(LatexEquation (display, tex)) -> do
pre <- askOracle (ParsedPreamble ())

let args = ["-T", "-F", "html"] ++ ["-d" | display]
Expand Down
12 changes: 10 additions & 2 deletions support/shake/app/Shake/Markdown.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Control.Applicative
import qualified Data.ByteString.Lazy as LazyBS
import qualified Data.Text.Encoding as Text
import qualified Data.Map.Lazy as Map
import qualified Data.Sequence as Seq
import qualified Data.Text.IO as Text
import qualified Data.Text as Text
import qualified Data.Set as Set
Expand Down Expand Up @@ -227,7 +228,14 @@ buildMarkdown modname input output = do

liftIO $ Dir.createDirectoryIfMissing True $ "_build/diagrams" </> modname

let refMap = Map.fromList $ map (\x -> (Cite.unItemId . Cite.referenceId $ x, x)) references
let
refMap = Map.fromList $ map (\x -> (Cite.unItemId . Cite.referenceId $ x, x)) references
(display, inline) = flip query markdown \case
Math DisplayMath contents -> (Seq.singleton contents, mempty)
Math InlineMath contents -> (mempty, Seq.singleton contents)
_ -> mempty

prerenderMaths (toList display) (toList inline)

Pandoc meta@(Meta metamap) blocks <-
walkM (patchInline refMap)
Expand Down Expand Up @@ -271,7 +279,7 @@ buildMarkdown modname input output = do
Text.writeFile output $ renderHTML5 tags
encodeFile ("_build/search" </> modname <.> "json") search

for_ (Map.toList defs) \(key, bs) -> traced ("writing fragment " <> Text.unpack (getMangled key)) do
for_ (Map.toList defs) \(key, bs) -> traced "writing fragment" do
text <- either (fail . show) pure =<<
runIO (renderMarkdown authors references modname baseUrl digest (Pandoc mempty bs))

Expand Down
4 changes: 2 additions & 2 deletions support/web/css/components/popup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
color: theme(--code-fg);
background: theme(--code-bg);

--font-size: 1rem;
--code-font-size: 1rem;
--font-size: 1.125rem;
--code-font-size: 1.125rem;

box-shadow: 2px 2px 6px theme(--shadow);

Expand Down
6 changes: 5 additions & 1 deletion support/web/css/default.scss
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,11 @@ div.diagram-container {
}
}

main a[href], div#return > a[href], div#top > a[href], aside#toc > div#toc-container ul a[href] {
main a[href],
div#return > a[href],
div#top > a[href],
aside#toc > div#toc-container ul a[href],
div.hover-popup a[href] {
color: theme(--primary);
text-decoration: none;

Expand Down
44 changes: 27 additions & 17 deletions support/web/js/lib/hover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ export class Hover {
e.classList.add('popup-hidden');
e.id = myself;

e.addEventListener("mouseenter", async () => await this.mouseEvent(true));
e.addEventListener("mouseleave", async () => await this.mouseEvent(false));
e.addEventListener("mouseenter", async () => this.fading || await this.mouseEvent(true));
e.addEventListener("mouseleave", async () => this.fading || await this.mouseEvent(false));

refreshLinks(e);

Expand Down Expand Up @@ -127,28 +127,33 @@ export class Hover {
}

/** Are we playing the closing animation? */
private closing: boolean = false;
private closing?: Timeout;

/**
* Hide the popup. Does not remove the element from the DOM!
*/
private async displace() {
const el = await this.element;
this.closing = true;

// Set ourselves to a hiding state immediately, so that if the user
// goes back onto the link while we're closing we can just
// immediately show again.
if (this.shown) this.shown();
delete this.shown;

if (!this.ephemeral) {
el.classList.remove(`popup-fade-in-${this.fadeDirection}`);
el.classList.add(`popup-fade-out-${this.fadeDirection}`);
}

await new Timeout(this.ephemeral ? 0 : 250).start();
this.closing = new Timeout(this.ephemeral ? 0 : 250, `displace ${this.anchor}`);

await this.closing.start();
el.classList.add('popup-hidden')
this.closing = false;

if (this.shown) this.shown();
delete this.shown;
delete this.closing;

if (this.ephemeral) this.destroy();
if (this.ephemeral) return this.destroy();
}

private async destroy() {
Expand All @@ -162,7 +167,7 @@ export class Hover {
/** Are we closing this popup, or does it associated with an anchor
* that belongs to a parent which is fading? */
private get fading(): boolean {
return this.closing || (!!this.parent && this.parent.fading)
return !!this.closing || (!!this.parent && this.parent.fading)
}

/** Timeout before the popup appears. */
Expand All @@ -174,13 +179,19 @@ export class Hover {
* either show it, or cancel a previous show timer if one exists..
*/
private async unhide() {
if (this.cursors >= 1 && !this.fading) {
this.showTimer = new Timeout(this.ephemeral ? 0 : showTimeout);
if (this.cursors >= 1) {
this.showTimer = new Timeout(this.ephemeral ? 0 : showTimeout, `unhide ${this.anchor}`);

// If we're currently playing the closing animation, but there's a
// positive number of cursors (necessarily on the anchor), then we
// should wait and show the popup again.
if (this.closing) await this.closing.start();

await Promise.all([ this.showTimer?.start(), this.element ]);
await this.place();

delete this.showTimer;
} else if (this.cursors <= 0 && !this.showTimer?.done) {
} else if (this.cursors <= 0 && this.showTimer && !this.showTimer.done) {
this.showTimer?.cancel();
if (this.ephemeral) this.destroy();
}
Expand All @@ -196,11 +207,10 @@ export class Hover {
*/
private async unshow() {
if (this.cursors <= 0) {
this.hideTimer = new Timeout(this.ephemeral ? 0 : hideTimeout);
this.hideTimer = new Timeout(this.ephemeral ? 0 : hideTimeout, `unshow ${this.anchor}`);

try {
await Promise.all(this.children.values());
await this.hideTimer.start();
await Promise.all(Array.from(this.children.values()).concat(this.hideTimer.start()));

if (this.cursors > 0) return;
await this.displace();
Expand All @@ -221,7 +231,7 @@ export class Hover {
* @param on Did the mouse move onto the popup, or off it?
*/
public async mouseEvent(on: boolean) {
this.cursors += on ? 1 : -1;
this.cursors = Math.max(this.cursors + (on ? 1 : -1), 0);

if (!this.shown) {
await this.unhide();
Expand Down
12 changes: 8 additions & 4 deletions support/web/js/lib/timeout.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
class TimeoutCancelled { };
class TimeoutCancelled { constructor(public name: string) { } };

export class Timeout {
public cancelled: TimeoutCancelled = new TimeoutCancelled();
public cancelled: TimeoutCancelled;

private token?: number;
private reject?: (t: TimeoutCancelled) => void;
public done: boolean = false;

constructor(private duration: number) {
constructor(private duration: number, public name: string) {
this.cancelled = new TimeoutCancelled(name);
}

private promise?: Promise<void>
public start(): Promise<void> {
if (this.duration === 0) {
return new Promise((resolve) => resolve());
}

return new Promise((resolve, reject) => {
if (this.promise) return this.promise;

return this.promise = new Promise((resolve, reject) => {
this.token = setTimeout(() => {
this.done = true;
resolve();
Expand Down

0 comments on commit c846bd6

Please sign in to comment.