Skip to content
This repository has been archived by the owner on Oct 24, 2023. It is now read-only.

Commit

Permalink
REFACTOR: rework of most of client side logic (#24)
Browse files Browse the repository at this point in the history
- updates markdown-it-footnote lib from 3.0.1 to 3.0.3
- fixes a handful of bugs related to the arrow
- ensures footnote separator is not taking height in the post
- simplify code
- adds a test
- get rid of es6
  • Loading branch information
jjaffeux committed Dec 7, 2021
1 parent a2071d7 commit 683fb3f
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 119 deletions.
Expand Up @@ -4,52 +4,78 @@ import { iconHTML } from "discourse-common/lib/icon-library";

let inlineFootnotePopper;

function createTooltip() {
const tooltip = document.createElement("div");
tooltip.id = "footnote-tooltip";

if (window.devicePixelRatio && window.devicePixelRatio > 1) {
tooltip.classList.add("retina");
}
function applyInlineFootnotes(elem) {
const footnoteRefs = elem.querySelectorAll("sup.footnote-ref");

const pointer = document.createElement("div");
pointer.classList.add("footnote-tooltip-pointer");
tooltip.append(pointer);
footnoteRefs.forEach((footnoteRef) => {
const button = document.createElement("button");
button.classList.add("expand-footnote", "btn", "btn-icon", "no-text");
button.innerHTML = iconHTML("ellipsis-h");
button.dataset.footnoteId = footnoteRef
.querySelector("a")
.id.replace("footnote-ref-", "");

const content = document.createElement("div");
content.classList.add("footnote-tooltip-content");
tooltip.append(content);
footnoteRef.after(button);
});

document.body.append(tooltip);
if (footnoteRefs.length) {
elem.classList.add("inline-footnotes");
}
}

return tooltip;
function buildTooltip() {
let html = `
<div id="footnote-tooltip" role="tooltip">
<div class="footnote-tooltip-content"></div>
<div id="arrow" data-popper-arrow></div>
</div>
`;

let template = document.createElement("template");
html = html.trim();
template.innerHTML = html;
return template.content.firstChild;
}

function showFootnote(event) {
function footNoteEventHandler(event) {
inlineFootnotePopper?.destroy();

let tooltip = document.querySelector("#footnote-tooltip");
const tooltip = document.getElementById("footnote-tooltip");

tooltip && tooltip.classList.remove("is-expanded");
// reset state by hidding tooltip, it handles "click outside"
// allowing to hide the tooltip when you click anywhere else
if (tooltip) {
tooltip.removeAttribute("data-show");
}

// if we didn't actually click a footnote button, exit early
if (!event.target.classList.contains("expand-footnote")) {
return;
}

// append footnote to tooltip body
const button = event.target;
const cooked = button.closest(".cooked");

tooltip = tooltip || createTooltip();
tooltip.classList.add("is-expanded");

const footnoteId = button.dataset.footnoteId;
const footnoteContent = tooltip.querySelector(".footnote-tooltip-content");
const newContent = cooked.querySelector(`#footnote-${footnoteId}`);
footnoteContent.innerHTML = newContent.innerHTML;

// eslint-disable-next-line
// remove backref from tooltip
const backRef = footnoteContent.querySelector(".footnote-backref");
backRef.parentNode.removeChild(backRef);

// display tooltip
tooltip.dataset.show = "";

// setup popper
inlineFootnotePopper?.destroy();
inlineFootnotePopper = createPopper(button, tooltip, {
modifiers: [
{
name: "arrow",
options: { element: tooltip.querySelector("#arrow") },
},
{
name: "offset",
options: {
Expand All @@ -60,30 +86,6 @@ function showFootnote(event) {
});
}

function inlineFootnotes(elem) {
const footnoteRefs = elem.querySelectorAll("sup.footnote-ref");

footnoteRefs.forEach((footnoteRef) => {
const button = document.createElement("button");
button.classList.add("expand-footnote", "btn", "btn-icon", "no-text");
button.innerHTML = iconHTML("ellipsis-h");
button.dataset.footnoteId = footnoteRef
.querySelector("a")
.id.replace("footnote-ref-", "");

footnoteRef.after(button);
});

if (footnoteRefs.length) {
elem.classList.add("inline-footnotes");
}
}

function clearPopper() {
inlineFootnotePopper?.destroy();
inlineFootnotePopper = null;
}

export default {
name: "inline-footnotes",

Expand All @@ -92,29 +94,21 @@ export default {
return;
}

document.documentElement.append(buildTooltip());

window.addEventListener("click", footNoteEventHandler);

withPluginApi("0.8.9", (api) => {
api.decorateCookedElement((elem) => inlineFootnotes(elem), {
api.decorateCookedElement((elem) => applyInlineFootnotes(elem), {
onlyStream: true,
id: "inline-footnotes",
});

api.cleanupStream(() => clearPopper);
});

const main = document.querySelector("#main");

if (main) {
main.addEventListener("click", showFootnote);
}
},

teardown() {
const main = document.querySelector("#main");

if (main) {
main.removeEventListener("click", showFootnote);
}

clearPopper();
inlineFootnotePopper?.destroy();
window.removeEventListener("click", footNoteEventHandler);
document.getElementById("footnote-tooltip")?.remove();
},
};
12 changes: 8 additions & 4 deletions assets/javascripts/vendor/markdown-it-footnote.js
@@ -1,5 +1,6 @@
/* eslint-disable */
/*! markdown-it-footnote 3.0.1 https://github.com//markdown-it/markdown-it-footnote @license MIT */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.markdownitFootnote = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){

/*! markdown-it-footnote 3.0.3 https://github.com//markdown-it/markdown-it-footnote @license MIT */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.markdownitFootnote = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
// Process footnotes
//
'use strict';
Expand Down Expand Up @@ -214,7 +215,10 @@ module.exports = function footnote_plugin(md) {
token = state.push('footnote_ref', '', 0);
token.meta = { id: footnoteId };

state.env.footnotes.list[footnoteId] = { tokens: tokens };
state.env.footnotes.list[footnoteId] = {
content: state.src.slice(labelStart, labelEnd),
tokens: tokens
};
}

state.pos = labelEnd + 1;
Expand Down Expand Up @@ -322,7 +326,7 @@ module.exports = function footnote_plugin(md) {

token = new state.Token('inline', '', 0);
token.children = list[i].tokens;
token.content = '';
token.content = list[i].content;
tokens.push(token);

token = new state.Token('paragraph_close', 'p', -1);
Expand All @@ -333,7 +337,7 @@ module.exports = function footnote_plugin(md) {
tokens = refTokens[':' + list[i].label];
}

state.tokens = state.tokens.concat(tokens);
if (tokens) state.tokens = state.tokens.concat(tokens);
if (state.tokens[state.tokens.length - 1].type === 'paragraph_close') {
lastParagraph = state.tokens.pop();
} else {
Expand Down
99 changes: 48 additions & 51 deletions assets/stylesheets/footnotes.scss
@@ -1,6 +1,3 @@
$footnote-tooltip-background: var(--secondary);
$footnote-tooltip-border: var(--primary-medium);

.inline-footnotes {
button.expand-footnote {
padding: 0px 0.5em;
Expand All @@ -16,7 +13,8 @@ $footnote-tooltip-border: var(--primary-medium);

// This is hack to work with lazy-loading, we will trick the browser
// to believe the image is in the DOM and can be loaded
.footnotes-list {
.footnotes-list,
.footnotes-sep {
position: absolute;
}

Expand All @@ -32,56 +30,17 @@ $footnote-tooltip-border: var(--primary-medium);
}

#footnote-tooltip {
background-color: $footnote-tooltip-background;
background-color: var(--primary-low);
color: var(--primary);
padding: 0.5em;
font-size: var(--font-down-1);
border-radius: 3px;
display: none;
z-index: z("tooltip");
border: 1px solid $footnote-tooltip-border;
max-width: 400px;
overflow-wrap: break-word;

&:not(.is-expanded) {
display: none;
}

&.retina {
border: 0.5px solid $footnote-tooltip-border;
}

.footnote-tooltip-pointer {
position: relative;
background: var(--secondary);
}

.footnote-tooltip-pointer:before,
.footnote-tooltip-pointer:after {
position: absolute;
pointer-events: none;
border: solid transparent;
bottom: 100%;
content: "";
height: 0;
width: 0;
}

.footnote-tooltip-pointer:after {
border-bottom-color: $footnote-tooltip-background;
border-width: 8px;
left: 50%;
margin-left: -8px;
margin-bottom: -0.5px;
}

.footnote-tooltip-pointer:before {
border-bottom-color: $footnote-tooltip-border;
border-width: 9px;
left: 50%;
margin-left: -9px;
}

.footnote-tooltip-content {
border: 5px solid var(--secondary);
font-size: 0.93em;
color: var(--primary-medium);
line-height: 1.4em;
overflow: hidden;
min-width: 100px;

Expand All @@ -90,8 +49,46 @@ $footnote-tooltip-border: var(--primary-medium);
max-width: 385px;
}

.footnote-backref {
display: none;
p {
margin: 0;
}
}
}

#footnote-tooltip[data-show] {
display: block;
}

#arrow,
#arrow::before {
position: absolute;
width: 10px;
height: 10px;
background: inherit;
}

#arrow {
visibility: hidden;
}

#arrow::before {
visibility: visible;
content: "";
transform: rotate(45deg);
}

#footnote-tooltip[data-popper-placement^="top"] > #arrow {
bottom: -4px;
}

#footnote-tooltip[data-popper-placement^="bottom"] > #arrow {
top: -4px;
}

#footnote-tooltip[data-popper-placement^="left"] > #arrow {
right: -4px;
}

#footnote-tooltip[data-popper-placement^="right"] > #arrow {
left: -4px;
}
2 changes: 1 addition & 1 deletion plugin.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true

# transpile_js: true
# name: discourse-footnote
# about: Adds markdown.it footnote support to Discourse
# version: 0.1
Expand Down
42 changes: 42 additions & 0 deletions test/javascripts/acceptance/footnote-test.js
@@ -0,0 +1,42 @@
import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
import { click, visit } from "@ember/test-helpers";
import { test } from "qunit";
import topicFixtures from "discourse/tests/fixtures/topic";
import { cloneJSON } from "discourse-common/lib/object";

acceptance("Discourse Foonote Plugin", function (needs) {
needs.user();

needs.settings({
display_footnotes_inline: true,
});

needs.pretender((server, helper) => {
server.get("/t/45.json", () => {
let topic = cloneJSON(topicFixtures["/t/28830/1.json"]);
topic["post_stream"]["posts"][0]["cooked"] = `
<p>Lorem ipsum dolor sit amet<sup class="footnote-ref"><a href="#footnote-17-1" id="footnote-ref-17-1">[1]</a></sup></p>
<hr class="footnotes-sep">
<ol class="footnotes-list">
<li id="footnote-17-1" class="footnote-item">
<p>consectetur adipiscing elit <a href="#footnote-ref-17-1" class="footnote-backref">↩︎</a></p>
</li>
</ol>
`;
return helper.response(topic);
});
});

test("displays the foonote on click", async function (assert) {
await visit("/t/45");

const tooltip = document.getElementById("footnote-tooltip");
assert.ok(exists(tooltip));

await click(".expand-footnote");

const tooltipContent = tooltip.querySelector(".footnote-tooltip-content")
.innerText;
assert.equal(tooltipContent, "consectetur adipiscing elit");
});
});

0 comments on commit 683fb3f

Please sign in to comment.