Skip to content

Commit 8ce713d

Browse files
committed
feat: handle routing without page reload
1 parent b83ceb2 commit 8ce713d

File tree

3 files changed

+189
-85
lines changed

3 files changed

+189
-85
lines changed

wiki/public/js/render_wiki.js

-54
Original file line numberDiff line numberDiff line change
@@ -151,60 +151,6 @@ window.RenderWiki = class RenderWiki extends Wiki {
151151
}
152152
}
153153

154-
set_toc() {
155-
$(document).ready(function () {
156-
$(window).scroll(function () {
157-
if (currentAnchor().not(".no-underline").hasClass("active")) return;
158-
$(".page-toc a").removeClass("active");
159-
currentAnchor().addClass("active");
160-
});
161-
162-
const navbarHeight = $(".navbar").height();
163-
$(".page-toc a").click(function (e) {
164-
e.preventDefault();
165-
var target = $(this).attr("href");
166-
var offset = $(target).offset().top - navbarHeight - 50;
167-
$("html, body").animate(
168-
{
169-
scrollTop: offset,
170-
},
171-
100
172-
);
173-
});
174-
});
175-
176-
function tocItem(anchor) {
177-
return $('[href="' + anchor + '"]');
178-
}
179-
180-
function heading(anchor) {
181-
return $("[id=" + anchor.substr(1) + "]");
182-
}
183-
184-
var _anchors = null;
185-
function anchors() {
186-
if (!_anchors) {
187-
_anchors = $(".page-toc .list-unstyled a").map(function () {
188-
return $(this).attr("href");
189-
});
190-
}
191-
return _anchors;
192-
}
193-
194-
function currentAnchor() {
195-
var winY = window.pageYOffset;
196-
var currAnchor = null;
197-
anchors().each(function () {
198-
var y = heading(this).position().top;
199-
if (y < winY + window.innerHeight * 0.23) {
200-
currAnchor = this;
201-
return;
202-
}
203-
});
204-
return tocItem(currAnchor);
205-
}
206-
}
207-
208154
set_nav_buttons() {
209155
var current_index = -1;
210156
const sidebar_items = $(".sidebar-column").find("a").not(".navbar-brand");

wiki/public/js/wiki.js

+174-31
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,96 @@
1+
function add_link_to_headings() {
2+
$(".from-markdown")
3+
.not(".revision-content")
4+
.find("h1, h2, h3, h4, h5, h6")
5+
.each((i, $heading) => {
6+
const text = $heading.textContent.trim();
7+
$heading.id = text
8+
.replace(/[^\u00C0-\u1FFF\u2C00-\uD7FF\w\- ]/g, "")
9+
.replace(/[ ]/g, "-")
10+
.toLowerCase();
11+
12+
let id = $heading.id;
13+
let $a = $('<a class="no-underline">')
14+
.prop("href", "#" + id)
15+
.attr("aria-hidden", "true").html(`
16+
<svg xmlns="http://www.w3.org/2000/svg" style="width: 0.8em; height: 0.8em;" viewBox="0 0 24 24" fill="none" stroke="currentColor"
17+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link">
18+
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
19+
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
20+
</svg>
21+
`);
22+
$($heading).append($a);
23+
});
24+
}
25+
26+
function add_click_to_copy() {
27+
$("pre code")
28+
.parent("pre")
29+
.prepend(
30+
`<button title="Copy Code" class="btn copy-btn" data-toggle="tooltip"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-clipboard"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg></button>`
31+
);
32+
33+
$(".copy-btn").on("click", function () {
34+
frappe.utils.copy_to_clipboard($(this).siblings("code").text());
35+
});
36+
}
37+
38+
function set_toc() {
39+
// Reset scroll event listener to avoid scroll jitterness
40+
$(window).off("scroll");
41+
$(document).ready(function () {
42+
$(window).scroll(function () {
43+
if (currentAnchor().not(".no-underline").hasClass("active")) return;
44+
$(".page-toc a").removeClass("active");
45+
currentAnchor().addClass("active");
46+
});
47+
48+
const navbarHeight = $(".navbar").height();
49+
$(".page-toc a").click(function (e) {
50+
e.preventDefault();
51+
var target = $(this).attr("href");
52+
var offset = $(target).offset().top - navbarHeight - 50;
53+
$("html, body").animate(
54+
{
55+
scrollTop: offset,
56+
},
57+
100
58+
);
59+
});
60+
});
61+
62+
function tocItem(anchor) {
63+
return $('[href="' + anchor + '"]');
64+
}
65+
66+
function heading(anchor) {
67+
return $("[id=" + anchor.substr(1) + "]");
68+
}
69+
70+
var _anchors = null;
71+
function anchors() {
72+
if (!_anchors) {
73+
_anchors = $(".page-toc .list-unstyled a").map(function () {
74+
return $(this).attr("href");
75+
});
76+
}
77+
return _anchors;
78+
}
79+
80+
function currentAnchor() {
81+
var winY = window.pageYOffset;
82+
var currAnchor = null;
83+
anchors().each(function () {
84+
var y = heading(this).position()?.top;
85+
if (y < winY + window.innerHeight * 0.23) {
86+
currAnchor = this;
87+
return;
88+
}
89+
});
90+
return tocItem(currAnchor);
91+
}
92+
}
93+
194
window.Wiki = class Wiki {
295
activate_sidebars() {
396
$(".sidebar-item").each(function (index) {
@@ -18,6 +111,20 @@ window.Wiki = class Wiki {
18111
.scrollTo(0, topOffset - 200);
19112
}, 50);
20113
}
114+
115+
$($(this))
116+
.find("a")
117+
.on("click", (e) => {
118+
e.preventDefault();
119+
const href = $(e.currentTarget).attr("href");
120+
loadWikiPage(href, e.currentTarget);
121+
$("html, body").animate(
122+
{
123+
scrollTop: 0,
124+
},
125+
100
126+
);
127+
});
21128
});
22129
}
23130

