dbml-erd-viewer 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
1
  (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("react"),require("@xyflow/react"),require("@dbml/core"),require("react/jsx-runtime")):typeof define==`function`&&define.amd?define([`exports`,`react`,`@xyflow/react`,`@dbml/core`,`react/jsx-runtime`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.DbmlErdViewer={},e.React,e.XYFlowReact,e.DbmlCore,e.jsxRuntime))})(this,function(e,t,n,r,i){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var a=Object.create,o=Object.defineProperty,s=Object.getOwnPropertyDescriptor,c=Object.getOwnPropertyNames,l=Object.getPrototypeOf,u=Object.prototype.hasOwnProperty,d=(e,t,n,r)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var i=c(t),a=0,l=i.length,d;a<l;a++)d=i[a],!u.call(e,d)&&d!==n&&o(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(r=s(t,d))||r.enumerable});return e};r=((e,t,n)=>(n=e==null?{}:a(l(e)),d(t||!e||!e.__esModule?o(n,`default`,{value:e,enumerable:!0}):n,e)))(r,1);var f=r,p=f.Parser??f.default?.Parser;if(!p)throw Error("Could not resolve `Parser` export from @dbml/core.");var m=p,h=`public`,g=class extends Error{diagnostics;constructor(e,t){super(e,t),this.name=`DbmlParseError`,this.diagnostics=t?.diagnostics??[]}};function _(e){let t=e?.diags;return Array.isArray(t)?t.map(e=>({message:e.message??`Unknown error`,line:e.location?.start?.line,column:e.location?.start?.column,code:e.code})):[]}function v(e,t){if(e.length===0)return`Failed to parse DBML: ${t}`;let n=e.map(e=>` • ${e.line==null?``:`Line ${e.line}${e.column==null?``:`:${e.column}`} — `}${e.message}`);return`${e.length===1?`Failed to parse DBML:`:`Failed to parse DBML (${e.length} errors):`}\n${n.join(`
2
- `)}`}function y(e){if(e)return typeof e==`string`?e:e.value??void 0}function b(e,t){return e&&e!==h?`${e}.${t}`:t}function x(e){let t=new Set;for(let n of e.fields)n.pk&&t.add(n.name);for(let n of e.indexes??[])if(n.pk)for(let e of n.columns??[])e.type===`column`&&e.value&&t.add(e.value);return t}function S(e){let t;try{t=new m().parse(e,`dbmlv2`)}catch(e){let t=_(e);throw new g(v(t,e instanceof Error?e.message:String(e)),{cause:e,diagnostics:t})}let n=new Map;for(let e of t.schemas)for(let t of e.tables){let r=b(e.name,t.name),i=x(t),a=new Map;for(let e of t.fields){let t=i.has(e.name);a.set(e.name,{pk:t,notNull:t||!!e.not_null,note:y(e.note)})}n.set(r,a)}let r=e=>{let t=n.get(e.tableId);return!t||e.columns.length===0?!1:e.columns.every(e=>t.get(e)?.pk===!0)},i=e=>e.columns.some(t=>{let r=n.get(e.tableId)?.get(t);return r?!r.notNull&&!r.pk:!0}),a=e=>e.columns.some(t=>n.get(e.tableId)?.get(t)?.note?.toLowerCase().includes(`optional`)??!1),o=[],s=new Set;for(let e of t.schemas)for(let t of e.refs){if(t.endpoints.length!==2)continue;let[n,c]=t.endpoints.map(t=>({tableId:b(t.schemaName??e.name,t.tableName),columns:t.fieldNames,relation:t.relation})),l,u,d=n.relation===`*`;if(d!==(c.relation===`*`))l=d?n:c,u=d?c:n;else{let e=r(n),t=r(c);e&&!t?(l=c,u=n):(l=n,u=c)}let f=r(l),p=i(l);l.columns.forEach(e=>s.add(`${l.tableId}::${e}`)),o.push({id:`${l.tableId}.${l.columns.join(`-`)}__${u.tableId}.${u.columns.join(`-`)}`,kind:f?`identifying`:`non-identifying`,from:{...l,optional:a(l)?!0:void 0},to:{...u,optional:p}})}let c=[];for(let e of t.schemas){let t=e.name;for(let n of e.tables){let e=b(t,n.name),r=x(n),i=n.fields.map(t=>({name:t.name,type:t.type?.type_name??`unknown`,pk:r.has(t.name),notNull:r.has(t.name)||!!t.not_null,unique:!!t.unique,increment:!!t.increment,defaultValue:t.dbdefault?.value,note:y(t.note),isForeignKey:s.has(`${e}::${t.name}`)}));c.push({id:e,name:n.name,schema:t===h?void 0:t,note:y(n.note),headerColor:n.headerColor??void 0,columns:i})}}return{tables:c,relations:o}}var C=240,w=40,T=28;function E(e){return 40+Math.max(e.columns.length,1)*28}function D(e){let t=new Map;for(let n of e.tables)t.set(n.id,0);let n=e.relations.map(e=>[e.from.tableId,e.to.tableId]).filter(([e,n])=>e!==n&&t.has(e)&&t.has(n));for(let r=0;r<e.tables.length;r++){let e=!1;for(let[r,i]of n){let n=t.get(r)+1;t.get(i)<n&&(t.set(i,n),e=!0)}if(!e)break}return t}function O(e,t={}){let n=t.horizontalGap??120,r=t.verticalGap??40,i=D(e),a=new Map;for(let t of e.tables){let e=i.get(t.id)??0,n=a.get(e);n||(n=[],a.set(e,n)),n.push(t)}let o=new Map;return[...a.keys()].sort((e,t)=>e-t).forEach((e,t)=>{let i=a.get(e).sort((e,t)=>e.name.localeCompare(t.name)),s=t*(240+n),c=0;for(let e of i){let t=E(e);o.set(e.id,{x:s,y:c,width:240,height:t}),c+=t+r}}),o}async function k(e,t={}){let n=await import(`@dagrejs/dagre`),r=t.horizontalGap??120,i=t.verticalGap??40,a=t.direction===`TB`?`TB`:`LR`,o=new n.graphlib.Graph;o.setGraph({rankdir:a,ranksep:r,nodesep:i}),o.setDefaultEdgeLabel(()=>({}));for(let t of e.tables)o.setNode(t.id,{width:240,height:E(t)});for(let t of e.relations)o.hasNode(t.from.tableId)&&o.hasNode(t.to.tableId)&&o.setEdge(t.from.tableId,t.to.tableId);n.layout(o);let s=new Map;for(let e of o.nodes()){let t=o.node(e);t&&s.set(e,{x:t.x-t.width/2,y:t.y-t.height/2,width:t.width,height:t.height})}return s}async function A(e,t={}){let n=new(await(import(`elkjs/lib/elk.bundled.js`))).default,r=t.horizontalGap??120,i=t.verticalGap??40,a={id:`root`,layoutOptions:{"elk.algorithm":`layered`,"elk.direction":t.direction===`TB`?`DOWN`:`RIGHT`,"elk.layered.spacing.nodeNodeBetweenLayers":String(r),"elk.spacing.nodeNode":String(i)},children:e.tables.map(e=>({id:e.id,width:240,height:E(e)})),edges:e.relations.map((e,t)=>({id:`e${t}`,sources:[e.from.tableId],targets:[e.to.tableId]}))},o=await n.layout(a),s=new Map;for(let e of o.children??[])s.set(e.id,{x:e.x??0,y:e.y??0,width:e.width??240,height:e.height??0});return s}var j=class extends Error{constructor(e,t){super(e,t),this.name=`LayoutError`}};async function M(e,t={}){let n=t.algorithm??`simple`;if(n===`simple`)return O(e,t);try{return n===`dagre`?await k(e,t):await A(e,t)}catch(e){throw new j(`"${n}" layout failed. Is the optional dependency "${n===`dagre`?`@dagrejs/dagre`:`elkjs`}" installed? (${e instanceof Error?e.message:String(e)})`,{cause:e})}}var N=[`--dv-canvas`,`--dv-bg`,`--dv-border`,`--dv-header-bg`,`--dv-header-fg`,`--dv-row-fg`,`--dv-type-fg`,`--dv-row-hover`,`--dv-row-highlight`,`--dv-pk`,`--dv-fk`,`--dv-null`,`--dv-edge`,`--dv-edge-active`,`--dv-font`],P=class extends Error{constructor(e,t){super(e,t),this.name=`ExportError`}};function F(e,t){let n=e.x,r=e.y,i=e.x+e.width,a=e.y+e.height;for(let e of t.querySelectorAll(`.dv-erd-edge`)){let t;try{t=e.getBBox()}catch{continue}t.width===0&&t.height===0||(n=Math.min(n,t.x),r=Math.min(r,t.y),i=Math.max(i,t.x+t.width),a=Math.max(a,t.y+t.height))}return{x:n,y:r,width:i-n,height:a-r}}async function I(e,t,n={}){let{type:r=`png`,padding:i=24,backgroundColor:a,pixelRatio:o=2}=n,s;try{s=await import(`html-to-image`)}catch(e){throw new P(`Export requires the optional dependency "html-to-image". Install it to enable PNG/SVG export.`,{cause:e})}if(typeof document<`u`&&document.fonts?.ready)try{await document.fonts.ready}catch{}let c=Math.ceil(t.width+i*2),l=Math.ceil(t.height+i*2),u=`translate(${-t.x+i}px, ${-t.y+i}px) scale(1)`,d={width:c,height:l,backgroundColor:a,style:{width:`${c}px`,height:`${l}px`,transform:u}},f=L(e,t,i);try{return r===`svg`?await s.toSvg(e,d):await s.toPng(e,{...d,pixelRatio:o})}catch(e){throw new P(`Failed to render diagram image: ${e instanceof Error?e.message:String(e)}`,{cause:e})}finally{f()}}function L(e,t,n){let r=[],i=e.closest(`.dv-viewer`);if(i){let t=getComputedStyle(i);for(let n of N){let i=t.getPropertyValue(n);if(!i)continue;let a=e.style.getPropertyValue(n);e.style.setProperty(n,i),r.push(()=>{a?e.style.setProperty(n,a):e.style.removeProperty(n)})}}let a=`${Math.ceil(t.x+t.width+n)}px`,o=`${Math.ceil(t.y+t.height+n)}px`;for(let t of e.querySelectorAll(`.react-flow__edges svg`)){let{width:e,height:n}=t.style;t.style.width=a,t.style.height=o,r.push(()=>{t.style.width=e,t.style.height=n})}return()=>r.forEach(e=>e())}function R(e,t){let n=document.createElement(`a`);n.href=e,n.download=t,n.click()}function z(e,t,n){return`${e}__${t}__${n}`}function B(e){return 40+e*28+28/2}function V({column:e,top:t}){let r={top:t,opacity:0};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.Handle,{id:z(e,`left`,`target`),type:`target`,position:n.Position.Left,style:r,isConnectable:!1}),(0,i.jsx)(n.Handle,{id:z(e,`left`,`source`),type:`source`,position:n.Position.Left,style:r,isConnectable:!1}),(0,i.jsx)(n.Handle,{id:z(e,`right`,`target`),type:`target`,position:n.Position.Right,style:r,isConnectable:!1}),(0,i.jsx)(n.Handle,{id:z(e,`right`,`source`),type:`source`,position:n.Position.Right,style:r,isConnectable:!1})]})}function H({data:e}){let{table:t,highlightedColumns:n}=e;return(0,i.jsxs)(`div`,{className:`dv-table`,style:{width:240},children:[(0,i.jsxs)(`div`,{className:`dv-table__header`,style:t.headerColor?{background:t.headerColor,height:40}:{height:40},title:t.note,children:[t.schema&&(0,i.jsxs)(`span`,{className:`dv-table__schema`,children:[t.schema,`.`]}),(0,i.jsx)(`span`,{className:`dv-table__name`,children:t.name})]}),(0,i.jsx)(`div`,{className:`dv-table__body`,children:t.columns.map((e,t)=>{let r=n?.has(e.name);return(0,i.jsxs)(`div`,{className:`dv-row${r?` dv-row--highlighted`:``}`,style:{height:28},title:e.note,children:[(0,i.jsxs)(`span`,{className:`dv-row__name${e.pk?` dv-row__name--pk`:``}`,children:[e.pk&&(0,i.jsx)(`span`,{className:`dv-badge dv-badge--pk`,title:`Primary key`,children:`PK`}),e.isForeignKey&&!e.pk&&(0,i.jsx)(`span`,{className:`dv-badge dv-badge--fk`,title:`Foreign key`,children:`FK`}),e.name]}),(0,i.jsxs)(`span`,{className:`dv-row__type`,children:[e.type,(0,i.jsx)(`span`,{className:`dv-row__null${e.notNull?` dv-row__null--notnull`:``}`,title:e.notNull?`Not null`:`Nullable`})]}),(0,i.jsx)(V,{column:e.name,top:B(t)})]},e.name)})})]})}var U=(0,t.memo)(H),W=8,G=13,K=7,q=13,J=16;function Y(e,t,r,a,o,s){let c=-(r===n.Position.Left?1:-1),l=[],u={stroke:o,strokeWidth:s},d=(n,r)=>l.push((0,i.jsx)(`line`,{x1:e+c*n,y1:t-W,x2:e+c*n,y2:t+W,style:u},r)),f=(n,r)=>l.push((0,i.jsx)(`circle`,{cx:e+c*n,cy:t,r:4,style:{stroke:o,strokeWidth:s,fill:`var(--dv-bg)`}},r));if(a.cardinality===`*`){let n=e+c*G;l.push((0,i.jsx)(`line`,{x1:n,y1:t,x2:e,y2:t-W,style:u},`cf1`),(0,i.jsx)(`line`,{x1:n,y1:t,x2:e,y2:t,style:u},`cf2`),(0,i.jsx)(`line`,{x1:n,y1:t,x2:e,y2:t+W,style:u},`cf3`)),a.optional===!0?f(17,`fr`):d(14,`fb`)}else d(K,`b1`),a.optional===!0?f(J,`r`):a.optional===!1&&d(q,`b2`);return(0,i.jsx)(`g`,{className:`dv-erd-marker`,children:l})}function X({sourceX:e,sourceY:t,targetX:r,targetY:a,sourcePosition:o,targetPosition:s,data:c,selected:l}){let[u]=(0,n.getSmoothStepPath)({sourceX:e,sourceY:t,sourcePosition:o,targetX:r,targetY:a,targetPosition:s,borderRadius:8}),d=c,f=d?.kind===`non-identifying`,p=l||d?.hovered,m=p?`var(--dv-edge-active)`:`var(--dv-edge)`,h=p?2:1.5;return(0,i.jsxs)(`g`,{className:`dv-erd-edge${p?` dv-erd-edge--active`:``}`,children:[(0,i.jsx)(`path`,{className:`dv-erd-edge__interaction`,d:u,fill:`none`}),(0,i.jsx)(`path`,{className:`dv-erd-edge__path`,d:u,fill:`none`,style:{stroke:m,strokeWidth:h},strokeDasharray:f?`6 4`:void 0}),d&&Y(e,t,o,d.sourceEnd,m,h),d&&Y(r,a,s,d.targetEnd,m,h)]})}var Z=(0,t.memo)(X);function ee(e,n,r){let[i,a]=(0,t.useState)(null),o=(0,t.useMemo)(()=>{let t=new Map;for(let n of e)t.set(n.id,n);return t},[e]),s=(0,t.useMemo)(()=>{let e=new Map,t=i?o.get(i):void 0;if(!t)return e;for(let n of[t.from,t.to]){let t=e.get(n.tableId)??new Set;n.columns.forEach(e=>t.add(e)),e.set(n.tableId,t)}return e},[i,o]);return{displayNodes:(0,t.useMemo)(()=>i?n.map(e=>{let t=s.get(e.id);return t?{...e,data:{...e.data,highlightedColumns:t}}:e}):n,[n,i,s]),displayEdges:(0,t.useMemo)(()=>i?r.map(e=>e.id===i?{...e,data:{...e.data,hovered:!0},zIndex:1}:e):r,[r,i]),onEdgeMouseEnter:(0,t.useCallback)((e,t)=>a(t.id),[]),onEdgeMouseLeave:(0,t.useCallback)(()=>a(null),[])}}var te={canvas:`--dv-canvas`,background:`--dv-bg`,border:`--dv-border`,headerBackground:`--dv-header-bg`,headerForeground:`--dv-header-fg`,rowForeground:`--dv-row-fg`,typeForeground:`--dv-type-fg`,rowHover:`--dv-row-hover`,rowHighlight:`--dv-row-highlight`,primaryKey:`--dv-pk`,foreignKey:`--dv-fk`,edge:`--dv-edge`,edgeActive:`--dv-edge-active`,fontFamily:`--dv-font`},ne={canvas:`#fafbfc`,background:`#ffffff`,border:`#d9dee5`,headerBackground:`#2d3748`,headerForeground:`#ffffff`,rowForeground:`#1a202c`,typeForeground:`#718096`,rowHover:`#f1f5f9`,rowHighlight:`#dbeafe`,primaryKey:`#b7791f`,foreignKey:`#2b6cb0`,edge:`#94a3b8`,edgeActive:`#2b6cb0`},re={canvas:`#0f172a`,background:`#1e293b`,border:`#334155`,headerBackground:`#334155`,headerForeground:`#f1f5f9`,rowForeground:`#e2e8f0`,typeForeground:`#94a3b8`,rowHover:`#334155`,rowHighlight:`#1e3a5f`,primaryKey:`#d69e2e`,foreignKey:`#4299e1`,edge:`#64748b`,edgeActive:`#60a5fa`};function Q(e){if(!e)return{};let t={};for(let n of Object.keys(e)){let r=e[n];r!=null&&(t[te[n]]=r)}return t}var ie=[],ae={table:U},oe={erd:Z};function se(){return(0,i.jsxs)(`svg`,{viewBox:`0 0 24 24`,width:`100%`,height:`100%`,fill:`currentColor`,"aria-hidden":`true`,children:[(0,i.jsx)(`rect`,{x:`3`,y:`3`,width:`8`,height:`8`,rx:`1.5`}),(0,i.jsx)(`rect`,{x:`13`,y:`3`,width:`8`,height:`8`,rx:`1.5`}),(0,i.jsx)(`rect`,{x:`3`,y:`13`,width:`8`,height:`8`,rx:`1.5`}),(0,i.jsx)(`rect`,{x:`13`,y:`13`,width:`8`,height:`8`,rx:`1.5`})]})}function ce(e,t,n,r){let i=new Map;for(let a of e.tables){let e=t.get(a.id);i.set(a.id,n?.[a.id]??r?.get(a.id)??{x:e?.x??0,y:e?.y??0})}return i}function le(e,t){if(e.length!==t.length)return!1;let n=new Set(e);return t.every(e=>n.has(e))}async function $(e,t,n){try{return await M(e,t)}catch(r){return n(r instanceof j?r:new j(String(r),{cause:r})),O(e,t)}}function ue(e,t){return e.tables.map(e=>({id:e.id,type:`table`,position:t.get(e.id)??{x:0,y:0},data:{table:e}}))}function de(e,t){return e.relations.map(e=>{let n=t.get(e.from.tableId),r=t.get(e.to.tableId),i=(n?.x??0)>(r?.x??0),a=i?`left`:`right`,o=i?`right`:`left`,s=e.from.columns[0]??``,c=e.to.columns[0]??``;return{id:e.id,source:e.from.tableId,target:e.to.tableId,sourceHandle:z(s,a,`source`),targetHandle:z(c,o,`target`),type:`erd`,data:{kind:e.kind,sourceEnd:{cardinality:e.from.relation,optional:e.from.optional},targetEnd:{cardinality:e.to.relation,optional:e.to.optional}}}})}var fe=(0,t.forwardRef)(function({dbml:e,className:r,style:a,theme:o,fitView:s=!0,showControls:c=!0,showMiniMap:l=!1,showBackground:u=!0,layoutOptions:d,onParseError:f,onLayoutError:p,nodePositions:m,onNodePositionsChange:h},_){let v=(0,t.useMemo)(()=>{try{return{ok:!0,schema:S(e)}}catch(e){return{ok:!1,error:e instanceof g?e:new g(String(e),{cause:e})}}},[e]),[y,b,x]=(0,n.useNodesState)([]),[C,w,T]=(0,n.useEdgesState)([]),E=(0,t.useRef)(null),D=(0,t.useRef)(null),O=(0,t.useRef)(y);O.current=y,(0,t.useImperativeHandle)(_,()=>{let e=async e=>{let t=E.current,r=D.current?.querySelector(`.react-flow__viewport`);if(!t||!r)throw new P(`The diagram is not ready to export yet.`);let i=F((0,n.getNodesBounds)(t.getNodes()),r),a=e?.backgroundColor??(D.current?getComputedStyle(D.current).backgroundColor:void 0);return I(r,i,{...e,backgroundColor:a})};return{toDataUrl:e,download:async(t,n)=>{R(await e(n),t)}}},[]);let k=(0,t.useRef)(p);k.current=p;let A=(0,t.useRef)(h);A.current=h;let j=(0,t.useRef)(m);j.current=m;let M=(0,t.useCallback)(()=>{let e=A.current;if(!e)return;let t={};for(let e of O.current)t[e.id]={x:e.position.x,y:e.position.y};e(t)},[]),{algorithm:N=`simple`,direction:L=`LR`,horizontalGap:z,verticalGap:B}=d??{},V=(0,t.useRef)(null),H=`${N}|${L}|${z}|${B}`,U=(0,t.useCallback)((e,t,n,r,i)=>{let a=ce(e,r,i.saved,i.prior);if(b(ue(e,a)),w(de(e,a)),V.current={ids:t,optionsKey:n},i.emit){let e={};a.forEach((t,n)=>{e[n]=t}),A.current?.(e)}i.fit&&requestAnimationFrame(()=>void E.current?.fitView())},[b,w]);(0,t.useEffect)(()=>{if(!v.ok){b([]),w([]),V.current=null;return}let e=v.schema,t={algorithm:N,direction:L,horizontalGap:z,verticalGap:B},n=e.tables.map(e=>e.id),r=new Map(O.current.map(e=>[e.id,{x:e.position.x,y:e.position.y}])),i=V.current,a=n.every(e=>r.has(e));if(i&&i.optionsKey===H&&a&&le(i.ids,n)){U(e,n,H,new Map,{prior:r});return}let o=i&&i.optionsKey===H?r:void 0,c=!1;return $(e,t,e=>k.current?.(e)).then(t=>{c||U(e,n,H,t,{saved:j.current,prior:o,fit:s})}),()=>{c=!0}},[v,N,L,z,B,H,s,U,b,w]);let W=(0,t.useCallback)(()=>{if(!v.ok)return;let e=v.schema,t={algorithm:N,direction:L,horizontalGap:z,verticalGap:B},n=e.tables.map(e=>e.id);$(e,t,e=>k.current?.(e)).then(t=>{U(e,n,H,t,{emit:!0,fit:!0})})},[v,N,L,z,B,H,U]),G=(0,t.useCallback)(()=>{v.ok||f?.(v.error)},[v,f]);(0,t.useEffect)(G,[G]);let{displayNodes:K,displayEdges:q,onEdgeMouseEnter:J,onEdgeMouseLeave:Y}=ee(v.ok?v.schema.relations:ie,y,C),X={width:`100%`,height:`100%`,...Q(o),...a};return v.ok?(0,i.jsx)(`div`,{ref:D,className:`dv-viewer${r?` ${r}`:``}`,style:X,children:(0,i.jsxs)(n.ReactFlow,{nodes:K,edges:q,nodeTypes:ae,edgeTypes:oe,onNodesChange:x,onEdgesChange:T,onEdgeMouseEnter:J,onEdgeMouseLeave:Y,onNodeDragStop:M,onInit:e=>{E.current=e},fitView:s,nodesConnectable:!1,elementsSelectable:!0,minZoom:.1,proOptions:{hideAttribution:!0},children:[u&&(0,i.jsx)(n.Background,{}),c&&(0,i.jsx)(n.Controls,{children:(0,i.jsx)(n.ControlButton,{onClick:W,title:`Auto layout`,"aria-label":`Auto layout`,children:(0,i.jsx)(se,{})})}),l&&(0,i.jsx)(n.MiniMap,{pannable:!0,zoomable:!0})]})}):(0,i.jsx)(`div`,{className:`dv-error${r?` ${r}`:``}`,style:X,children:(0,i.jsx)(`pre`,{className:`dv-error__message`,children:v.error.message})})});e.DbmlParseError=g,e.DbmlViewer=fe,e.ErdEdge=Z,e.ExportError=P,e.HEADER_HEIGHT=w,e.LayoutError=j,e.NODE_WIDTH=C,e.ROW_HEIGHT=T,e.TableNode=U,e.computeLayout=M,e.darkTheme=re,e.downloadDataUrl=R,e.layoutSchema=O,e.lightTheme=ne,e.parseDbml=S,e.renderDiagram=I,e.tableHeight=E,e.themeToCssVars=Q});
2
+ `)}`}function y(e){if(e)return typeof e==`string`?e:e.value??void 0}function b(e,t){return e&&e!==h?`${e}.${t}`:t}function x(e){let t=new Set;for(let n of e.fields)n.pk&&t.add(n.name);for(let n of e.indexes??[])if(n.pk)for(let e of n.columns??[])e.type===`column`&&e.value&&t.add(e.value);return t}function ee(e){let t;try{t=new m().parse(e,`dbmlv2`)}catch(e){let t=_(e);throw new g(v(t,e instanceof Error?e.message:String(e)),{cause:e,diagnostics:t})}let n=new Map;for(let e of t.schemas)for(let t of e.tables){let r=b(e.name,t.name),i=x(t),a=new Map;for(let e of t.fields){let t=i.has(e.name);a.set(e.name,{pk:t,notNull:t||!!e.not_null,note:y(e.note)})}n.set(r,a)}let r=e=>{let t=n.get(e.tableId);return!t||e.columns.length===0?!1:e.columns.every(e=>t.get(e)?.pk===!0)},i=e=>e.columns.some(t=>{let r=n.get(e.tableId)?.get(t);return r?!r.notNull&&!r.pk:!0}),a=e=>e.columns.some(t=>n.get(e.tableId)?.get(t)?.note?.toLowerCase().includes(`optional`)??!1),o=[],s=new Set;for(let e of t.schemas)for(let t of e.refs){if(t.endpoints.length!==2)continue;let[n,c]=t.endpoints.map(t=>({tableId:b(t.schemaName??e.name,t.tableName),columns:t.fieldNames,relation:t.relation})),l,u,d=n.relation===`*`;if(d!==(c.relation===`*`))l=d?n:c,u=d?c:n;else{let e=r(n),t=r(c);e&&!t?(l=c,u=n):(l=n,u=c)}let f=r(l),p=i(l);l.columns.forEach(e=>s.add(`${l.tableId}::${e}`)),o.push({id:`${l.tableId}.${l.columns.join(`-`)}__${u.tableId}.${u.columns.join(`-`)}`,kind:f?`identifying`:`non-identifying`,from:{...l,optional:a(l)?!0:void 0},to:{...u,optional:p}})}let c=[];for(let e of t.schemas){let t=e.name;for(let n of e.tables){let e=b(t,n.name),r=x(n),i=n.fields.map(t=>({name:t.name,type:t.type?.type_name??`unknown`,pk:r.has(t.name),notNull:r.has(t.name)||!!t.not_null,unique:!!t.unique,increment:!!t.increment,defaultValue:t.dbdefault?.value,note:y(t.note),isForeignKey:s.has(`${e}::${t.name}`)}));c.push({id:e,name:n.name,schema:t===h?void 0:t,note:y(n.note),headerColor:n.headerColor??void 0,columns:i})}}return{tables:c,relations:o}}var S=160,C=320,w=320,te=40,T=28;function E(e){return 40+Math.max(e.columns.length,1)*28}var D=24,O=8,k=24,A=12,ne=24,j=6.8,M=7.4;function N(e,t={}){let n=t.minWidth??160,r=t.maxWidth??320,i=ne+P(e)*M;for(let t of e.columns){let e=(t.pk||t.isForeignKey?k:0)+t.name.length*j,n=t.type.length*j+A;i=Math.max(i,D+e+O+n)}return Math.round(Math.min(r,Math.max(n,i)))}function P(e){return(e.schema?e.schema.length+1:0)+e.name.length}function F(e,t){let n=new Map;for(let t of e)n.set(t.id,0);let r=t.map(e=>[e.from.tableId,e.to.tableId]).filter(([e,t])=>e!==t&&n.has(e)&&n.has(t));for(let t=0;t<e.length;t++){let e=!1;for(let[t,i]of r){let r=n.get(t)+1;n.get(i)<r&&(n.set(i,r),e=!0)}if(!e)break}return n}function I(e,t={}){let n=t.horizontalGap??120,r=t.verticalGap??40,i={minWidth:t.minNodeWidth,maxWidth:t.maxNodeWidth},a=new Set;for(let t of e.relations)a.add(t.from.tableId),a.add(t.to.tableId);let o=e.tables.filter(e=>a.has(e.id)),s=e.tables.filter(e=>!a.has(e.id)),c=new Map,l=F(o,e.relations),u=new Map;for(let e of o){let t=l.get(e.id)??0,n=u.get(t);n||(n=[],u.set(t,n)),n.push(e)}let d=0,f=0,p=0;for(let e of[...u.keys()].sort((e,t)=>e-t)){let t=u.get(e).sort((e,t)=>e.name.localeCompare(t.name)),a=0,o=0;for(let e of t){let t=N(e,i),n=E(e);c.set(e.id,{x:p,y:o,width:t,height:n}),a=Math.max(a,t),o+=n+r}d=Math.max(d,p+a),f=Math.max(f,o-r),p+=a+n}if(s.length>0){let e=d>0?d:Math.ceil(Math.sqrt(s.length))*(320+n),t=0,a=f>0?f+r*2:0,o=0;for(let l of[...s].sort((e,t)=>e.name.localeCompare(t.name))){let s=N(l,i),u=E(l);t>0&&t+s>e&&(t=0,a+=o+r,o=0),c.set(l.id,{x:t,y:a,width:s,height:u}),t+=s+n,o=Math.max(o,u)}}return c}async function L(e,t={}){let n=await import(`@dagrejs/dagre`),r=t.horizontalGap??120,i=t.verticalGap??40,a={minWidth:t.minNodeWidth,maxWidth:t.maxNodeWidth},o=t.direction===`TB`?`TB`:`LR`,s=new n.graphlib.Graph;s.setGraph({rankdir:o,ranksep:r,nodesep:i}),s.setDefaultEdgeLabel(()=>({}));for(let t of e.tables)s.setNode(t.id,{width:N(t,a),height:E(t)});for(let t of e.relations)s.hasNode(t.from.tableId)&&s.hasNode(t.to.tableId)&&s.setEdge(t.from.tableId,t.to.tableId);n.layout(s);let c=new Map;for(let e of s.nodes()){let t=s.node(e);t&&c.set(e,{x:t.x-t.width/2,y:t.y-t.height/2,width:t.width,height:t.height})}return c}async function R(e,t={}){let n=new(await(import(`elkjs/lib/elk.bundled.js`))).default,r=t.horizontalGap??120,i=t.verticalGap??40,a={minWidth:t.minNodeWidth,maxWidth:t.maxNodeWidth},o={id:`root`,layoutOptions:{"elk.algorithm":`layered`,"elk.direction":t.direction===`TB`?`DOWN`:`RIGHT`,"elk.layered.spacing.nodeNodeBetweenLayers":String(r),"elk.spacing.nodeNode":String(i)},children:e.tables.map(e=>({id:e.id,width:N(e,a),height:E(e)})),edges:e.relations.map((e,t)=>({id:`e${t}`,sources:[e.from.tableId],targets:[e.to.tableId]}))},s=await n.layout(o),c=new Map;for(let e of s.children??[])c.set(e.id,{x:e.x??0,y:e.y??0,width:e.width??160,height:e.height??0});return c}var z=class extends Error{constructor(e,t){super(e,t),this.name=`LayoutError`}};async function B(e,t={}){let n=t.algorithm??`simple`;if(n===`simple`)return I(e,t);try{return n===`dagre`?await L(e,t):await R(e,t)}catch(e){throw new z(`"${n}" layout failed. Is the optional dependency "${n===`dagre`?`@dagrejs/dagre`:`elkjs`}" installed? (${e instanceof Error?e.message:String(e)})`,{cause:e})}}var V=[`--dv-canvas`,`--dv-bg`,`--dv-border`,`--dv-header-bg`,`--dv-header-fg`,`--dv-row-fg`,`--dv-type-fg`,`--dv-row-hover`,`--dv-row-highlight`,`--dv-pk`,`--dv-fk`,`--dv-null`,`--dv-edge`,`--dv-edge-active`,`--dv-font`],H=class extends Error{constructor(e,t){super(e,t),this.name=`ExportError`}};function re(e,t){let n=e.x,r=e.y,i=e.x+e.width,a=e.y+e.height;for(let e of t.querySelectorAll(`.dv-erd-edge`)){let t;try{t=e.getBBox()}catch{continue}t.width===0&&t.height===0||(n=Math.min(n,t.x),r=Math.min(r,t.y),i=Math.max(i,t.x+t.width),a=Math.max(a,t.y+t.height))}return{x:n,y:r,width:i-n,height:a-r}}async function U(e,t,n={}){let{type:r=`png`,padding:i=24,backgroundColor:a,pixelRatio:o=2}=n,s;try{s=await import(`html-to-image`)}catch(e){throw new H(`Export requires the optional dependency "html-to-image". Install it to enable PNG/SVG export.`,{cause:e})}if(typeof document<`u`&&document.fonts?.ready)try{await document.fonts.ready}catch{}let c=Math.ceil(t.width+i*2),l=Math.ceil(t.height+i*2),u=`translate(${-t.x+i}px, ${-t.y+i}px) scale(1)`,d={width:c,height:l,backgroundColor:a,style:{width:`${c}px`,height:`${l}px`,transform:u}},f=W(e,t,i);try{return r===`svg`?await s.toSvg(e,d):await s.toPng(e,{...d,pixelRatio:o})}catch(e){throw new H(`Failed to render diagram image: ${e instanceof Error?e.message:String(e)}`,{cause:e})}finally{f()}}function W(e,t,n){let r=[],i=e.closest(`.dv-viewer`);if(i){let t=getComputedStyle(i);for(let n of V){let i=t.getPropertyValue(n);if(!i)continue;let a=e.style.getPropertyValue(n);e.style.setProperty(n,i),r.push(()=>{a?e.style.setProperty(n,a):e.style.removeProperty(n)})}}let a=`${Math.ceil(t.x+t.width+n)}px`,o=`${Math.ceil(t.y+t.height+n)}px`;for(let t of e.querySelectorAll(`.react-flow__edges svg`)){let{width:e,height:n}=t.style;t.style.width=a,t.style.height=o,r.push(()=>{t.style.width=e,t.style.height=n})}return()=>r.forEach(e=>e())}function G(e,t){let n=document.createElement(`a`);n.href=e,n.download=t,n.click()}function K(e,t,n){return`${e}__${t}__${n}`}var q=(0,t.createContext)(void 0),J=(0,t.createContext)(`column`);function Y(e){return 40+e*28+28/2}var X=4;function ie({column:e,top:t}){let r={top:t-X,opacity:0},a={top:t+X,opacity:0};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.Handle,{id:K(e,`left`,`target`),type:`target`,position:n.Position.Left,style:a,isConnectable:!1}),(0,i.jsx)(n.Handle,{id:K(e,`left`,`source`),type:`source`,position:n.Position.Left,style:r,isConnectable:!1}),(0,i.jsx)(n.Handle,{id:K(e,`right`,`target`),type:`target`,position:n.Position.Right,style:a,isConnectable:!1}),(0,i.jsx)(n.Handle,{id:K(e,`right`,`source`),type:`source`,position:n.Position.Right,style:r,isConnectable:!1})]})}function Z({data:e}){let{table:r,highlightedColumns:a,widthBounds:o,connectedColumns:s}=e,c=(0,t.useContext)(q),l=(0,t.useContext)(J)===`floating`;return(0,i.jsxs)(`div`,{className:`dv-table`,style:{width:N(r,o)},children:[l&&(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.Handle,{type:`source`,position:n.Position.Right,style:{opacity:0},isConnectable:!1}),(0,i.jsx)(n.Handle,{type:`target`,position:n.Position.Left,style:{opacity:0},isConnectable:!1})]}),(0,i.jsxs)(`div`,{className:`dv-table__header`,style:r.headerColor?{background:r.headerColor,height:40}:{height:40},title:r.note,children:[r.schema&&(0,i.jsxs)(`span`,{className:`dv-table__schema`,children:[r.schema,`.`]}),(0,i.jsx)(`span`,{className:`dv-table__name`,title:r.name,children:r.name})]}),(0,i.jsx)(`div`,{className:`dv-table__body`,children:r.columns.map((e,t)=>{let n=a?.has(e.name);return(0,i.jsxs)(`div`,{className:`dv-row${n?` dv-row--highlighted`:``}`,style:{height:28},title:e.note,onMouseEnter:()=>c?.(r.id,e.name),onMouseLeave:()=>c?.(r.id,null),children:[(0,i.jsxs)(`span`,{className:`dv-row__name${e.pk?` dv-row__name--pk`:``}`,children:[e.pk&&(0,i.jsx)(`span`,{className:`dv-badge dv-badge--pk`,title:`Primary key`,children:`PK`}),e.isForeignKey&&!e.pk&&(0,i.jsx)(`span`,{className:`dv-badge dv-badge--fk`,title:`Foreign key`,children:`FK`}),(0,i.jsx)(`span`,{className:`dv-row__label`,title:e.name,children:e.name})]}),(0,i.jsxs)(`span`,{className:`dv-row__type`,children:[(0,i.jsx)(`span`,{className:`dv-row__typename`,title:e.type,children:e.type}),(0,i.jsx)(`span`,{className:`dv-row__null${e.notNull?` dv-row__null--notnull`:``}`,title:e.notNull?`Not null`:`Nullable`})]}),!l&&s?.has(e.name)&&(0,i.jsx)(ie,{column:e.name,top:Y(t)})]},e.name)})})]})}var ae=(0,t.memo)(Z),oe=8,Q=13,se=7,ce=13,le=16,ue={[n.Position.Right]:0,[n.Position.Left]:180,[n.Position.Top]:-90,[n.Position.Bottom]:90};function de(e,t,n,r,a,o){let s=[],c={stroke:a,strokeWidth:o},l=(e,t)=>s.push((0,i.jsx)(`line`,{x1:e,y1:-8,x2:e,y2:oe,style:c},t)),u=(e,t)=>s.push((0,i.jsx)(`circle`,{cx:e,cy:0,r:4,style:{stroke:a,strokeWidth:o,fill:`var(--dv-bg)`}},t));return r.cardinality===`*`?(s.push((0,i.jsx)(`line`,{x1:Q,y1:0,x2:0,y2:-8,style:c},`cf1`),(0,i.jsx)(`line`,{x1:Q,y1:0,x2:0,y2:0,style:c},`cf2`),(0,i.jsx)(`line`,{x1:Q,y1:0,x2:0,y2:oe,style:c},`cf3`)),r.optional===!0?u(17,`fr`):l(14,`fb`)):(l(se,`b1`),r.optional===!0?u(le,`r`):r.optional===!1&&l(ce,`b2`)),(0,i.jsx)(`g`,{className:`dv-erd-marker`,transform:`translate(${e}, ${t}) rotate(${ue[n]})`,children:s})}function fe(e,t,n,r,a,o,s,c,l){let u=t?.kind===`non-identifying`,d=n?`var(--dv-edge-active)`:`var(--dv-edge)`,f=n?2:1.5;return(0,i.jsxs)(`g`,{className:`dv-erd-edge${n?` dv-erd-edge--active`:``}`,children:[(0,i.jsx)(`path`,{className:`dv-erd-edge__interaction`,d:e,fill:`none`}),(0,i.jsx)(`path`,{className:`dv-erd-edge__path`,d:e,fill:`none`,style:{stroke:d,strokeWidth:f},strokeDasharray:u?`6 4`:void 0}),t&&de(r,a,o,t.sourceEnd,d,f),t&&de(s,c,l,t.targetEnd,d,f)]})}function pe({sourceX:e,sourceY:t,targetX:r,targetY:i,sourcePosition:a,targetPosition:o,data:s,selected:c}){let[l]=(0,n.getSmoothStepPath)({sourceX:e,sourceY:t,sourcePosition:a,targetX:r,targetY:i,targetPosition:o,borderRadius:8}),u=s;return fe(l,u,!!(c||u?.hovered),e,t,a,r,i,o)}var me=(0,t.memo)(pe);function $(e){let t=e.measured?.width??0,n=e.measured?.height??0,{x:r,y:i}=e.internals.positionAbsolute;return{x:r,y:i,w:t,h:n,cx:r+t/2,cy:i+n/2}}function he(e,t){let n=$(e),r=$(t),i=n.w/2,a=n.h/2,o=(r.cx-n.cx)/(2*i)-(r.cy-n.cy)/(2*a),s=(r.cx-n.cx)/(2*i)+(r.cy-n.cy)/(2*a),c=1/(Math.abs(o)+Math.abs(s)||1);return{x:i*c*(o+s)+n.cx,y:a*c*(-o+s)+n.cy}}function ge(e,t){let r=$(e),i=Math.round(t.x),a=Math.round(t.y);return i<=Math.round(r.x)+1?n.Position.Left:i>=Math.round(r.x+r.w)-1?n.Position.Right:a<=Math.round(r.y)+1?n.Position.Top:n.Position.Bottom}var _e=14;function ve({id:e,source:t,target:r,data:i,selected:a}){let o=(0,n.useInternalNode)(t),s=(0,n.useInternalNode)(r);if(!o||!s)return null;let c=i,l=!!(a||c?.hovered),u,d,f,p,m,h;if(t===r){let e=$(o);u=f=e.x+e.w,d=e.cy-_e,p=e.cy+_e,m=h=n.Position.Right}else{let e=he(o,s),t=he(s,o);u=e.x,d=e.y,f=t.x,p=t.y,m=ge(o,e),h=ge(s,t)}let[g]=(0,n.getBezierPath)({sourceX:u,sourceY:d,sourcePosition:m,targetX:f,targetY:p,targetPosition:h});return fe(g,c,l,u,d,m,f,p,h)}var ye=(0,t.memo)(ve);function be(e,n,r){let[i,a]=(0,t.useState)(null),[o,s]=(0,t.useState)(null),c=(0,t.useMemo)(()=>{let t=new Map;for(let n of e)t.set(n.id,n);return t},[e]),l=(0,t.useMemo)(()=>{let t=new Set;if(i&&t.add(i),o)for(let n of e){let e=e=>e.tableId===o.tableId&&e.columns.includes(o.column);(e(n.from)||e(n.to))&&t.add(n.id)}return t},[i,o,e]),u=(0,t.useMemo)(()=>{let e=new Map;for(let t of l){let n=c.get(t);if(n)for(let t of[n.from,n.to]){let n=e.get(t.tableId)??new Set;t.columns.forEach(e=>n.add(e)),e.set(t.tableId,n)}}return e},[l,c]),d=(0,t.useCallback)((e,t)=>{s(t?{tableId:e,column:t}:null)},[]);return{displayNodes:(0,t.useMemo)(()=>l.size===0?n:n.map(e=>{let t=u.get(e.id);return t?{...e,data:{...e.data,highlightedColumns:t}}:e}),[n,l,u]),displayEdges:(0,t.useMemo)(()=>l.size===0?r:r.map(e=>l.has(e.id)?{...e,data:{...e.data,hovered:!0},zIndex:1}:e),[r,l]),onEdgeMouseEnter:(0,t.useCallback)((e,t)=>a(t.id),[]),onEdgeMouseLeave:(0,t.useCallback)(()=>a(null),[]),onColumnHover:d}}var xe={canvas:`--dv-canvas`,background:`--dv-bg`,border:`--dv-border`,headerBackground:`--dv-header-bg`,headerForeground:`--dv-header-fg`,rowForeground:`--dv-row-fg`,typeForeground:`--dv-type-fg`,rowHover:`--dv-row-hover`,rowHighlight:`--dv-row-highlight`,primaryKey:`--dv-pk`,foreignKey:`--dv-fk`,edge:`--dv-edge`,edgeActive:`--dv-edge-active`,fontFamily:`--dv-font`},Se={canvas:`#fafbfc`,background:`#ffffff`,border:`#d9dee5`,headerBackground:`#2d3748`,headerForeground:`#ffffff`,rowForeground:`#1a202c`,typeForeground:`#718096`,rowHover:`#f1f5f9`,rowHighlight:`#dbeafe`,primaryKey:`#b7791f`,foreignKey:`#2b6cb0`,edge:`#94a3b8`,edgeActive:`#2b6cb0`},Ce={canvas:`#0f172a`,background:`#1e293b`,border:`#334155`,headerBackground:`#334155`,headerForeground:`#f1f5f9`,rowForeground:`#e2e8f0`,typeForeground:`#94a3b8`,rowHover:`#334155`,rowHighlight:`#1e3a5f`,primaryKey:`#d69e2e`,foreignKey:`#4299e1`,edge:`#64748b`,edgeActive:`#60a5fa`};function we(e){if(!e)return{};let t={};for(let n of Object.keys(e)){let r=e[n];r!=null&&(t[xe[n]]=r)}return t}var Te=[],Ee={table:ae},De={erd:me,"erd-floating":ye};function Oe(){return(0,i.jsxs)(`svg`,{viewBox:`0 0 24 24`,width:`100%`,height:`100%`,fill:`currentColor`,"aria-hidden":`true`,children:[(0,i.jsx)(`rect`,{x:`3`,y:`3`,width:`8`,height:`8`,rx:`1.5`}),(0,i.jsx)(`rect`,{x:`13`,y:`3`,width:`8`,height:`8`,rx:`1.5`}),(0,i.jsx)(`rect`,{x:`3`,y:`13`,width:`8`,height:`8`,rx:`1.5`}),(0,i.jsx)(`rect`,{x:`13`,y:`13`,width:`8`,height:`8`,rx:`1.5`})]})}function ke(e,t,n,r){let i=new Map;for(let a of e.tables){let e=t.get(a.id);i.set(a.id,n?.[a.id]??r?.get(a.id)??{x:e?.x??0,y:e?.y??0})}return i}function Ae(e,t){if(e.length!==t.length)return!1;let n=new Set(e);return t.every(e=>n.has(e))}function je(e,t,n){return e.x<t.x+t.w+n&&e.x+e.w+n>t.x&&e.y<t.y+t.h+n&&e.y+e.h+n>t.y}function Me(e,t,n,r,i){let a=(e,t)=>({x:t.x,y:t.y,w:N(e,i),h:E(e)}),o=[];for(let r of e.tables)n(r.id)&&o.push(a(r,t.get(r.id)));for(let i of e.tables){if(n(i.id))continue;let e=t.get(i.id),s=a(i,e),c=0;for(;c++<1e3;){let t=o.filter(e=>je(s,e,r));if(t.length===0)break;let n=Math.max(...t.map(e=>e.y+e.h));e={x:e.x,y:n+r},s=a(i,e)}t.set(i.id,e),o.push(s)}}async function Ne(e,t,n){try{return await B(e,t)}catch(r){return n(r instanceof z?r:new z(String(r),{cause:r})),I(e,t)}}function Pe(e){let t=new Map,n=(e,n)=>{if(!n)return;let r=t.get(e);r||(r=new Set,t.set(e,r)),r.add(n)};for(let t of e.relations)t.from.columns.forEach(e=>n(t.from.tableId,e)),t.to.columns.forEach(e=>n(t.to.tableId,e));return t}function Fe(e,t,n,r){return e.tables.map(e=>({id:e.id,type:`table`,position:t.get(e.id)??{x:0,y:0},data:{table:e,widthBounds:n,connectedColumns:r.get(e.id)}}))}function Ie(e,t){let n=e.from.tableId===e.to.tableId,r=n?!1:(t.get(e.from.tableId)?.x??0)>(t.get(e.to.tableId)?.x??0),i=r?`left`:`right`,a=n||r?`right`:`left`;return{sourceHandle:K(e.from.columns[0]??``,i,`source`),targetHandle:K(e.to.columns[0]??``,a,`target`)}}function Le(e,t,n){let r=n===`floating`;return e.relations.map(e=>{let n=r?{sourceHandle:void 0,targetHandle:void 0}:Ie(e,t);return{id:e.id,source:e.from.tableId,target:e.to.tableId,...n,type:r?`erd-floating`:`erd`,data:{kind:e.kind,sourceEnd:{cardinality:e.from.relation,optional:e.from.optional},targetEnd:{cardinality:e.to.relation,optional:e.to.optional}}}})}var Re=(0,t.forwardRef)(function({dbml:e,className:r,style:a,theme:o,fitView:s=!0,showControls:c=!0,showMiniMap:l=!1,showBackground:u=!0,layoutOptions:d,edgeConnection:f=`column`,onParseError:p,onLayoutError:m,nodePositions:h,onNodePositionsChange:_},v){let y=(0,t.useMemo)(()=>{try{return{ok:!0,schema:ee(e)}}catch(e){return{ok:!1,error:e instanceof g?e:new g(String(e),{cause:e})}}},[e]),[b,x,S]=(0,n.useNodesState)([]),[C,w,te]=(0,n.useEdgesState)([]),T=(0,t.useRef)(null),E=(0,t.useRef)(null),D=(0,t.useRef)(b);D.current=b,(0,t.useImperativeHandle)(v,()=>{let e=async e=>{let t=T.current,r=E.current?.querySelector(`.react-flow__viewport`);if(!t||!r)throw new H(`The diagram is not ready to export yet.`);let i=re((0,n.getNodesBounds)(t.getNodes()),r),a=e?.backgroundColor??(E.current?getComputedStyle(E.current).backgroundColor:void 0);return U(r,i,{...e,backgroundColor:a})};return{toDataUrl:e,download:async(t,n)=>{G(await e(n),t)},fitView:e=>T.current?.fitView(e),zoomIn:e=>T.current?.zoomIn(e),zoomOut:e=>T.current?.zoomOut(e),zoomTo:(e,t)=>T.current?.zoomTo(e,t),setCenter:(e,t,n)=>T.current?.setCenter(e,t,n),fitBounds:(e,t)=>T.current?.fitBounds(e,t),setViewport:(e,t)=>T.current?.setViewport(e,t),getViewport:()=>T.current?.getViewport()}},[]);let O=(0,t.useRef)(m);O.current=m;let k=(0,t.useRef)(_);k.current=_;let A=(0,t.useRef)(h);A.current=h;let ne=(0,t.useCallback)(()=>{let e=k.current;if(!e)return;let t={};for(let e of D.current)t[e.id]={x:e.position.x,y:e.position.y};e(t)},[]),{algorithm:j=`simple`,direction:M=`LR`,horizontalGap:N,verticalGap:P,minNodeWidth:F,maxNodeWidth:I}=d??{},L=(0,t.useRef)(null),R=`${j}|${M}|${N}|${P}|${F}|${I}`,z=(0,t.useCallback)((e,t,n,r,i)=>{let a={minWidth:F,maxWidth:I},o=ke(e,r,i.saved,i.prior);if(i.prior){let t=i.prior,n=i.saved;Me(e,o,e=>t.has(e)||n?.[e]!==void 0,P??40,a)}if(x(Fe(e,o,a,Pe(e))),w(Le(e,o,f)),L.current={ids:t,optionsKey:n},i.emit){let e={};o.forEach((t,n)=>{e[n]=t}),k.current?.(e)}i.fit&&requestAnimationFrame(()=>void T.current?.fitView())},[x,w,P,F,I,f]);(0,t.useEffect)(()=>{if(!y.ok){x([]),w([]),L.current=null;return}let e=y.schema,t={algorithm:j,direction:M,horizontalGap:N,verticalGap:P,minNodeWidth:F,maxNodeWidth:I},n=e.tables.map(e=>e.id),r=new Map(D.current.map(e=>[e.id,{x:e.position.x,y:e.position.y}])),i=L.current,a=n.every(e=>r.has(e));if(i&&i.optionsKey===R&&a&&Ae(i.ids,n)){z(e,n,R,new Map,{prior:r});return}let o=i&&i.optionsKey===R?r:void 0,c=!1;return Ne(e,t,e=>O.current?.(e)).then(t=>{c||z(e,n,R,t,{saved:A.current,prior:o,fit:s})}),()=>{c=!0}},[y,j,M,N,P,F,I,R,s,z,x,w]);let B=(0,t.useCallback)(()=>{if(!y.ok)return;let e=y.schema,t={algorithm:j,direction:M,horizontalGap:N,verticalGap:P,minNodeWidth:F,maxNodeWidth:I},n=e.tables.map(e=>e.id);Ne(e,t,e=>O.current?.(e)).then(t=>{z(e,n,R,t,{emit:!0,fit:!0})})},[y,j,M,N,P,F,I,R,z]),V=(0,t.useCallback)(()=>{y.ok||p?.(y.error)},[y,p]);(0,t.useEffect)(V,[V]);let{displayNodes:W,displayEdges:K,onEdgeMouseEnter:Y,onEdgeMouseLeave:X,onColumnHover:ie}=be(y.ok?y.schema.relations:Te,b,C),Z={width:`100%`,height:`100%`,...we(o),...a};return y.ok?(0,i.jsx)(`div`,{ref:E,className:`dv-viewer${r?` ${r}`:``}`,style:Z,children:(0,i.jsx)(J.Provider,{value:f,children:(0,i.jsx)(q.Provider,{value:ie,children:(0,i.jsxs)(n.ReactFlow,{nodes:W,edges:K,nodeTypes:Ee,edgeTypes:De,onNodesChange:S,onEdgesChange:te,onEdgeMouseEnter:Y,onEdgeMouseLeave:X,onNodeDragStop:ne,onInit:e=>{T.current=e},fitView:s,nodesConnectable:!1,elementsSelectable:!0,minZoom:.1,proOptions:{hideAttribution:!0},children:[u&&(0,i.jsx)(n.Background,{}),c&&(0,i.jsx)(n.Controls,{children:(0,i.jsx)(n.ControlButton,{onClick:B,title:`Auto layout`,"aria-label":`Auto layout`,children:(0,i.jsx)(Oe,{})})}),l&&(0,i.jsx)(n.MiniMap,{pannable:!0,zoomable:!0})]})})})}):(0,i.jsx)(`div`,{className:`dv-error${r?` ${r}`:``}`,style:Z,children:(0,i.jsx)(`pre`,{className:`dv-error__message`,children:y.error.message})})});e.DbmlParseError=g,e.DbmlViewer=Re,e.ErdEdge=me,e.ExportError=H,e.HEADER_HEIGHT=te,e.LayoutError=z,e.MAX_NODE_WIDTH=C,e.MIN_NODE_WIDTH=S,e.NODE_WIDTH=w,e.ROW_HEIGHT=T,e.TableNode=ae,e.computeLayout=B,e.darkTheme=Ce,e.downloadDataUrl=G,e.layoutSchema=I,e.lightTheme=Se,e.parseDbml=ee,e.renderDiagram=U,e.tableHeight=E,e.tableWidth=N,e.themeToCssVars=we});
package/dist/index.d.ts CHANGED
@@ -1,12 +1,17 @@
1
1
  import { CSSProperties } from 'react';
