@msdshsk/react-er-canvas 0.0.1 → 0.0.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,4 +1,5 @@
1
1
  import { CSSProperties } from 'react';
2
+ import { FitViewOptions, Rect, Viewport } from '@xyflow/react';
2
3
  import { LayoutAlgorithm, LayoutDirection } from '../core/layout';
3
4
  import { ColumnRef, ERModel, Join, PartialColumnRef } from '../core/model';
4
5
  export interface NodePosition {
@@ -6,6 +7,29 @@ export interface NodePosition {
6
7
  y: number;
7
8
  }
8
9
  export type NodePositions = Record<string, NodePosition>;
10
+ /**
11
+ * Imperative API exposed via `ref`. Designed to give consumers the primitives
12
+ * needed for image export (fit → snapshot → restore) without baking any
13
+ * specific export library into this package. Methods that change the viewport
14
+ * return Promise<boolean> matching React Flow's underlying API.
15
+ */
16
+ export interface MermaidERHandle {
17
+ /** Fit all nodes into the visible viewport. */
18
+ fitView: (options?: FitViewOptions) => Promise<boolean>;
19
+ /** Read the current viewport (x, y, zoom) — capture before export to restore later. */
20
+ getViewport: () => Viewport;
21
+ /** Restore (or set) viewport. */
22
+ setViewport: (viewport: Viewport) => Promise<boolean>;
23
+ /** Bounding box of all current nodes. Useful for sizing offscreen canvases. */
24
+ getNodesBounds: () => Rect;
25
+ /** Outer wrapper element (the div that receives `className` / `style`). */
26
+ getWrapperElement: () => HTMLDivElement | null;
27
+ /**
28
+ * The internal `.react-flow__viewport` element. Typical snapshot target
29
+ * when you want to exclude `<Controls>` and `<MiniMap>` from the image.
30
+ */
31
+ getViewportElement: () => HTMLElement | null;
32
+ }
9
33
  export interface MermaidERProps {
10
34
  /** Mermaid ER source. Mutually exclusive with `model`. */
11
35
  source?: string;
@@ -42,5 +66,5 @@ export interface MermaidERProps {
42
66
  className?: string;
43
67
  style?: CSSProperties;
44
68
  }
45
- export declare function MermaidER(props: MermaidERProps): import("react/jsx-runtime").JSX.Element;
69
+ export declare const MermaidER: import('react').ForwardRefExoticComponent<MermaidERProps & import('react').RefAttributes<MermaidERHandle>>;
46
70
  //# sourceMappingURL=MermaidER.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MermaidER.d.ts","sourceRoot":"","sources":["../../src/components/MermaidER.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,OAAO,CAAC;AAkBf,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,eAAe,EAErB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EACV,SAAS,EACT,OAAO,EACP,IAAI,EACJ,gBAAgB,EAEjB,MAAM,eAAe,CAAC;AAqBvB,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,IAAI,CAAC;IACvD,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC;IAC9B,uBAAuB,CAAC,EAAE,CAAC,eAAe,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;IACjE,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,8DAA8D;IAC9D,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC7E,8EAA8E;IAC9E,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,0FAA0F;IAC1F,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,6GAA6G;IAC7G,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AA2CD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,2CAmU9C"}
1
+ {"version":3,"file":"MermaidER.d.ts","sourceRoot":"","sources":["../../src/components/MermaidER.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,aAAa,EAEnB,MAAM,OAAO,CAAC;AACf,OAAO,EAUL,KAAK,cAAc,EAGnB,KAAK,IAAI,EACT,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AAMvB,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,eAAe,EAErB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EACV,SAAS,EACT,OAAO,EACP,IAAI,EACJ,gBAAgB,EAEjB,MAAM,eAAe,CAAC;AAqBvB,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,uFAAuF;IACvF,WAAW,EAAE,MAAM,QAAQ,CAAC;IAC5B,iCAAiC;IACjC,WAAW,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,+EAA+E;IAC/E,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,cAAc,GAAG,IAAI,CAAC;IAC/C;;;OAGG;IACH,kBAAkB,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,IAAI,CAAC;IACvD,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC;IAC9B,uBAAuB,CAAC,EAAE,CAAC,eAAe,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;IACjE,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,8DAA8D;IAC9D,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC7E,8EAA8E;IAC9E,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,0FAA0F;IAC1F,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,6GAA6G;IAC7G,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAyED,eAAO,MAAM,SAAS,4GA0UpB,CAAC"}
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./core-BtdV83x9.cjs`);let t=require(`react`),n=require(`@xyflow/react`),r=require(`react/jsx-runtime`);var i=(0,t.createContext)(new Map),a=(0,t.createContext)({enabled:!1,selected:new Set,onToggle:()=>void 0}),o=(0,t.createContext)(!1),s=(0,t.createContext)({}),c={pk:{label:`PK`,bg:`#f59e0b`},fk:{label:`FK`,bg:`#3b82f6`},uk:{label:`UK`,bg:`#10b981`}};function l({tableName:e,column:t,highlighted:n,onClick:i,selectionEnabled:a,selected:o,onSelectToggle:s}){let l=[];return t.keys.pk&&l.push(c.pk),t.keys.fk&&l.push(c.fk),t.keys.uk&&l.push(c.uk),(0,r.jsxs)(`div`,{title:t.comment,onClick:i?()=>i(e,t.name):void 0,style:{display:`flex`,alignItems:`center`,gap:6,padding:`0 10px`,fontSize:12,fontFamily:`ui-monospace, SFMono-Regular, Consolas, monospace`,borderTop:`1px solid #eee`,height:22,boxSizing:`border-box`,cursor:i?`pointer`:`default`,background:n?`#fef3c7`:o?`#eff6ff`:`transparent`,transition:`background 0.15s`},children:[a&&(0,r.jsx)(`input`,{type:`checkbox`,checked:o,onChange:e=>s(e.target.checked),onClick:e=>e.stopPropagation(),style:{width:13,height:13,margin:0,flexShrink:0,cursor:`pointer`}}),(0,r.jsx)(`span`,{style:{display:`flex`,gap:2,flexShrink:0,minWidth:18},children:l.map(e=>(0,r.jsx)(`span`,{style:{display:`inline-block`,padding:`0 4px`,borderRadius:3,background:e.bg,color:`#fff`,fontSize:9,fontWeight:700,lineHeight:`14px`},children:e.label},e.label))}),(0,r.jsx)(`span`,{style:{flexShrink:0,fontWeight:t.keys.pk?600:400,color:`#1f2937`},children:t.name}),t.type&&(0,r.jsx)(`span`,{style:{flex:1,textAlign:`right`,color:`#9ca3af`,fontStyle:`italic`,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:t.type})]})}var u=(0,t.memo)(function({data:e}){let{table:c}=e,u=(0,t.useContext)(i),d=(0,t.useContext)(a),f=(0,t.useContext)(o),p=(0,t.useContext)(s),m=p.onColumnClick,h=u.get(c.name),g=c.group?`#1e40af`:`#374151`,_=e=>({...e,width:f?9:6,height:f?9:6,background:f?`#3b82f6`:`transparent`,border:f?`1.5px solid #fff`:`none`,boxShadow:f?`0 0 0 1px rgba(59,130,246,0.4)`:`none`,opacity:f?.85:0,pointerEvents:f?`auto`:`none`,cursor:f?`crosshair`:`default`});return(0,r.jsxs)(`div`,{style:{background:`#fff`,border:`1px solid #c4c4c4`,borderRadius:6,boxShadow:`0 2px 6px rgba(0,0,0,0.08)`,overflow:`visible`,width:`100%`,height:`100%`,display:`flex`,flexDirection:`column`,position:`relative`},children:[(0,r.jsx)(n.Handle,{id:`__default-target`,type:`target`,position:n.Position.Left,style:_({top:32/2})}),(0,r.jsx)(n.Handle,{id:`__default-source`,type:`source`,position:n.Position.Right,style:_({top:32/2})}),(0,r.jsxs)(`div`,{style:{padding:`6px 10px`,background:g,color:`#fff`,fontWeight:600,fontSize:13,height:32,boxSizing:`border-box`,display:`flex`,alignItems:`center`,justifyContent:`space-between`,gap:8,borderTopLeftRadius:6,borderTopRightRadius:6},children:[(0,r.jsx)(`span`,{style:{overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:c.name}),(0,r.jsxs)(`span`,{style:{display:`flex`,alignItems:`center`,gap:4,flexShrink:0},children:[c.group&&(0,r.jsx)(`span`,{style:{fontSize:10,opacity:.75,fontWeight:400,padding:`1px 5px`,border:`1px solid rgba(255,255,255,0.3)`,borderRadius:3},children:c.group}),p.onTableRemove&&(0,r.jsx)(`button`,{className:`nodrag`,onClick:e=>{e.stopPropagation(),p.onTableRemove?.(c.name)},onMouseDown:e=>e.stopPropagation(),title:`Remove this table`,style:{background:`rgba(255,255,255,0.18)`,border:`none`,color:`#fff`,width:18,height:18,borderRadius:3,cursor:`pointer`,fontSize:13,fontWeight:700,padding:0,lineHeight:1,display:`flex`,alignItems:`center`,justifyContent:`center`},children:`×`})]})]}),c.columns.map((e,i)=>{let a=32+i*22+22/2,o=h?.has(e.name)??!1,s=`${c.name}.${e.name}`,u=d.enabled&&d.selected.has(s);return(0,r.jsxs)(t.Fragment,{children:[(0,r.jsx)(n.Handle,{id:`${e.name}__target`,type:`target`,position:n.Position.Left,style:_({top:a})}),(0,r.jsx)(n.Handle,{id:`${e.name}__source`,type:`source`,position:n.Position.Right,style:_({top:a})}),(0,r.jsx)(l,{tableName:c.name,column:e,highlighted:o,onClick:m,selectionEnabled:d.enabled,selected:u,onSelectToggle:t=>d.onToggle(c.name,e.name,t)})]},`${e.name}-${i}`)})]})}),d=(0,t.memo)(function({id:e,sourceX:t,sourceY:i,targetX:a,targetY:o,sourcePosition:s,targetPosition:c,data:l,selected:u}){let[d,f,p]=(0,n.getSmoothStepPath)({sourceX:t,sourceY:i,sourcePosition:s,targetX:a,targetY:o,targetPosition:c}),m=l??{},h=!!u||!!m.hovered,g=m.color||`#3b82f6`;return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.BaseEdge,{id:e,path:d,style:{stroke:g,strokeWidth:h?2.5:2,strokeDasharray:`6 3`}}),(0,r.jsx)(n.EdgeLabelRenderer,{children:(0,r.jsxs)(`div`,{className:`nodrag nopan`,style:{position:`absolute`,transform:`translate(-50%, -50%) translate(${f}px, ${p}px)`,background:g,color:`#fff`,fontSize:10,fontFamily:`ui-monospace, SFMono-Regular, Consolas, monospace`,fontWeight:700,borderRadius:3,padding:`2px 4px 2px 6px`,display:`flex`,alignItems:`center`,gap:4,pointerEvents:`all`,boxShadow:h?`0 0 0 2px rgba(0,0,0,0.4)`:`0 1px 2px rgba(0,0,0,0.15)`,userSelect:`none`},children:[(0,r.jsx)(`span`,{children:m.type}),m.onDelete&&(0,r.jsx)(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),m.onDelete?.()},onMouseDown:e=>e.stopPropagation(),title:`Remove this JOIN`,style:{background:`rgba(255,255,255,0.25)`,border:`none`,color:`#fff`,width:14,height:14,borderRadius:2,cursor:`pointer`,fontSize:11,fontWeight:700,padding:0,lineHeight:1,display:`flex`,alignItems:`center`,justifyContent:`center`},children:`×`})]})})]})}),f={table:u},p={joinEdge:d};function m(t){try{return{model:e.c(t),error:null}}catch(t){if(t instanceof e.s)return{model:null,error:t};throw t}}function h(e,t){return t?`${t}__${e}`:`__default-${e}`}var g=`join:`;function _(e){if(!e||e.startsWith(`__default-`))return;let t=/^(.+)__(?:source|target)$/.exec(e);return t?t[1]:void 0}function ee(e){return`${e.table}.${e.column}`}var v={INNER:`#3b82f6`,LEFT:`#8b5cf6`,RIGHT:`#a855f7`,FULL:`#ec4899`,CROSS:`#6b7280`};function y(c){let{source:l,model:u,algorithm:d,direction:y,aspectRatio:b,positions:x,onPositionsChange:S,showColumnCheckboxes:C,selectedColumns:w,onColumnSelectionChange:T,enableManualJoins:E,joins:D,onJoinConnect:O,onJoinDelete:k,onTableRemove:A,onColumnClick:j,onTableClick:M,deleteKeyCode:N=`Delete`,className:P,style:F,highlightReferencesOnHover:I=!0}=c,{model:L,error:R}=(0,t.useMemo)(()=>u?{model:u,error:null}:l==null?{model:null,error:null}:m(l),[l,u]),[z,B]=(0,t.useState)(null),[V,H]=(0,t.useState)(null);(0,t.useEffect)(()=>{if(!L){B(null);return}let t=!1;return e.o(L,{algorithm:d,direction:y,aspectRatio:b}).then(e=>{t||B(e)}),()=>{t=!0}},[L,d,y,b]);let U=(0,t.useMemo)(()=>{if(!z||!L)return[];let e=new Map(L.tables.map(e=>[e.name,e])),t=[];for(let n of z.nodes){let r=e.get(n.id);if(!r)continue;let i=x?.[n.id],a={table:r};t.push({id:n.id,type:`table`,position:i??{x:n.x,y:n.y},data:a,width:n.width,height:n.height,draggable:!0,deletable:!1})}return t},[z,L,x]),[W,G,K]=(0,n.useNodesState)(U);(0,t.useEffect)(()=>{G(U)},[U,G]);let q=(0,t.useCallback)((e,t,n)=>{if(!S)return;let r={...x??{}};for(let e of n)r[e.id]={x:e.position.x,y:e.position.y};S(r)},[x,S]),J=(0,t.useMemo)(()=>{if(!(!V||!L))return L.relations.find(e=>e.id===V)},[V,L]),Y=(0,t.useMemo)(()=>{if(!V||!D||!V.startsWith(g))return;let e=V.slice(5);return D.find(t=>t.id===e)},[V,D]),X=(0,t.useMemo)(()=>{let e=new Map;if(!I)return e;let t=(t,n)=>{if(!n)return;let r=e.get(t)??new Set;r.add(n),e.set(t,r)};return J&&(t(J.from,J.fromColumn),t(J.to,J.toColumn)),Y&&(t(Y.source.table,Y.source.column),t(Y.target.table,Y.target.column)),e},[J,Y,I]),Z=(0,t.useMemo)(()=>new Set((w??[]).map(ee)),[w]),Q=(0,t.useCallback)((e,t,n)=>{if(!T)return;let r=w??[];if(n){if(r.some(n=>n.table===e&&n.column===t))return;T([...r,{table:e,column:t}])}else T(r.filter(n=>!(n.table===e&&n.column===t)))},[w,T]),te=(0,t.useMemo)(()=>({enabled:C??!1,selected:Z,onToggle:Q}),[C,Z,Q]),ne=(0,t.useMemo)(()=>{if(!z||!L)return[];let e=new Set(L.tables.map(e=>e.name)),t=new Map(L.relations.map(e=>[e.id,e])),n=[];for(let r of z.edges){if(!e.has(r.source)||!e.has(r.target))continue;let i=t.get(r.id),a=r.id===V;n.push({id:r.id,source:r.source,target:r.target,sourceHandle:h(`source`,i?.fromColumn),targetHandle:h(`target`,i?.toColumn),type:`smoothstep`,animated:a,deletable:!1,style:{stroke:a?`#f59e0b`:`#9ca3af`,strokeWidth:a?2:1.5},label:i?.label,labelStyle:{fontSize:10,fill:`#6b7280`},labelBgStyle:{fill:`#fff`,fillOpacity:.85},labelBgPadding:[4,2],labelBgBorderRadius:3})}let r=[];for(let t of D??[]){if(!e.has(t.source.table)||!e.has(t.target.table))continue;let n=`${g}${t.id}`,i=n===V,a=v[t.type]??`#3b82f6`,o={type:t.type,color:a,hovered:i,onDelete:k?()=>k(t.id):void 0};r.push({id:n,source:t.source.table,target:t.target.table,sourceHandle:h(`source`,t.source.column),targetHandle:h(`target`,t.target.column),type:`joinEdge`,deletable:!0,data:o})}return[...n,...r]},[z,L,D,V,k]),re=(0,t.useCallback)(e=>{O&&(!e.source||!e.target||O({table:e.source,column:_(e.sourceHandle)},{table:e.target,column:_(e.targetHandle)}))},[O]),ie=(0,t.useCallback)(e=>{if(k)for(let t of e)t.id.startsWith(g)&&k(t.id.slice(5))},[k]),$=!!E,ae=(0,t.useMemo)(()=>({onTableRemove:A,onColumnClick:j}),[A,j]);return(0,r.jsxs)(`div`,{className:P,style:{width:`100%`,height:`100%`,position:`relative`,...F},children:[R&&(0,r.jsx)(`div`,{style:{position:`absolute`,top:8,left:8,right:8,zIndex:10,padding:`8px 12px`,background:`#fef2f2`,border:`1px solid #fecaca`,borderRadius:6,color:`#991b1b`,fontFamily:`ui-monospace, SFMono-Regular, Consolas, monospace`,fontSize:12,whiteSpace:`pre-wrap`},children:R.message}),!R&&!z&&(0,r.jsx)(`div`,{style:{position:`absolute`,inset:0,display:`flex`,alignItems:`center`,justifyContent:`center`,color:`#9ca3af`,fontSize:13},children:`Computing layout…`}),(0,r.jsx)(i.Provider,{value:X,children:(0,r.jsx)(a.Provider,{value:te,children:(0,r.jsx)(o.Provider,{value:$,children:(0,r.jsx)(s.Provider,{value:ae,children:(0,r.jsxs)(n.ReactFlow,{nodes:W,edges:ne,nodeTypes:f,edgeTypes:p,fitView:!0,onNodesChange:K,onNodeDragStop:q,onNodeClick:M?(e,t)=>M(t.id):void 0,onEdgeMouseEnter:(e,t)=>H(t.id),onEdgeMouseLeave:()=>H(null),onConnect:re,onEdgesDelete:ie,nodesDraggable:!0,nodesConnectable:$,elementsSelectable:!0,deleteKeyCode:N,minZoom:.1,maxZoom:4,children:[(0,r.jsx)(n.Background,{}),(0,r.jsx)(n.Controls,{}),(0,r.jsx)(n.MiniMap,{pannable:!0,zoomable:!0})]})})})})})]})}exports.HEADER_HEIGHT=e.t,exports.MermaidER=y,exports.MermaidERParseError=e.s,exports.NODE_WIDTH=e.n,exports.ROW_HEIGHT=e.r,exports.VERTICAL_PADDING=e.i,exports.estimateNodeHeight=e.a,exports.layoutER=e.o,exports.parseMermaidER=e.c;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./core-BtdV83x9.cjs`);let t=require(`react`),n=require(`@xyflow/react`),r=require(`react/jsx-runtime`);var i=(0,t.createContext)(new Map),a=(0,t.createContext)({enabled:!1,selected:new Set,onToggle:()=>void 0}),o=(0,t.createContext)(!1),s=(0,t.createContext)({}),c={pk:{label:`PK`,bg:`#f59e0b`},fk:{label:`FK`,bg:`#3b82f6`},uk:{label:`UK`,bg:`#10b981`}};function l({tableName:e,column:t,highlighted:n,onClick:i,selectionEnabled:a,selected:o,onSelectToggle:s}){let l=[];return t.keys.pk&&l.push(c.pk),t.keys.fk&&l.push(c.fk),t.keys.uk&&l.push(c.uk),(0,r.jsxs)(`div`,{title:t.comment,onClick:i?()=>i(e,t.name):void 0,style:{display:`flex`,alignItems:`center`,gap:6,padding:`0 10px`,fontSize:12,fontFamily:`ui-monospace, SFMono-Regular, Consolas, monospace`,borderTop:`1px solid #eee`,height:22,boxSizing:`border-box`,cursor:i?`pointer`:`default`,background:n?`#fef3c7`:o?`#eff6ff`:`transparent`,transition:`background 0.15s`},children:[a&&(0,r.jsx)(`input`,{type:`checkbox`,checked:o,onChange:e=>s(e.target.checked),onClick:e=>e.stopPropagation(),style:{width:13,height:13,margin:0,flexShrink:0,cursor:`pointer`}}),(0,r.jsx)(`span`,{style:{display:`flex`,gap:2,flexShrink:0,minWidth:18},children:l.map(e=>(0,r.jsx)(`span`,{style:{display:`inline-block`,padding:`0 4px`,borderRadius:3,background:e.bg,color:`#fff`,fontSize:9,fontWeight:700,lineHeight:`14px`},children:e.label},e.label))}),(0,r.jsx)(`span`,{style:{flexShrink:0,fontWeight:t.keys.pk?600:400,color:`#1f2937`},children:t.name}),t.type&&(0,r.jsx)(`span`,{style:{flex:1,textAlign:`right`,color:`#9ca3af`,fontStyle:`italic`,overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:t.type})]})}var u=(0,t.memo)(function({data:e}){let{table:c}=e,u=(0,t.useContext)(i),d=(0,t.useContext)(a),f=(0,t.useContext)(o),p=(0,t.useContext)(s),m=p.onColumnClick,h=u.get(c.name),g=c.group?`#1e40af`:`#374151`,_=e=>({...e,width:f?9:6,height:f?9:6,background:f?`#3b82f6`:`transparent`,border:f?`1.5px solid #fff`:`none`,boxShadow:f?`0 0 0 1px rgba(59,130,246,0.4)`:`none`,opacity:f?.85:0,pointerEvents:f?`auto`:`none`,cursor:f?`crosshair`:`default`});return(0,r.jsxs)(`div`,{style:{background:`#fff`,border:`1px solid #c4c4c4`,borderRadius:6,boxShadow:`0 2px 6px rgba(0,0,0,0.08)`,overflow:`visible`,width:`100%`,height:`100%`,display:`flex`,flexDirection:`column`,position:`relative`},children:[(0,r.jsx)(n.Handle,{id:`__default-target`,type:`target`,position:n.Position.Left,style:_({top:32/2})}),(0,r.jsx)(n.Handle,{id:`__default-source`,type:`source`,position:n.Position.Right,style:_({top:32/2})}),(0,r.jsxs)(`div`,{style:{padding:`6px 10px`,background:g,color:`#fff`,fontWeight:600,fontSize:13,height:32,boxSizing:`border-box`,display:`flex`,alignItems:`center`,justifyContent:`space-between`,gap:8,borderTopLeftRadius:6,borderTopRightRadius:6},children:[(0,r.jsx)(`span`,{style:{overflow:`hidden`,textOverflow:`ellipsis`,whiteSpace:`nowrap`},children:c.name}),(0,r.jsxs)(`span`,{style:{display:`flex`,alignItems:`center`,gap:4,flexShrink:0},children:[c.group&&(0,r.jsx)(`span`,{style:{fontSize:10,opacity:.75,fontWeight:400,padding:`1px 5px`,border:`1px solid rgba(255,255,255,0.3)`,borderRadius:3},children:c.group}),p.onTableRemove&&(0,r.jsx)(`button`,{className:`nodrag`,onClick:e=>{e.stopPropagation(),p.onTableRemove?.(c.name)},onMouseDown:e=>e.stopPropagation(),title:`Remove this table`,style:{background:`rgba(255,255,255,0.18)`,border:`none`,color:`#fff`,width:18,height:18,borderRadius:3,cursor:`pointer`,fontSize:13,fontWeight:700,padding:0,lineHeight:1,display:`flex`,alignItems:`center`,justifyContent:`center`},children:`×`})]})]}),c.columns.map((e,i)=>{let a=32+i*22+22/2,o=h?.has(e.name)??!1,s=`${c.name}.${e.name}`,u=d.enabled&&d.selected.has(s);return(0,r.jsxs)(t.Fragment,{children:[(0,r.jsx)(n.Handle,{id:`${e.name}__target`,type:`target`,position:n.Position.Left,style:_({top:a})}),(0,r.jsx)(n.Handle,{id:`${e.name}__source`,type:`source`,position:n.Position.Right,style:_({top:a})}),(0,r.jsx)(l,{tableName:c.name,column:e,highlighted:o,onClick:m,selectionEnabled:d.enabled,selected:u,onSelectToggle:t=>d.onToggle(c.name,e.name,t)})]},`${e.name}-${i}`)})]})}),d=(0,t.memo)(function({id:e,sourceX:t,sourceY:i,targetX:a,targetY:o,sourcePosition:s,targetPosition:c,data:l,selected:u}){let[d,f,p]=(0,n.getSmoothStepPath)({sourceX:t,sourceY:i,sourcePosition:s,targetX:a,targetY:o,targetPosition:c}),m=l??{},h=!!u||!!m.hovered,g=m.color||`#3b82f6`;return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.BaseEdge,{id:e,path:d,style:{stroke:g,strokeWidth:h?2.5:2,strokeDasharray:`6 3`}}),(0,r.jsx)(n.EdgeLabelRenderer,{children:(0,r.jsxs)(`div`,{className:`nodrag nopan`,style:{position:`absolute`,transform:`translate(-50%, -50%) translate(${f}px, ${p}px)`,background:g,color:`#fff`,fontSize:10,fontFamily:`ui-monospace, SFMono-Regular, Consolas, monospace`,fontWeight:700,borderRadius:3,padding:`2px 4px 2px 6px`,display:`flex`,alignItems:`center`,gap:4,pointerEvents:`all`,boxShadow:h?`0 0 0 2px rgba(0,0,0,0.4)`:`0 1px 2px rgba(0,0,0,0.15)`,userSelect:`none`},children:[(0,r.jsx)(`span`,{children:m.type}),m.onDelete&&(0,r.jsx)(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),m.onDelete?.()},onMouseDown:e=>e.stopPropagation(),title:`Remove this JOIN`,style:{background:`rgba(255,255,255,0.25)`,border:`none`,color:`#fff`,width:14,height:14,borderRadius:2,cursor:`pointer`,fontSize:11,fontWeight:700,padding:0,lineHeight:1,display:`flex`,alignItems:`center`,justifyContent:`center`},children:`×`})]})})]})}),f={table:u},p={joinEdge:d};function m(t){try{return{model:e.c(t),error:null}}catch(t){if(t instanceof e.s)return{model:null,error:t};throw t}}function h(e,t){return t?`${t}__${e}`:`__default-${e}`}var g=`join:`;function _(e){if(!e||e.startsWith(`__default-`))return;let t=/^(.+)__(?:source|target)$/.exec(e);return t?t[1]:void 0}function v(e){return`${e.table}.${e.column}`}var y={INNER:`#3b82f6`,LEFT:`#8b5cf6`,RIGHT:`#a855f7`,FULL:`#ec4899`,CROSS:`#6b7280`};function ee({apiRef:e,wrapperRef:r}){let i=(0,n.useReactFlow)();return(0,t.useImperativeHandle)(e,()=>({fitView:e=>i.fitView(e),getViewport:()=>i.getViewport(),setViewport:e=>i.setViewport(e),getNodesBounds:()=>i.getNodesBounds(i.getNodes()),getWrapperElement:()=>r.current,getViewportElement:()=>r.current?.querySelector(`.react-flow__viewport`)??null}),[i,r]),null}var b=(0,t.forwardRef)(function(c,l){let{source:u,model:d,algorithm:b,direction:x,aspectRatio:S,positions:C,onPositionsChange:w,showColumnCheckboxes:T,selectedColumns:E,onColumnSelectionChange:D,enableManualJoins:O,joins:k,onJoinConnect:A,onJoinDelete:j,onTableRemove:M,onColumnClick:N,onTableClick:P,deleteKeyCode:F=`Delete`,className:I,style:L,highlightReferencesOnHover:R=!0}=c,{model:z,error:B}=(0,t.useMemo)(()=>d?{model:d,error:null}:u==null?{model:null,error:null}:m(u),[u,d]),[V,H]=(0,t.useState)(null),[U,W]=(0,t.useState)(null);(0,t.useEffect)(()=>{if(!z){H(null);return}let t=!1;return e.o(z,{algorithm:b,direction:x,aspectRatio:S}).then(e=>{t||H(e)}),()=>{t=!0}},[z,b,x,S]);let G=(0,t.useMemo)(()=>{if(!V||!z)return[];let e=new Map(z.tables.map(e=>[e.name,e])),t=[];for(let n of V.nodes){let r=e.get(n.id);if(!r)continue;let i=C?.[n.id],a={table:r};t.push({id:n.id,type:`table`,position:i??{x:n.x,y:n.y},data:a,width:n.width,height:n.height,draggable:!0,deletable:!1})}return t},[V,z,C]),[K,q,te]=(0,n.useNodesState)(G);(0,t.useEffect)(()=>{q(G)},[G,q]);let ne=(0,t.useCallback)((e,t,n)=>{if(!w)return;let r={...C??{}};for(let e of n)r[e.id]={x:e.position.x,y:e.position.y};w(r)},[C,w]),J=(0,t.useMemo)(()=>{if(!(!U||!z))return z.relations.find(e=>e.id===U)},[U,z]),Y=(0,t.useMemo)(()=>{if(!U||!k||!U.startsWith(g))return;let e=U.slice(5);return k.find(t=>t.id===e)},[U,k]),re=(0,t.useMemo)(()=>{let e=new Map;if(!R)return e;let t=(t,n)=>{if(!n)return;let r=e.get(t)??new Set;r.add(n),e.set(t,r)};return J&&(t(J.from,J.fromColumn),t(J.to,J.toColumn)),Y&&(t(Y.source.table,Y.source.column),t(Y.target.table,Y.target.column)),e},[J,Y,R]),X=(0,t.useMemo)(()=>new Set((E??[]).map(v)),[E]),Z=(0,t.useCallback)((e,t,n)=>{if(!D)return;let r=E??[];if(n){if(r.some(n=>n.table===e&&n.column===t))return;D([...r,{table:e,column:t}])}else D(r.filter(n=>!(n.table===e&&n.column===t)))},[E,D]),ie=(0,t.useMemo)(()=>({enabled:T??!1,selected:X,onToggle:Z}),[T,X,Z]),ae=(0,t.useMemo)(()=>{if(!V||!z)return[];let e=new Set(z.tables.map(e=>e.name)),t=new Map(z.relations.map(e=>[e.id,e])),n=[];for(let r of V.edges){if(!e.has(r.source)||!e.has(r.target))continue;let i=t.get(r.id),a=r.id===U;n.push({id:r.id,source:r.source,target:r.target,sourceHandle:h(`source`,i?.fromColumn),targetHandle:h(`target`,i?.toColumn),type:`smoothstep`,animated:a,deletable:!1,style:{stroke:a?`#f59e0b`:`#9ca3af`,strokeWidth:a?2:1.5},label:i?.label,labelStyle:{fontSize:10,fill:`#6b7280`},labelBgStyle:{fill:`#fff`,fillOpacity:.85},labelBgPadding:[4,2],labelBgBorderRadius:3})}let r=[];for(let t of k??[]){if(!e.has(t.source.table)||!e.has(t.target.table))continue;let n=`${g}${t.id}`,i=n===U,a=y[t.type]??`#3b82f6`,o={type:t.type,color:a,hovered:i,onDelete:j?()=>j(t.id):void 0};r.push({id:n,source:t.source.table,target:t.target.table,sourceHandle:h(`source`,t.source.column),targetHandle:h(`target`,t.target.column),type:`joinEdge`,deletable:!0,data:o})}return[...n,...r]},[V,z,k,U,j]),oe=(0,t.useCallback)(e=>{A&&(!e.source||!e.target||A({table:e.source,column:_(e.sourceHandle)},{table:e.target,column:_(e.targetHandle)}))},[A]),se=(0,t.useCallback)(e=>{if(j)for(let t of e)t.id.startsWith(g)&&j(t.id.slice(5))},[j]),Q=!!O,ce=(0,t.useMemo)(()=>({onTableRemove:M,onColumnClick:N}),[M,N]),$=(0,t.useRef)(null);return(0,r.jsxs)(`div`,{ref:$,className:I,style:{width:`100%`,height:`100%`,position:`relative`,...L},children:[B&&(0,r.jsx)(`div`,{style:{position:`absolute`,top:8,left:8,right:8,zIndex:10,padding:`8px 12px`,background:`#fef2f2`,border:`1px solid #fecaca`,borderRadius:6,color:`#991b1b`,fontFamily:`ui-monospace, SFMono-Regular, Consolas, monospace`,fontSize:12,whiteSpace:`pre-wrap`},children:B.message}),!B&&!V&&(0,r.jsx)(`div`,{style:{position:`absolute`,inset:0,display:`flex`,alignItems:`center`,justifyContent:`center`,color:`#9ca3af`,fontSize:13},children:`Computing layout…`}),(0,r.jsx)(i.Provider,{value:re,children:(0,r.jsx)(a.Provider,{value:ie,children:(0,r.jsx)(o.Provider,{value:Q,children:(0,r.jsx)(s.Provider,{value:ce,children:(0,r.jsxs)(n.ReactFlow,{nodes:K,edges:ae,nodeTypes:f,edgeTypes:p,fitView:!0,onNodesChange:te,onNodeDragStop:ne,onNodeClick:P?(e,t)=>P(t.id):void 0,onEdgeMouseEnter:(e,t)=>W(t.id),onEdgeMouseLeave:()=>W(null),onConnect:oe,onEdgesDelete:se,nodesDraggable:!0,nodesConnectable:Q,elementsSelectable:!0,deleteKeyCode:F,minZoom:.1,maxZoom:4,children:[(0,r.jsx)(n.Background,{}),(0,r.jsx)(n.Controls,{}),(0,r.jsx)(n.MiniMap,{pannable:!0,zoomable:!0}),(0,r.jsx)(ee,{apiRef:l,wrapperRef:$})]})})})})})]})});exports.HEADER_HEIGHT=e.t,exports.MermaidER=b,exports.MermaidERParseError=e.s,exports.NODE_WIDTH=e.n,exports.ROW_HEIGHT=e.r,exports.VERTICAL_PADDING=e.i,exports.estimateNodeHeight=e.a,exports.layoutER=e.o,exports.parseMermaidER=e.c;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/components/TableNode.tsx","../src/components/JoinEdge.tsx","../src/components/MermaidER.tsx"],"sourcesContent":["import { Fragment, createContext, memo, useContext } from 'react';\nimport { Handle, Position } from '@xyflow/react';\nimport type { NodeProps } from '@xyflow/react';\nimport type { Column, Table } from '../core/model';\nimport { HEADER_HEIGHT, ROW_HEIGHT } from '../core/layout';\n\nexport interface TableNodeData extends Record<string, unknown> {\n table: Table;\n}\n\n/**\n * Map from table name -> set of highlighted column names.\n * Provided by MermaidER, consumed by TableNode for hover-driven highlighting\n * without forcing the whole node array to recompute on every hover change.\n */\nexport const HighlightContext = createContext<ReadonlyMap<string, ReadonlySet<string>>>(new Map());\n\nexport interface ColumnSelectionContextValue {\n enabled: boolean;\n /** \"table.column\" keys for fast lookup. */\n selected: ReadonlySet<string>;\n onToggle: (table: string, column: string, checked: boolean) => void;\n}\n\nexport const ColumnSelectionContext = createContext<ColumnSelectionContextValue>({\n enabled: false,\n selected: new Set(),\n onToggle: () => undefined,\n});\n\n/** When true, column handles become visible/connectable for manual JOIN drawing. */\nexport const ConnectModeContext = createContext<boolean>(false);\n\nexport interface TableActionsContextValue {\n /** When provided, a delete affordance is shown on the table header. */\n onTableRemove?: (table: string) => void;\n /** Per-column click handler. Provided via context (not node data) so its\n * identity can change without invalidating the React Flow node array. */\n onColumnClick?: (table: string, column: string) => void;\n}\n\nexport const TableActionsContext = createContext<TableActionsContextValue>({});\n\nconst KEY_STYLES: Record<'pk' | 'fk' | 'uk', { label: string; bg: string }> = {\n pk: { label: 'PK', bg: '#f59e0b' },\n fk: { label: 'FK', bg: '#3b82f6' },\n uk: { label: 'UK', bg: '#10b981' },\n};\n\nfunction ColumnRow({\n tableName,\n column,\n highlighted,\n onClick,\n selectionEnabled,\n selected,\n onSelectToggle,\n}: {\n tableName: string;\n column: Column;\n highlighted: boolean;\n onClick?: (table: string, column: string) => void;\n selectionEnabled: boolean;\n selected: boolean;\n onSelectToggle: (checked: boolean) => void;\n}) {\n const badges: Array<{ label: string; bg: string }> = [];\n if (column.keys.pk) badges.push(KEY_STYLES.pk);\n if (column.keys.fk) badges.push(KEY_STYLES.fk);\n if (column.keys.uk) badges.push(KEY_STYLES.uk);\n\n return (\n <div\n title={column.comment}\n onClick={onClick ? () => onClick(tableName, column.name) : undefined}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '0 10px',\n fontSize: 12,\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n borderTop: '1px solid #eee',\n height: ROW_HEIGHT,\n boxSizing: 'border-box',\n cursor: onClick ? 'pointer' : 'default',\n background: highlighted ? '#fef3c7' : selected ? '#eff6ff' : 'transparent',\n transition: 'background 0.15s',\n }}\n >\n {selectionEnabled && (\n <input\n type=\"checkbox\"\n checked={selected}\n onChange={(e) => onSelectToggle(e.target.checked)}\n onClick={(e) => e.stopPropagation()}\n style={{\n width: 13,\n height: 13,\n margin: 0,\n flexShrink: 0,\n cursor: 'pointer',\n }}\n />\n )}\n <span style={{ display: 'flex', gap: 2, flexShrink: 0, minWidth: 18 }}>\n {badges.map((b) => (\n <span\n key={b.label}\n style={{\n display: 'inline-block',\n padding: '0 4px',\n borderRadius: 3,\n background: b.bg,\n color: '#fff',\n fontSize: 9,\n fontWeight: 700,\n lineHeight: '14px',\n }}\n >\n {b.label}\n </span>\n ))}\n </span>\n <span\n style={{\n flexShrink: 0,\n fontWeight: column.keys.pk ? 600 : 400,\n color: '#1f2937',\n }}\n >\n {column.name}\n </span>\n {column.type && (\n <span\n style={{\n flex: 1,\n textAlign: 'right',\n color: '#9ca3af',\n fontStyle: 'italic',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {column.type}\n </span>\n )}\n </div>\n );\n}\n\nexport const TableNode = memo(function TableNode({ data }: NodeProps) {\n const { table } = data as TableNodeData;\n const highlightMap = useContext(HighlightContext);\n const selection = useContext(ColumnSelectionContext);\n const connectMode = useContext(ConnectModeContext);\n const tableActions = useContext(TableActionsContext);\n const onColumnClick = tableActions.onColumnClick;\n const highlightedCols = highlightMap.get(table.name);\n const headerBg = table.group ? '#1e40af' : '#374151';\n\n const handleStyle = (extra: { top?: number }): React.CSSProperties => ({\n ...extra,\n width: connectMode ? 9 : 6,\n height: connectMode ? 9 : 6,\n background: connectMode ? '#3b82f6' : 'transparent',\n border: connectMode ? '1.5px solid #fff' : 'none',\n boxShadow: connectMode ? '0 0 0 1px rgba(59,130,246,0.4)' : 'none',\n opacity: connectMode ? 0.85 : 0,\n pointerEvents: connectMode ? 'auto' : 'none',\n cursor: connectMode ? 'crosshair' : 'default',\n });\n\n return (\n <div\n style={{\n background: '#fff',\n border: '1px solid #c4c4c4',\n borderRadius: 6,\n boxShadow: '0 2px 6px rgba(0,0,0,0.08)',\n overflow: 'visible',\n width: '100%',\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n position: 'relative',\n }}\n >\n <Handle\n id=\"__default-target\"\n type=\"target\"\n position={Position.Left}\n style={handleStyle({ top: HEADER_HEIGHT / 2 })}\n />\n <Handle\n id=\"__default-source\"\n type=\"source\"\n position={Position.Right}\n style={handleStyle({ top: HEADER_HEIGHT / 2 })}\n />\n\n <div\n style={{\n padding: '6px 10px',\n background: headerBg,\n color: '#fff',\n fontWeight: 600,\n fontSize: 13,\n height: HEADER_HEIGHT,\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8,\n borderTopLeftRadius: 6,\n borderTopRightRadius: 6,\n }}\n >\n <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>\n {table.name}\n </span>\n <span style={{ display: 'flex', alignItems: 'center', gap: 4, flexShrink: 0 }}>\n {table.group && (\n <span\n style={{\n fontSize: 10,\n opacity: 0.75,\n fontWeight: 400,\n padding: '1px 5px',\n border: '1px solid rgba(255,255,255,0.3)',\n borderRadius: 3,\n }}\n >\n {table.group}\n </span>\n )}\n {tableActions.onTableRemove && (\n <button\n className=\"nodrag\"\n onClick={(e) => {\n e.stopPropagation();\n tableActions.onTableRemove?.(table.name);\n }}\n onMouseDown={(e) => e.stopPropagation()}\n title=\"Remove this table\"\n style={{\n background: 'rgba(255,255,255,0.18)',\n border: 'none',\n color: '#fff',\n width: 18,\n height: 18,\n borderRadius: 3,\n cursor: 'pointer',\n fontSize: 13,\n fontWeight: 700,\n padding: 0,\n lineHeight: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n ×\n </button>\n )}\n </span>\n </div>\n\n {table.columns.map((col, i) => {\n const handleY = HEADER_HEIGHT + i * ROW_HEIGHT + ROW_HEIGHT / 2;\n const highlighted = highlightedCols?.has(col.name) ?? false;\n const selectionKey = `${table.name}.${col.name}`;\n const selected = selection.enabled && selection.selected.has(selectionKey);\n return (\n <Fragment key={`${col.name}-${i}`}>\n <Handle\n id={`${col.name}__target`}\n type=\"target\"\n position={Position.Left}\n style={handleStyle({ top: handleY })}\n />\n <Handle\n id={`${col.name}__source`}\n type=\"source\"\n position={Position.Right}\n style={handleStyle({ top: handleY })}\n />\n <ColumnRow\n tableName={table.name}\n column={col}\n highlighted={highlighted}\n onClick={onColumnClick}\n selectionEnabled={selection.enabled}\n selected={selected}\n onSelectToggle={(checked) =>\n selection.onToggle(table.name, col.name, checked)\n }\n />\n </Fragment>\n );\n })}\n </div>\n );\n});\n","import { memo } from 'react';\nimport {\n BaseEdge,\n EdgeLabelRenderer,\n getSmoothStepPath,\n type EdgeProps,\n} from '@xyflow/react';\nimport type { JoinType } from '../core/model';\n\nexport interface JoinEdgeData extends Record<string, unknown> {\n type: JoinType;\n color: string;\n hovered?: boolean;\n onDelete?: () => void;\n}\n\nexport const JoinEdge = memo(function JoinEdge({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n data,\n selected,\n}: EdgeProps) {\n const [edgePath, labelX, labelY] = getSmoothStepPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n });\n\n const d = (data ?? {}) as JoinEdgeData;\n const emphasized = !!selected || !!d.hovered;\n const color = d.color || '#3b82f6';\n\n return (\n <>\n <BaseEdge\n id={id}\n path={edgePath}\n style={{\n stroke: color,\n strokeWidth: emphasized ? 2.5 : 2,\n strokeDasharray: '6 3',\n }}\n />\n <EdgeLabelRenderer>\n <div\n className=\"nodrag nopan\"\n style={{\n position: 'absolute',\n transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,\n background: color,\n color: '#fff',\n fontSize: 10,\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n fontWeight: 700,\n borderRadius: 3,\n padding: '2px 4px 2px 6px',\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n pointerEvents: 'all',\n boxShadow: emphasized\n ? '0 0 0 2px rgba(0,0,0,0.4)'\n : '0 1px 2px rgba(0,0,0,0.15)',\n userSelect: 'none',\n }}\n >\n <span>{d.type}</span>\n {d.onDelete && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n d.onDelete?.();\n }}\n onMouseDown={(e) => e.stopPropagation()}\n title=\"Remove this JOIN\"\n style={{\n background: 'rgba(255,255,255,0.25)',\n border: 'none',\n color: '#fff',\n width: 14,\n height: 14,\n borderRadius: 2,\n cursor: 'pointer',\n fontSize: 11,\n fontWeight: 700,\n padding: 0,\n lineHeight: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n ×\n </button>\n )}\n </div>\n </EdgeLabelRenderer>\n </>\n );\n});\n","import {\n useCallback,\n useEffect,\n useMemo,\n useState,\n type CSSProperties,\n} from 'react';\nimport {\n ReactFlow,\n Background,\n Controls,\n MiniMap,\n useNodesState,\n type Connection,\n type Edge,\n type EdgeTypes,\n type Node,\n type NodeTypes,\n} from '@xyflow/react';\n// React Flow's CSS is intentionally NOT imported here. Consumers must add it\n// once at their app entry point: `import '@xyflow/react/dist/style.css';`.\n// Importing it from this file would cause double-injection in apps that\n// already use React Flow, and inflate this library's bundled assets.\nimport { parseMermaidER, MermaidERParseError } from '../core/parser';\nimport {\n layoutER,\n type LayoutAlgorithm,\n type LayoutDirection,\n type LayoutResult,\n} from '../core/layout';\nimport type {\n ColumnRef,\n ERModel,\n Join,\n PartialColumnRef,\n Relation,\n} from '../core/model';\nimport {\n ColumnSelectionContext,\n ConnectModeContext,\n HighlightContext,\n TableActionsContext,\n TableNode,\n type ColumnSelectionContextValue,\n type TableActionsContextValue,\n type TableNodeData,\n} from './TableNode';\nimport { JoinEdge, type JoinEdgeData } from './JoinEdge';\n\nconst nodeTypes: NodeTypes = {\n table: TableNode,\n};\n\nconst edgeTypes: EdgeTypes = {\n joinEdge: JoinEdge,\n};\n\nexport interface NodePosition {\n x: number;\n y: number;\n}\n\nexport type NodePositions = Record<string, NodePosition>;\n\nexport interface MermaidERProps {\n /** Mermaid ER source. Mutually exclusive with `model`. */\n source?: string;\n /** Pre-built ER model. Takes precedence over `source`. */\n model?: ERModel;\n layout?: 'elk';\n algorithm?: LayoutAlgorithm;\n direction?: LayoutDirection;\n aspectRatio?: number;\n positions?: NodePositions;\n onPositionsChange?: (positions: NodePositions) => void;\n showColumnCheckboxes?: boolean;\n selectedColumns?: ColumnRef[];\n onColumnSelectionChange?: (selectedColumns: ColumnRef[]) => void;\n /** Enable column-to-column / card-to-card drag for manual JOINs. */\n enableManualJoins?: boolean;\n /** Existing manual joins to render alongside FK relations. */\n joins?: Join[];\n /**\n * Fired when the user finishes a connect drag. The consumer typically opens\n * a dialog to ask for join type, then appends a complete `Join` to its state.\n * `column` may be undefined when the drag landed on a default (table-center) handle.\n */\n onJoinConnect?: (source: PartialColumnRef, target: PartialColumnRef) => void;\n /** Fired when the user removes a manual join via Delete or the trash icon. */\n onJoinDelete?: (joinId: string) => void;\n /** When provided, a small × appears on each table header to remove it from the canvas. */\n onTableRemove?: (table: string) => void;\n highlightReferencesOnHover?: boolean;\n onColumnClick?: (table: string, column: string) => void;\n onTableClick?: (table: string) => void;\n /** Override the default delete-key code(s). Default is 'Delete' (Backspace ignored to prevent accidents). */\n deleteKeyCode?: string | string[] | null;\n className?: string;\n style?: CSSProperties;\n}\n\ninterface ParseState {\n model: ERModel | null;\n error: MermaidERParseError | null;\n}\n\nfunction safeParse(source: string): ParseState {\n try {\n return { model: parseMermaidER(source), error: null };\n } catch (e) {\n if (e instanceof MermaidERParseError) {\n return { model: null, error: e };\n }\n throw e;\n }\n}\n\nfunction handleIdFor(side: 'source' | 'target', column: string | undefined): string {\n return column ? `${column}__${side}` : `__default-${side}`;\n}\n\nconst JOIN_EDGE_PREFIX = 'join:';\n\nfunction parseHandleColumn(handleId: string | null | undefined): string | undefined {\n if (!handleId) return undefined;\n if (handleId.startsWith('__default-')) return undefined;\n const m = /^(.+)__(?:source|target)$/.exec(handleId);\n return m ? m[1] : undefined;\n}\n\nfunction refKey(ref: ColumnRef): string {\n return `${ref.table}.${ref.column}`;\n}\n\nconst JOIN_TYPE_COLOR: Record<Join['type'], string> = {\n INNER: '#3b82f6',\n LEFT: '#8b5cf6',\n RIGHT: '#a855f7',\n FULL: '#ec4899',\n CROSS: '#6b7280',\n};\n\nexport function MermaidER(props: MermaidERProps) {\n const {\n source,\n model: modelProp,\n algorithm,\n direction,\n aspectRatio,\n positions,\n onPositionsChange,\n showColumnCheckboxes,\n selectedColumns,\n onColumnSelectionChange,\n enableManualJoins,\n joins,\n onJoinConnect,\n onJoinDelete,\n onTableRemove,\n onColumnClick,\n onTableClick,\n deleteKeyCode = 'Delete',\n className,\n style,\n highlightReferencesOnHover = true,\n } = props;\n\n const { model, error } = useMemo<ParseState>(() => {\n if (modelProp) return { model: modelProp, error: null };\n if (source != null) return safeParse(source);\n return { model: null, error: null };\n }, [source, modelProp]);\n\n const [layout, setLayout] = useState<LayoutResult | null>(null);\n const [hoveredEdgeId, setHoveredEdgeId] = useState<string | null>(null);\n\n useEffect(() => {\n if (!model) {\n setLayout(null);\n return;\n }\n let cancelled = false;\n layoutER(model, { algorithm, direction, aspectRatio }).then((result) => {\n if (!cancelled) setLayout(result);\n });\n return () => {\n cancelled = true;\n };\n }, [model, algorithm, direction, aspectRatio]);\n\n const baseNodes = useMemo<Node[]>(() => {\n if (!layout || !model) return [];\n const tableMap = new Map(model.tables.map((t) => [t.name, t]));\n const result: Node[] = [];\n for (const n of layout.nodes) {\n const table = tableMap.get(n.id);\n // Skip stale nodes: a model update can arrive before the new layout\n // finishes; in that gap, layout may still reference a removed table.\n if (!table) continue;\n const override = positions?.[n.id];\n const data: TableNodeData = { table };\n result.push({\n id: n.id,\n type: 'table',\n position: override ?? { x: n.x, y: n.y },\n data,\n width: n.width,\n height: n.height,\n draggable: true,\n deletable: false,\n });\n }\n return result;\n // `onColumnClick` is intentionally NOT a dep — it flows via TableActionsContext\n // so a non-stable callback identity from the parent doesn't reset React Flow\n // node state (which would clobber an in-flight drag).\n }, [layout, model, positions]);\n\n const [nodes, setNodes, onNodesChange] = useNodesState<Node>(baseNodes);\n\n useEffect(() => {\n setNodes(baseNodes);\n }, [baseNodes, setNodes]);\n\n const handleNodeDragStop = useCallback(\n (_: unknown, _primary: Node, dragged: Node[]) => {\n if (!onPositionsChange) return;\n const next: NodePositions = { ...(positions ?? {}) };\n for (const n of dragged) {\n next[n.id] = { x: n.position.x, y: n.position.y };\n }\n onPositionsChange(next);\n },\n [positions, onPositionsChange],\n );\n\n const hoveredRelation = useMemo<Relation | undefined>(() => {\n if (!hoveredEdgeId || !model) return undefined;\n return model.relations.find((r) => r.id === hoveredEdgeId);\n }, [hoveredEdgeId, model]);\n\n const hoveredJoin = useMemo<Join | undefined>(() => {\n if (!hoveredEdgeId || !joins) return undefined;\n if (!hoveredEdgeId.startsWith(JOIN_EDGE_PREFIX)) return undefined;\n const id = hoveredEdgeId.slice(JOIN_EDGE_PREFIX.length);\n return joins.find((j) => j.id === id);\n }, [hoveredEdgeId, joins]);\n\n const highlightMap = useMemo<ReadonlyMap<string, ReadonlySet<string>>>(() => {\n const map = new Map<string, Set<string>>();\n if (!highlightReferencesOnHover) return map;\n\n const add = (table: string, column: string | undefined) => {\n if (!column) return;\n const set = map.get(table) ?? new Set();\n set.add(column);\n map.set(table, set);\n };\n\n if (hoveredRelation) {\n add(hoveredRelation.from, hoveredRelation.fromColumn);\n add(hoveredRelation.to, hoveredRelation.toColumn);\n }\n if (hoveredJoin) {\n add(hoveredJoin.source.table, hoveredJoin.source.column);\n add(hoveredJoin.target.table, hoveredJoin.target.column);\n }\n return map;\n }, [hoveredRelation, hoveredJoin, highlightReferencesOnHover]);\n\n const selectionSet = useMemo<ReadonlySet<string>>(() => {\n return new Set((selectedColumns ?? []).map(refKey));\n }, [selectedColumns]);\n\n const handleColumnSelectToggle = useCallback(\n (table: string, column: string, checked: boolean) => {\n if (!onColumnSelectionChange) return;\n const list = selectedColumns ?? [];\n if (checked) {\n if (list.some((r) => r.table === table && r.column === column)) return;\n onColumnSelectionChange([...list, { table, column }]);\n } else {\n onColumnSelectionChange(\n list.filter((r) => !(r.table === table && r.column === column)),\n );\n }\n },\n [selectedColumns, onColumnSelectionChange],\n );\n\n const selectionContext = useMemo<ColumnSelectionContextValue>(\n () => ({\n enabled: showColumnCheckboxes ?? false,\n selected: selectionSet,\n onToggle: handleColumnSelectToggle,\n }),\n [showColumnCheckboxes, selectionSet, handleColumnSelectToggle],\n );\n\n const edges = useMemo<Edge[]>(() => {\n if (!layout || !model) return [];\n const tableSet = new Set(model.tables.map((t) => t.name));\n const relMap = new Map(model.relations.map((r) => [r.id, r]));\n\n const fkEdges: Edge[] = [];\n for (const e of layout.edges) {\n // Skip stale edges referencing tables removed since the layout was computed.\n if (!tableSet.has(e.source) || !tableSet.has(e.target)) continue;\n const rel = relMap.get(e.id);\n const isHovered = e.id === hoveredEdgeId;\n fkEdges.push({\n id: e.id,\n source: e.source,\n target: e.target,\n sourceHandle: handleIdFor('source', rel?.fromColumn),\n targetHandle: handleIdFor('target', rel?.toColumn),\n type: 'smoothstep',\n animated: isHovered,\n deletable: false,\n style: {\n stroke: isHovered ? '#f59e0b' : '#9ca3af',\n strokeWidth: isHovered ? 2 : 1.5,\n },\n label: rel?.label,\n labelStyle: { fontSize: 10, fill: '#6b7280' },\n labelBgStyle: { fill: '#fff', fillOpacity: 0.85 },\n labelBgPadding: [4, 2] as [number, number],\n labelBgBorderRadius: 3,\n });\n }\n\n const joinEdges: Edge[] = [];\n for (const j of joins ?? []) {\n if (!tableSet.has(j.source.table) || !tableSet.has(j.target.table)) continue;\n const edgeId = `${JOIN_EDGE_PREFIX}${j.id}`;\n const isHovered = edgeId === hoveredEdgeId;\n const color = JOIN_TYPE_COLOR[j.type] ?? '#3b82f6';\n const data: JoinEdgeData = {\n type: j.type,\n color,\n hovered: isHovered,\n onDelete: onJoinDelete ? () => onJoinDelete(j.id) : undefined,\n };\n joinEdges.push({\n id: edgeId,\n source: j.source.table,\n target: j.target.table,\n sourceHandle: handleIdFor('source', j.source.column),\n targetHandle: handleIdFor('target', j.target.column),\n type: 'joinEdge',\n deletable: true,\n data: data as unknown as Record<string, unknown>,\n });\n }\n\n return [...fkEdges, ...joinEdges];\n }, [layout, model, joins, hoveredEdgeId, onJoinDelete]);\n\n const handleConnect = useCallback(\n (conn: Connection) => {\n if (!onJoinConnect) return;\n if (!conn.source || !conn.target) return;\n onJoinConnect(\n { table: conn.source, column: parseHandleColumn(conn.sourceHandle) },\n { table: conn.target, column: parseHandleColumn(conn.targetHandle) },\n );\n },\n [onJoinConnect],\n );\n\n const handleEdgesDelete = useCallback(\n (deleted: Edge[]) => {\n if (!onJoinDelete) return;\n for (const e of deleted) {\n if (e.id.startsWith(JOIN_EDGE_PREFIX)) {\n onJoinDelete(e.id.slice(JOIN_EDGE_PREFIX.length));\n }\n }\n },\n [onJoinDelete],\n );\n\n const connectModeOn = !!enableManualJoins;\n const tableActions = useMemo<TableActionsContextValue>(\n () => ({ onTableRemove, onColumnClick }),\n [onTableRemove, onColumnClick],\n );\n\n return (\n <div\n className={className}\n style={{ width: '100%', height: '100%', position: 'relative', ...style }}\n >\n {error && (\n <div\n style={{\n position: 'absolute',\n top: 8,\n left: 8,\n right: 8,\n zIndex: 10,\n padding: '8px 12px',\n background: '#fef2f2',\n border: '1px solid #fecaca',\n borderRadius: 6,\n color: '#991b1b',\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n fontSize: 12,\n whiteSpace: 'pre-wrap',\n }}\n >\n {error.message}\n </div>\n )}\n {!error && !layout && (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#9ca3af',\n fontSize: 13,\n }}\n >\n Computing layout…\n </div>\n )}\n <HighlightContext.Provider value={highlightMap}>\n <ColumnSelectionContext.Provider value={selectionContext}>\n <ConnectModeContext.Provider value={connectModeOn}>\n <TableActionsContext.Provider value={tableActions}>\n <ReactFlow\n nodes={nodes}\n edges={edges}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n fitView\n onNodesChange={onNodesChange}\n onNodeDragStop={handleNodeDragStop}\n onNodeClick={\n onTableClick ? (_, node) => onTableClick(node.id) : undefined\n }\n onEdgeMouseEnter={(_, edge) => setHoveredEdgeId(edge.id)}\n onEdgeMouseLeave={() => setHoveredEdgeId(null)}\n onConnect={handleConnect}\n onEdgesDelete={handleEdgesDelete}\n nodesDraggable\n nodesConnectable={connectModeOn}\n elementsSelectable\n deleteKeyCode={deleteKeyCode}\n minZoom={0.1}\n maxZoom={4}\n >\n <Background />\n <Controls />\n <MiniMap pannable zoomable />\n </ReactFlow>\n </TableActionsContext.Provider>\n </ConnectModeContext.Provider>\n </ColumnSelectionContext.Provider>\n </HighlightContext.Provider>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/components/TableNode.tsx","../src/components/JoinEdge.tsx","../src/components/MermaidER.tsx"],"sourcesContent":["import { Fragment, createContext, memo, useContext } from 'react';\nimport { Handle, Position } from '@xyflow/react';\nimport type { NodeProps } from '@xyflow/react';\nimport type { Column, Table } from '../core/model';\nimport { HEADER_HEIGHT, ROW_HEIGHT } from '../core/layout';\n\nexport interface TableNodeData extends Record<string, unknown> {\n table: Table;\n}\n\n/**\n * Map from table name -> set of highlighted column names.\n * Provided by MermaidER, consumed by TableNode for hover-driven highlighting\n * without forcing the whole node array to recompute on every hover change.\n */\nexport const HighlightContext = createContext<ReadonlyMap<string, ReadonlySet<string>>>(new Map());\n\nexport interface ColumnSelectionContextValue {\n enabled: boolean;\n /** \"table.column\" keys for fast lookup. */\n selected: ReadonlySet<string>;\n onToggle: (table: string, column: string, checked: boolean) => void;\n}\n\nexport const ColumnSelectionContext = createContext<ColumnSelectionContextValue>({\n enabled: false,\n selected: new Set(),\n onToggle: () => undefined,\n});\n\n/** When true, column handles become visible/connectable for manual JOIN drawing. */\nexport const ConnectModeContext = createContext<boolean>(false);\n\nexport interface TableActionsContextValue {\n /** When provided, a delete affordance is shown on the table header. */\n onTableRemove?: (table: string) => void;\n /** Per-column click handler. Provided via context (not node data) so its\n * identity can change without invalidating the React Flow node array. */\n onColumnClick?: (table: string, column: string) => void;\n}\n\nexport const TableActionsContext = createContext<TableActionsContextValue>({});\n\nconst KEY_STYLES: Record<'pk' | 'fk' | 'uk', { label: string; bg: string }> = {\n pk: { label: 'PK', bg: '#f59e0b' },\n fk: { label: 'FK', bg: '#3b82f6' },\n uk: { label: 'UK', bg: '#10b981' },\n};\n\nfunction ColumnRow({\n tableName,\n column,\n highlighted,\n onClick,\n selectionEnabled,\n selected,\n onSelectToggle,\n}: {\n tableName: string;\n column: Column;\n highlighted: boolean;\n onClick?: (table: string, column: string) => void;\n selectionEnabled: boolean;\n selected: boolean;\n onSelectToggle: (checked: boolean) => void;\n}) {\n const badges: Array<{ label: string; bg: string }> = [];\n if (column.keys.pk) badges.push(KEY_STYLES.pk);\n if (column.keys.fk) badges.push(KEY_STYLES.fk);\n if (column.keys.uk) badges.push(KEY_STYLES.uk);\n\n return (\n <div\n title={column.comment}\n onClick={onClick ? () => onClick(tableName, column.name) : undefined}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '0 10px',\n fontSize: 12,\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n borderTop: '1px solid #eee',\n height: ROW_HEIGHT,\n boxSizing: 'border-box',\n cursor: onClick ? 'pointer' : 'default',\n background: highlighted ? '#fef3c7' : selected ? '#eff6ff' : 'transparent',\n transition: 'background 0.15s',\n }}\n >\n {selectionEnabled && (\n <input\n type=\"checkbox\"\n checked={selected}\n onChange={(e) => onSelectToggle(e.target.checked)}\n onClick={(e) => e.stopPropagation()}\n style={{\n width: 13,\n height: 13,\n margin: 0,\n flexShrink: 0,\n cursor: 'pointer',\n }}\n />\n )}\n <span style={{ display: 'flex', gap: 2, flexShrink: 0, minWidth: 18 }}>\n {badges.map((b) => (\n <span\n key={b.label}\n style={{\n display: 'inline-block',\n padding: '0 4px',\n borderRadius: 3,\n background: b.bg,\n color: '#fff',\n fontSize: 9,\n fontWeight: 700,\n lineHeight: '14px',\n }}\n >\n {b.label}\n </span>\n ))}\n </span>\n <span\n style={{\n flexShrink: 0,\n fontWeight: column.keys.pk ? 600 : 400,\n color: '#1f2937',\n }}\n >\n {column.name}\n </span>\n {column.type && (\n <span\n style={{\n flex: 1,\n textAlign: 'right',\n color: '#9ca3af',\n fontStyle: 'italic',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {column.type}\n </span>\n )}\n </div>\n );\n}\n\nexport const TableNode = memo(function TableNode({ data }: NodeProps) {\n const { table } = data as TableNodeData;\n const highlightMap = useContext(HighlightContext);\n const selection = useContext(ColumnSelectionContext);\n const connectMode = useContext(ConnectModeContext);\n const tableActions = useContext(TableActionsContext);\n const onColumnClick = tableActions.onColumnClick;\n const highlightedCols = highlightMap.get(table.name);\n const headerBg = table.group ? '#1e40af' : '#374151';\n\n const handleStyle = (extra: { top?: number }): React.CSSProperties => ({\n ...extra,\n width: connectMode ? 9 : 6,\n height: connectMode ? 9 : 6,\n background: connectMode ? '#3b82f6' : 'transparent',\n border: connectMode ? '1.5px solid #fff' : 'none',\n boxShadow: connectMode ? '0 0 0 1px rgba(59,130,246,0.4)' : 'none',\n opacity: connectMode ? 0.85 : 0,\n pointerEvents: connectMode ? 'auto' : 'none',\n cursor: connectMode ? 'crosshair' : 'default',\n });\n\n return (\n <div\n style={{\n background: '#fff',\n border: '1px solid #c4c4c4',\n borderRadius: 6,\n boxShadow: '0 2px 6px rgba(0,0,0,0.08)',\n overflow: 'visible',\n width: '100%',\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n position: 'relative',\n }}\n >\n <Handle\n id=\"__default-target\"\n type=\"target\"\n position={Position.Left}\n style={handleStyle({ top: HEADER_HEIGHT / 2 })}\n />\n <Handle\n id=\"__default-source\"\n type=\"source\"\n position={Position.Right}\n style={handleStyle({ top: HEADER_HEIGHT / 2 })}\n />\n\n <div\n style={{\n padding: '6px 10px',\n background: headerBg,\n color: '#fff',\n fontWeight: 600,\n fontSize: 13,\n height: HEADER_HEIGHT,\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8,\n borderTopLeftRadius: 6,\n borderTopRightRadius: 6,\n }}\n >\n <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>\n {table.name}\n </span>\n <span style={{ display: 'flex', alignItems: 'center', gap: 4, flexShrink: 0 }}>\n {table.group && (\n <span\n style={{\n fontSize: 10,\n opacity: 0.75,\n fontWeight: 400,\n padding: '1px 5px',\n border: '1px solid rgba(255,255,255,0.3)',\n borderRadius: 3,\n }}\n >\n {table.group}\n </span>\n )}\n {tableActions.onTableRemove && (\n <button\n className=\"nodrag\"\n onClick={(e) => {\n e.stopPropagation();\n tableActions.onTableRemove?.(table.name);\n }}\n onMouseDown={(e) => e.stopPropagation()}\n title=\"Remove this table\"\n style={{\n background: 'rgba(255,255,255,0.18)',\n border: 'none',\n color: '#fff',\n width: 18,\n height: 18,\n borderRadius: 3,\n cursor: 'pointer',\n fontSize: 13,\n fontWeight: 700,\n padding: 0,\n lineHeight: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n ×\n </button>\n )}\n </span>\n </div>\n\n {table.columns.map((col, i) => {\n const handleY = HEADER_HEIGHT + i * ROW_HEIGHT + ROW_HEIGHT / 2;\n const highlighted = highlightedCols?.has(col.name) ?? false;\n const selectionKey = `${table.name}.${col.name}`;\n const selected = selection.enabled && selection.selected.has(selectionKey);\n return (\n <Fragment key={`${col.name}-${i}`}>\n <Handle\n id={`${col.name}__target`}\n type=\"target\"\n position={Position.Left}\n style={handleStyle({ top: handleY })}\n />\n <Handle\n id={`${col.name}__source`}\n type=\"source\"\n position={Position.Right}\n style={handleStyle({ top: handleY })}\n />\n <ColumnRow\n tableName={table.name}\n column={col}\n highlighted={highlighted}\n onClick={onColumnClick}\n selectionEnabled={selection.enabled}\n selected={selected}\n onSelectToggle={(checked) =>\n selection.onToggle(table.name, col.name, checked)\n }\n />\n </Fragment>\n );\n })}\n </div>\n );\n});\n","import { memo } from 'react';\nimport {\n BaseEdge,\n EdgeLabelRenderer,\n getSmoothStepPath,\n type EdgeProps,\n} from '@xyflow/react';\nimport type { JoinType } from '../core/model';\n\nexport interface JoinEdgeData extends Record<string, unknown> {\n type: JoinType;\n color: string;\n hovered?: boolean;\n onDelete?: () => void;\n}\n\nexport const JoinEdge = memo(function JoinEdge({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n data,\n selected,\n}: EdgeProps) {\n const [edgePath, labelX, labelY] = getSmoothStepPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n });\n\n const d = (data ?? {}) as JoinEdgeData;\n const emphasized = !!selected || !!d.hovered;\n const color = d.color || '#3b82f6';\n\n return (\n <>\n <BaseEdge\n id={id}\n path={edgePath}\n style={{\n stroke: color,\n strokeWidth: emphasized ? 2.5 : 2,\n strokeDasharray: '6 3',\n }}\n />\n <EdgeLabelRenderer>\n <div\n className=\"nodrag nopan\"\n style={{\n position: 'absolute',\n transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,\n background: color,\n color: '#fff',\n fontSize: 10,\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n fontWeight: 700,\n borderRadius: 3,\n padding: '2px 4px 2px 6px',\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n pointerEvents: 'all',\n boxShadow: emphasized\n ? '0 0 0 2px rgba(0,0,0,0.4)'\n : '0 1px 2px rgba(0,0,0,0.15)',\n userSelect: 'none',\n }}\n >\n <span>{d.type}</span>\n {d.onDelete && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n d.onDelete?.();\n }}\n onMouseDown={(e) => e.stopPropagation()}\n title=\"Remove this JOIN\"\n style={{\n background: 'rgba(255,255,255,0.25)',\n border: 'none',\n color: '#fff',\n width: 14,\n height: 14,\n borderRadius: 2,\n cursor: 'pointer',\n fontSize: 11,\n fontWeight: 700,\n padding: 0,\n lineHeight: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n ×\n </button>\n )}\n </div>\n </EdgeLabelRenderer>\n </>\n );\n});\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type Ref,\n} from 'react';\nimport {\n ReactFlow,\n Background,\n Controls,\n MiniMap,\n useNodesState,\n useReactFlow,\n type Connection,\n type Edge,\n type EdgeTypes,\n type FitViewOptions,\n type Node,\n type NodeTypes,\n type Rect,\n type Viewport,\n} from '@xyflow/react';\n// React Flow's CSS is intentionally NOT imported here. Consumers must add it\n// once at their app entry point: `import '@xyflow/react/dist/style.css';`.\n// Importing it from this file would cause double-injection in apps that\n// already use React Flow, and inflate this library's bundled assets.\nimport { parseMermaidER, MermaidERParseError } from '../core/parser';\nimport {\n layoutER,\n type LayoutAlgorithm,\n type LayoutDirection,\n type LayoutResult,\n} from '../core/layout';\nimport type {\n ColumnRef,\n ERModel,\n Join,\n PartialColumnRef,\n Relation,\n} from '../core/model';\nimport {\n ColumnSelectionContext,\n ConnectModeContext,\n HighlightContext,\n TableActionsContext,\n TableNode,\n type ColumnSelectionContextValue,\n type TableActionsContextValue,\n type TableNodeData,\n} from './TableNode';\nimport { JoinEdge, type JoinEdgeData } from './JoinEdge';\n\nconst nodeTypes: NodeTypes = {\n table: TableNode,\n};\n\nconst edgeTypes: EdgeTypes = {\n joinEdge: JoinEdge,\n};\n\nexport interface NodePosition {\n x: number;\n y: number;\n}\n\nexport type NodePositions = Record<string, NodePosition>;\n\n/**\n * Imperative API exposed via `ref`. Designed to give consumers the primitives\n * needed for image export (fit → snapshot → restore) without baking any\n * specific export library into this package. Methods that change the viewport\n * return Promise<boolean> matching React Flow's underlying API.\n */\nexport interface MermaidERHandle {\n /** Fit all nodes into the visible viewport. */\n fitView: (options?: FitViewOptions) => Promise<boolean>;\n /** Read the current viewport (x, y, zoom) — capture before export to restore later. */\n getViewport: () => Viewport;\n /** Restore (or set) viewport. */\n setViewport: (viewport: Viewport) => Promise<boolean>;\n /** Bounding box of all current nodes. Useful for sizing offscreen canvases. */\n getNodesBounds: () => Rect;\n /** Outer wrapper element (the div that receives `className` / `style`). */\n getWrapperElement: () => HTMLDivElement | null;\n /**\n * The internal `.react-flow__viewport` element. Typical snapshot target\n * when you want to exclude `<Controls>` and `<MiniMap>` from the image.\n */\n getViewportElement: () => HTMLElement | null;\n}\n\nexport interface MermaidERProps {\n /** Mermaid ER source. Mutually exclusive with `model`. */\n source?: string;\n /** Pre-built ER model. Takes precedence over `source`. */\n model?: ERModel;\n layout?: 'elk';\n algorithm?: LayoutAlgorithm;\n direction?: LayoutDirection;\n aspectRatio?: number;\n positions?: NodePositions;\n onPositionsChange?: (positions: NodePositions) => void;\n showColumnCheckboxes?: boolean;\n selectedColumns?: ColumnRef[];\n onColumnSelectionChange?: (selectedColumns: ColumnRef[]) => void;\n /** Enable column-to-column / card-to-card drag for manual JOINs. */\n enableManualJoins?: boolean;\n /** Existing manual joins to render alongside FK relations. */\n joins?: Join[];\n /**\n * Fired when the user finishes a connect drag. The consumer typically opens\n * a dialog to ask for join type, then appends a complete `Join` to its state.\n * `column` may be undefined when the drag landed on a default (table-center) handle.\n */\n onJoinConnect?: (source: PartialColumnRef, target: PartialColumnRef) => void;\n /** Fired when the user removes a manual join via Delete or the trash icon. */\n onJoinDelete?: (joinId: string) => void;\n /** When provided, a small × appears on each table header to remove it from the canvas. */\n onTableRemove?: (table: string) => void;\n highlightReferencesOnHover?: boolean;\n onColumnClick?: (table: string, column: string) => void;\n onTableClick?: (table: string) => void;\n /** Override the default delete-key code(s). Default is 'Delete' (Backspace ignored to prevent accidents). */\n deleteKeyCode?: string | string[] | null;\n className?: string;\n style?: CSSProperties;\n}\n\ninterface ParseState {\n model: ERModel | null;\n error: MermaidERParseError | null;\n}\n\nfunction safeParse(source: string): ParseState {\n try {\n return { model: parseMermaidER(source), error: null };\n } catch (e) {\n if (e instanceof MermaidERParseError) {\n return { model: null, error: e };\n }\n throw e;\n }\n}\n\nfunction handleIdFor(side: 'source' | 'target', column: string | undefined): string {\n return column ? `${column}__${side}` : `__default-${side}`;\n}\n\nconst JOIN_EDGE_PREFIX = 'join:';\n\nfunction parseHandleColumn(handleId: string | null | undefined): string | undefined {\n if (!handleId) return undefined;\n if (handleId.startsWith('__default-')) return undefined;\n const m = /^(.+)__(?:source|target)$/.exec(handleId);\n return m ? m[1] : undefined;\n}\n\nfunction refKey(ref: ColumnRef): string {\n return `${ref.table}.${ref.column}`;\n}\n\nconst JOIN_TYPE_COLOR: Record<Join['type'], string> = {\n INNER: '#3b82f6',\n LEFT: '#8b5cf6',\n RIGHT: '#a855f7',\n FULL: '#ec4899',\n CROSS: '#6b7280',\n};\n\n/**\n * Bridge: `useReactFlow()` only works inside the <ReactFlow> provider, but the\n * imperative ref must be attached to MermaidER (which is the *parent* of\n * <ReactFlow>). This null-rendering child runs the hook in the right scope and\n * forwards the methods back out.\n */\nfunction HandleBridge({\n apiRef,\n wrapperRef,\n}: {\n apiRef: Ref<MermaidERHandle> | undefined;\n wrapperRef: React.RefObject<HTMLDivElement | null>;\n}) {\n const rf = useReactFlow();\n useImperativeHandle(\n apiRef,\n () => ({\n fitView: (options) => rf.fitView(options),\n getViewport: () => rf.getViewport(),\n setViewport: (viewport) => rf.setViewport(viewport),\n getNodesBounds: () => rf.getNodesBounds(rf.getNodes()),\n getWrapperElement: () => wrapperRef.current,\n getViewportElement: () =>\n wrapperRef.current?.querySelector<HTMLElement>('.react-flow__viewport') ?? null,\n }),\n [rf, wrapperRef],\n );\n return null;\n}\n\nexport const MermaidER = forwardRef<MermaidERHandle, MermaidERProps>(function MermaidER(\n props,\n ref,\n) {\n const {\n source,\n model: modelProp,\n algorithm,\n direction,\n aspectRatio,\n positions,\n onPositionsChange,\n showColumnCheckboxes,\n selectedColumns,\n onColumnSelectionChange,\n enableManualJoins,\n joins,\n onJoinConnect,\n onJoinDelete,\n onTableRemove,\n onColumnClick,\n onTableClick,\n deleteKeyCode = 'Delete',\n className,\n style,\n highlightReferencesOnHover = true,\n } = props;\n\n const { model, error } = useMemo<ParseState>(() => {\n if (modelProp) return { model: modelProp, error: null };\n if (source != null) return safeParse(source);\n return { model: null, error: null };\n }, [source, modelProp]);\n\n const [layout, setLayout] = useState<LayoutResult | null>(null);\n const [hoveredEdgeId, setHoveredEdgeId] = useState<string | null>(null);\n\n useEffect(() => {\n if (!model) {\n setLayout(null);\n return;\n }\n let cancelled = false;\n layoutER(model, { algorithm, direction, aspectRatio }).then((result) => {\n if (!cancelled) setLayout(result);\n });\n return () => {\n cancelled = true;\n };\n }, [model, algorithm, direction, aspectRatio]);\n\n const baseNodes = useMemo<Node[]>(() => {\n if (!layout || !model) return [];\n const tableMap = new Map(model.tables.map((t) => [t.name, t]));\n const result: Node[] = [];\n for (const n of layout.nodes) {\n const table = tableMap.get(n.id);\n // Skip stale nodes: a model update can arrive before the new layout\n // finishes; in that gap, layout may still reference a removed table.\n if (!table) continue;\n const override = positions?.[n.id];\n const data: TableNodeData = { table };\n result.push({\n id: n.id,\n type: 'table',\n position: override ?? { x: n.x, y: n.y },\n data,\n width: n.width,\n height: n.height,\n draggable: true,\n deletable: false,\n });\n }\n return result;\n // `onColumnClick` is intentionally NOT a dep — it flows via TableActionsContext\n // so a non-stable callback identity from the parent doesn't reset React Flow\n // node state (which would clobber an in-flight drag).\n }, [layout, model, positions]);\n\n const [nodes, setNodes, onNodesChange] = useNodesState<Node>(baseNodes);\n\n useEffect(() => {\n setNodes(baseNodes);\n }, [baseNodes, setNodes]);\n\n const handleNodeDragStop = useCallback(\n (_: unknown, _primary: Node, dragged: Node[]) => {\n if (!onPositionsChange) return;\n const next: NodePositions = { ...(positions ?? {}) };\n for (const n of dragged) {\n next[n.id] = { x: n.position.x, y: n.position.y };\n }\n onPositionsChange(next);\n },\n [positions, onPositionsChange],\n );\n\n const hoveredRelation = useMemo<Relation | undefined>(() => {\n if (!hoveredEdgeId || !model) return undefined;\n return model.relations.find((r) => r.id === hoveredEdgeId);\n }, [hoveredEdgeId, model]);\n\n const hoveredJoin = useMemo<Join | undefined>(() => {\n if (!hoveredEdgeId || !joins) return undefined;\n if (!hoveredEdgeId.startsWith(JOIN_EDGE_PREFIX)) return undefined;\n const id = hoveredEdgeId.slice(JOIN_EDGE_PREFIX.length);\n return joins.find((j) => j.id === id);\n }, [hoveredEdgeId, joins]);\n\n const highlightMap = useMemo<ReadonlyMap<string, ReadonlySet<string>>>(() => {\n const map = new Map<string, Set<string>>();\n if (!highlightReferencesOnHover) return map;\n\n const add = (table: string, column: string | undefined) => {\n if (!column) return;\n const set = map.get(table) ?? new Set();\n set.add(column);\n map.set(table, set);\n };\n\n if (hoveredRelation) {\n add(hoveredRelation.from, hoveredRelation.fromColumn);\n add(hoveredRelation.to, hoveredRelation.toColumn);\n }\n if (hoveredJoin) {\n add(hoveredJoin.source.table, hoveredJoin.source.column);\n add(hoveredJoin.target.table, hoveredJoin.target.column);\n }\n return map;\n }, [hoveredRelation, hoveredJoin, highlightReferencesOnHover]);\n\n const selectionSet = useMemo<ReadonlySet<string>>(() => {\n return new Set((selectedColumns ?? []).map(refKey));\n }, [selectedColumns]);\n\n const handleColumnSelectToggle = useCallback(\n (table: string, column: string, checked: boolean) => {\n if (!onColumnSelectionChange) return;\n const list = selectedColumns ?? [];\n if (checked) {\n if (list.some((r) => r.table === table && r.column === column)) return;\n onColumnSelectionChange([...list, { table, column }]);\n } else {\n onColumnSelectionChange(\n list.filter((r) => !(r.table === table && r.column === column)),\n );\n }\n },\n [selectedColumns, onColumnSelectionChange],\n );\n\n const selectionContext = useMemo<ColumnSelectionContextValue>(\n () => ({\n enabled: showColumnCheckboxes ?? false,\n selected: selectionSet,\n onToggle: handleColumnSelectToggle,\n }),\n [showColumnCheckboxes, selectionSet, handleColumnSelectToggle],\n );\n\n const edges = useMemo<Edge[]>(() => {\n if (!layout || !model) return [];\n const tableSet = new Set(model.tables.map((t) => t.name));\n const relMap = new Map(model.relations.map((r) => [r.id, r]));\n\n const fkEdges: Edge[] = [];\n for (const e of layout.edges) {\n // Skip stale edges referencing tables removed since the layout was computed.\n if (!tableSet.has(e.source) || !tableSet.has(e.target)) continue;\n const rel = relMap.get(e.id);\n const isHovered = e.id === hoveredEdgeId;\n fkEdges.push({\n id: e.id,\n source: e.source,\n target: e.target,\n sourceHandle: handleIdFor('source', rel?.fromColumn),\n targetHandle: handleIdFor('target', rel?.toColumn),\n type: 'smoothstep',\n animated: isHovered,\n deletable: false,\n style: {\n stroke: isHovered ? '#f59e0b' : '#9ca3af',\n strokeWidth: isHovered ? 2 : 1.5,\n },\n label: rel?.label,\n labelStyle: { fontSize: 10, fill: '#6b7280' },\n labelBgStyle: { fill: '#fff', fillOpacity: 0.85 },\n labelBgPadding: [4, 2] as [number, number],\n labelBgBorderRadius: 3,\n });\n }\n\n const joinEdges: Edge[] = [];\n for (const j of joins ?? []) {\n if (!tableSet.has(j.source.table) || !tableSet.has(j.target.table)) continue;\n const edgeId = `${JOIN_EDGE_PREFIX}${j.id}`;\n const isHovered = edgeId === hoveredEdgeId;\n const color = JOIN_TYPE_COLOR[j.type] ?? '#3b82f6';\n const data: JoinEdgeData = {\n type: j.type,\n color,\n hovered: isHovered,\n onDelete: onJoinDelete ? () => onJoinDelete(j.id) : undefined,\n };\n joinEdges.push({\n id: edgeId,\n source: j.source.table,\n target: j.target.table,\n sourceHandle: handleIdFor('source', j.source.column),\n targetHandle: handleIdFor('target', j.target.column),\n type: 'joinEdge',\n deletable: true,\n data: data as unknown as Record<string, unknown>,\n });\n }\n\n return [...fkEdges, ...joinEdges];\n }, [layout, model, joins, hoveredEdgeId, onJoinDelete]);\n\n const handleConnect = useCallback(\n (conn: Connection) => {\n if (!onJoinConnect) return;\n if (!conn.source || !conn.target) return;\n onJoinConnect(\n { table: conn.source, column: parseHandleColumn(conn.sourceHandle) },\n { table: conn.target, column: parseHandleColumn(conn.targetHandle) },\n );\n },\n [onJoinConnect],\n );\n\n const handleEdgesDelete = useCallback(\n (deleted: Edge[]) => {\n if (!onJoinDelete) return;\n for (const e of deleted) {\n if (e.id.startsWith(JOIN_EDGE_PREFIX)) {\n onJoinDelete(e.id.slice(JOIN_EDGE_PREFIX.length));\n }\n }\n },\n [onJoinDelete],\n );\n\n const connectModeOn = !!enableManualJoins;\n const tableActions = useMemo<TableActionsContextValue>(\n () => ({ onTableRemove, onColumnClick }),\n [onTableRemove, onColumnClick],\n );\n\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n return (\n <div\n ref={wrapperRef}\n className={className}\n style={{ width: '100%', height: '100%', position: 'relative', ...style }}\n >\n {error && (\n <div\n style={{\n position: 'absolute',\n top: 8,\n left: 8,\n right: 8,\n zIndex: 10,\n padding: '8px 12px',\n background: '#fef2f2',\n border: '1px solid #fecaca',\n borderRadius: 6,\n color: '#991b1b',\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n fontSize: 12,\n whiteSpace: 'pre-wrap',\n }}\n >\n {error.message}\n </div>\n )}\n {!error && !layout && (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#9ca3af',\n fontSize: 13,\n }}\n >\n Computing layout…\n </div>\n )}\n <HighlightContext.Provider value={highlightMap}>\n <ColumnSelectionContext.Provider value={selectionContext}>\n <ConnectModeContext.Provider value={connectModeOn}>\n <TableActionsContext.Provider value={tableActions}>\n <ReactFlow\n nodes={nodes}\n edges={edges}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n fitView\n onNodesChange={onNodesChange}\n onNodeDragStop={handleNodeDragStop}\n onNodeClick={\n onTableClick ? (_, node) => onTableClick(node.id) : undefined\n }\n onEdgeMouseEnter={(_, edge) => setHoveredEdgeId(edge.id)}\n onEdgeMouseLeave={() => setHoveredEdgeId(null)}\n onConnect={handleConnect}\n onEdgesDelete={handleEdgesDelete}\n nodesDraggable\n nodesConnectable={connectModeOn}\n elementsSelectable\n deleteKeyCode={deleteKeyCode}\n minZoom={0.1}\n maxZoom={4}\n >\n <Background />\n <Controls />\n <MiniMap pannable zoomable />\n <HandleBridge apiRef={ref} wrapperRef={wrapperRef} />\n </ReactFlow>\n </TableActionsContext.Provider>\n </ConnectModeContext.Provider>\n </ColumnSelectionContext.Provider>\n </HighlightContext.Provider>\n </div>\n );\n});\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { MermaidER } from './components/MermaidER';
2
- export type { MermaidERProps, NodePosition, NodePositions, } from './components/MermaidER';
2
+ export type { MermaidERHandle, MermaidERProps, NodePosition, NodePositions, } from './components/MermaidER';
3
3
  export type { TableNodeData } from './components/TableNode';
4
4
  export type { JoinEdgeData } from './components/JoinEdge';
5
5
  export * from './core';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY,EACV,cAAc,EACd,YAAY,EACZ,aAAa,GACd,MAAM,wBAAwB,CAAC;AAKhC,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,cAAc,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY,EACV,eAAe,EACf,cAAc,EACd,YAAY,EACZ,aAAa,GACd,MAAM,wBAAwB,CAAC;AAKhC,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,cAAc,QAAQ,CAAC"}
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { a as e, c as t, i as n, n as r, o as i, r as a, s as o, t as s } from "./core-DZ30VgUT.js";
2
- import { Fragment as c, createContext as l, memo as u, useCallback as d, useContext as f, useEffect as p, useMemo as m, useState as h } from "react";
3
- import { Background as ee, BaseEdge as g, Controls as _, EdgeLabelRenderer as v, Handle as y, MiniMap as te, Position as b, ReactFlow as ne, getSmoothStepPath as x, useNodesState as S } from "@xyflow/react";
4
- import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
2
+ import { Fragment as c, createContext as l, forwardRef as u, memo as d, useCallback as f, useContext as p, useEffect as m, useImperativeHandle as h, useMemo as g, useRef as ee, useState as _ } from "react";
3
+ import { Background as v, BaseEdge as y, Controls as te, EdgeLabelRenderer as b, Handle as x, MiniMap as S, Position as C, ReactFlow as ne, getSmoothStepPath as w, useNodesState as re, useReactFlow as T } from "@xyflow/react";
4
+ import { Fragment as E, jsx as D, jsxs as O } from "react/jsx-runtime";
5
5
  //#region src/components/TableNode.tsx
6
- var E = l(/* @__PURE__ */ new Map()), D = l({
6
+ var k = l(/* @__PURE__ */ new Map()), A = l({
7
7
  enabled: !1,
8
8
  selected: /* @__PURE__ */ new Set(),
9
9
  onToggle: () => void 0
10
- }), O = l(!1), k = l({}), A = {
10
+ }), j = l(!1), M = l({}), N = {
11
11
  pk: {
12
12
  label: "PK",
13
13
  bg: "#f59e0b"
@@ -21,9 +21,9 @@ var E = l(/* @__PURE__ */ new Map()), D = l({
21
21
  bg: "#10b981"
22
22
  }
23
23
  };
24
- function j({ tableName: e, column: t, highlighted: n, onClick: r, selectionEnabled: i, selected: a, onSelectToggle: o }) {
24
+ function P({ tableName: e, column: t, highlighted: n, onClick: r, selectionEnabled: i, selected: a, onSelectToggle: o }) {
25
25
  let s = [];
26
- return t.keys.pk && s.push(A.pk), t.keys.fk && s.push(A.fk), t.keys.uk && s.push(A.uk), /* @__PURE__ */ T("div", {
26
+ return t.keys.pk && s.push(N.pk), t.keys.fk && s.push(N.fk), t.keys.uk && s.push(N.uk), /* @__PURE__ */ O("div", {
27
27
  title: t.comment,
28
28
  onClick: r ? () => r(e, t.name) : void 0,
29
29
  style: {
@@ -41,7 +41,7 @@ function j({ tableName: e, column: t, highlighted: n, onClick: r, selectionEnabl
41
41
  transition: "background 0.15s"
42
42
  },
43
43
  children: [
44
- i && /* @__PURE__ */ w("input", {
44
+ i && /* @__PURE__ */ D("input", {
45
45
  type: "checkbox",
46
46
  checked: a,
47
47
  onChange: (e) => o(e.target.checked),
@@ -54,14 +54,14 @@ function j({ tableName: e, column: t, highlighted: n, onClick: r, selectionEnabl
54
54
  cursor: "pointer"
55
55
  }
56
56
  }),
57
- /* @__PURE__ */ w("span", {
57
+ /* @__PURE__ */ D("span", {
58
58
  style: {
59
59
  display: "flex",
60
60
  gap: 2,
61
61
  flexShrink: 0,
62
62
  minWidth: 18
63
63
  },
64
- children: s.map((e) => /* @__PURE__ */ w("span", {
64
+ children: s.map((e) => /* @__PURE__ */ D("span", {
65
65
  style: {
66
66
  display: "inline-block",
67
67
  padding: "0 4px",
@@ -75,7 +75,7 @@ function j({ tableName: e, column: t, highlighted: n, onClick: r, selectionEnabl
75
75
  children: e.label
76
76
  }, e.label))
77
77
  }),
78
- /* @__PURE__ */ w("span", {
78
+ /* @__PURE__ */ D("span", {
79
79
  style: {
80
80
  flexShrink: 0,
81
81
  fontWeight: t.keys.pk ? 600 : 400,
@@ -83,7 +83,7 @@ function j({ tableName: e, column: t, highlighted: n, onClick: r, selectionEnabl
83
83
  },
84
84
  children: t.name
85
85
  }),
86
- t.type && /* @__PURE__ */ w("span", {
86
+ t.type && /* @__PURE__ */ D("span", {
87
87
  style: {
88
88
  flex: 1,
89
89
  textAlign: "right",
@@ -98,8 +98,8 @@ function j({ tableName: e, column: t, highlighted: n, onClick: r, selectionEnabl
98
98
  ]
99
99
  });
100
100
  }
101
- var M = u(function({ data: e }) {
102
- let { table: t } = e, n = f(E), r = f(D), i = f(O), a = f(k), o = a.onColumnClick, s = n.get(t.name), l = t.group ? "#1e40af" : "#374151", u = (e) => ({
101
+ var F = d(function({ data: e }) {
102
+ let { table: t } = e, n = p(k), r = p(A), i = p(j), a = p(M), o = a.onColumnClick, s = n.get(t.name), l = t.group ? "#1e40af" : "#374151", u = (e) => ({
103
103
  ...e,
104
104
  width: i ? 9 : 6,
105
105
  height: i ? 9 : 6,
@@ -110,7 +110,7 @@ var M = u(function({ data: e }) {
110
110
  pointerEvents: i ? "auto" : "none",
111
111
  cursor: i ? "crosshair" : "default"
112
112
  });
113
- return /* @__PURE__ */ T("div", {
113
+ return /* @__PURE__ */ O("div", {
114
114
  style: {
115
115
  background: "#fff",
116
116
  border: "1px solid #c4c4c4",
@@ -124,19 +124,19 @@ var M = u(function({ data: e }) {
124
124
  position: "relative"
125
125
  },
126
126
  children: [
127
- /* @__PURE__ */ w(y, {
127
+ /* @__PURE__ */ D(x, {
128
128
  id: "__default-target",
129
129
  type: "target",
130
- position: b.Left,
130
+ position: C.Left,
131
131
  style: u({ top: 32 / 2 })
132
132
  }),
133
- /* @__PURE__ */ w(y, {
133
+ /* @__PURE__ */ D(x, {
134
134
  id: "__default-source",
135
135
  type: "source",
136
- position: b.Right,
136
+ position: C.Right,
137
137
  style: u({ top: 32 / 2 })
138
138
  }),
139
- /* @__PURE__ */ T("div", {
139
+ /* @__PURE__ */ O("div", {
140
140
  style: {
141
141
  padding: "6px 10px",
142
142
  background: l,
@@ -152,21 +152,21 @@ var M = u(function({ data: e }) {
152
152
  borderTopLeftRadius: 6,
153
153
  borderTopRightRadius: 6
154
154
  },
155
- children: [/* @__PURE__ */ w("span", {
155
+ children: [/* @__PURE__ */ D("span", {
156
156
  style: {
157
157
  overflow: "hidden",
158
158
  textOverflow: "ellipsis",
159
159
  whiteSpace: "nowrap"
160
160
  },
161
161
  children: t.name
162
- }), /* @__PURE__ */ T("span", {
162
+ }), /* @__PURE__ */ O("span", {
163
163
  style: {
164
164
  display: "flex",
165
165
  alignItems: "center",
166
166
  gap: 4,
167
167
  flexShrink: 0
168
168
  },
169
- children: [t.group && /* @__PURE__ */ w("span", {
169
+ children: [t.group && /* @__PURE__ */ D("span", {
170
170
  style: {
171
171
  fontSize: 10,
172
172
  opacity: .75,
@@ -176,7 +176,7 @@ var M = u(function({ data: e }) {
176
176
  borderRadius: 3
177
177
  },
178
178
  children: t.group
179
- }), a.onTableRemove && /* @__PURE__ */ w("button", {
179
+ }), a.onTableRemove && /* @__PURE__ */ D("button", {
180
180
  className: "nodrag",
181
181
  onClick: (e) => {
182
182
  e.stopPropagation(), a.onTableRemove?.(t.name);
@@ -205,20 +205,20 @@ var M = u(function({ data: e }) {
205
205
  }),
206
206
  t.columns.map((e, n) => {
207
207
  let i = 32 + n * 22 + 22 / 2, a = s?.has(e.name) ?? !1, l = `${t.name}.${e.name}`, d = r.enabled && r.selected.has(l);
208
- return /* @__PURE__ */ T(c, { children: [
209
- /* @__PURE__ */ w(y, {
208
+ return /* @__PURE__ */ O(c, { children: [
209
+ /* @__PURE__ */ D(x, {
210
210
  id: `${e.name}__target`,
211
211
  type: "target",
212
- position: b.Left,
212
+ position: C.Left,
213
213
  style: u({ top: i })
214
214
  }),
215
- /* @__PURE__ */ w(y, {
215
+ /* @__PURE__ */ D(x, {
216
216
  id: `${e.name}__source`,
217
217
  type: "source",
218
- position: b.Right,
218
+ position: C.Right,
219
219
  style: u({ top: i })
220
220
  }),
221
- /* @__PURE__ */ w(j, {
221
+ /* @__PURE__ */ D(P, {
222
222
  tableName: t.name,
223
223
  column: e,
224
224
  highlighted: a,
@@ -231,8 +231,8 @@ var M = u(function({ data: e }) {
231
231
  })
232
232
  ]
233
233
  });
234
- }), N = u(function({ id: e, sourceX: t, sourceY: n, targetX: r, targetY: i, sourcePosition: a, targetPosition: o, data: s, selected: c }) {
235
- let [l, u, d] = x({
234
+ }), I = d(function({ id: e, sourceX: t, sourceY: n, targetX: r, targetY: i, sourcePosition: a, targetPosition: o, data: s, selected: c }) {
235
+ let [l, u, d] = w({
236
236
  sourceX: t,
237
237
  sourceY: n,
238
238
  sourcePosition: a,
@@ -240,7 +240,7 @@ var M = u(function({ data: e }) {
240
240
  targetY: i,
241
241
  targetPosition: o
242
242
  }), f = s ?? {}, p = !!c || !!f.hovered, m = f.color || "#3b82f6";
243
- return /* @__PURE__ */ T(C, { children: [/* @__PURE__ */ w(g, {
243
+ return /* @__PURE__ */ O(E, { children: [/* @__PURE__ */ D(y, {
244
244
  id: e,
245
245
  path: l,
246
246
  style: {
@@ -248,7 +248,7 @@ var M = u(function({ data: e }) {
248
248
  strokeWidth: p ? 2.5 : 2,
249
249
  strokeDasharray: "6 3"
250
250
  }
251
- }), /* @__PURE__ */ w(v, { children: /* @__PURE__ */ T("div", {
251
+ }), /* @__PURE__ */ D(b, { children: /* @__PURE__ */ O("div", {
252
252
  className: "nodrag nopan",
253
253
  style: {
254
254
  position: "absolute",
@@ -267,7 +267,7 @@ var M = u(function({ data: e }) {
267
267
  boxShadow: p ? "0 0 0 2px rgba(0,0,0,0.4)" : "0 1px 2px rgba(0,0,0,0.15)",
268
268
  userSelect: "none"
269
269
  },
270
- children: [/* @__PURE__ */ w("span", { children: f.type }), f.onDelete && /* @__PURE__ */ w("button", {
270
+ children: [/* @__PURE__ */ D("span", { children: f.type }), f.onDelete && /* @__PURE__ */ D("button", {
271
271
  type: "button",
272
272
  onClick: (e) => {
273
273
  e.stopPropagation(), f.onDelete?.();
@@ -293,8 +293,8 @@ var M = u(function({ data: e }) {
293
293
  children: "×"
294
294
  })]
295
295
  }) })] });
296
- }), P = { table: M }, re = { joinEdge: N };
297
- function ie(e) {
296
+ }), ie = { table: F }, L = { joinEdge: I };
297
+ function ae(e) {
298
298
  try {
299
299
  return {
300
300
  model: t(e),
@@ -308,61 +308,72 @@ function ie(e) {
308
308
  throw e;
309
309
  }
310
310
  }
311
- function F(e, t) {
311
+ function R(e, t) {
312
312
  return t ? `${t}__${e}` : `__default-${e}`;
313
313
  }
314
- var I = "join:";
315
- function L(e) {
314
+ var z = "join:";
315
+ function B(e) {
316
316
  if (!e || e.startsWith("__default-")) return;
317
317
  let t = /^(.+)__(?:source|target)$/.exec(e);
318
318
  return t ? t[1] : void 0;
319
319
  }
320
- function R(e) {
320
+ function oe(e) {
321
321
  return `${e.table}.${e.column}`;
322
322
  }
323
- var ae = {
323
+ var se = {
324
324
  INNER: "#3b82f6",
325
325
  LEFT: "#8b5cf6",
326
326
  RIGHT: "#a855f7",
327
327
  FULL: "#ec4899",
328
328
  CROSS: "#6b7280"
329
329
  };
330
- function z(e) {
331
- let { source: t, model: n, algorithm: r, direction: a, aspectRatio: o, positions: s, onPositionsChange: c, showColumnCheckboxes: l, selectedColumns: u, onColumnSelectionChange: f, enableManualJoins: g, joins: v, onJoinConnect: y, onJoinDelete: b, onTableRemove: x, onColumnClick: C, onTableClick: A, deleteKeyCode: j = "Delete", className: M, style: N, highlightReferencesOnHover: z = !0 } = e, { model: B, error: V } = m(() => n ? {
332
- model: n,
330
+ function ce({ apiRef: e, wrapperRef: t }) {
331
+ let n = T();
332
+ return h(e, () => ({
333
+ fitView: (e) => n.fitView(e),
334
+ getViewport: () => n.getViewport(),
335
+ setViewport: (e) => n.setViewport(e),
336
+ getNodesBounds: () => n.getNodesBounds(n.getNodes()),
337
+ getWrapperElement: () => t.current,
338
+ getViewportElement: () => t.current?.querySelector(".react-flow__viewport") ?? null
339
+ }), [n, t]), null;
340
+ }
341
+ var V = u(function(e, t) {
342
+ let { source: n, model: r, algorithm: a, direction: o, aspectRatio: s, positions: c, onPositionsChange: l, showColumnCheckboxes: u, selectedColumns: d, onColumnSelectionChange: p, enableManualJoins: h, joins: y, onJoinConnect: b, onJoinDelete: x, onTableRemove: C, onColumnClick: w, onTableClick: T, deleteKeyCode: E = "Delete", className: N, style: P, highlightReferencesOnHover: F = !0 } = e, { model: I, error: V } = g(() => r ? {
343
+ model: r,
333
344
  error: null
334
- } : t == null ? {
345
+ } : n == null ? {
335
346
  model: null,
336
347
  error: null
337
- } : ie(t), [t, n]), [H, U] = h(null), [W, G] = h(null);
338
- p(() => {
339
- if (!B) {
348
+ } : ae(n), [n, r]), [H, U] = _(null), [W, G] = _(null);
349
+ m(() => {
350
+ if (!I) {
340
351
  U(null);
341
352
  return;
342
353
  }
343
354
  let e = !1;
344
- return i(B, {
345
- algorithm: r,
346
- direction: a,
347
- aspectRatio: o
355
+ return i(I, {
356
+ algorithm: a,
357
+ direction: o,
358
+ aspectRatio: s
348
359
  }).then((t) => {
349
360
  e || U(t);
350
361
  }), () => {
351
362
  e = !0;
352
363
  };
353
364
  }, [
354
- B,
355
- r,
365
+ I,
356
366
  a,
357
- o
367
+ o,
368
+ s
358
369
  ]);
359
- let K = m(() => {
360
- if (!H || !B) return [];
361
- let e = new Map(B.tables.map((e) => [e.name, e])), t = [];
370
+ let K = g(() => {
371
+ if (!H || !I) return [];
372
+ let e = new Map(I.tables.map((e) => [e.name, e])), t = [];
362
373
  for (let n of H.nodes) {
363
374
  let r = e.get(n.id);
364
375
  if (!r) continue;
365
- let i = s?.[n.id], a = { table: r };
376
+ let i = c?.[n.id], a = { table: r };
366
377
  t.push({
367
378
  id: n.id,
368
379
  type: "table",
@@ -380,60 +391,60 @@ function z(e) {
380
391
  return t;
381
392
  }, [
382
393
  H,
383
- B,
384
- s
385
- ]), [q, J, oe] = S(K);
386
- p(() => {
387
- J(K);
388
- }, [K, J]);
389
- let se = d((e, t, n) => {
390
- if (!c) return;
391
- let r = { ...s ?? {} };
394
+ I,
395
+ c
396
+ ]), [le, q, ue] = re(K);
397
+ m(() => {
398
+ q(K);
399
+ }, [K, q]);
400
+ let de = f((e, t, n) => {
401
+ if (!l) return;
402
+ let r = { ...c ?? {} };
392
403
  for (let e of n) r[e.id] = {
393
404
  x: e.position.x,
394
405
  y: e.position.y
395
406
  };
396
- c(r);
397
- }, [s, c]), Y = m(() => {
398
- if (!(!W || !B)) return B.relations.find((e) => e.id === W);
399
- }, [W, B]), X = m(() => {
400
- if (!W || !v || !W.startsWith(I)) return;
407
+ l(r);
408
+ }, [c, l]), J = g(() => {
409
+ if (!(!W || !I)) return I.relations.find((e) => e.id === W);
410
+ }, [W, I]), Y = g(() => {
411
+ if (!W || !y || !W.startsWith(z)) return;
401
412
  let e = W.slice(5);
402
- return v.find((t) => t.id === e);
403
- }, [W, v]), ce = m(() => {
413
+ return y.find((t) => t.id === e);
414
+ }, [W, y]), fe = g(() => {
404
415
  let e = /* @__PURE__ */ new Map();
405
- if (!z) return e;
416
+ if (!F) return e;
406
417
  let t = (t, n) => {
407
418
  if (!n) return;
408
419
  let r = e.get(t) ?? /* @__PURE__ */ new Set();
409
420
  r.add(n), e.set(t, r);
410
421
  };
411
- return Y && (t(Y.from, Y.fromColumn), t(Y.to, Y.toColumn)), X && (t(X.source.table, X.source.column), t(X.target.table, X.target.column)), e;
422
+ return J && (t(J.from, J.fromColumn), t(J.to, J.toColumn)), Y && (t(Y.source.table, Y.source.column), t(Y.target.table, Y.target.column)), e;
412
423
  }, [
424
+ J,
413
425
  Y,
414
- X,
415
- z
416
- ]), Z = m(() => new Set((u ?? []).map(R)), [u]), Q = d((e, t, n) => {
417
- if (!f) return;
418
- let r = u ?? [];
426
+ F
427
+ ]), X = g(() => new Set((d ?? []).map(oe)), [d]), Z = f((e, t, n) => {
428
+ if (!p) return;
429
+ let r = d ?? [];
419
430
  if (n) {
420
431
  if (r.some((n) => n.table === e && n.column === t)) return;
421
- f([...r, {
432
+ p([...r, {
422
433
  table: e,
423
434
  column: t
424
435
  }]);
425
- } else f(r.filter((n) => !(n.table === e && n.column === t)));
426
- }, [u, f]), le = m(() => ({
427
- enabled: l ?? !1,
428
- selected: Z,
429
- onToggle: Q
436
+ } else p(r.filter((n) => !(n.table === e && n.column === t)));
437
+ }, [d, p]), pe = g(() => ({
438
+ enabled: u ?? !1,
439
+ selected: X,
440
+ onToggle: Z
430
441
  }), [
431
- l,
432
- Z,
433
- Q
434
- ]), ue = m(() => {
435
- if (!H || !B) return [];
436
- let e = new Set(B.tables.map((e) => e.name)), t = new Map(B.relations.map((e) => [e.id, e])), n = [];
442
+ u,
443
+ X,
444
+ Z
445
+ ]), me = g(() => {
446
+ if (!H || !I) return [];
447
+ let e = new Set(I.tables.map((e) => e.name)), t = new Map(I.relations.map((e) => [e.id, e])), n = [];
437
448
  for (let r of H.edges) {
438
449
  if (!e.has(r.source) || !e.has(r.target)) continue;
439
450
  let i = t.get(r.id), a = r.id === W;
@@ -441,8 +452,8 @@ function z(e) {
441
452
  id: r.id,
442
453
  source: r.source,
443
454
  target: r.target,
444
- sourceHandle: F("source", i?.fromColumn),
445
- targetHandle: F("target", i?.toColumn),
455
+ sourceHandle: R("source", i?.fromColumn),
456
+ targetHandle: R("target", i?.toColumn),
446
457
  type: "smoothstep",
447
458
  animated: a,
448
459
  deletable: !1,
@@ -464,20 +475,20 @@ function z(e) {
464
475
  });
465
476
  }
466
477
  let r = [];
467
- for (let t of v ?? []) {
478
+ for (let t of y ?? []) {
468
479
  if (!e.has(t.source.table) || !e.has(t.target.table)) continue;
469
- let n = `${I}${t.id}`, i = n === W, a = ae[t.type] ?? "#3b82f6", o = {
480
+ let n = `${z}${t.id}`, i = n === W, a = se[t.type] ?? "#3b82f6", o = {
470
481
  type: t.type,
471
482
  color: a,
472
483
  hovered: i,
473
- onDelete: b ? () => b(t.id) : void 0
484
+ onDelete: x ? () => x(t.id) : void 0
474
485
  };
475
486
  r.push({
476
487
  id: n,
477
488
  source: t.source.table,
478
489
  target: t.target.table,
479
- sourceHandle: F("source", t.source.column),
480
- targetHandle: F("target", t.target.column),
490
+ sourceHandle: R("source", t.source.column),
491
+ targetHandle: R("target", t.target.column),
481
492
  type: "joinEdge",
482
493
  deletable: !0,
483
494
  data: o
@@ -486,34 +497,35 @@ function z(e) {
486
497
  return [...n, ...r];
487
498
  }, [
488
499
  H,
489
- B,
490
- v,
500
+ I,
501
+ y,
491
502
  W,
492
- b
493
- ]), de = d((e) => {
494
- y && (!e.source || !e.target || y({
503
+ x
504
+ ]), he = f((e) => {
505
+ b && (!e.source || !e.target || b({
495
506
  table: e.source,
496
- column: L(e.sourceHandle)
507
+ column: B(e.sourceHandle)
497
508
  }, {
498
509
  table: e.target,
499
- column: L(e.targetHandle)
510
+ column: B(e.targetHandle)
500
511
  }));
501
- }, [y]), fe = d((e) => {
502
- if (b) for (let t of e) t.id.startsWith(I) && b(t.id.slice(5));
503
- }, [b]), $ = !!g, pe = m(() => ({
504
- onTableRemove: x,
505
- onColumnClick: C
506
- }), [x, C]);
507
- return /* @__PURE__ */ T("div", {
508
- className: M,
512
+ }, [b]), ge = f((e) => {
513
+ if (x) for (let t of e) t.id.startsWith(z) && x(t.id.slice(5));
514
+ }, [x]), Q = !!h, _e = g(() => ({
515
+ onTableRemove: C,
516
+ onColumnClick: w
517
+ }), [C, w]), $ = ee(null);
518
+ return /* @__PURE__ */ O("div", {
519
+ ref: $,
520
+ className: N,
509
521
  style: {
510
522
  width: "100%",
511
523
  height: "100%",
512
524
  position: "relative",
513
- ...N
525
+ ...P
514
526
  },
515
527
  children: [
516
- V && /* @__PURE__ */ w("div", {
528
+ V && /* @__PURE__ */ D("div", {
517
529
  style: {
518
530
  position: "absolute",
519
531
  top: 8,
@@ -531,7 +543,7 @@ function z(e) {
531
543
  },
532
544
  children: V.message
533
545
  }),
534
- !V && !H && /* @__PURE__ */ w("div", {
546
+ !V && !H && /* @__PURE__ */ D("div", {
535
547
  style: {
536
548
  position: "absolute",
537
549
  inset: 0,
@@ -543,39 +555,43 @@ function z(e) {
543
555
  },
544
556
  children: "Computing layout…"
545
557
  }),
546
- /* @__PURE__ */ w(E.Provider, {
547
- value: ce,
548
- children: /* @__PURE__ */ w(D.Provider, {
549
- value: le,
550
- children: /* @__PURE__ */ w(O.Provider, {
551
- value: $,
552
- children: /* @__PURE__ */ w(k.Provider, {
553
- value: pe,
554
- children: /* @__PURE__ */ T(ne, {
555
- nodes: q,
556
- edges: ue,
557
- nodeTypes: P,
558
- edgeTypes: re,
558
+ /* @__PURE__ */ D(k.Provider, {
559
+ value: fe,
560
+ children: /* @__PURE__ */ D(A.Provider, {
561
+ value: pe,
562
+ children: /* @__PURE__ */ D(j.Provider, {
563
+ value: Q,
564
+ children: /* @__PURE__ */ D(M.Provider, {
565
+ value: _e,
566
+ children: /* @__PURE__ */ O(ne, {
567
+ nodes: le,
568
+ edges: me,
569
+ nodeTypes: ie,
570
+ edgeTypes: L,
559
571
  fitView: !0,
560
- onNodesChange: oe,
561
- onNodeDragStop: se,
562
- onNodeClick: A ? (e, t) => A(t.id) : void 0,
572
+ onNodesChange: ue,
573
+ onNodeDragStop: de,
574
+ onNodeClick: T ? (e, t) => T(t.id) : void 0,
563
575
  onEdgeMouseEnter: (e, t) => G(t.id),
564
576
  onEdgeMouseLeave: () => G(null),
565
- onConnect: de,
566
- onEdgesDelete: fe,
577
+ onConnect: he,
578
+ onEdgesDelete: ge,
567
579
  nodesDraggable: !0,
568
- nodesConnectable: $,
580
+ nodesConnectable: Q,
569
581
  elementsSelectable: !0,
570
- deleteKeyCode: j,
582
+ deleteKeyCode: E,
571
583
  minZoom: .1,
572
584
  maxZoom: 4,
573
585
  children: [
574
- /* @__PURE__ */ w(ee, {}),
575
- /* @__PURE__ */ w(_, {}),
576
- /* @__PURE__ */ w(te, {
586
+ /* @__PURE__ */ D(v, {}),
587
+ /* @__PURE__ */ D(te, {}),
588
+ /* @__PURE__ */ D(S, {
577
589
  pannable: !0,
578
590
  zoomable: !0
591
+ }),
592
+ /* @__PURE__ */ D(ce, {
593
+ apiRef: t,
594
+ wrapperRef: $
579
595
  })
580
596
  ]
581
597
  })
@@ -585,8 +601,8 @@ function z(e) {
585
601
  })
586
602
  ]
587
603
  });
588
- }
604
+ });
589
605
  //#endregion
590
- export { s as HEADER_HEIGHT, z as MermaidER, o as MermaidERParseError, r as NODE_WIDTH, a as ROW_HEIGHT, n as VERTICAL_PADDING, e as estimateNodeHeight, i as layoutER, t as parseMermaidER };
606
+ export { s as HEADER_HEIGHT, V as MermaidER, o as MermaidERParseError, r as NODE_WIDTH, a as ROW_HEIGHT, n as VERTICAL_PADDING, e as estimateNodeHeight, i as layoutER, t as parseMermaidER };
591
607
 
592
608
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/components/TableNode.tsx","../src/components/JoinEdge.tsx","../src/components/MermaidER.tsx"],"sourcesContent":["import { Fragment, createContext, memo, useContext } from 'react';\nimport { Handle, Position } from '@xyflow/react';\nimport type { NodeProps } from '@xyflow/react';\nimport type { Column, Table } from '../core/model';\nimport { HEADER_HEIGHT, ROW_HEIGHT } from '../core/layout';\n\nexport interface TableNodeData extends Record<string, unknown> {\n table: Table;\n}\n\n/**\n * Map from table name -> set of highlighted column names.\n * Provided by MermaidER, consumed by TableNode for hover-driven highlighting\n * without forcing the whole node array to recompute on every hover change.\n */\nexport const HighlightContext = createContext<ReadonlyMap<string, ReadonlySet<string>>>(new Map());\n\nexport interface ColumnSelectionContextValue {\n enabled: boolean;\n /** \"table.column\" keys for fast lookup. */\n selected: ReadonlySet<string>;\n onToggle: (table: string, column: string, checked: boolean) => void;\n}\n\nexport const ColumnSelectionContext = createContext<ColumnSelectionContextValue>({\n enabled: false,\n selected: new Set(),\n onToggle: () => undefined,\n});\n\n/** When true, column handles become visible/connectable for manual JOIN drawing. */\nexport const ConnectModeContext = createContext<boolean>(false);\n\nexport interface TableActionsContextValue {\n /** When provided, a delete affordance is shown on the table header. */\n onTableRemove?: (table: string) => void;\n /** Per-column click handler. Provided via context (not node data) so its\n * identity can change without invalidating the React Flow node array. */\n onColumnClick?: (table: string, column: string) => void;\n}\n\nexport const TableActionsContext = createContext<TableActionsContextValue>({});\n\nconst KEY_STYLES: Record<'pk' | 'fk' | 'uk', { label: string; bg: string }> = {\n pk: { label: 'PK', bg: '#f59e0b' },\n fk: { label: 'FK', bg: '#3b82f6' },\n uk: { label: 'UK', bg: '#10b981' },\n};\n\nfunction ColumnRow({\n tableName,\n column,\n highlighted,\n onClick,\n selectionEnabled,\n selected,\n onSelectToggle,\n}: {\n tableName: string;\n column: Column;\n highlighted: boolean;\n onClick?: (table: string, column: string) => void;\n selectionEnabled: boolean;\n selected: boolean;\n onSelectToggle: (checked: boolean) => void;\n}) {\n const badges: Array<{ label: string; bg: string }> = [];\n if (column.keys.pk) badges.push(KEY_STYLES.pk);\n if (column.keys.fk) badges.push(KEY_STYLES.fk);\n if (column.keys.uk) badges.push(KEY_STYLES.uk);\n\n return (\n <div\n title={column.comment}\n onClick={onClick ? () => onClick(tableName, column.name) : undefined}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '0 10px',\n fontSize: 12,\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n borderTop: '1px solid #eee',\n height: ROW_HEIGHT,\n boxSizing: 'border-box',\n cursor: onClick ? 'pointer' : 'default',\n background: highlighted ? '#fef3c7' : selected ? '#eff6ff' : 'transparent',\n transition: 'background 0.15s',\n }}\n >\n {selectionEnabled && (\n <input\n type=\"checkbox\"\n checked={selected}\n onChange={(e) => onSelectToggle(e.target.checked)}\n onClick={(e) => e.stopPropagation()}\n style={{\n width: 13,\n height: 13,\n margin: 0,\n flexShrink: 0,\n cursor: 'pointer',\n }}\n />\n )}\n <span style={{ display: 'flex', gap: 2, flexShrink: 0, minWidth: 18 }}>\n {badges.map((b) => (\n <span\n key={b.label}\n style={{\n display: 'inline-block',\n padding: '0 4px',\n borderRadius: 3,\n background: b.bg,\n color: '#fff',\n fontSize: 9,\n fontWeight: 700,\n lineHeight: '14px',\n }}\n >\n {b.label}\n </span>\n ))}\n </span>\n <span\n style={{\n flexShrink: 0,\n fontWeight: column.keys.pk ? 600 : 400,\n color: '#1f2937',\n }}\n >\n {column.name}\n </span>\n {column.type && (\n <span\n style={{\n flex: 1,\n textAlign: 'right',\n color: '#9ca3af',\n fontStyle: 'italic',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {column.type}\n </span>\n )}\n </div>\n );\n}\n\nexport const TableNode = memo(function TableNode({ data }: NodeProps) {\n const { table } = data as TableNodeData;\n const highlightMap = useContext(HighlightContext);\n const selection = useContext(ColumnSelectionContext);\n const connectMode = useContext(ConnectModeContext);\n const tableActions = useContext(TableActionsContext);\n const onColumnClick = tableActions.onColumnClick;\n const highlightedCols = highlightMap.get(table.name);\n const headerBg = table.group ? '#1e40af' : '#374151';\n\n const handleStyle = (extra: { top?: number }): React.CSSProperties => ({\n ...extra,\n width: connectMode ? 9 : 6,\n height: connectMode ? 9 : 6,\n background: connectMode ? '#3b82f6' : 'transparent',\n border: connectMode ? '1.5px solid #fff' : 'none',\n boxShadow: connectMode ? '0 0 0 1px rgba(59,130,246,0.4)' : 'none',\n opacity: connectMode ? 0.85 : 0,\n pointerEvents: connectMode ? 'auto' : 'none',\n cursor: connectMode ? 'crosshair' : 'default',\n });\n\n return (\n <div\n style={{\n background: '#fff',\n border: '1px solid #c4c4c4',\n borderRadius: 6,\n boxShadow: '0 2px 6px rgba(0,0,0,0.08)',\n overflow: 'visible',\n width: '100%',\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n position: 'relative',\n }}\n >\n <Handle\n id=\"__default-target\"\n type=\"target\"\n position={Position.Left}\n style={handleStyle({ top: HEADER_HEIGHT / 2 })}\n />\n <Handle\n id=\"__default-source\"\n type=\"source\"\n position={Position.Right}\n style={handleStyle({ top: HEADER_HEIGHT / 2 })}\n />\n\n <div\n style={{\n padding: '6px 10px',\n background: headerBg,\n color: '#fff',\n fontWeight: 600,\n fontSize: 13,\n height: HEADER_HEIGHT,\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8,\n borderTopLeftRadius: 6,\n borderTopRightRadius: 6,\n }}\n >\n <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>\n {table.name}\n </span>\n <span style={{ display: 'flex', alignItems: 'center', gap: 4, flexShrink: 0 }}>\n {table.group && (\n <span\n style={{\n fontSize: 10,\n opacity: 0.75,\n fontWeight: 400,\n padding: '1px 5px',\n border: '1px solid rgba(255,255,255,0.3)',\n borderRadius: 3,\n }}\n >\n {table.group}\n </span>\n )}\n {tableActions.onTableRemove && (\n <button\n className=\"nodrag\"\n onClick={(e) => {\n e.stopPropagation();\n tableActions.onTableRemove?.(table.name);\n }}\n onMouseDown={(e) => e.stopPropagation()}\n title=\"Remove this table\"\n style={{\n background: 'rgba(255,255,255,0.18)',\n border: 'none',\n color: '#fff',\n width: 18,\n height: 18,\n borderRadius: 3,\n cursor: 'pointer',\n fontSize: 13,\n fontWeight: 700,\n padding: 0,\n lineHeight: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n ×\n </button>\n )}\n </span>\n </div>\n\n {table.columns.map((col, i) => {\n const handleY = HEADER_HEIGHT + i * ROW_HEIGHT + ROW_HEIGHT / 2;\n const highlighted = highlightedCols?.has(col.name) ?? false;\n const selectionKey = `${table.name}.${col.name}`;\n const selected = selection.enabled && selection.selected.has(selectionKey);\n return (\n <Fragment key={`${col.name}-${i}`}>\n <Handle\n id={`${col.name}__target`}\n type=\"target\"\n position={Position.Left}\n style={handleStyle({ top: handleY })}\n />\n <Handle\n id={`${col.name}__source`}\n type=\"source\"\n position={Position.Right}\n style={handleStyle({ top: handleY })}\n />\n <ColumnRow\n tableName={table.name}\n column={col}\n highlighted={highlighted}\n onClick={onColumnClick}\n selectionEnabled={selection.enabled}\n selected={selected}\n onSelectToggle={(checked) =>\n selection.onToggle(table.name, col.name, checked)\n }\n />\n </Fragment>\n );\n })}\n </div>\n );\n});\n","import { memo } from 'react';\nimport {\n BaseEdge,\n EdgeLabelRenderer,\n getSmoothStepPath,\n type EdgeProps,\n} from '@xyflow/react';\nimport type { JoinType } from '../core/model';\n\nexport interface JoinEdgeData extends Record<string, unknown> {\n type: JoinType;\n color: string;\n hovered?: boolean;\n onDelete?: () => void;\n}\n\nexport const JoinEdge = memo(function JoinEdge({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n data,\n selected,\n}: EdgeProps) {\n const [edgePath, labelX, labelY] = getSmoothStepPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n });\n\n const d = (data ?? {}) as JoinEdgeData;\n const emphasized = !!selected || !!d.hovered;\n const color = d.color || '#3b82f6';\n\n return (\n <>\n <BaseEdge\n id={id}\n path={edgePath}\n style={{\n stroke: color,\n strokeWidth: emphasized ? 2.5 : 2,\n strokeDasharray: '6 3',\n }}\n />\n <EdgeLabelRenderer>\n <div\n className=\"nodrag nopan\"\n style={{\n position: 'absolute',\n transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,\n background: color,\n color: '#fff',\n fontSize: 10,\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n fontWeight: 700,\n borderRadius: 3,\n padding: '2px 4px 2px 6px',\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n pointerEvents: 'all',\n boxShadow: emphasized\n ? '0 0 0 2px rgba(0,0,0,0.4)'\n : '0 1px 2px rgba(0,0,0,0.15)',\n userSelect: 'none',\n }}\n >\n <span>{d.type}</span>\n {d.onDelete && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n d.onDelete?.();\n }}\n onMouseDown={(e) => e.stopPropagation()}\n title=\"Remove this JOIN\"\n style={{\n background: 'rgba(255,255,255,0.25)',\n border: 'none',\n color: '#fff',\n width: 14,\n height: 14,\n borderRadius: 2,\n cursor: 'pointer',\n fontSize: 11,\n fontWeight: 700,\n padding: 0,\n lineHeight: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n ×\n </button>\n )}\n </div>\n </EdgeLabelRenderer>\n </>\n );\n});\n","import {\n useCallback,\n useEffect,\n useMemo,\n useState,\n type CSSProperties,\n} from 'react';\nimport {\n ReactFlow,\n Background,\n Controls,\n MiniMap,\n useNodesState,\n type Connection,\n type Edge,\n type EdgeTypes,\n type Node,\n type NodeTypes,\n} from '@xyflow/react';\n// React Flow's CSS is intentionally NOT imported here. Consumers must add it\n// once at their app entry point: `import '@xyflow/react/dist/style.css';`.\n// Importing it from this file would cause double-injection in apps that\n// already use React Flow, and inflate this library's bundled assets.\nimport { parseMermaidER, MermaidERParseError } from '../core/parser';\nimport {\n layoutER,\n type LayoutAlgorithm,\n type LayoutDirection,\n type LayoutResult,\n} from '../core/layout';\nimport type {\n ColumnRef,\n ERModel,\n Join,\n PartialColumnRef,\n Relation,\n} from '../core/model';\nimport {\n ColumnSelectionContext,\n ConnectModeContext,\n HighlightContext,\n TableActionsContext,\n TableNode,\n type ColumnSelectionContextValue,\n type TableActionsContextValue,\n type TableNodeData,\n} from './TableNode';\nimport { JoinEdge, type JoinEdgeData } from './JoinEdge';\n\nconst nodeTypes: NodeTypes = {\n table: TableNode,\n};\n\nconst edgeTypes: EdgeTypes = {\n joinEdge: JoinEdge,\n};\n\nexport interface NodePosition {\n x: number;\n y: number;\n}\n\nexport type NodePositions = Record<string, NodePosition>;\n\nexport interface MermaidERProps {\n /** Mermaid ER source. Mutually exclusive with `model`. */\n source?: string;\n /** Pre-built ER model. Takes precedence over `source`. */\n model?: ERModel;\n layout?: 'elk';\n algorithm?: LayoutAlgorithm;\n direction?: LayoutDirection;\n aspectRatio?: number;\n positions?: NodePositions;\n onPositionsChange?: (positions: NodePositions) => void;\n showColumnCheckboxes?: boolean;\n selectedColumns?: ColumnRef[];\n onColumnSelectionChange?: (selectedColumns: ColumnRef[]) => void;\n /** Enable column-to-column / card-to-card drag for manual JOINs. */\n enableManualJoins?: boolean;\n /** Existing manual joins to render alongside FK relations. */\n joins?: Join[];\n /**\n * Fired when the user finishes a connect drag. The consumer typically opens\n * a dialog to ask for join type, then appends a complete `Join` to its state.\n * `column` may be undefined when the drag landed on a default (table-center) handle.\n */\n onJoinConnect?: (source: PartialColumnRef, target: PartialColumnRef) => void;\n /** Fired when the user removes a manual join via Delete or the trash icon. */\n onJoinDelete?: (joinId: string) => void;\n /** When provided, a small × appears on each table header to remove it from the canvas. */\n onTableRemove?: (table: string) => void;\n highlightReferencesOnHover?: boolean;\n onColumnClick?: (table: string, column: string) => void;\n onTableClick?: (table: string) => void;\n /** Override the default delete-key code(s). Default is 'Delete' (Backspace ignored to prevent accidents). */\n deleteKeyCode?: string | string[] | null;\n className?: string;\n style?: CSSProperties;\n}\n\ninterface ParseState {\n model: ERModel | null;\n error: MermaidERParseError | null;\n}\n\nfunction safeParse(source: string): ParseState {\n try {\n return { model: parseMermaidER(source), error: null };\n } catch (e) {\n if (e instanceof MermaidERParseError) {\n return { model: null, error: e };\n }\n throw e;\n }\n}\n\nfunction handleIdFor(side: 'source' | 'target', column: string | undefined): string {\n return column ? `${column}__${side}` : `__default-${side}`;\n}\n\nconst JOIN_EDGE_PREFIX = 'join:';\n\nfunction parseHandleColumn(handleId: string | null | undefined): string | undefined {\n if (!handleId) return undefined;\n if (handleId.startsWith('__default-')) return undefined;\n const m = /^(.+)__(?:source|target)$/.exec(handleId);\n return m ? m[1] : undefined;\n}\n\nfunction refKey(ref: ColumnRef): string {\n return `${ref.table}.${ref.column}`;\n}\n\nconst JOIN_TYPE_COLOR: Record<Join['type'], string> = {\n INNER: '#3b82f6',\n LEFT: '#8b5cf6',\n RIGHT: '#a855f7',\n FULL: '#ec4899',\n CROSS: '#6b7280',\n};\n\nexport function MermaidER(props: MermaidERProps) {\n const {\n source,\n model: modelProp,\n algorithm,\n direction,\n aspectRatio,\n positions,\n onPositionsChange,\n showColumnCheckboxes,\n selectedColumns,\n onColumnSelectionChange,\n enableManualJoins,\n joins,\n onJoinConnect,\n onJoinDelete,\n onTableRemove,\n onColumnClick,\n onTableClick,\n deleteKeyCode = 'Delete',\n className,\n style,\n highlightReferencesOnHover = true,\n } = props;\n\n const { model, error } = useMemo<ParseState>(() => {\n if (modelProp) return { model: modelProp, error: null };\n if (source != null) return safeParse(source);\n return { model: null, error: null };\n }, [source, modelProp]);\n\n const [layout, setLayout] = useState<LayoutResult | null>(null);\n const [hoveredEdgeId, setHoveredEdgeId] = useState<string | null>(null);\n\n useEffect(() => {\n if (!model) {\n setLayout(null);\n return;\n }\n let cancelled = false;\n layoutER(model, { algorithm, direction, aspectRatio }).then((result) => {\n if (!cancelled) setLayout(result);\n });\n return () => {\n cancelled = true;\n };\n }, [model, algorithm, direction, aspectRatio]);\n\n const baseNodes = useMemo<Node[]>(() => {\n if (!layout || !model) return [];\n const tableMap = new Map(model.tables.map((t) => [t.name, t]));\n const result: Node[] = [];\n for (const n of layout.nodes) {\n const table = tableMap.get(n.id);\n // Skip stale nodes: a model update can arrive before the new layout\n // finishes; in that gap, layout may still reference a removed table.\n if (!table) continue;\n const override = positions?.[n.id];\n const data: TableNodeData = { table };\n result.push({\n id: n.id,\n type: 'table',\n position: override ?? { x: n.x, y: n.y },\n data,\n width: n.width,\n height: n.height,\n draggable: true,\n deletable: false,\n });\n }\n return result;\n // `onColumnClick` is intentionally NOT a dep — it flows via TableActionsContext\n // so a non-stable callback identity from the parent doesn't reset React Flow\n // node state (which would clobber an in-flight drag).\n }, [layout, model, positions]);\n\n const [nodes, setNodes, onNodesChange] = useNodesState<Node>(baseNodes);\n\n useEffect(() => {\n setNodes(baseNodes);\n }, [baseNodes, setNodes]);\n\n const handleNodeDragStop = useCallback(\n (_: unknown, _primary: Node, dragged: Node[]) => {\n if (!onPositionsChange) return;\n const next: NodePositions = { ...(positions ?? {}) };\n for (const n of dragged) {\n next[n.id] = { x: n.position.x, y: n.position.y };\n }\n onPositionsChange(next);\n },\n [positions, onPositionsChange],\n );\n\n const hoveredRelation = useMemo<Relation | undefined>(() => {\n if (!hoveredEdgeId || !model) return undefined;\n return model.relations.find((r) => r.id === hoveredEdgeId);\n }, [hoveredEdgeId, model]);\n\n const hoveredJoin = useMemo<Join | undefined>(() => {\n if (!hoveredEdgeId || !joins) return undefined;\n if (!hoveredEdgeId.startsWith(JOIN_EDGE_PREFIX)) return undefined;\n const id = hoveredEdgeId.slice(JOIN_EDGE_PREFIX.length);\n return joins.find((j) => j.id === id);\n }, [hoveredEdgeId, joins]);\n\n const highlightMap = useMemo<ReadonlyMap<string, ReadonlySet<string>>>(() => {\n const map = new Map<string, Set<string>>();\n if (!highlightReferencesOnHover) return map;\n\n const add = (table: string, column: string | undefined) => {\n if (!column) return;\n const set = map.get(table) ?? new Set();\n set.add(column);\n map.set(table, set);\n };\n\n if (hoveredRelation) {\n add(hoveredRelation.from, hoveredRelation.fromColumn);\n add(hoveredRelation.to, hoveredRelation.toColumn);\n }\n if (hoveredJoin) {\n add(hoveredJoin.source.table, hoveredJoin.source.column);\n add(hoveredJoin.target.table, hoveredJoin.target.column);\n }\n return map;\n }, [hoveredRelation, hoveredJoin, highlightReferencesOnHover]);\n\n const selectionSet = useMemo<ReadonlySet<string>>(() => {\n return new Set((selectedColumns ?? []).map(refKey));\n }, [selectedColumns]);\n\n const handleColumnSelectToggle = useCallback(\n (table: string, column: string, checked: boolean) => {\n if (!onColumnSelectionChange) return;\n const list = selectedColumns ?? [];\n if (checked) {\n if (list.some((r) => r.table === table && r.column === column)) return;\n onColumnSelectionChange([...list, { table, column }]);\n } else {\n onColumnSelectionChange(\n list.filter((r) => !(r.table === table && r.column === column)),\n );\n }\n },\n [selectedColumns, onColumnSelectionChange],\n );\n\n const selectionContext = useMemo<ColumnSelectionContextValue>(\n () => ({\n enabled: showColumnCheckboxes ?? false,\n selected: selectionSet,\n onToggle: handleColumnSelectToggle,\n }),\n [showColumnCheckboxes, selectionSet, handleColumnSelectToggle],\n );\n\n const edges = useMemo<Edge[]>(() => {\n if (!layout || !model) return [];\n const tableSet = new Set(model.tables.map((t) => t.name));\n const relMap = new Map(model.relations.map((r) => [r.id, r]));\n\n const fkEdges: Edge[] = [];\n for (const e of layout.edges) {\n // Skip stale edges referencing tables removed since the layout was computed.\n if (!tableSet.has(e.source) || !tableSet.has(e.target)) continue;\n const rel = relMap.get(e.id);\n const isHovered = e.id === hoveredEdgeId;\n fkEdges.push({\n id: e.id,\n source: e.source,\n target: e.target,\n sourceHandle: handleIdFor('source', rel?.fromColumn),\n targetHandle: handleIdFor('target', rel?.toColumn),\n type: 'smoothstep',\n animated: isHovered,\n deletable: false,\n style: {\n stroke: isHovered ? '#f59e0b' : '#9ca3af',\n strokeWidth: isHovered ? 2 : 1.5,\n },\n label: rel?.label,\n labelStyle: { fontSize: 10, fill: '#6b7280' },\n labelBgStyle: { fill: '#fff', fillOpacity: 0.85 },\n labelBgPadding: [4, 2] as [number, number],\n labelBgBorderRadius: 3,\n });\n }\n\n const joinEdges: Edge[] = [];\n for (const j of joins ?? []) {\n if (!tableSet.has(j.source.table) || !tableSet.has(j.target.table)) continue;\n const edgeId = `${JOIN_EDGE_PREFIX}${j.id}`;\n const isHovered = edgeId === hoveredEdgeId;\n const color = JOIN_TYPE_COLOR[j.type] ?? '#3b82f6';\n const data: JoinEdgeData = {\n type: j.type,\n color,\n hovered: isHovered,\n onDelete: onJoinDelete ? () => onJoinDelete(j.id) : undefined,\n };\n joinEdges.push({\n id: edgeId,\n source: j.source.table,\n target: j.target.table,\n sourceHandle: handleIdFor('source', j.source.column),\n targetHandle: handleIdFor('target', j.target.column),\n type: 'joinEdge',\n deletable: true,\n data: data as unknown as Record<string, unknown>,\n });\n }\n\n return [...fkEdges, ...joinEdges];\n }, [layout, model, joins, hoveredEdgeId, onJoinDelete]);\n\n const handleConnect = useCallback(\n (conn: Connection) => {\n if (!onJoinConnect) return;\n if (!conn.source || !conn.target) return;\n onJoinConnect(\n { table: conn.source, column: parseHandleColumn(conn.sourceHandle) },\n { table: conn.target, column: parseHandleColumn(conn.targetHandle) },\n );\n },\n [onJoinConnect],\n );\n\n const handleEdgesDelete = useCallback(\n (deleted: Edge[]) => {\n if (!onJoinDelete) return;\n for (const e of deleted) {\n if (e.id.startsWith(JOIN_EDGE_PREFIX)) {\n onJoinDelete(e.id.slice(JOIN_EDGE_PREFIX.length));\n }\n }\n },\n [onJoinDelete],\n );\n\n const connectModeOn = !!enableManualJoins;\n const tableActions = useMemo<TableActionsContextValue>(\n () => ({ onTableRemove, onColumnClick }),\n [onTableRemove, onColumnClick],\n );\n\n return (\n <div\n className={className}\n style={{ width: '100%', height: '100%', position: 'relative', ...style }}\n >\n {error && (\n <div\n style={{\n position: 'absolute',\n top: 8,\n left: 8,\n right: 8,\n zIndex: 10,\n padding: '8px 12px',\n background: '#fef2f2',\n border: '1px solid #fecaca',\n borderRadius: 6,\n color: '#991b1b',\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n fontSize: 12,\n whiteSpace: 'pre-wrap',\n }}\n >\n {error.message}\n </div>\n )}\n {!error && !layout && (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#9ca3af',\n fontSize: 13,\n }}\n >\n Computing layout…\n </div>\n )}\n <HighlightContext.Provider value={highlightMap}>\n <ColumnSelectionContext.Provider value={selectionContext}>\n <ConnectModeContext.Provider value={connectModeOn}>\n <TableActionsContext.Provider value={tableActions}>\n <ReactFlow\n nodes={nodes}\n edges={edges}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n fitView\n onNodesChange={onNodesChange}\n onNodeDragStop={handleNodeDragStop}\n onNodeClick={\n onTableClick ? (_, node) => onTableClick(node.id) : undefined\n }\n onEdgeMouseEnter={(_, edge) => setHoveredEdgeId(edge.id)}\n onEdgeMouseLeave={() => setHoveredEdgeId(null)}\n onConnect={handleConnect}\n onEdgesDelete={handleEdgesDelete}\n nodesDraggable\n nodesConnectable={connectModeOn}\n elementsSelectable\n deleteKeyCode={deleteKeyCode}\n minZoom={0.1}\n maxZoom={4}\n >\n <Background />\n <Controls />\n <MiniMap pannable zoomable />\n </ReactFlow>\n </TableActionsContext.Provider>\n </ConnectModeContext.Provider>\n </ColumnSelectionContext.Provider>\n </HighlightContext.Provider>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/components/TableNode.tsx","../src/components/JoinEdge.tsx","../src/components/MermaidER.tsx"],"sourcesContent":["import { Fragment, createContext, memo, useContext } from 'react';\nimport { Handle, Position } from '@xyflow/react';\nimport type { NodeProps } from '@xyflow/react';\nimport type { Column, Table } from '../core/model';\nimport { HEADER_HEIGHT, ROW_HEIGHT } from '../core/layout';\n\nexport interface TableNodeData extends Record<string, unknown> {\n table: Table;\n}\n\n/**\n * Map from table name -> set of highlighted column names.\n * Provided by MermaidER, consumed by TableNode for hover-driven highlighting\n * without forcing the whole node array to recompute on every hover change.\n */\nexport const HighlightContext = createContext<ReadonlyMap<string, ReadonlySet<string>>>(new Map());\n\nexport interface ColumnSelectionContextValue {\n enabled: boolean;\n /** \"table.column\" keys for fast lookup. */\n selected: ReadonlySet<string>;\n onToggle: (table: string, column: string, checked: boolean) => void;\n}\n\nexport const ColumnSelectionContext = createContext<ColumnSelectionContextValue>({\n enabled: false,\n selected: new Set(),\n onToggle: () => undefined,\n});\n\n/** When true, column handles become visible/connectable for manual JOIN drawing. */\nexport const ConnectModeContext = createContext<boolean>(false);\n\nexport interface TableActionsContextValue {\n /** When provided, a delete affordance is shown on the table header. */\n onTableRemove?: (table: string) => void;\n /** Per-column click handler. Provided via context (not node data) so its\n * identity can change without invalidating the React Flow node array. */\n onColumnClick?: (table: string, column: string) => void;\n}\n\nexport const TableActionsContext = createContext<TableActionsContextValue>({});\n\nconst KEY_STYLES: Record<'pk' | 'fk' | 'uk', { label: string; bg: string }> = {\n pk: { label: 'PK', bg: '#f59e0b' },\n fk: { label: 'FK', bg: '#3b82f6' },\n uk: { label: 'UK', bg: '#10b981' },\n};\n\nfunction ColumnRow({\n tableName,\n column,\n highlighted,\n onClick,\n selectionEnabled,\n selected,\n onSelectToggle,\n}: {\n tableName: string;\n column: Column;\n highlighted: boolean;\n onClick?: (table: string, column: string) => void;\n selectionEnabled: boolean;\n selected: boolean;\n onSelectToggle: (checked: boolean) => void;\n}) {\n const badges: Array<{ label: string; bg: string }> = [];\n if (column.keys.pk) badges.push(KEY_STYLES.pk);\n if (column.keys.fk) badges.push(KEY_STYLES.fk);\n if (column.keys.uk) badges.push(KEY_STYLES.uk);\n\n return (\n <div\n title={column.comment}\n onClick={onClick ? () => onClick(tableName, column.name) : undefined}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n padding: '0 10px',\n fontSize: 12,\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n borderTop: '1px solid #eee',\n height: ROW_HEIGHT,\n boxSizing: 'border-box',\n cursor: onClick ? 'pointer' : 'default',\n background: highlighted ? '#fef3c7' : selected ? '#eff6ff' : 'transparent',\n transition: 'background 0.15s',\n }}\n >\n {selectionEnabled && (\n <input\n type=\"checkbox\"\n checked={selected}\n onChange={(e) => onSelectToggle(e.target.checked)}\n onClick={(e) => e.stopPropagation()}\n style={{\n width: 13,\n height: 13,\n margin: 0,\n flexShrink: 0,\n cursor: 'pointer',\n }}\n />\n )}\n <span style={{ display: 'flex', gap: 2, flexShrink: 0, minWidth: 18 }}>\n {badges.map((b) => (\n <span\n key={b.label}\n style={{\n display: 'inline-block',\n padding: '0 4px',\n borderRadius: 3,\n background: b.bg,\n color: '#fff',\n fontSize: 9,\n fontWeight: 700,\n lineHeight: '14px',\n }}\n >\n {b.label}\n </span>\n ))}\n </span>\n <span\n style={{\n flexShrink: 0,\n fontWeight: column.keys.pk ? 600 : 400,\n color: '#1f2937',\n }}\n >\n {column.name}\n </span>\n {column.type && (\n <span\n style={{\n flex: 1,\n textAlign: 'right',\n color: '#9ca3af',\n fontStyle: 'italic',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {column.type}\n </span>\n )}\n </div>\n );\n}\n\nexport const TableNode = memo(function TableNode({ data }: NodeProps) {\n const { table } = data as TableNodeData;\n const highlightMap = useContext(HighlightContext);\n const selection = useContext(ColumnSelectionContext);\n const connectMode = useContext(ConnectModeContext);\n const tableActions = useContext(TableActionsContext);\n const onColumnClick = tableActions.onColumnClick;\n const highlightedCols = highlightMap.get(table.name);\n const headerBg = table.group ? '#1e40af' : '#374151';\n\n const handleStyle = (extra: { top?: number }): React.CSSProperties => ({\n ...extra,\n width: connectMode ? 9 : 6,\n height: connectMode ? 9 : 6,\n background: connectMode ? '#3b82f6' : 'transparent',\n border: connectMode ? '1.5px solid #fff' : 'none',\n boxShadow: connectMode ? '0 0 0 1px rgba(59,130,246,0.4)' : 'none',\n opacity: connectMode ? 0.85 : 0,\n pointerEvents: connectMode ? 'auto' : 'none',\n cursor: connectMode ? 'crosshair' : 'default',\n });\n\n return (\n <div\n style={{\n background: '#fff',\n border: '1px solid #c4c4c4',\n borderRadius: 6,\n boxShadow: '0 2px 6px rgba(0,0,0,0.08)',\n overflow: 'visible',\n width: '100%',\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n position: 'relative',\n }}\n >\n <Handle\n id=\"__default-target\"\n type=\"target\"\n position={Position.Left}\n style={handleStyle({ top: HEADER_HEIGHT / 2 })}\n />\n <Handle\n id=\"__default-source\"\n type=\"source\"\n position={Position.Right}\n style={handleStyle({ top: HEADER_HEIGHT / 2 })}\n />\n\n <div\n style={{\n padding: '6px 10px',\n background: headerBg,\n color: '#fff',\n fontWeight: 600,\n fontSize: 13,\n height: HEADER_HEIGHT,\n boxSizing: 'border-box',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8,\n borderTopLeftRadius: 6,\n borderTopRightRadius: 6,\n }}\n >\n <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>\n {table.name}\n </span>\n <span style={{ display: 'flex', alignItems: 'center', gap: 4, flexShrink: 0 }}>\n {table.group && (\n <span\n style={{\n fontSize: 10,\n opacity: 0.75,\n fontWeight: 400,\n padding: '1px 5px',\n border: '1px solid rgba(255,255,255,0.3)',\n borderRadius: 3,\n }}\n >\n {table.group}\n </span>\n )}\n {tableActions.onTableRemove && (\n <button\n className=\"nodrag\"\n onClick={(e) => {\n e.stopPropagation();\n tableActions.onTableRemove?.(table.name);\n }}\n onMouseDown={(e) => e.stopPropagation()}\n title=\"Remove this table\"\n style={{\n background: 'rgba(255,255,255,0.18)',\n border: 'none',\n color: '#fff',\n width: 18,\n height: 18,\n borderRadius: 3,\n cursor: 'pointer',\n fontSize: 13,\n fontWeight: 700,\n padding: 0,\n lineHeight: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n ×\n </button>\n )}\n </span>\n </div>\n\n {table.columns.map((col, i) => {\n const handleY = HEADER_HEIGHT + i * ROW_HEIGHT + ROW_HEIGHT / 2;\n const highlighted = highlightedCols?.has(col.name) ?? false;\n const selectionKey = `${table.name}.${col.name}`;\n const selected = selection.enabled && selection.selected.has(selectionKey);\n return (\n <Fragment key={`${col.name}-${i}`}>\n <Handle\n id={`${col.name}__target`}\n type=\"target\"\n position={Position.Left}\n style={handleStyle({ top: handleY })}\n />\n <Handle\n id={`${col.name}__source`}\n type=\"source\"\n position={Position.Right}\n style={handleStyle({ top: handleY })}\n />\n <ColumnRow\n tableName={table.name}\n column={col}\n highlighted={highlighted}\n onClick={onColumnClick}\n selectionEnabled={selection.enabled}\n selected={selected}\n onSelectToggle={(checked) =>\n selection.onToggle(table.name, col.name, checked)\n }\n />\n </Fragment>\n );\n })}\n </div>\n );\n});\n","import { memo } from 'react';\nimport {\n BaseEdge,\n EdgeLabelRenderer,\n getSmoothStepPath,\n type EdgeProps,\n} from '@xyflow/react';\nimport type { JoinType } from '../core/model';\n\nexport interface JoinEdgeData extends Record<string, unknown> {\n type: JoinType;\n color: string;\n hovered?: boolean;\n onDelete?: () => void;\n}\n\nexport const JoinEdge = memo(function JoinEdge({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n data,\n selected,\n}: EdgeProps) {\n const [edgePath, labelX, labelY] = getSmoothStepPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n });\n\n const d = (data ?? {}) as JoinEdgeData;\n const emphasized = !!selected || !!d.hovered;\n const color = d.color || '#3b82f6';\n\n return (\n <>\n <BaseEdge\n id={id}\n path={edgePath}\n style={{\n stroke: color,\n strokeWidth: emphasized ? 2.5 : 2,\n strokeDasharray: '6 3',\n }}\n />\n <EdgeLabelRenderer>\n <div\n className=\"nodrag nopan\"\n style={{\n position: 'absolute',\n transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,\n background: color,\n color: '#fff',\n fontSize: 10,\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n fontWeight: 700,\n borderRadius: 3,\n padding: '2px 4px 2px 6px',\n display: 'flex',\n alignItems: 'center',\n gap: 4,\n pointerEvents: 'all',\n boxShadow: emphasized\n ? '0 0 0 2px rgba(0,0,0,0.4)'\n : '0 1px 2px rgba(0,0,0,0.15)',\n userSelect: 'none',\n }}\n >\n <span>{d.type}</span>\n {d.onDelete && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n d.onDelete?.();\n }}\n onMouseDown={(e) => e.stopPropagation()}\n title=\"Remove this JOIN\"\n style={{\n background: 'rgba(255,255,255,0.25)',\n border: 'none',\n color: '#fff',\n width: 14,\n height: 14,\n borderRadius: 2,\n cursor: 'pointer',\n fontSize: 11,\n fontWeight: 700,\n padding: 0,\n lineHeight: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n ×\n </button>\n )}\n </div>\n </EdgeLabelRenderer>\n </>\n );\n});\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type Ref,\n} from 'react';\nimport {\n ReactFlow,\n Background,\n Controls,\n MiniMap,\n useNodesState,\n useReactFlow,\n type Connection,\n type Edge,\n type EdgeTypes,\n type FitViewOptions,\n type Node,\n type NodeTypes,\n type Rect,\n type Viewport,\n} from '@xyflow/react';\n// React Flow's CSS is intentionally NOT imported here. Consumers must add it\n// once at their app entry point: `import '@xyflow/react/dist/style.css';`.\n// Importing it from this file would cause double-injection in apps that\n// already use React Flow, and inflate this library's bundled assets.\nimport { parseMermaidER, MermaidERParseError } from '../core/parser';\nimport {\n layoutER,\n type LayoutAlgorithm,\n type LayoutDirection,\n type LayoutResult,\n} from '../core/layout';\nimport type {\n ColumnRef,\n ERModel,\n Join,\n PartialColumnRef,\n Relation,\n} from '../core/model';\nimport {\n ColumnSelectionContext,\n ConnectModeContext,\n HighlightContext,\n TableActionsContext,\n TableNode,\n type ColumnSelectionContextValue,\n type TableActionsContextValue,\n type TableNodeData,\n} from './TableNode';\nimport { JoinEdge, type JoinEdgeData } from './JoinEdge';\n\nconst nodeTypes: NodeTypes = {\n table: TableNode,\n};\n\nconst edgeTypes: EdgeTypes = {\n joinEdge: JoinEdge,\n};\n\nexport interface NodePosition {\n x: number;\n y: number;\n}\n\nexport type NodePositions = Record<string, NodePosition>;\n\n/**\n * Imperative API exposed via `ref`. Designed to give consumers the primitives\n * needed for image export (fit → snapshot → restore) without baking any\n * specific export library into this package. Methods that change the viewport\n * return Promise<boolean> matching React Flow's underlying API.\n */\nexport interface MermaidERHandle {\n /** Fit all nodes into the visible viewport. */\n fitView: (options?: FitViewOptions) => Promise<boolean>;\n /** Read the current viewport (x, y, zoom) — capture before export to restore later. */\n getViewport: () => Viewport;\n /** Restore (or set) viewport. */\n setViewport: (viewport: Viewport) => Promise<boolean>;\n /** Bounding box of all current nodes. Useful for sizing offscreen canvases. */\n getNodesBounds: () => Rect;\n /** Outer wrapper element (the div that receives `className` / `style`). */\n getWrapperElement: () => HTMLDivElement | null;\n /**\n * The internal `.react-flow__viewport` element. Typical snapshot target\n * when you want to exclude `<Controls>` and `<MiniMap>` from the image.\n */\n getViewportElement: () => HTMLElement | null;\n}\n\nexport interface MermaidERProps {\n /** Mermaid ER source. Mutually exclusive with `model`. */\n source?: string;\n /** Pre-built ER model. Takes precedence over `source`. */\n model?: ERModel;\n layout?: 'elk';\n algorithm?: LayoutAlgorithm;\n direction?: LayoutDirection;\n aspectRatio?: number;\n positions?: NodePositions;\n onPositionsChange?: (positions: NodePositions) => void;\n showColumnCheckboxes?: boolean;\n selectedColumns?: ColumnRef[];\n onColumnSelectionChange?: (selectedColumns: ColumnRef[]) => void;\n /** Enable column-to-column / card-to-card drag for manual JOINs. */\n enableManualJoins?: boolean;\n /** Existing manual joins to render alongside FK relations. */\n joins?: Join[];\n /**\n * Fired when the user finishes a connect drag. The consumer typically opens\n * a dialog to ask for join type, then appends a complete `Join` to its state.\n * `column` may be undefined when the drag landed on a default (table-center) handle.\n */\n onJoinConnect?: (source: PartialColumnRef, target: PartialColumnRef) => void;\n /** Fired when the user removes a manual join via Delete or the trash icon. */\n onJoinDelete?: (joinId: string) => void;\n /** When provided, a small × appears on each table header to remove it from the canvas. */\n onTableRemove?: (table: string) => void;\n highlightReferencesOnHover?: boolean;\n onColumnClick?: (table: string, column: string) => void;\n onTableClick?: (table: string) => void;\n /** Override the default delete-key code(s). Default is 'Delete' (Backspace ignored to prevent accidents). */\n deleteKeyCode?: string | string[] | null;\n className?: string;\n style?: CSSProperties;\n}\n\ninterface ParseState {\n model: ERModel | null;\n error: MermaidERParseError | null;\n}\n\nfunction safeParse(source: string): ParseState {\n try {\n return { model: parseMermaidER(source), error: null };\n } catch (e) {\n if (e instanceof MermaidERParseError) {\n return { model: null, error: e };\n }\n throw e;\n }\n}\n\nfunction handleIdFor(side: 'source' | 'target', column: string | undefined): string {\n return column ? `${column}__${side}` : `__default-${side}`;\n}\n\nconst JOIN_EDGE_PREFIX = 'join:';\n\nfunction parseHandleColumn(handleId: string | null | undefined): string | undefined {\n if (!handleId) return undefined;\n if (handleId.startsWith('__default-')) return undefined;\n const m = /^(.+)__(?:source|target)$/.exec(handleId);\n return m ? m[1] : undefined;\n}\n\nfunction refKey(ref: ColumnRef): string {\n return `${ref.table}.${ref.column}`;\n}\n\nconst JOIN_TYPE_COLOR: Record<Join['type'], string> = {\n INNER: '#3b82f6',\n LEFT: '#8b5cf6',\n RIGHT: '#a855f7',\n FULL: '#ec4899',\n CROSS: '#6b7280',\n};\n\n/**\n * Bridge: `useReactFlow()` only works inside the <ReactFlow> provider, but the\n * imperative ref must be attached to MermaidER (which is the *parent* of\n * <ReactFlow>). This null-rendering child runs the hook in the right scope and\n * forwards the methods back out.\n */\nfunction HandleBridge({\n apiRef,\n wrapperRef,\n}: {\n apiRef: Ref<MermaidERHandle> | undefined;\n wrapperRef: React.RefObject<HTMLDivElement | null>;\n}) {\n const rf = useReactFlow();\n useImperativeHandle(\n apiRef,\n () => ({\n fitView: (options) => rf.fitView(options),\n getViewport: () => rf.getViewport(),\n setViewport: (viewport) => rf.setViewport(viewport),\n getNodesBounds: () => rf.getNodesBounds(rf.getNodes()),\n getWrapperElement: () => wrapperRef.current,\n getViewportElement: () =>\n wrapperRef.current?.querySelector<HTMLElement>('.react-flow__viewport') ?? null,\n }),\n [rf, wrapperRef],\n );\n return null;\n}\n\nexport const MermaidER = forwardRef<MermaidERHandle, MermaidERProps>(function MermaidER(\n props,\n ref,\n) {\n const {\n source,\n model: modelProp,\n algorithm,\n direction,\n aspectRatio,\n positions,\n onPositionsChange,\n showColumnCheckboxes,\n selectedColumns,\n onColumnSelectionChange,\n enableManualJoins,\n joins,\n onJoinConnect,\n onJoinDelete,\n onTableRemove,\n onColumnClick,\n onTableClick,\n deleteKeyCode = 'Delete',\n className,\n style,\n highlightReferencesOnHover = true,\n } = props;\n\n const { model, error } = useMemo<ParseState>(() => {\n if (modelProp) return { model: modelProp, error: null };\n if (source != null) return safeParse(source);\n return { model: null, error: null };\n }, [source, modelProp]);\n\n const [layout, setLayout] = useState<LayoutResult | null>(null);\n const [hoveredEdgeId, setHoveredEdgeId] = useState<string | null>(null);\n\n useEffect(() => {\n if (!model) {\n setLayout(null);\n return;\n }\n let cancelled = false;\n layoutER(model, { algorithm, direction, aspectRatio }).then((result) => {\n if (!cancelled) setLayout(result);\n });\n return () => {\n cancelled = true;\n };\n }, [model, algorithm, direction, aspectRatio]);\n\n const baseNodes = useMemo<Node[]>(() => {\n if (!layout || !model) return [];\n const tableMap = new Map(model.tables.map((t) => [t.name, t]));\n const result: Node[] = [];\n for (const n of layout.nodes) {\n const table = tableMap.get(n.id);\n // Skip stale nodes: a model update can arrive before the new layout\n // finishes; in that gap, layout may still reference a removed table.\n if (!table) continue;\n const override = positions?.[n.id];\n const data: TableNodeData = { table };\n result.push({\n id: n.id,\n type: 'table',\n position: override ?? { x: n.x, y: n.y },\n data,\n width: n.width,\n height: n.height,\n draggable: true,\n deletable: false,\n });\n }\n return result;\n // `onColumnClick` is intentionally NOT a dep — it flows via TableActionsContext\n // so a non-stable callback identity from the parent doesn't reset React Flow\n // node state (which would clobber an in-flight drag).\n }, [layout, model, positions]);\n\n const [nodes, setNodes, onNodesChange] = useNodesState<Node>(baseNodes);\n\n useEffect(() => {\n setNodes(baseNodes);\n }, [baseNodes, setNodes]);\n\n const handleNodeDragStop = useCallback(\n (_: unknown, _primary: Node, dragged: Node[]) => {\n if (!onPositionsChange) return;\n const next: NodePositions = { ...(positions ?? {}) };\n for (const n of dragged) {\n next[n.id] = { x: n.position.x, y: n.position.y };\n }\n onPositionsChange(next);\n },\n [positions, onPositionsChange],\n );\n\n const hoveredRelation = useMemo<Relation | undefined>(() => {\n if (!hoveredEdgeId || !model) return undefined;\n return model.relations.find((r) => r.id === hoveredEdgeId);\n }, [hoveredEdgeId, model]);\n\n const hoveredJoin = useMemo<Join | undefined>(() => {\n if (!hoveredEdgeId || !joins) return undefined;\n if (!hoveredEdgeId.startsWith(JOIN_EDGE_PREFIX)) return undefined;\n const id = hoveredEdgeId.slice(JOIN_EDGE_PREFIX.length);\n return joins.find((j) => j.id === id);\n }, [hoveredEdgeId, joins]);\n\n const highlightMap = useMemo<ReadonlyMap<string, ReadonlySet<string>>>(() => {\n const map = new Map<string, Set<string>>();\n if (!highlightReferencesOnHover) return map;\n\n const add = (table: string, column: string | undefined) => {\n if (!column) return;\n const set = map.get(table) ?? new Set();\n set.add(column);\n map.set(table, set);\n };\n\n if (hoveredRelation) {\n add(hoveredRelation.from, hoveredRelation.fromColumn);\n add(hoveredRelation.to, hoveredRelation.toColumn);\n }\n if (hoveredJoin) {\n add(hoveredJoin.source.table, hoveredJoin.source.column);\n add(hoveredJoin.target.table, hoveredJoin.target.column);\n }\n return map;\n }, [hoveredRelation, hoveredJoin, highlightReferencesOnHover]);\n\n const selectionSet = useMemo<ReadonlySet<string>>(() => {\n return new Set((selectedColumns ?? []).map(refKey));\n }, [selectedColumns]);\n\n const handleColumnSelectToggle = useCallback(\n (table: string, column: string, checked: boolean) => {\n if (!onColumnSelectionChange) return;\n const list = selectedColumns ?? [];\n if (checked) {\n if (list.some((r) => r.table === table && r.column === column)) return;\n onColumnSelectionChange([...list, { table, column }]);\n } else {\n onColumnSelectionChange(\n list.filter((r) => !(r.table === table && r.column === column)),\n );\n }\n },\n [selectedColumns, onColumnSelectionChange],\n );\n\n const selectionContext = useMemo<ColumnSelectionContextValue>(\n () => ({\n enabled: showColumnCheckboxes ?? false,\n selected: selectionSet,\n onToggle: handleColumnSelectToggle,\n }),\n [showColumnCheckboxes, selectionSet, handleColumnSelectToggle],\n );\n\n const edges = useMemo<Edge[]>(() => {\n if (!layout || !model) return [];\n const tableSet = new Set(model.tables.map((t) => t.name));\n const relMap = new Map(model.relations.map((r) => [r.id, r]));\n\n const fkEdges: Edge[] = [];\n for (const e of layout.edges) {\n // Skip stale edges referencing tables removed since the layout was computed.\n if (!tableSet.has(e.source) || !tableSet.has(e.target)) continue;\n const rel = relMap.get(e.id);\n const isHovered = e.id === hoveredEdgeId;\n fkEdges.push({\n id: e.id,\n source: e.source,\n target: e.target,\n sourceHandle: handleIdFor('source', rel?.fromColumn),\n targetHandle: handleIdFor('target', rel?.toColumn),\n type: 'smoothstep',\n animated: isHovered,\n deletable: false,\n style: {\n stroke: isHovered ? '#f59e0b' : '#9ca3af',\n strokeWidth: isHovered ? 2 : 1.5,\n },\n label: rel?.label,\n labelStyle: { fontSize: 10, fill: '#6b7280' },\n labelBgStyle: { fill: '#fff', fillOpacity: 0.85 },\n labelBgPadding: [4, 2] as [number, number],\n labelBgBorderRadius: 3,\n });\n }\n\n const joinEdges: Edge[] = [];\n for (const j of joins ?? []) {\n if (!tableSet.has(j.source.table) || !tableSet.has(j.target.table)) continue;\n const edgeId = `${JOIN_EDGE_PREFIX}${j.id}`;\n const isHovered = edgeId === hoveredEdgeId;\n const color = JOIN_TYPE_COLOR[j.type] ?? '#3b82f6';\n const data: JoinEdgeData = {\n type: j.type,\n color,\n hovered: isHovered,\n onDelete: onJoinDelete ? () => onJoinDelete(j.id) : undefined,\n };\n joinEdges.push({\n id: edgeId,\n source: j.source.table,\n target: j.target.table,\n sourceHandle: handleIdFor('source', j.source.column),\n targetHandle: handleIdFor('target', j.target.column),\n type: 'joinEdge',\n deletable: true,\n data: data as unknown as Record<string, unknown>,\n });\n }\n\n return [...fkEdges, ...joinEdges];\n }, [layout, model, joins, hoveredEdgeId, onJoinDelete]);\n\n const handleConnect = useCallback(\n (conn: Connection) => {\n if (!onJoinConnect) return;\n if (!conn.source || !conn.target) return;\n onJoinConnect(\n { table: conn.source, column: parseHandleColumn(conn.sourceHandle) },\n { table: conn.target, column: parseHandleColumn(conn.targetHandle) },\n );\n },\n [onJoinConnect],\n );\n\n const handleEdgesDelete = useCallback(\n (deleted: Edge[]) => {\n if (!onJoinDelete) return;\n for (const e of deleted) {\n if (e.id.startsWith(JOIN_EDGE_PREFIX)) {\n onJoinDelete(e.id.slice(JOIN_EDGE_PREFIX.length));\n }\n }\n },\n [onJoinDelete],\n );\n\n const connectModeOn = !!enableManualJoins;\n const tableActions = useMemo<TableActionsContextValue>(\n () => ({ onTableRemove, onColumnClick }),\n [onTableRemove, onColumnClick],\n );\n\n const wrapperRef = useRef<HTMLDivElement>(null);\n\n return (\n <div\n ref={wrapperRef}\n className={className}\n style={{ width: '100%', height: '100%', position: 'relative', ...style }}\n >\n {error && (\n <div\n style={{\n position: 'absolute',\n top: 8,\n left: 8,\n right: 8,\n zIndex: 10,\n padding: '8px 12px',\n background: '#fef2f2',\n border: '1px solid #fecaca',\n borderRadius: 6,\n color: '#991b1b',\n fontFamily: 'ui-monospace, SFMono-Regular, Consolas, monospace',\n fontSize: 12,\n whiteSpace: 'pre-wrap',\n }}\n >\n {error.message}\n </div>\n )}\n {!error && !layout && (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: '#9ca3af',\n fontSize: 13,\n }}\n >\n Computing layout…\n </div>\n )}\n <HighlightContext.Provider value={highlightMap}>\n <ColumnSelectionContext.Provider value={selectionContext}>\n <ConnectModeContext.Provider value={connectModeOn}>\n <TableActionsContext.Provider value={tableActions}>\n <ReactFlow\n nodes={nodes}\n edges={edges}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n fitView\n onNodesChange={onNodesChange}\n onNodeDragStop={handleNodeDragStop}\n onNodeClick={\n onTableClick ? (_, node) => onTableClick(node.id) : undefined\n }\n onEdgeMouseEnter={(_, edge) => setHoveredEdgeId(edge.id)}\n onEdgeMouseLeave={() => setHoveredEdgeId(null)}\n onConnect={handleConnect}\n onEdgesDelete={handleEdgesDelete}\n nodesDraggable\n nodesConnectable={connectModeOn}\n elementsSelectable\n deleteKeyCode={deleteKeyCode}\n minZoom={0.1}\n maxZoom={4}\n >\n <Background />\n <Controls />\n <MiniMap pannable zoomable />\n <HandleBridge apiRef={ref} wrapperRef={wrapperRef} />\n </ReactFlow>\n </TableActionsContext.Provider>\n </ConnectModeContext.Provider>\n </ColumnSelectionContext.Provider>\n </HighlightContext.Provider>\n </div>\n );\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@msdshsk/react-er-canvas",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Render Mermaid-format ER diagrams in React/Electron with extended visual features (PK/FK icons, schema groups, FK reference highlighting, manual JOINs, drag-to-position).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",