new column layout + modal global graph + partial tree menu

This commit is contained in:
DhammaCharts 2022-07-09 21:34:21 +01:00
parent 74993d19b7
commit cae6e69d8c
12 changed files with 511 additions and 53 deletions

91
assets/js/drawTree.js Normal file
View File

@ -0,0 +1,91 @@
async function drawTree(pathBase){
// async function drawTree(){
const { content } = await fetchData;
// COMMENT : add a uid for pages and folders id ? will avoid problems if duplicates in page name and folder name
// we want to build an array of objects, one for each page and folder (type)
const tree = [];
for (let path in content) {
const c = content[path];
const pageTitle = c.title;
const crumb = path.split("/");
// ['', 'folder1','folder2', ... , pageId ]
let pageId = crumb.pop();
if (pageId == '') pageId = '_ROOT_';
let parentFolderId = crumb.slice(-1)[0];
if (parentFolderId == '' && pageId == '_ROOT_') parentFolderId = 'SUPER-ROOT';
if (parentFolderId == '') parentFolderId = 'ROOT';
parentFolderId = '_' + parentFolderId + '_'; // added to distinguished from pageId
// we found a page
tree.push({
id: pageId,
parentId: parentFolderId,
name: pageTitle,
type: 'page',
href: pathBase.slice(0, pathBase.length - 1) + path
})
// if the page is in one or more folders
crumb.forEach((folderId, level) => {
let parentId = crumb[level - 1];
if (parentId == '') {
parentId = '_ROOT_'
} else {
parentId = '_' + parentId + '_';
}
// we found a folder
const push = {
id: '_' + folderId + '_',
parentId: parentId,
name: folderId.replace(/-/g, ' '),
type: 'folder',
// type : Tree.FOLDER,
level: level
}
// avoid duplicates of folders
if (folderId != '' && !tree.some(el => JSON.stringify(el) === JSON.stringify(push)))
tree.push(push);
});
}
// METHODE 1
// FYI https://www.jstree.com/docs/json/ doesn't need a hierarchial JSON
// it needs jQuery though. Not used for the moment
//METHODE 2
// build the hierarchial JSON
// from https://typeofnan.dev/an-easy-way-to-build-a-tree-with-object-references/
let root;
const idMapping = tree.reduce((acc, el, i) => {
acc[el.id] = i;
return acc;
}, {});
tree.forEach((el) => {
// Handle the root element
if (el.parentId == '_SUPER-ROOT_') {
root = el;
return;
}
// Use our mapping to locate the parent element in our data array
const parentEl = tree[idMapping[el.parentId]];
// Add our current el to its parent's `children` array
parentEl.children = [...(parentEl.children || []), el];
});
// display tree structure
// from https://www.cssscript.com/folder-tree-json/
// keep track of the original node objects
const structure = root.children;
return structure
}

View File

@ -1,4 +1,4 @@
async function drawGraph(baseUrl, isHome, pathColors, graphConfig) { async function drawGraph(baseUrl, isHome, pathColors, graphConfig, modal = false) {
let { let {
depth, depth,
@ -10,7 +10,9 @@ async function drawGraph(baseUrl, isHome, pathColors, graphConfig) {
repelForce, repelForce,
fontSize} = graphConfig; fontSize} = graphConfig;
const container = document.getElementById("graph-container") const elementId = modal ? "graph-container-modal" : "graph-container" ;
const container = document.getElementById(elementId);
const { index, links, content } = await fetchData const { index, links, content } = await fetchData
// Use .pathname to remove hashes / searchParams / text fragments // Use .pathname to remove hashes / searchParams / text fragments
@ -109,7 +111,7 @@ async function drawGraph(baseUrl, isHome, pathColors, graphConfig) {
.force("center", d3.forceCenter()) .force("center", d3.forceCenter())
const svg = d3 const svg = d3
.select("#graph-container") .select('#'+elementId)
.append("svg") .append("svg")
.attr("width", width) .attr("width", width)
.attr("height", height) .attr("height", height)

View File

@ -184,11 +184,11 @@ article {
&.broken { &.broken {
opacity: 0.5; opacity: 0.5;
background-color: transparent; background-color: transparent;
} }
} }
} }
& p { & p {
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
@ -293,7 +293,7 @@ footer {
text-align: center; text-align: center;
& ul { & ul {
padding-left: 0; padding-left: 0;
} }
} }
hr { hr {
@ -319,6 +319,11 @@ hr {
flex-direction: row; flex-direction: row;
gap: 2em; gap: 2em;
&.vertical{
flex-direction: column;
gap: 0;
}
@media all and (max-width: 780px) { @media all and (max-width: 780px) {
flex-direction: column; flex-direction: column;
} }
@ -352,6 +357,18 @@ hr {
} }
} }
#graph-container-modal {
border: var(--outlinegray) 1px solid;
border-radius: 5px;
box-sizing: border-box;
min-height: 250px;
& > svg {
margin-bottom: -5px;
}
}
} }
.centered { .centered {
@ -591,5 +608,3 @@ header {
padding: 0 1em; padding: 0 1em;
} }
} }