2
2
  import { EdgeProps } from '@xyflow/react';
3
+ import { FitViewOptions } from '@xyflow/react';
3
4
  import { ForwardRefExoticComponent } from 'react';
4
5
  import { JSX } from 'react';
5
6
  import { MemoExoticComponent } from 'react';
6
7
  import { Node as Node_2 } from '@xyflow/react';
7
8
  import { NodeProps } from '@xyflow/react';
9
+ import { ReactNode } from 'react';
8
10
  import { Rect } from '@xyflow/react';
9
11
  import { RefAttributes } from 'react';
12
+ import { SetCenterOptions } from '@xyflow/react';
13
+ import { Viewport } from '@xyflow/react';
14
+ import { ViewportHelperFunctionOptions } from '@xyflow/react';
10
15
 
11
16
  /** Cardinality of one side of a relation. `'1'` = one, `'*'` = many. */
12
17
  export declare type Cardinality = '1' | '*';
@@ -86,6 +91,22 @@ export declare interface DbmlViewerHandle {
86
91
  toDataUrl(options?: DiagramExportOptions): Promise<string>;
87
92
  /** Render the diagram and trigger a browser download. */
88
93
  download(filename: string, options?: DiagramExportOptions): Promise<void>;
94
+ /** Fit the whole diagram into the viewport. */
95
+ fitView(options?: FitViewOptions): void;
96
+ /** Zoom in by one step. */
97
+ zoomIn(options?: ViewportHelperFunctionOptions): void;
98
+ /** Zoom out by one step. */
99
+ zoomOut(options?: ViewportHelperFunctionOptions): void;
100
+ /** Zoom to a specific level (e.g. `1` = 100%). */
101
+ zoomTo(zoomLevel: number, options?: ViewportHelperFunctionOptions): void;
102
+ /** Center the viewport on a flow-coordinate point. */
103
+ setCenter(x: number, y: number, options?: SetCenterOptions): void;
104
+ /** Fit a specific flow-coordinate rectangle into the viewport. */
105
+ fitBounds(bounds: Rect, options?: ViewportHelperFunctionOptions): void;
106
+ /** Set the viewport position and zoom directly. */
107
+ setViewport(viewport: Viewport, options?: ViewportHelperFunctionOptions): void;
108
+ /** Read the current viewport (`{ x, y, zoom }`); returns `undefined` before the diagram mounts. */
109
+ getViewport(): Viewport | undefined;
89
110
  }
