Skip to content

Commit

Permalink
Display inheritance graph in model explorer (#1114)
Browse files Browse the repository at this point in the history
* Add basic inheritance graph for schema docs

* Fix schema explorer TOC

* Add loading indicator

* Make use of Mermaid’s own theming

* Refactor: Make inheritance graph reusable

For example to display a full graph of the model on the explorer index page…

* Implement workaround for Astro bug

This is a workaround due to an Astro bug. The bug has been fixed, but we're still on an older Astro version, and using a different file extension works fine for now.

* Improve dark mode

* Adjust edge/arrow styles

* Use schema name (instead of label) in model explorer

Most people using the model explorer will probably search for the schema name as that is what they use when creating mappings, writing crawlers, debugging problems with Aleph etc. The schema label is still visible on the index page, but we now use the name consistently in headings, page titles, navigation links etc.
  • Loading branch information
tillprochaska authored Jun 22, 2023
1 parent eba3773 commit 3ec49e0
Show file tree
Hide file tree
Showing 18 changed files with 1,291 additions and 67 deletions.
1,039 changes: 1,033 additions & 6 deletions docs/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@astrojs/markdown-component": "^1.0.2",
"astro": "^1.4.3",
"astro-theme-docs": "github:alephdata/astro-theme-docs",
"mermaid": "^10.2.1",
"unist-util-visit": "^4.1.1"
},
"devDependencies": {
Expand Down
77 changes: 77 additions & 0 deletions docs/src/components/common/Mermaid.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
import Spinner from '@components/common/Spinner.astro';
---

<script>
import mermaid from 'mermaid';
import themeCSS from './mermaid-theme.css.txt?raw';

mermaid.initialize({
themeCSS,
securityLevel: 'loose',
fontFamily: 'var(--font-family)',
startOnLoad: false,
});

mermaid.run({
querySelector: '.mermaid__graph',
postRenderCallback: (id) => {
const element = document.getElementById(id);
element.parentNode.dataset.rendered = 'true';
},
});
</script>

<style>
.mermaid {
position: relative;
padding: var(--space-sm);
aspect-ratio: 16 / 9;

background-color: var(--color-bg-elevated);
border: 1px solid var(--color-border-default);
border-radius: var(--radius);
}

@media screen and (prefers-color-scheme: dark) {
.mermaid {
background-color: rgba(0, 0, 0, 0.15);
border-color: rgba(255, 255, 255, 0.1);
}
}

.mermaid__graph:not([data-processed='true']) {
display: none;
}

.mermaid__graph[data-rendered='true'] + .mermaid__spinner {
display: none;
}

.mermaid__graph {
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
}

.mermaid__graph > :global(svg) {
max-width: 100%;
max-height: 100%;
}

.mermaid__spinner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>

<div class="mermaid">
<div class="mermaid__graph">
<slot />
</div>
<Spinner class="mermaid__spinner" />
</div>
43 changes: 43 additions & 0 deletions docs/src/components/common/Spinner.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
const { class: className } = Astro.props;
---

<style>
@keyframes spinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

.spinner__circle {
display: inline-block;
width: var(--space-xl);
height: var(--space-xl);
border-radius: 50%;
border: 3px solid var(--color-fg-muted);
border-right-color: var(--color-bg-subtle);
animation: spinner 1s linear infinite;
}

.spinner__progress {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
border: 0;
padding: 0;

white-space: nowrap;
clip-path: inset(100%);
clip: rect(0 0 0 0);
overflow: hidden;
}
</style>

<div class:list={['spinner', className]}>
<progress class="spinner__progress"></progress>
<span class="spinner__circle"></span>
</div>
42 changes: 42 additions & 0 deletions docs/src/components/common/mermaid-theme.css.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.node .label-container {
fill: var(--color-bg-default);
stroke: var(--color-border-default);
}

.node.clickable:hover .label-container {
fill: var(--color-bg-subtle);
stroke: var(--color-border-emphasis);
}

.node-primary .label-container {
fill: var(--color-bg-primary);
stroke: var(--color-border-primary);
}

.node .nodeLabel {
color: var(--color-fg-muted);
}

.node-primary .nodeLabel {
color: var(--color-fg-primary);
}

.node.clickable:hover .nodeLabel {
color: var(--color-fg-default);
}

.flowchart-link {
stroke-width: 1.5px;
stroke: var(--color-fg-muted);
}

.marker {
fill: var(--color-fg-muted);
stroke: none;
}

.arrowMarkerPath {
// Workaround to change size of arrow heads as there is no
// Mermaid config option
transform: scale(0.75) translate(4px, 2px)
}
4 changes: 2 additions & 2 deletions docs/src/components/explorer/ExplorerNav.astro
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const basePath = Astro.site?.pathname || '';
<NavItem
link={path.join(basePath, '/explorer/schemata/', schema.name)}
active={schema.name === activeSchema?.name}
title={schema.label}
title={schema.name}
/>
))
}
Expand All @@ -35,7 +35,7 @@ const basePath = Astro.site?.pathname || '';
<NavItem
link={path.join(basePath, '/explorer/types/', type.name)}
active={type.name === activeType?.name}
title={type.label}
title={type.name}
/>
))
}
Expand Down
31 changes: 31 additions & 0 deletions docs/src/components/explorer/InheritanceGraph.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
import Mermaid from '@components/common/Mermaid.astro';
import { getInheritanceEdges } from '@util/ftm';
import { schemaLink } from '@util/links';
const { schemata, activeSchema } = Astro.props;
const base = Astro.site?.pathname;
const edges = getInheritanceEdges(schemata);
const graph = [];
for (const schema of schemata) {
graph.push(`${schema.name}(${schema.name})`);
if (schema !== activeSchema) {
graph.push(`click ${schema.name} "${schemaLink(base, schema)}"`);
}
}
if (activeSchema) {
graph.push(`class ${activeSchema.name} node-primary`);
}
for (const [child, parent] of edges) {
graph.push(`${child.name}-->${parent.name}`);
}
---

