1
0
Fork 0
freesewing/sites/shared/components/mdx/dot.js

178 lines
5.9 KiB
JavaScript
Raw Normal View History

import { useEffect, useState, useRef } from 'react'
import Viz from 'viz.js'
import { Module, render } from 'viz.js/full.render.js'
import coarse from './dot-rough.js'
import Popout from 'shared/components/popout'
import colors from 'tailwindcss/colors'
// Some regex voodoo to allow people to use tailwind colors
// Takes colors like tc-orange-500 and makes it 'just work'
const colorMatch = Object.keys(colors).map(color => `(${color})`).join('|')
const regexA = new RegExp(`(tc-(${colorMatch})-([0-9]+))`, 'g')
const regexB = new RegExp(`(tc-(${colorMatch}))`, 'g')
const getColorTint = (a,b,color) => colors[color][a.split('-').pop()]
const getColor = (a,b,color,tint=false) => colors[color][500]
const colorDot = dot => dot
.replace(regexA, getColorTint)
.replace(regexB, getColor)
const fixSvg = svg => svg.replace(/#000000/g, () => 'currentColor')
2022-09-14 15:02:39 +02:00
const wrapDot = dot => {
if (dot.slice(0,7) === 'digraph') return dot
2022-09-14 15:02:39 +02:00
return plain
? `digraph G { bgcolor=transparent; ${colorDot(dot)} }`
: `digraph G {
graph [fontname = "Indie Flower"];
node [fontname = "Indie Flower"];
edge [fontname = "Indie Flower"];
bgcolor=transparent;
overlap=false;
${colorDot(dot)} }`
}
// Supported layout engines
const engines = [ 'circo', 'dot', 'fdp', 'neato', 'osage', 'twopi' ]
const Dot = props => {
2022-09-14 15:02:39 +02:00
// Extract code/caption from props
const [code, caption] = props.children
// Extract dot code from mdx
const dot = wrapDot(code.props.children.props.children)
// State and effect are needed to run async code as this
// library always returns a promise
const svgRef = useRef(null)
const [svg, setSvg] = useState('')
const [reveal, setReveal] = useState(false)
const [zoom, setZoom] = useState(false)
const [plain, setPlain] = useState(false)
const [engine, setEngine] = useState(props.engine || 'dot')
// Keep track of coarse calls
const [rough, setRough] = useState(0)
useEffect(() => {
if (!reveal) {
viz.renderString(dot, { engine }).then(res => {
// Replace the default #000000 with currentColor for themeing
setSvg(fixSvg(res))
if (!plain && rough < 2) {
coarse(svgRef.current)
setRough(rough+1)
}
// Strip width/height so it's responsive
if (svgRef.current.children?.[0]) {
svgRef.current.children[0].attributes.width.value = ""
svgRef.current.children[0].attributes.height.value = ""
// Set style for theme support
svgRef.current.children[0].setAttribute('stroke', "currentColor")
}
})
}
}, [dot, rough, plain, reveal, engine])
const togglePlain = () => {
setRough(0)
setPlain(!plain)
}
const toggleReveal = () => {
setRough(1)
setReveal(!reveal)
}
const changeEngine = (eng) => {
if (eng !== engine) {
setRough(0)
setEngine(eng)
}
}
// Initialize viz library
const viz = new Viz({ Module, render })
return (
<figure className="py-2">
<div className="flex flex-row justify-center gap-8 mb-4">
<div className="flex items-center gap-1">
<span className="text-xs font-bold">plain</span>
<input
type="checkbox" className="toggle toggle-xs toggle-primary"
checked={!plain} onChange={togglePlain}
disabled={reveal}
/>
<span style={{fontFamily: "Indie Flower"}}
className="font-bold text-sm">sketch</span>
</div>
<div className={`dropdown dropdown-end`}>
<div tabIndex="0" className="m-0 btn btn-ghost btn-xs flex flex-row gap-2">
<span className="capitalize">Layout: {engine}</span>
</div>
<ul tabIndex="0" className="shadow menu dropdown-content bg-base-100 rounded-lg border p-0 py-2 pl-0">
{engines.map(eng => (
<li key={eng}>
<button
onClick={() => changeEngine(eng)}
className="btn btn-ghost btn-xs hover:bg-base-200 px-4 py-1"
>
<span className="text-xs">{eng}</span>
</button>
</li>
))}
</ul>
</div>
<div className="flex items-center gap-1">
<span className="text-xs font-bold">graph</span>
<input
type="checkbox" className="toggle toggle-xs toggle-primary"
checked={reveal} onChange={toggleReveal}
/>
<span className="font-bold text-xs">code</span>
</div>
</div>
{reveal
? (
<>
<Popout tip>
<h4>You can edit this online</h4>
<p>
Use a site like <a href="https://sketchviz.com">SketchViz</a> or
another online <a href="https://graphviz.gitlab.io/">Graphviz</a> editor
to edit this diagram. You can use its source code below as a starting point.
</p>
</Popout>
<pre className={`
language-txt text-base lg:text-lg whitespace-pre-wrap break-words
bg-neutral p-4 px-6 rounded-lg text-neutral-content
`}
>
{dot}
</pre>
</>
) : (
<div
className={zoom ? `fixed top-0 left-0 right-0 w-screen h-screen
bg-base-100 bg-opacity-90 z-30 hover:cursor-zoom-out
p-12 flex flex-col justify-center` : ''}
onClick={() => zoom ? setZoom(false) : null}
>
<div
ref={svgRef}
onClick={() => setZoom(!zoom)}
className={zoom
? 'svg-zoom'
: "dot shadow p-2 rounded -mt-8 hover:cursor-zoom-in text-base-content"
}
dangerouslySetInnerHTML={{__html: svg}}
/>
<figcaption className="text-center -mt-2 italic">
{caption || ''}
</figcaption>
</div>
)}
</figure>
)
}
export default Dot