90
111
 
91
112
  export declare interface DbmlViewerProps {
@@ -107,6 +128,12 @@ export declare interface DbmlViewerProps {
107
128
  showBackground?: boolean;
108
129
  /** Layout tuning: algorithm (`'simple'` | `'dagre'` | `'elk'`), direction, gaps. */
109
130
  layoutOptions?: LayoutOptions;
131
+ /**
132
+ * How edges attach to tables. `'column'` (default) anchors each edge to its specific
133
+ * FK/PK column rows. `'floating'` connects table-to-table, attaching wherever the two
134
+ * tables face each other and following them as they move.
135
+ */
136
+ edgeConnection?: EdgeConnection;
110
137
  /** Called when the DBML fails to parse. */
111
138
  onParseError?: (error: DbmlParseError) => void;
112
139
  /** Called when a non-default layout fails (e.g. optional dependency missing). */
@@ -171,6 +198,9 @@ export declare interface DiagramExportOptions {
171
198
  /** Trigger a browser download of a data URL. */
172
199
  export declare function downloadDataUrl(dataUrl: string, filename: string): void;
173
200
 
201
+ /** How edges attach to tables. */
202
+ export declare type EdgeConnection = 'column' | 'floating';
203
+
174
204
  /** Cardinality + optionality of one rendered edge end. */
175
205
  export declare interface EdgeEndSpec {
176
206
  cardinality: Cardinality;
@@ -180,7 +210,11 @@ export declare interface EdgeEndSpec {
180
210
 
181
211
  export declare const ErdEdge: MemoExoticComponent<typeof ErdEdgeComponent>;
182
212
 
183
- declare function ErdEdgeComponent({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data, selected, }: EdgeProps): JSX.Element;
213
+ /**
214
+ * Column-anchored edge: endpoints are fixed to per-column handles on the left/right of each
215
+ * table (the default rendering). React Flow supplies the resolved handle coordinates.
216
+ */
217
+ declare function ErdEdgeComponent({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data, selected, }: EdgeProps): ReactNode;
184
218
 
185
219
  export declare interface ErdEdgeData {
186
220
  kind: RelationKind;
@@ -203,6 +237,8 @@ export declare class ExportError extends Error {
203
237
  /** Image format for diagram export. */
204
238
  export declare type ExportFormat = 'png' | 'svg';
205
239
 
240
+ export { FitViewOptions }
241
+
206
242
  export declare const HEADER_HEIGHT = 40;
207
243
 
208
244
  /** Layout algorithm to position the table nodes. */
@@ -231,13 +267,23 @@ export declare interface LayoutOptions {
231
267
  horizontalGap?: number;
232
268
  /** Vertical gap between stacked tables. */
233
269
  verticalGap?: number;
270
+ /** Minimum table-node width in px. Default {@link MIN_NODE_WIDTH} (160). */
271
+ minNodeWidth?: number;
272
+ /**
273
+ * Maximum table-node width in px; content wider than this is truncated with an ellipsis.
274
+ * Default {@link MAX_NODE_WIDTH} (320). If set below `minNodeWidth`, the max wins.
275
+ */
276
+ maxNodeWidth?: number;
234
277
  }
235
278
 
236
279
  /**
237
280
  * Compute node positions for the parsed schema.
238
281
  *
239
- * Tables are arranged left-to-right by their layer in the FK graph and stacked
240
- * vertically within each layer. The result is keyed by {@link TableInfo.id}.
282
+ * FK-connected tables are arranged left-to-right by their layer in the FK graph and stacked
283
+ * vertically within each layer. Tables with no relations at all are packed into a grid below
284
+ * that layout — filled left-to-right then wrapped — spanning roughly its width, so a large
285
+ * number of unrelated tables no longer pile up in a single tall left-hand column. The result
286
+ * is keyed by {@link TableInfo.id}.
241
287
  */
242
288
  export declare function layoutSchema(schema: ParsedSchema, options?: LayoutOptions): Map<string, NodeBox>;
243
289
 
@@ -247,8 +293,16 @@ export declare function layoutSchema(schema: ParsedSchema, options?: LayoutOptio
247
293
  */
248
294
  export declare const lightTheme: DbmlViewerTheme;
249
295
 
296
+ export declare const MAX_NODE_WIDTH = 320;
297
+
250
298
  /** Visual sizing used both for layout math and for the node component. */
251
- export declare const NODE_WIDTH = 240;
299
+ export declare const MIN_NODE_WIDTH = 160;
300
+
301
+ /**
302
+ * @deprecated A node's width is now content-derived per table — use {@link tableWidth}.
303
+ * Kept as a stable reference (equal to {@link MAX_NODE_WIDTH}) for back-compat.
304
+ */
305
+ export declare const NODE_WIDTH = 320;
252
306
 
253
307
  /** Position and measured size of a laid-out table node. */
254
308
  export declare interface NodeBox {
@@ -261,6 +315,12 @@ export declare interface NodeBox {
261
315
  /** Map of table id → position, used to persist and restore where the user dragged tables. */
262
316
  export declare type NodePositions = Record<string, XYPosition>;
263
317
 
318
+ /** Lower/upper bounds for {@link tableWidth}. Unset fields fall back to the module defaults. */
319
+ export declare interface NodeWidthBounds {
320
+ minWidth?: number;
321
+ maxWidth?: number;
322
+ }
323
+
264
324
  /**
265
325
  * Parse a DBML document into a normalized {@link ParsedSchema} of tables and relations.
266
326
  *
@@ -275,6 +335,8 @@ export declare interface ParsedSchema {
275
335
  relations: RelationInfo[];
276
336
  }
277
337
 
338
+ export { Rect }
339
+
278
340
  /** One end of a relation. */
279
341
  export declare interface RelationEndpoint {
280
342
  /** Id of the table this endpoint belongs to (matches {@link TableInfo.id}). */
@@ -321,6 +383,8 @@ export declare function renderDiagram(viewport: HTMLElement, bounds: Rect, optio
321
383
 
322
384
  export declare const ROW_HEIGHT = 28;
323
385
 
386
+ export { SetCenterOptions }
387
+
324
388
  /** Height of a table node given its column count. */
325
389
  export declare function tableHeight(table: TableInfo): number;
326
390
 
@@ -348,16 +412,32 @@ export declare type TableNodeData = {
348
412
  table: TableInfo;
349
413
  /** Columns currently highlighted (e.g. endpoints of a hovered relation). */
350
414
  highlightedColumns?: Set<string>;
415
+ /** Min/max width bounds; must match what layout used so render and layout agree. */
416
+ widthBounds?: NodeWidthBounds;
417
+ /** Columns that an edge attaches to; only these get connection handles. */
418
+ connectedColumns?: Set<string>;
351
419
  };
352
420
 
353
421
  export declare type TableNodeType = Node_2<TableNodeData, 'table'>;
354
422
 
423
+ /**
424
+ * Estimate a table node's natural width from its content (the longest column row and the
425
+ * header), clamped to `[MIN_NODE_WIDTH, MAX_NODE_WIDTH]`. Used for both layout math and the
426
+ * rendered node, so they stay in sync. Text is measured by an average-character heuristic
427
+ * (no DOM); any underestimate is absorbed by the CSS ellipsis at the max width.
428
+ */
429
+ export declare function tableWidth(table: TableInfo, bounds?: NodeWidthBounds): number;
430
+
355
431
  /**
356
432
  * Convert a {@link DbmlViewerTheme} into inline CSS custom properties. Only the keys
357
433
  * present in `theme` are emitted, so unset tokens keep their stylesheet defaults.
358
434
  */
359
435
  export declare function themeToCssVars(theme?: DbmlViewerTheme): CSSProperties;
360
436
 
437
+ export { Viewport }
438
+
439
+ export { ViewportHelperFunctionOptions }
440
+
361
441
  /** A 2D position. */
362
442
  export declare interface XYPosition {
363
443
  x: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dbml-erd-viewer",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "React component library that renders DBML database schemas as an interactive diagram.",
5
5
  "keywords": [
6
6
  "database",