@@ -96,41 +203,16 @@ window.Wiki = class Wiki {
96203
});
97204
}
98205

206+
set_toc() {
207+
set_toc();
208+
}
209+
99210
add_link_to_headings() {
100-
$(".from-markdown")
101-
.not(".revision-content")
102-
.find("h1, h2, h3, h4, h5, h6")
103-
.each((i, $heading) => {
104-
const text = $heading.textContent.trim();
105-
$heading.id = text
106-
.replace(/[^\u00C0-\u1FFF\u2C00-\uD7FF\w\- ]/g, "")
107-
.replace(/[ ]/g, "-")
108-
.toLowerCase();
109-
110-
let id = $heading.id;
111-
let $a = $('<a class="no-underline">')
112-
.prop("href", "#" + id)
113-
.attr("aria-hidden", "true").html(`
114-
<svg xmlns="http://www.w3.org/2000/svg" style="width: 0.8em; height: 0.8em;" viewBox="0 0 24 24" fill="none" stroke="currentColor"
115-
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link">
116-
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
117-
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
118-
</svg>
119-
`);
120-
$($heading).append($a);
121-
});
211+
add_link_to_headings();
122212
}
123213

124214
add_click_to_copy() {
125-
$("pre code")
126-
.parent("pre")
127-
.prepend(
128-
`<button title="Copy Code" class="btn copy-btn" data-toggle="tooltip"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-clipboard"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg></button>`
129-
);
130-
131-
$(".copy-btn").on("click", function () {
132-
frappe.utils.copy_to_clipboard($(this).siblings("code").text());
133-
});
215+
add_click_to_copy();
134216
}
135217
};
136218

@@ -146,3 +228,64 @@ $(document).on("click", function (e) {
146228
$("#navbar-dropdown-content").addClass("hide");
147229
}
148230
});
231+
232+
function loadWikiPage(url, pageElement, replaceState = false) {
233+
// Update URL and history state
234+
const historyMethod = replaceState ? "replaceState" : "pushState";
235+
window[`history`][historyMethod](
236+
{
237+
pageName: $(pageElement).closest(".sidebar-item").data("name"),
238+
url: url,
239+
},
240+
"",
241+
url
242+
);
243+
244+
// Get the wiki page name from the parent element's data attribute
245+
const pageName = $(pageElement).closest(".sidebar-item").data("name");
246+
247+
frappe.call({
248+
method: "wiki.wiki.doctype.wiki_page.wiki_page.get_page_content",
249+
args: { wiki_page_name: pageName },
250+
callback: (r) => {
251+
if (r.message) {
252+
$(".wiki-content").html(r.message.content);
253+
254+
$(".wiki-title").html(r.message.title);
255+
256+
if (r.message.toc_html) {
257+
$(".page-toc .list-unstyled").html(r.message.toc_html);
258+
}
259+
260+
// Update active sidebar item
261+
$(".sidebar-item").removeClass("active");
262+
$(".sidebar-item").find("a").removeClass("active");
263+
$(pageElement).closest(".sidebar-item").addClass("active");
264+
$(pageElement).addClass("active");
265+
266+
// Re-initialize necessary components
267+
add_link_to_headings();
268+
add_click_to_copy();
269+
set_toc();
270+
}
271+
},
272+
});
273+
}
274+
275+
window.addEventListener("popstate", function (event) {
276+
if (event.state && event.state.pageName) {
277+
const sidebarItem = $(`.sidebar-item[data-name="${event.state.pageName}"]`);
278+
if (sidebarItem.length) {
279+
const pageElement = sidebarItem.find("a")[0];
280+
loadWikiPage(event.state.url, pageElement, true);
281+
}
282+
} else {
283+
// Fallback to path-based lookup
284+
const path = window.location.pathname;
285+
const sidebarItem = $(`.sidebar-item[data-route="${path.slice(1)}"]`);
286+
if (sidebarItem.length) {
287+
const pageElement = sidebarItem.find("a")[0];
288+
loadWikiPage(path, pageElement, true);
289+
}
290+
}
291+
});

wiki/wiki/doctype/wiki_page/wiki_page.py

+15
Original file line numberDiff line numberDiff line change
@@ -635,3 +635,18 @@ def get_markdown_content(wikiPageName, wikiPagePatch):
635635
new_code, new_title = frappe.db.get_value("Wiki Page Patch", wikiPagePatch, ["new_code", "new_title"])
636636
return {"content": new_code, "title": new_title}
637637
return frappe.db.get_value("Wiki Page", wikiPageName, ["content", "title"], as_dict=True)
638+
639+
640+
@frappe.whitelist(allow_guest=True)
641+
def get_page_content(wiki_page_name):
642+
wiki_page = frappe.get_cached_doc("Wiki Page", wiki_page_name)
643+
content = wiki_page.content
644+
645+
html = frappe.utils.md_to_html(content)
646+
wiki_settings = frappe.get_single("Wiki Settings")
647+
648+
return {
649+
"title": wiki_page.title,
650+
"content": html,
651+
"toc_html": (wiki_page.calculate_toc_html(html) if wiki_settings.enable_table_of_contents else None),
652+
}

0 commit comments

Comments
 (0)