Port the graph to alpineJS (#119)

* Port the graph to alpinejs

* removed whitespace

* remove console log
This commit is contained in:
Utsob Roy 2023-04-18 11:59:22 +06:00 committed by GitHub
parent 80616447c8
commit b2dc085f48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 81 additions and 110 deletions

View File

@ -1,12 +1,9 @@
<script> <script>
const getCssVar = (variable) => getComputedStyle(document.body).getPropertyValue(variable); async function fetchGraphData() {
const graphData = await fetch('/graph.json').then(res => res.json());
function htmlDecode(input) { const fullGraphData = filterFullGraphData(graphData);
var doc = new DOMParser().parseFromString(input, "text/html"); return {graphData, fullGraphData}
return doc.documentElement.textContent;
} }
window.graphData = null;
window.maxGraphDepth = 1;
function getNextLevelNeighbours(existing, remaining) { function getNextLevelNeighbours(existing, remaining) {
const keys = Object.values(existing).map((n) => n.neighbors).flat(); const keys = Object.values(existing).map((n) => n.neighbors).flat();
@ -23,8 +20,12 @@
return existing, n_remaining; return existing, n_remaining;
} }
function filterToDepth(data) { function filterLocalGraphData(graphData, depth) {
let remaining = JSON.parse(JSON.stringify(data.nodes)); if (graphData == null) {
return null;
}
let remaining = JSON.parse(JSON.stringify(graphData.nodes));
let links = JSON.parse(JSON.stringify(graphData.links));
let currentLink = decodeURI(window.location.pathname); let currentLink = decodeURI(window.location.pathname);
let currentNode = remaining[currentLink] || Object.values(remaining).find((v) => v.home); let currentNode = remaining[currentLink] || Object.values(remaining).find((v) => v.home);
delete remaining[currentNode.url]; delete remaining[currentNode.url];
@ -35,7 +36,7 @@
currentNode.current = true; currentNode.current = true;
let existing = {}; let existing = {};
existing[currentNode.url] = currentNode; existing[currentNode.url] = currentNode;
for (let i = 0; i < window.maxGraphDepth; i++) { for (let i = 0; i < depth; i++) {
existing, remaining = getNextLevelNeighbours(existing, remaining); existing, remaining = getNextLevelNeighbours(existing, remaining);
} }
nodes = Object.values(existing); nodes = Object.values(existing);
@ -43,16 +44,25 @@
nodes = nodes.filter(n => !n.home); nodes = nodes.filter(n => !n.home);
} }
let ids = nodes.map((n) => n.id); let ids = nodes.map((n) => n.id);
let graphData = { return {
nodes, nodes,
links: data.links.filter((con) => ids.indexOf(con.target) > -1 && ids.indexOf(con.source) > -1), links: links.filter(function (con) {
return ids.indexOf(con.target) > -1 && ids.indexOf(con.source) > -1;
}),
} }
return graphData;
} }
var Graph; function getCssVar(variable) {return getComputedStyle(document.body).getPropertyValue(variable)}
function renderGraph(graphData, id, delay) { function htmlDecode(input) {
var doc = new DOMParser().parseFromString(input, "text/html");
return doc.documentElement.textContent;
}
function renderGraph(graphData, id, delay, fullScreen) {
if (graphData == null) {
return;
}
const el = document.getElementById(id); const el = document.getElementById(id);
width = el.offsetWidth; width = el.offsetWidth;
height = el.offsetHeight; height = el.offsetHeight;
@ -60,7 +70,6 @@
let hoverNode = null; let hoverNode = null;
const color = getCssVar("--graph-main"); const color = getCssVar("--graph-main");
const mutedColor = getCssVar("--graph-muted"); const mutedColor = getCssVar("--graph-muted");
let Graph = ForceGraph() let Graph = ForceGraph()
(el) (el)
.graphData(graphData) .graphData(graphData)
@ -131,86 +140,75 @@
hoverNode = node || null; hoverNode = node || null;
}); });
if (delay != null && graphData.nodes.length > 4) { if (fullScreen || (delay != null && graphData.nodes.length > 4)) {
setTimeout(() => { setTimeout(() => {
Graph.zoomToFit(5, 75); Graph.zoomToFit(5, 75);
}, delay); }, delay || 200);
} }
return Graph; return Graph;
} }
function fetchGraphData() { function renderLocalGraph(graphData, depth, fullScreen) {
fetch('/graph.json').then(res => res.json()).then(data => { if (window.graph){
window.graphData = data; window.graph._destructor();
Graph = renderGraph(filterToDepth(JSON.parse(JSON.stringify(data))), "link-graph", 1); }
}); const data = filterLocalGraphData(graphData, depth);
return renderGraph(data, 'link-graph', null, fullScreen);
} }
fetchGraphData(); function filterFullGraphData(graphData) {
window.document.getElementById('graph-depth').value = window.maxGraphDepth; if (graphData == null) {
window.document.getElementById('depth-display').innerText = window.maxGraphDepth; return null;
window.document.getElementById('graph-depth').addEventListener('input', (evt) => {
window.maxGraphDepth = evt.target.value;
window.document.getElementById('depth-display').innerText = window.maxGraphDepth;
if (Graph != null) {
Graph._destructor();
Graph = null;
} }
Graph = renderGraph(filterToDepth(JSON.parse(JSON.stringify(window.graphData))), "link-graph", 1); graphData = JSON.parse(JSON.stringify(graphData));
setTimeout(() => { const hiddens = Object.values(graphData.nodes).filter((n) => n.hide).map((n) => n.id);
Graph.zoomToFit(5, 75); const data = {
}, 1); links: JSON.parse(JSON.stringify(graphData.links)).filter((l) => hiddens.indexOf(l.source) == -1 && hiddens.indexOf(l.target) == -1),
}); nodes: [...Object.values(graphData.nodes).filter((n) => !n.hide)]
}
return data
}
window.fullGraph = null; function openFullGraph(fullGraphData) {
function renderFullGraph() { lucide.createIcons({
if (!window.fullGraph) {
const hiddens = Object.values(window.graphData.nodes).filter((n) => n.hide).map((n) => n.id);
const graphData = {
links: JSON.parse(JSON.stringify(window.graphData.links)).filter((l) => hiddens.indexOf(l.source) == -1 && hiddens.indexOf(l.target) == -1),
nodes: [...Object.values(window.graphData.nodes).filter((n) => !n.hide)]
}
let g = document.createElement('div');
g.id = 'full-graph';
g.classList.add('show');
document.body.appendChild(g);
g.innerHTML = '<span id="full-graph-close"><i icon-name="x" aria-hidden="true"></i></span><div id="full-graph-container"></div>';
lucide.createIcons({
attrs: { attrs: {
class: ["svg-icon"] class: ["svg-icon"]
} }
}); });
window.fullGraph = renderGraph(graphData, "full-graph-container", 200); return renderGraph(fullGraphData, "full-graph-container", 200, false);;
document.getElementById('full-graph-close').addEventListener('click', (evt) => {
g.classList.remove('show');
window.fullGraph._destructor();
window.fullGraph = null;
document.getElementById('full-graph').remove()
});
}
} }
document.getElementById('graph-fs-btn').addEventListener('click', (evt) => { function closefullGraph(fullGraph) {
const el = document.querySelector('.graph'); if (fullGraph) {
if (el.classList.contains('graph-fs')) { fullGraph._destructor();
el.classList.remove('graph-fs');
Graph.width(el.offsetWidth).height(el.offsetWidth);
} else {
el.classList.add('graph-fs');
Graph.width(el.offsetWidth).height(el.offsetWidth);
} }
setTimeout(() => { return null;
Graph.zoomToFit(5, 75); }
}, 1);
});
document.getElementById('global-graph-btn').addEventListener('click', (evt) => {
if (!window.fullGraph) {
renderFullGraph();
}
});
</script> </script>
<div x-init="{graphData, fullGraphData} = await fetchGraphData();" x-data="{ graphData: null, depth: 1, graph: null, fullGraph: null, showFullGraph: false, fullScreen: false, fullGraphData: null}" id="graph-component" x-bind:class="fullScreen ? 'graph graph-fs' : 'graph'" v-scope>
<div class="graph-title-container">
<div class="graph-title">Connected Pages</div>
<div id="graph-controls">
<div class="depth-control">
<label for="graph-depth">Depth</label>
<div class="slider">
<input x-model.number="depth" name="graph-depth" list="depthmarkers" type="range" step="1" min="1" max="3" id="graph-depth"/>
<datalist id="depthmarkers">
<option value="1" label="1"></option>
<option value="2" label="2"></option>
<option value="3" label="3"></option>
</datalist>
</div>
<span id="depth-display" x-text="depth"></span>
</div>
<div class="ctrl-right">
<span id="global-graph-btn" x-on:click="showFullGraph = true; setTimeout(() => {fullGraph = openFullGraph(fullGraphData)}, 100)"><i icon-name="globe" aria-hidden="true"></i></span>
<span id="graph-fs-btn" x-on:click="fullScreen = !fullScreen"><i icon-name="expand" aria-hidden="true"></i></span>
</div>
</div>
</div>
<div x-effect="window.graph = renderLocalGraph(graphData, depth, fullScreen)" id="link-graph" ></div>
<div x-show="showFullGraph" id="full-graph" class="show">
<span id="full-graph-close" x-on:click="fullGraph = closefullGraph(fullGraph); showFullGraph = false;"><i icon-name="x" aria-hidden="true"></i></span><div id="full-graph-container"></div>
</div>
</div>

