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') // Supported layout engines const engines = [ 'circo', 'dot', 'fdp', 'neato', 'osage', 'twopi' ] const Dot = props => { // 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) } } const wrapDot = dot => { if (dot.slice(0,7) === 'digraph') return dot 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)} }` } // Extract code/caption from props const [code, caption] = props.children // Extract dot code from mdx const dot = wrapDot(code.props.children.props.children) // Initialize viz library const viz = new Viz({ Module, render }) return (
plain sketch
Layout: {engine}
    {engines.map(eng => (
  • ))}
graph code
{reveal ? ( <>

You can edit this online

Use a site like SketchViz or another online Graphviz editor to edit this diagram. You can use its source code below as a starting point.

              {dot}
            
) : (
zoom ? setZoom(false) : null} >
setZoom(!zoom)} className={zoom ? 'svg-zoom' : "dot shadow p-2 rounded -mt-8 hover:cursor-zoom-in text-base-content" } dangerouslySetInnerHTML={{__html: svg}} />
{caption || ''}
)}
) } export default Dot