<Mermaid>
graph BT
{graph.join('\n')}
</Mermaid>
4 changes: 1 addition & 3 deletions docs/src/components/explorer/PropertyTypesIndexTable.astro
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ const types = Object.values(model.types).sort((a, b) =>
<code>{type.name}</code>
</PropertyTypeLink>
</td>
<td>
<PropertyTypeLink {type}>{type.label}</PropertyTypeLink>
</td>
<td>{type.label}</td>
<td>
<BooleanValue value={type.matchable} />
</td>
Expand Down
39 changes: 0 additions & 39 deletions docs/src/components/explorer/SchemaExtends.astro

This file was deleted.

21 changes: 21 additions & 0 deletions docs/src/components/explorer/SchemaInheritance.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
import { RichContent } from 'astro-theme-docs/components';
import InheritanceGraph from '@components/explorer/InheritanceGraph.astro';
const { schema, ...rest } = Astro.props;
const parents = schema.getParents();
const children = schema
.getChildren()
.filter((child) => child.getExtends().includes(schema));
---

<div class="schema-inheritance" {...rest}>
<RichContent>
<h2 class="beta">Inheritance</h2>
<InheritanceGraph
schemata={[schema, ...parents, ...children]}
activeSchema={schema}
/>
</RichContent>
</div>
5 changes: 2 additions & 3 deletions docs/src/components/explorer/SchemaLink.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
import path from 'node:path';
import { Schema } from '@alephdata/followthemoney';
import { model } from '@util/ftm';
import { schemaLink } from '@util/links';
let { schema, text } = Astro.props;
schema = model.getSchema(schema);
const basePath = Astro.site?.pathname || '';
const link = path.join(basePath, '/explorer/schemata/', schema.name);
const link = schemaLink(Astro.site?.pathname, schema);
---

<a href={link}>{Astro.slots.default ? <slot /> : text || schema.label}</a>
2 changes: 1 addition & 1 deletion docs/src/components/explorer/SchemaSemantics.astro
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const end = schema.getTemporalEndProperties();
{
start <= 0 && (
<li>
{schema.label} entities have no temporal extent, i.e. they are not
{schema.name} entities have no temporal extent, i.e. they are not
suitable for representation in a timeline.
</li>
)
Expand Down
4 changes: 1 addition & 3 deletions docs/src/components/explorer/SchemataIndexTable.astro
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ const schemata = Object.values(model.schemata).sort((a, b) =>
<code>{schema.name}</code>
</SchemaLink>
</td>
<td>
<SchemaLink {schema}>{schema.label}</SchemaLink>
</td>
<td>{schema.label}</td>
<td>
<BooleanValue value={schema.abstract} />
</td>
Expand Down
2 changes: 1 addition & 1 deletion docs/src/components/page/SchemaPageHeader.astro
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ try {
</style>

<header class="SchemaPageHeader">
<h1 class="SchemaPageHeader__name alpha">{schema.label}</h1>
<h1 class="SchemaPageHeader__name alpha">{schema.name}</h1>
{firstVersion && <VersionLink version={firstVersion} />}

{
Expand Down
6 changes: 3 additions & 3 deletions docs/src/layouts/ExplorerLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import ExplorerNav from '@components/explorer/ExplorerNav.astro';
import options from '../options.json';
const { activeSchema = null, activeType = null } = Astro.props;
const label =
(activeSchema && activeSchema.label) || (activeType && activeType.label);
const name =
(activeSchema && activeSchema.name) || (activeType && activeType.name);
---

<DefaultLayout layout="explorer" {options} {...Astro.props} title={label}>
<DefaultLayout layout="explorer" {options} {...Astro.props} title={name}>
<ExpandableSidebar slot="sidebar">
<ExplorerNav {activeType} {activeSchema} />
</ExpandableSidebar>
Expand Down
10 changes: 5 additions & 5 deletions docs/src/pages/explorer/schemata/[name].astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { model } from '@util/ftm';
import { Stack, TocNav } from 'astro-theme-docs/components';
import ExplorerLayout from '@layouts/ExplorerLayout.astro';
import SchemaPageHeader from '@components/page/SchemaPageHeader.astro';
import SchemaExtends from '@components/explorer/SchemaExtends.astro';
import SchemaInheritance from '@components/explorer/SchemaInheritance.astro';
import SchemaSemantics from '@components/explorer/SchemaSemantics.astro';
import SchemaProperties from '@components/explorer/SchemaProperties.astro';
Expand All @@ -18,9 +18,9 @@ export function getStaticPaths() {
}
const headings = [
{ text: 'Inheritance', slug: 'inheritance' },
{ text: 'Semantics', slug: 'semantics' },
{ text: 'Properties', slug: 'properties' },
{ text: 'Inheritance', slug: 'inheritance', depth: 2 },
{ text: 'Semantics', slug: 'semantics', depth: 2 },
{ text: 'Properties', slug: 'properties', depth: 2 },
];
---

Expand All @@ -29,7 +29,7 @@ const headings = [

<Stack size="xl">
<SchemaPageHeader schema={schema} />
<SchemaExtends id="inheritance" schema={schema} />
<SchemaInheritance id="inheritance" schema={schema} />
<SchemaSemantics id="semantics" schema={schema} />
<SchemaProperties id="properties" schema={schema} />
</Stack>
Expand Down
Loading

0 comments on commit 3ec49e0

Please sign in to comment.