View File

@ -5,30 +5,7 @@
{% include imp %} {% include imp %}
{% endfor %} {% endfor %}
{%if settings.dgShowLocalGraph === true%} {%if settings.dgShowLocalGraph === true%}
<div class="graph"> {%include "components/graphScript.njk"%}
<div class="graph-title-container">
<div class="graph-title">Connected Pages</div>
<div id="graph-controls">
<div class="depth-control">
<label for="graph-depth">Depth</label>
<div class="slider">
<input name="graph-depth" list="depthmarkers" type="range" step="1" min="1" max="3" id="graph-depth"/>
<datalist id="depthmarkers">
<option value="1" label="1"></option>
<option value="2" label="2"></option>
<option value="3" label="3"></option>
</datalist>
</div>
<span id="depth-display"></span>
</div>
<div class="ctrl-right">
<span id="global-graph-btn"><i icon-name="globe" aria-hidden="true"></i></span>
<span id="graph-fs-btn"><i icon-name="expand" aria-hidden="true"></i></span>
</div>
</div>
</div>
<div id="link-graph"></div>
</div>
{%endif%} {%endif%}
{%if settings.dgShowToc === true%} {%if settings.dgShowToc === true%}
@ -89,8 +66,4 @@
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
</aside> </aside>
{%if settings.dgShowLocalGraph === true %}
{%include "components/graphScript.njk"%}
{% endif %}