44
assets/styles/column.scss Normal file
View File

@ -0,0 +1,44 @@
div.sticky {
position: -webkit-sticky;
position: sticky;
top: 0;
// background-color: yellow;
}
/* The Modal (background) */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 100px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
/* Modal Content */
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
/* The Close Button */
.close {
color: #aaaaaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}

File diff suppressed because one or more lines are too long

119
assets/styles/tree.scss Normal file
View File

@ -0,0 +1,119 @@
/* simple tree */
.simple-tree {
user-select: none;
-moz-user-select: none;
}
.simple-tree>details>summary {
display: none;
}
.simple-tree a,
.simple-tree summary {
display: block;
width: fit-content;
width: -moz-fit-content;
border: solid 1px transparent;
padding: 0 2px;
outline: none;
cursor: pointer;
}
.simple-tree a {
text-decoration: none;
// color: inherit;
font-weight: 600;
font-size: .9rem;
}
.simple-tree ::-webkit-details-marker {
display: none;
}
.simple-tree summary {
list-style-type: none;
// background-color: #eee;
outline: none;
}
.simple-tree.dark summary {
background-color: #444;
}
.simple-tree details>:not(details),
.simple-tree details {
position: relative;
}
.simple-tree details :not(summary) {
margin-left: 20px;
}
.simple-tree.nodots details :not(summary) {
margin-left: 12px;
}
.simple-tree details::before,
.simple-tree details>:not(details)::before {
content: '';
width: 10px;
display: block;
position: absolute;
}
.simple-tree details::before,
.simple-tree details>:not(details)::before {
// background: url('data:image/svg+xml;utf8,<svg viewBox="0 0 2 2" xmlns="http://www.w3.org/2000/svg"><g><rect x="0" y="0" width="1" height="1"/></g></svg>') left top / 2px 2px;
}
.simple-tree.dark details::before,
.simple-tree.dark details>:not(summary)::before {
background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 2 2" xmlns="http://www.w3.org/2000/svg"><g><rect x="0" y="0" width="1" height="1" fill="white"/></g></svg>');
}
.simple-tree.nodots details::before,
.simple-tree.nodots details>:not(summary)::before {
background-image: none;
}
.simple-tree details::before {
top: 0;
height: 100%;
background-repeat: repeat-y;
left: 5px;
z-index: -1;
}
.simple-tree details>:not(details)::before {
top: 8px;
height: calc(100% - 8px);
background-repeat: repeat-x;
left: -15px;
}
.simple-tree details>summary::before {
// background: url('data:image/svg+xml;utf8,<svg viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><g><rect x="0" y="0" width="12" height="12" fill="white" stroke="gray" stroke-width="1"/><line x1="3" y1="6" x2="9" y2="6" stroke="black" stroke-width="2"/><line x1="6" y1="3" x2="6" y2="9" stroke="black" stroke-width="2"/></g></svg>') center center / 12px 12px no-repeat;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 330 330" style="enable-background:new 0 0 330 330;" xml:space="preserve">
<path id="XMLID_222_" d="M250.606,154.389l-150-149.996c-5.857-5.858-15.355-5.858-21.213,0.001 c-5.857,5.858-5.857,15.355,0.001,21.213l139.393,139.39L79.393,304.394c-5.857,5.858-5.857,15.355,0.001,21.213 C82.322,328.536,86.161,330,90,330s7.678-1.464,10.607-4.394l149.999-150.004c2.814-2.813,4.394-6.628,4.394-10.606 C255,161.018,253.42,157.202,250.606,154.389z"/></svg>')center center / 12px 12px no-repeat;
left: -22px;
top: 2px;
width: 16px;
height: 16px;
}
.simple-tree details[open]>summary::before {
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 330 330" style="enable-background:new 0 0 330 330;" xml:space="preserve"><path id="XMLID_225_" d="M325.607,79.393c-5.857-5.857-15.355-5.858-21.213,0.001l-139.39,139.393L25.607,79.393 c-5.857-5.857-15.355-5.858-21.213,0.001c-5.858,5.858-5.858,15.355,0,21.213l150.004,150c2.813,2.813,6.628,4.393,10.606,4.393 s7.794-1.581,10.606-4.394l149.996-150C331.465,94.749,331.465,85.251,325.607,79.393z"/></svg>') center center / 12px 12px no-repeat;
// background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><title/><g><rect x="0" y="0" width="12" height="12" fill="white" stroke="gray" stroke-width="1"/><line x1="3" y1="6" x2="9" y2="6" stroke="black" stroke-width="2"/></g></svg>');
}
/* async tree */
.async-tree details[open][data-loaded=false] {
pointer-events: none;
}
.async-tree details[open][data-loaded=false]>summary::before {
background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"><g><animateTransform attributeName="transform" type="rotate" from="0 32 32" to="360 32 32" dur="1s" repeatCount="indefinite"/><circle cx="32" cy="32" r="32" fill="whitesmoke"/><path d="M 62 32 A 30 30 0 0 0 32 2" style="stroke: black; stroke-width:6; fill:none;"/></g></svg>');
}
.async-tree.black details[open][data-loaded=false]>summary::before {
background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"><g><animateTransform attributeName="transform" type="rotate" from="0 32 32" to="360 32 32" dur="1s" repeatCount="indefinite"/><circle cx="32" cy="32" r="32" fill="whitesmoke"/><path d="M 62 32 A 30 30 0 0 0 32 2" style="stroke: white; stroke-width:6; fill:none;"/></g></svg>');
}
/* select tree */
.select-tree .selected {
// background-color: #beebff;
border: var(--outlinegray)1px solid;
border-radius: 5px;
// z-index: 1;
}
.select-tree.dark .selected {
// background-color: #3a484e;
border-color: #99defd;
border-radius: 5px;
}
.selectedOnPage {
// background-color: #3a484e;
border-color: #99defd;
}

View File

@ -1,4 +1,4 @@
baseURL = "https://quartz.jzhao.xyz/" baseURL = "https://www.dhammacharts.org/quartz/"
languageCode = "en-us" languageCode = "en-us"
googleAnalytics = "G-XYFD95KB4J" googleAnalytics = "G-XYFD95KB4J"
pygmentsUseClasses = true pygmentsUseClasses = true

View File

@ -1,4 +1,5 @@
name: Jacky Zhao name: Jacky Zhao
enableColumnLayout: true
enableToc: true enableToc: true
openToc: false openToc: false
enableLinkPreview: true enableLinkPreview: true

View File

@ -3,31 +3,80 @@
{{ partial "head.html" . }} {{ partial "head.html" . }}
<body> <body>
{{partial "search.html" .}} {{partial "search.html" .}}
<div class="singlePage">
{{if $.Site.Data.config.enableColumnLayout}}
<r-c join>
<main data-lg1-2 data-lg2 data-m2 data-sm1 data-xs1 class="singlePage">
{{else}}
<main class="singlePage">
{{end}}
<!-- Begin actual content --> <!-- Begin actual content -->
<header> <header>
<h1 id="page-title"><a href="{{ .Site.BaseURL }}">{{ .Site.Data.config.page_title }}</a></h1> <h1 id="page-title"><a href="{{ .Site.BaseURL }}">{{ .Site.Data.config.page_title }}</a></h1>
<svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg> <svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7">
<title id="title">Search Icon</title>
<desc id="desc">Icon to open search</desc>
<g class="search-path" fill="none">
<path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4" />
<circle cx="8" cy="8" r="7" />
</g>
</svg>
<div class="spacer"></div> <div class="spacer"></div>
{{partial "darkmode.html" .}} {{partial "darkmode.html" .}}
</header> </header>
<article> <article>
{{if .Title}}<h1>{{ .Title }}</h1>{{end}} {{if .Title}}
<h1>{{ .Title }}</h1>{{end}}
<p class="meta"> <p class="meta">
Last updated {{if ne .Date .Lastmod}}{{ .Lastmod.Format "January 2, 2006" }}{{else}}Unknown{{end}}. Last updated {{if ne .Date .Lastmod}}{{ .Lastmod.Format "January 2, 2006" }}
{{else}}Unknown{{end}}.
{{ partial "github.html" . }} {{ partial "github.html" . }}
</p> </p>
<ul class="tags"> <ul class="tags">
{{ range (.GetTerms "tags") }} {{ range (.GetTerms "tags") }}
<li><a href="{{ .Permalink }}">{{ .LinkTitle | humanize }}</a></li> <li><a href="{{ .Permalink }}">{{ .LinkTitle | humanize }}</a></li>
{{ end }} {{ end }}
</ul> </ul>
{{partial "toc.html" .}} {{partial "toc.html" .}}
{{partial "textprocessing.html" . }} {{partial "textprocessing.html" . }}
</article> </article>
{{partial "footer.html" .}} {{if not $.Site.Data.config.enableColumnLayout}}
</div> {{partial "footer.html" .}}
{{partial "contact.html" .}}
{{end}}
</main>
{{if $.Site.Data.config.enableColumnLayout}}
<aside data-sm1-2 data-md1-2 data-lg1-4 data-lg1 data-m1 data-sm2 data-xs3 class="singlePage">
<div class="sticky page-end vertical">
<h3> Menu </h3>
<div id="tree"></div>
</div>
</aside>
<aside data-sm1-2 data-md1-2 data-lg1-4 data-lg3 data-m3 data-sm3 data-xs2 class="singlePage">
<div class="sticky">
<div class="page-end vertical">
<button id="myBtn">Open Modal</button>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<div id="graph-container-modal"></div>
</div>
</div>
</div>
{{partial "footer.html" .}}
</div>
</aside>
</r-c>
<footer data-r-c data-join>
<c1-1>
{{partial "contact.html" .}}
</c1-1>
</footer>
{{end}}
</body> </body>
</html> </html>

View File

@ -3,23 +3,73 @@
{{ partial "head.html" . }} {{ partial "head.html" . }}
<body> <body>
{{partial "search.html" .}} {{partial "search.html" .}}
<div class="singlePage">
<!-- Begin actual content --> {{if $.Site.Data.config.enableColumnLayout}}
<header> <r-c join>
<h1>{{if .Title}}{{ .Title }}{{else}}Untitled{{end}}</h1> <main data-lg1-2 data-lg2 data-m2 data-sm1 data-xs1 class="singlePage">
<svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg> {{else}}
<div class="spacer"></div> <main class="singlePage">
{{partial "darkmode.html" .}} {{end}}
</header>
<article> <!-- Begin actual content -->
{{partial "toc.html" .}} <header>
{{partial "textprocessing.html" . }} <h1>{{if .Title}}{{ .Title }}
{{if $.Site.Data.config.enableRecentNotes}} {{else}}Untitled{{end}}
{{partial "recent.html" . }} </h1>
<svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7">
<title id="title">Search Icon</title>
<desc id="desc">Icon to open search</desc>
<g class="search-path" fill="none">
<path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4" />
<circle cx="8" cy="8" r="7" />
</g>
</svg>
<div class="spacer"></div>
{{partial "darkmode.html" .}}
</header>
<article>
{{partial "toc.html" .}}
{{partial "textprocessing.html" . }}
{{if $.Site.Data.config.enableRecentNotes}}
{{partial "recent.html" . }}
{{end}}
</article>
{{if not $.Site.Data.config.enableColumnLayout}}
{{partial "footerIndex.html" .}}
{{partial "contact.html" .}}
{{end}}
</main>
{{if $.Site.Data.config.enableColumnLayout}}
<aside data-sm1-2 data-md1-2 data-lg1-4 data-lg1 data-m1 data-sm2 data-xs3 class="singlePage">
<div class="sticky page-end vertical">
<h3> Menu </h3>
<div id="tree"></div>
</div>
</aside>
<aside data-sm1-2 data-md1-2 data-lg1-4 data-lg3 data-m3 data-sm3 data-xs2 class="singlePage">
<div class="sticky">
<div class="page-end vertical">
<button id="myBtn">Open Modal</button>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<div id="graph-container-modal"></div>
</div>
</div>
</div>
{{partial "footer.html" .}}
</div>
</aside>
</r-c>
<footer data-r-c data-join>
<c1-1>
{{partial "contact.html" .}}
</c1-1>
</footer>
{{end}} {{end}}
</article>
{{partial "footerIndex.html" .}}
</div>
</body> </body>
</html> </html>

View File

@ -1,16 +1,25 @@
{{if $.Site.Data.config.enableColumnLayout}}
{{if $.Site.Data.config.enableFooter}}
<div class="page-end vertical">
<div>
{{partial "graph.html" .}}
</div>
<div class="backlinks-container">
{{partial "backlinks.html" .}}
</div>
</div>
{{end}}
{{else}}
<hr/>
{{if $.Site.Data.config.enableFooter}}
<hr/> <div class="page-end">
<div class="backlinks-container">
{{if $.Site.Data.config.enableFooter}} {{partial "backlinks.html" .}}
<div class="page-end"> </div>
<div class="backlinks-container"> <div>
{{partial "backlinks.html" .}} {{partial "graph.html" .}}
</div> </div>
<div> </div>
{{partial "graph.html" .}} {{end}}
</div>
</div>
{{end}} {{end}}
{{partial "contact.html" .}}

View File

@ -59,6 +59,12 @@
<script src="{{$clipboard.Permalink}}"></script> <script src="{{$clipboard.Permalink}}"></script>
{{ end }} {{ end }}
<!-- columns layout scripts -->
{{ if $.Site.Data.config.enableColumnLayout }}
{{ $drawTree := resources.Get "js/drawTree.js" | resources.Fingerprint "md5" | resources.Minify }}
<script src="{{$drawTree.Permalink}}"></script>
{{ end }}
<!-- Preload page vars --> <!-- Preload page vars -->
{{$linkIndex := resources.Get "indices/linkIndex.json" | resources.Fingerprint {{$linkIndex := resources.Get "indices/linkIndex.json" | resources.Fingerprint
"md5" | resources.Minify | }} {{$contentIndex := resources.Get "md5" | resources.Minify | }} {{$contentIndex := resources.Get
@ -115,6 +121,13 @@
{{end}} {{end}}
drawGraph(
{{strings.TrimRight "/" .Site.BaseURL}},
500,
{{$.Site.Data.graphConfig.paths}},
{{$.Site.Data.graphConfig.globalGraph}},
true
);
{{if $.Site.Data.config.enableLinkPreview}} {{if $.Site.Data.config.enableLinkPreview}}
initPopover( initPopover(
@ -142,6 +155,70 @@
], ],
throwOnError : false throwOnError : false
}); });
const siteBaseURL = new URL(BASE_URL);
const pathBase = siteBaseURL.pathname;
// a temporary function to traverse the tree and allowing to display something
function* traverse(o, path = []) {
for (var i in o) {
const itemPath = path.concat(i);
yield [i, o[i], itemPath, o];
if (o[i] !== null && typeof o[i] == "object") {
//going one step down in the object tree!!
yield* traverse(o[i], itemPath);
}
}
}
{{if $.Site.Data.config.enableColumnLayout}}
drawTree(pathBase).then(
function(structure) {
// console.log(structure)
for (var [key, value, path] of traverse(structure)) {
// console.log(key);
// console.log(value);
// console.log(path);
// console.log("---");
let doc = document.getElementById("tree").innerHTML
if (value?.type == "folder") {
document.getElementById("tree").innerHTML = doc + '<h3>'+value.name + '</h3>'
}
if (value?.type == "page") {
document.getElementById("tree").innerHTML = doc + '&emsp;<a href="' + value.href + '">'+ value.name+'</a><br/>'
}
}
}
);
{{end}}
// Get the modal
var modal = document.getElementById("myModal");
// Get the button that opens the modal
var btn = document.getElementById("myBtn");
// Get the <span> element that closes the modal
var span = document.getElementsByClassName("close")[0];
// When the user clicks the button, open the modal
btn.onclick = function() {
modal.style.display = "block";
}
// When the user clicks on <span> (x), close the modal
span.onclick = function() {
modal.style.display = "none";
}
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
{{end}} {{end}}
}; };
</script> </script>