Updating the Krystal horizontal Storyriver plugin for 5.3

Hey ho friends of the horizontal storyriver.
I just updated my wiki with my implementation of crazko’s horizontal storyriver and encountered some things that do not work as would like them to.
An update of crazkos original wiki also failed.
My apporach some time ago was to turn crazkos wiki into a plugin which uses the new Layout-feature.

  • The most unexpected thing is that the reavealwidget (or the qualify widget?!) of the button to switch the layout does not work.
  • The other thing that does not work are the rotated titles of the compressed tiddlers right and left. The span class (krystal-tiddler__title tc-droppable krystal-tiddler__title–start-active) is there but not shown.

Anybody can help? The win is a horizontal storyriver…

2 Likes

This is the code of the horizontal storyview in $:/plugins/krystal/plugin.js
It seems to be that something in this code is deprecated since the 5.3 update because the class ```
krystal-tiddler__title–start-active is not applied anymore.

/*\
title: $:/plugins/krystal/plugin.js
type: application/javascript
module-type: startup

Sets plugin behavior

\*/
(function () {
  const STORY_TIDDLER_TITLE = "$:/StoryList";
  const ACTIVE_LINK_CLASS = "krystal-link--active";
  const MAXIMIZED_TIDDLER_CLASS = "krystal-tiddler__frame--maximized";

  const KRYSTAL_CONFIG = {
    highlight: "$:/plugins/krystal/config/highlight",
    tiddlerwidth: "$:/plugins/krystal/config/tiddlerwidth",
  };

  exports.after = ["render"];

  exports.startup = function () {

    const throttledTiddlerFrameEffects = throttle(tiddlerFrameEffects, 100);
    window.addEventListener("scroll", throttledTiddlerFrameEffects, true);

    $tw.rootWidget.addEventListener("tm-remove", tiddlersCount);
    $tw.rootWidget.addEventListener("tm-scroll", function (event) {
      if (event.type === "tm-scroll") {
        tiddlersCount(event);
        scroll(event);
      }
    });

    $tw.rootWidget.addEventListener("tm-maximize", function (event) {
      if (event.type === "tm-maximize") {
        tiddlerFullscreen(event.param);
      }
    });

    $tw.hooks.addHook("th-navigating", closeTiddlersToRight);

    $tw.wiki.addEventListener("change", highlightOpenTiddlerLinks);
    $tw.wiki.addEventListener("change", reinitiateTiddlerFrameEffects);
  };

  function highlightOpenTiddlerLinks(changes) {
    if (!$tw.utils.hop(changes, STORY_TIDDLER_TITLE)) {
      return;
    }

    const config = $tw.wiki.getTiddler(KRYSTAL_CONFIG.highlight);

    if (config && config.fields && config.fields.text === "disable") {
      return;
    }

    const tiddlers = $tw.wiki.getTiddlerList(STORY_TIDDLER_TITLE);

    Array.from(document.querySelectorAll(`.${ACTIVE_LINK_CLASS}`)).forEach(
      (link) => link.classList.remove(ACTIVE_LINK_CLASS)
    );

    tiddlers.forEach((tiddler) => {
      Array.from(
        document.querySelectorAll(
          `.tc-tiddler-body a[href="#${encodeURIComponent(tiddler)}"]`
        )
      ).forEach((link) => link.classList.add(ACTIVE_LINK_CLASS));
    });
  }

  function tiddlersCount(event) {
    const { widget } = event;
    var tiddlersCount = widget.list.length;
    document.body.style.setProperty("--tiddler-count", tiddlersCount);
  }

  function scroll(event) {
    const { target: tiddlerElement } = event;
    const mediaQueryList = window.matchMedia("(min-width: 960px)");

    if (mediaQueryList.matches) {
      var storyRiver = tiddlerElement.parentElement;

      var tiddlers = Array.from(
        storyRiver.querySelectorAll("div[data-tiddler-title]")
      );
      var tiddlerPosition = tiddlers.indexOf(tiddlerElement);
      var tiddlerWidth = $tw.wiki.getTiddler(KRYSTAL_CONFIG.tiddlerwidth).fields
        .text;
      var windowWidth = window.innerWidth;

      var position = windowWidth / 2 - tiddlerWidth / 2;
      var newRiverPosition = Math.max(
        tiddlerPosition * tiddlerWidth - position,
        0
      );

      storyRiver.scroll({ left: newRiverPosition, behavior: "smooth" });
    } else {
      tiddlerElement.scrollIntoView({ behavior: "smooth" });
    }
  }

  function tiddlerFrameEffects() {
    var tiddlers = Array.from(document.querySelectorAll(".tc-tiddler-frame"));
    var tiddlersCount = tiddlers.length;

    if (tiddlersCount === 0) {
      return;
    }

    var offset = 100;
    var tiddlerPadding = Number(
      window.getComputedStyle(tiddlers[0]).paddingRight.slice(0, -2)
    );
    var tiddlerWidth = tiddlers[0].offsetWidth;
    var windowWidth = window.innerWidth;

    tiddlers.forEach(function (tiddler, i) {
      if (i === 0) {
        return;
      }

      var tiddlerTitle = tiddler.querySelector(".krystal-tiddler__title");

      // Edit Template
      if (!tiddlerTitle) {
        return;
      }

      var previousTiddler = tiddlers[i - 1];
      var previousTiddlerTitle = previousTiddler.querySelector(
        ".krystal-tiddler__title"
      );

      var x = tiddler.getBoundingClientRect().left;

      var start = x < offset + i * tiddlerPadding;
      var end =
        x > windowWidth - (offset + (tiddlersCount - i) * tiddlerPadding);

      var startOverlay = x < tiddlerWidth + (i - 1) * tiddlerPadding;

      tiddler.classList.toggle("krystal-tiddler__frame--overlay", startOverlay);

      if (previousTiddlerTitle) {
        previousTiddlerTitle.classList.toggle(
          "krystal-tiddler__title--start-active",
          start
        );

        if (!end) {
          previousTiddler.classList.toggle(
            "krystal-tiddler__frame--hide",
            start
          );
        }
      }

      if (start) {
        tiddlerTitle.classList.remove("krystal-tiddler__title--end");
      }

      if (end) {
        tiddler.classList.add("krystal-tiddler__frame--overlay");
        tiddler.classList.add("krystal-tiddler__frame--hide");
        tiddlerTitle.classList.add(
          "krystal-tiddler__title--end",
          "krystal-tiddler__title--end-active"
        );
      } else {
        if (!startOverlay) {
          tiddler.classList.remove("krystal-tiddler__frame--overlay");
        }

        tiddler.classList.remove("krystal-tiddler__frame--hide");
        tiddlerTitle.classList.remove("krystal-tiddler__title--end-active");
      }
    });
  }

  function reinitiateTiddlerFrameEffects(changes) {
    if (!$tw.utils.hop(changes, STORY_TIDDLER_TITLE)) {
      return;
    }
    var duration = $tw.utils.getAnimationDuration() || 0;
    setTimeout(tiddlerFrameEffects, duration);
  }

  function closeTiddlersToRight(event) {
    const storyTiddler = $tw.wiki.getTiddler(STORY_TIDDLER_TITLE);
    const tiddlers = $tw.wiki.getTiddlerList(STORY_TIDDLER_TITLE);

    const navigateFrom = event.navigateFromTitle;
    const navigateTo = event.navigateTo;

    const config = $tw.wiki.getTiddler(
      "$:/plugins/krystal/config/close"
    );

    if (config && config.fields && config.fields.text === "disable") {
      return event;
    }

    // Navigating from outside open tiddlers
    if (!navigateFrom) {
      return event;
    }

    // Destination tiddler already open
    if (tiddlers.indexOf(navigateTo) === -1) {
      const currentTiddlerIndex = tiddlers.indexOf(navigateFrom);
      const tiddlersToClose = tiddlers.slice(currentTiddlerIndex + 1);

      if (tiddlersToClose.length === 0) {
        return event;
      }

      const newStoryList = tiddlers.filter(
        (title) => !tiddlersToClose.includes(title)
      );

      $tw.wiki.addTiddler(
        new $tw.Tiddler({ title: STORY_TIDDLER_TITLE }, storyTiddler, {
          list: newStoryList,
        })
      );
    }

    return event;
  }

  function tiddlerFullscreen(tiddlerTitle) {
    const tiddler = document.querySelector(
      `div[data-tiddler-title="${tiddlerTitle}"]`
    );

    tiddler.classList.toggle(MAXIMIZED_TIDDLER_CLASS);
  }

  // ---

  function throttle(callback, limit) {
    var wait = false; // Initially, we're not waiting

    return function () {
      // We return a throttled function
      if (!wait) {
        // If we're not waiting
        callback.call(); // Execute users function
        wait = true; // Prevent future invocations
        setTimeout(function () {
          // After a period of time
          wait = false; // And allow future invocations
        }, limit);
      }
    };
  }
})();

To encircle the problem, I assume it occurs in the function tiddlerFrameEffects- Is there something that does not work anymore in 5.3:

  function tiddlerFrameEffects() {
    var tiddlers = Array.from(document.querySelectorAll(".tc-tiddler-frame"));
    var tiddlersCount = tiddlers.length;

    if (tiddlersCount === 0) {
      return;
    }

    var offset = 100;
    var tiddlerPadding = Number(
      window.getComputedStyle(tiddlers[0]).paddingRight.slice(0, -2)
    );
    var tiddlerWidth = tiddlers[0].offsetWidth;
    var windowWidth = window.innerWidth;

    tiddlers.forEach(function (tiddler, i) {
      if (i === 0) {
        return;
      }

      var tiddlerTitle = tiddler.querySelector(".krystal-tiddler__title");

      // Edit Template
      if (!tiddlerTitle) {
        return;
      }

      var previousTiddler = tiddlers[i - 1];
      var previousTiddlerTitle = previousTiddler.querySelector(
        ".krystal-tiddler__title"
      );

      var x = tiddler.getBoundingClientRect().left;

      var start = x < offset + i * tiddlerPadding;
      var end =
        x > windowWidth - (offset + (tiddlersCount - i) * tiddlerPadding);

      var startOverlay = x < tiddlerWidth + (i - 1) * tiddlerPadding;

      tiddler.classList.toggle("krystal-tiddler__frame--overlay", startOverlay);

      if (previousTiddlerTitle) {
        previousTiddlerTitle.classList.toggle(
          "krystal-tiddler__title--start-active",
          start
        );

        if (!end) {
          previousTiddler.classList.toggle(
            "krystal-tiddler__frame--hide",
            start
          );
        }
      }

      if (start) {
        tiddlerTitle.classList.remove("krystal-tiddler__title--end");
      }

      if (end) {
        tiddler.classList.add("krystal-tiddler__frame--overlay");
        tiddler.classList.add("krystal-tiddler__frame--hide");
        tiddlerTitle.classList.add(
          "krystal-tiddler__title--end",
          "krystal-tiddler__title--end-active"
        );
      } else {
        if (!startOverlay) {
          tiddler.classList.remove("krystal-tiddler__frame--overlay");
        }

        tiddler.classList.remove("krystal-tiddler__frame--hide");
        tiddlerTitle.classList.remove("krystal-tiddler__title--end-active");
      }
    });
  }

  function reinitiateTiddlerFrameEffects(changes) {
    if (!$tw.utils.hop(changes, STORY_TIDDLER_TITLE)) {
      return;
    }
    var duration = $tw.utils.getAnimationDuration() || 0;
    setTimeout(tiddlerFrameEffects, duration);
  }