@diagrammo/dgmo 0.46.0 → 0.48.0
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.
- package/dist/advanced.d.ts +3 -855
- package/dist/advanced.js +541 -70107
- package/dist/analyzer-UYKBXAKR.js +15 -0
- package/dist/auto.js +1 -1
- package/dist/auto.mjs +1 -1
- package/dist/block.js +83 -69417
- package/dist/calculator-LDYQCDDY.js +11 -0
- package/dist/charts-d3-QOZHKTCT.js +25 -0
- package/dist/chunk-2DXMZRWJ.js +800 -0
- package/dist/chunk-2LACINWN.js +786 -0
- package/dist/chunk-33LCUH4H.js +893 -0
- package/dist/chunk-3CIOJ5YJ.js +35 -0
- package/dist/chunk-3HDBAXF5.js +86 -0
- package/dist/chunk-3JLKYGBQ.js +290 -0
- package/dist/chunk-3MQQNXPD.js +415 -0
- package/dist/chunk-3R5POCTZ.js +967 -0
- package/dist/chunk-3SMGIJDD.js +112 -0
- package/dist/chunk-44CM72SQ.js +409 -0
- package/dist/chunk-4GRI7MEB.js +1604 -0
- package/dist/chunk-4OHACBGX.js +606 -0
- package/dist/chunk-5DH3KYFH.js +21 -0
- package/dist/chunk-5MLYVTYM.js +76 -0
- package/dist/chunk-5QXL4NYS.js +553 -0
- package/dist/chunk-5WRI5ZAA.js +31 -0
- package/dist/chunk-6EK55ART.js +117 -0
- package/dist/chunk-6FZYCKXB.js +2065 -0
- package/dist/chunk-6HO5ZTXK.js +277 -0
- package/dist/chunk-6KFWC4CO.js +7 -0
- package/dist/chunk-6MTWIQVO.js +121 -0
- package/dist/chunk-6NYWYNBI.js +329 -0
- package/dist/chunk-6SM5NJQH.js +620 -0
- package/dist/chunk-725LDASD.js +422 -0
- package/dist/chunk-76OWZ3BA.js +130 -0
- package/{src/block/parser.ts → dist/chunk-7JEGTRLD.js} +100 -189
- package/dist/chunk-A44USUV7.js +923 -0
- package/dist/chunk-A4GOY3PE.js +25 -0
- package/dist/chunk-A4VFQH5G.js +251 -0
- package/dist/chunk-AELOBFW5.js +375 -0
- package/dist/chunk-AN2MOO5T.js +522 -0
- package/dist/chunk-BMR4CIEO.js +41 -0
- package/dist/chunk-BS7SV7C3.js +51 -0
- package/dist/chunk-BUPDIHML.js +613 -0
- package/dist/chunk-BUVJFRPY.js +887 -0
- package/dist/chunk-C3Z4DX3V.js +112 -0
- package/dist/chunk-CI2G3OEB.js +1818 -0
- package/dist/chunk-CKNVO24D.js +54 -0
- package/dist/chunk-CUVHQHZ7.js +391 -0
- package/dist/chunk-CXK4DZH3.js +114 -0
- package/dist/{editor.cjs → chunk-DKEPS5LH.js} +14 -225
- package/dist/chunk-DU7W62N5.js +487 -0
- package/dist/chunk-DZAQBW35.js +751 -0
- package/dist/chunk-EB6FPQUP.js +316 -0
- package/dist/chunk-EMS2SK26.js +376 -0
- package/dist/chunk-ERLBPDGU.js +510 -0
- package/dist/chunk-EVCVDTXE.js +732 -0
- package/dist/chunk-FCIIHT24.js +384 -0
- package/dist/chunk-FFUN5D66.js +97 -0
- package/dist/chunk-FITOLXSX.js +714 -0
- package/dist/chunk-FMDSF3OV.js +68 -0
- package/dist/chunk-FNB3PNGF.js +410 -0
- package/dist/chunk-G2H74XJZ.js +2104 -0
- package/dist/chunk-GMR5MFHJ.js +339 -0
- package/dist/chunk-H6PI3KJT.js +305 -0
- package/dist/chunk-I46X5VUA.js +59 -0
- package/dist/chunk-I5IGB56C.js +255 -0
- package/dist/chunk-IH6ZMVQ5.js +3950 -0
- package/dist/chunk-IJZFN3HM.js +463 -0
- package/dist/chunk-IPWPWM6R.js +3079 -0
- package/dist/chunk-IYULMRBE.js +23 -0
- package/dist/chunk-IZMRON6C.js +167 -0
- package/dist/chunk-J5FIXQRT.js +813 -0
- package/dist/chunk-JED6ZDEE.js +73 -0
- package/dist/chunk-JEUJOX5X.js +136 -0
- package/dist/chunk-JM4LEPY3.js +436 -0
- package/dist/chunk-L3UHP6TD.js +101 -0
- package/dist/chunk-L66GEFB6.js +524 -0
- package/dist/chunk-LIOHW36Z.js +469 -0
- package/dist/chunk-LP7EC4MI.js +16 -0
- package/dist/chunk-MITI6NOL.js +548 -0
- package/dist/chunk-MO5YHHMQ.js +168 -0
- package/dist/chunk-MZSBFAPJ.js +885 -0
- package/dist/chunk-NNAAJVBU.js +9 -0
- package/dist/chunk-NOTA6TEI.js +47 -0
- package/dist/chunk-NURGB5Z4.js +335 -0
- package/dist/chunk-NX4EQS3U.js +1062 -0
- package/dist/chunk-NXMESY4J.js +1 -0
- package/dist/chunk-OJR3MQYV.js +1458 -0
- package/dist/chunk-OURHMODR.js +454 -0
- package/dist/chunk-P235SVAH.js +1057 -0
- package/dist/chunk-P4RH4OSP.js +1038 -0
- package/dist/chunk-P6T66CCZ.js +226 -0
- package/dist/chunk-PBAHEPLQ.js +223 -0
- package/dist/chunk-PBHE5UG6.js +1530 -0
- package/dist/chunk-PDKUANDZ.js +35 -0
- package/dist/chunk-PWIHZ6NJ.js +296 -0
- package/{src/c4/layout.ts → dist/chunk-QEBUY5NY.js} +384 -1140
- package/dist/chunk-QLCCJEJI.js +253 -0
- package/dist/chunk-QOOUC2J5.js +52 -0
- package/dist/chunk-QYTPPDAO.js +402 -0
- package/dist/chunk-R3XI2VC7.js +801 -0
- package/dist/chunk-RCERWOJR.js +154 -0
- package/dist/chunk-RJE5JTVK.js +362 -0
- package/dist/chunk-RTLBSQ3G.js +1155 -0
- package/dist/chunk-RUNNI6VE.js +448 -0
- package/dist/chunk-RVMRZAPB.js +354 -0
- package/dist/chunk-RYEGFMUO.js +955 -0
- package/dist/chunk-RZCF2VVM.js +528 -0
- package/dist/chunk-SML67VPV.js +1025 -0
- package/dist/chunk-SQ7IY53Q.js +44 -0
- package/dist/chunk-SQAG3UXH.js +161 -0
- package/dist/chunk-SRTU6HMQ.js +1851 -0
- package/dist/chunk-T2OE3YQB.js +550 -0
- package/dist/chunk-T3UBLACL.js +299 -0
- package/dist/chunk-TLVIHFRO.js +296 -0
- package/dist/chunk-TYS6CSS5.js +52 -0
- package/dist/chunk-U6EQI3U6.js +130 -0
- package/dist/chunk-UJ4FJLBF.js +86 -0
- package/dist/chunk-ULOEATBZ.js +347 -0
- package/dist/chunk-UNCCZ3NU.js +127 -0
- package/dist/chunk-USZW2HHU.js +197 -0
- package/dist/chunk-UWN33VNX.js +1610 -0
- package/dist/chunk-UZ7EZW3B.js +40 -0
- package/dist/chunk-VOKGH627.js +37 -0
- package/dist/chunk-WBRZJOEC.js +204 -0
- package/dist/chunk-WCSOJFU3.js +380 -0
- package/dist/chunk-WCT3NYCV.js +160 -0
- package/dist/chunk-WVAJFAIA.js +529 -0
- package/dist/chunk-X5LCYCL5.js +394 -0
- package/dist/chunk-XAKEUMEJ.js +1256 -0
- package/dist/chunk-Y2ZE3MSG.js +590 -0
- package/dist/chunk-Y3J7VR52.js +646 -0
- package/dist/chunk-Y5QGVTVC.js +33 -0
- package/dist/chunk-YAA6IID3.js +548 -0
- package/dist/chunk-YGJTDXVR.js +692 -0
- package/{src/utils/time-ticks.ts → dist/chunk-YRYKTYMQ.js} +45 -112
- package/dist/chunk-YYVTBZZC.js +61 -0
- package/dist/chunk-Z5CGSFJB.js +647 -0
- package/{src/gantt/calculator.ts → dist/chunk-Z72XRXD4.js} +236 -428
- package/dist/chunk-ZGR7ULFJ.js +978 -0
- package/dist/chunk-ZJEL4H2R.js +851 -0
- package/dist/chunk-ZO27FGJS.js +554 -0
- package/dist/collapse-CLPJD2RL.js +11 -0
- package/dist/collapse-SLINJQW6.js +9 -0
- package/dist/collapse-ZWUFFCK2.js +9 -0
- package/dist/compute-G55RG53N.js +8 -0
- package/dist/dgmo-router-DZCIDUO5.js +73 -0
- package/dist/dimensions-J3FHIMJ6.js +22 -0
- package/dist/element.js +1 -1
- package/dist/element.mjs +1 -1
- package/dist/flowchart-parser-ZNSKIBVR.js +21 -0
- package/dist/flowchart-renderer-ZAEOL4DX.js +35 -0
- package/dist/index.d.ts +3 -469
- package/dist/index.js +137 -69685
- package/dist/layout-46N4JJYK.js +17 -0
- package/dist/layout-5RA6QWR6.js +26 -0
- package/dist/layout-5T6RH5M6.js +14 -0
- package/dist/layout-7QXCNOVS.js +13 -0
- package/dist/layout-EE6GE46Y.js +17 -0
- package/dist/layout-G23F7LDH.js +9 -0
- package/dist/layout-HRBZPFCN.js +23 -0
- package/dist/layout-HWDSRX5E.js +20 -0
- package/dist/layout-HYZPWAK5.js +18 -0
- package/dist/layout-N76NR4ZO.js +19 -0
- package/dist/layout-OB3RI63S.js +14 -0
- package/dist/layout-S2QBYSK2.js +15 -0
- package/dist/layout-UM5F4DJB.js +15 -0
- package/dist/layout-search-OYV77HHS.js +1655 -0
- package/dist/load-data-EHDDZ5ZS.js +8 -0
- package/dist/palettes-PRJ2RI6D.js +56 -0
- package/dist/parser-2MFP2H2E.js +11 -0
- package/dist/parser-3STE62GB.js +21 -0
- package/dist/parser-4FL2CMDG.js +18 -0
- package/dist/parser-4OGOBJJ4.js +13 -0
- package/dist/parser-6BTRLKRZ.js +15 -0
- package/dist/parser-AQETEVRW.js +21 -0
- package/dist/parser-CFZD5IBL.js +12 -0
- package/dist/parser-FUSKFGZN.js +16 -0
- package/dist/parser-I6KXEYFK.js +13 -0
- package/dist/parser-IXSZZQHY.js +18 -0
- package/dist/parser-JAL2HD4I.js +13 -0
- package/dist/parser-KNSQSKQO.js +20 -0
- package/dist/parser-N5O4IHUN.js +13 -0
- package/dist/parser-NGU3ETO6.js +16 -0
- package/dist/parser-NRAI6O25.js +19 -0
- package/dist/parser-O5J4VBII.js +11 -0
- package/dist/parser-OV7NOBW6.js +21 -0
- package/dist/parser-PQ3POX45.js +23 -0
- package/dist/parser-TFZFBVWN.js +11 -0
- package/dist/parser-TRTHDFPP.js +18 -0
- package/dist/parser-VQP2UJ6W.js +11 -0
- package/dist/parser-XDVELHRK.js +13 -0
- package/dist/parser-XKRP7QKP.js +11 -0
- package/dist/parser-Y6WTBU76.js +14 -0
- package/dist/parser-ZSDPZYQP.js +12 -0
- package/dist/renderer-2XQQQ6QR.js +28 -0
- package/dist/renderer-5NDVNDAZ.js +26 -0
- package/dist/renderer-6EEZHMH4.js +26 -0
- package/dist/renderer-6W5462RB.js +43 -0
- package/dist/renderer-7MVLWWQD.js +22 -0
- package/dist/renderer-AWWAF6XD.js +33 -0
- package/dist/renderer-BYCNMBF7.js +34 -0
- package/dist/renderer-CLJHQPBJ.js +29 -0
- package/dist/renderer-GHPZMFU2.js +15 -0
- package/dist/renderer-JGBKUCZD.js +16 -0
- package/dist/renderer-KQB7UGDS.js +19 -0
- package/dist/renderer-PGX2TKVS.js +24 -0
- package/dist/renderer-QIWVHWZO.js +15 -0
- package/dist/renderer-RPOA5YPZ.js +35 -0
- package/dist/renderer-SMIBCRVB.js +24 -0
- package/dist/renderer-T7YKC5VP.js +28 -0
- package/dist/renderer-TOCOVLHS.js +37 -0
- package/dist/renderer-UWA5XS74.js +39 -0
- package/dist/renderer-VACVQYML.js +25 -0
- package/dist/renderer-W4M6QLIJ.js +37 -0
- package/dist/renderer-WZRREQ2P.js +23 -0
- package/dist/renderer-YY2OSF6E.js +27 -0
- package/dist/renderer-Z7I3W2PP.js +24 -0
- package/dist/renderer-ZGNTUWC4.js +22 -0
- package/dist/renderer-ZTR3DER3.js +19 -0
- package/dist/renderer-radial-C32GPJQ7.js +21 -0
- package/dist/resolver-NOAA7ID7.js +13 -0
- package/dist/state-collapse-HCGD6IVJ.js +8 -0
- package/dist/state-parser-Z2V23ATR.js +18 -0
- package/dist/state-renderer-RFYHSP4K.js +34 -0
- package/dist/themes-1-CuKpeH.d.ts +908 -0
- package/package.json +19 -70
- package/dist/advanced.cjs +0 -71648
- package/dist/advanced.d.cts +0 -6571
- package/dist/auto.cjs +0 -70648
- package/dist/auto.d.cts +0 -54
- package/dist/block.cjs +0 -70208
- package/dist/block.d.cts +0 -115
- package/dist/editor.d.cts +0 -26
- package/dist/element.cjs +0 -70530
- package/dist/element.d.cts +0 -62
- package/dist/highlight.cjs +0 -852
- package/dist/highlight.d.cts +0 -49
- package/dist/index.cjs +0 -70491
- package/dist/index.d.cts +0 -626
- package/dist/pert.cjs +0 -325
- package/dist/pert.d.cts +0 -559
- package/src/advanced.ts +0 -873
- package/src/arc/parser.ts +0 -15
- package/src/arc/renderer.ts +0 -633
- package/src/auto/index.ts +0 -621
- package/src/auto/shared.ts +0 -370
- package/src/auto/styles.ts +0 -73
- package/src/block/layout.ts +0 -198
- package/src/block/renderer.ts +0 -484
- package/src/block/types.ts +0 -67
- package/src/boxes-and-lines/collapse.ts +0 -96
- package/src/boxes-and-lines/focus.ts +0 -257
- package/src/boxes-and-lines/label-placement.ts +0 -339
- package/src/boxes-and-lines/layout-grouped.ts +0 -912
- package/src/boxes-and-lines/layout-layered.ts +0 -722
- package/src/boxes-and-lines/layout-search.ts +0 -1678
- package/src/boxes-and-lines/layout.ts +0 -398
- package/src/boxes-and-lines/parser.ts +0 -1181
- package/src/boxes-and-lines/renderer.ts +0 -1465
- package/src/boxes-and-lines/types.ts +0 -66
- package/src/c4/parser.ts +0 -1158
- package/src/c4/renderer.ts +0 -2008
- package/src/c4/types.ts +0 -89
- package/src/chart-type-registry.ts +0 -529
- package/src/chart-types.ts +0 -225
- package/src/chart.ts +0 -685
- package/src/charts-d3/bar.ts +0 -338
- package/src/charts-d3/chord.ts +0 -140
- package/src/charts-d3/function.ts +0 -147
- package/src/charts-d3/funnel.ts +0 -104
- package/src/charts-d3/heatmap.ts +0 -145
- package/src/charts-d3/index.ts +0 -337
- package/src/charts-d3/interactions.ts +0 -477
- package/src/charts-d3/line.ts +0 -344
- package/src/charts-d3/mount.ts +0 -151
- package/src/charts-d3/pie.ts +0 -117
- package/src/charts-d3/polar.ts +0 -89
- package/src/charts-d3/radar.ts +0 -0
- package/src/charts-d3/sankey.ts +0 -0
- package/src/charts-d3/scatter.ts +0 -185
- package/src/charts-d3/shared.ts +0 -208
- package/src/class/layout.ts +0 -326
- package/src/class/parser.ts +0 -635
- package/src/class/renderer.ts +0 -793
- package/src/class/types.ts +0 -58
- package/src/cli-banner.ts +0 -106
- package/src/cli.ts +0 -1080
- package/src/colors-diagnostics.ts +0 -35
- package/src/colors.ts +0 -379
- package/src/completion-types.ts +0 -27
- package/src/completion.ts +0 -2668
- package/src/cycle/layout.ts +0 -1026
- package/src/cycle/parser.ts +0 -387
- package/src/cycle/renderer.ts +0 -653
- package/src/cycle/types.ts +0 -101
- package/src/d3.ts +0 -1434
- package/src/data-chart-parser.ts +0 -1007
- package/src/dgmo-router.ts +0 -296
- package/src/diagnostics-registry.ts +0 -108
- package/src/diagnostics.ts +0 -0
- package/src/dimensions.ts +0 -20
- package/src/directives-registry.ts +0 -396
- package/src/editor/dgmo.grammar +0 -76
- package/src/editor/dgmo.grammar.d.ts +0 -2
- package/src/editor/dgmo.grammar.js +0 -18
- package/src/editor/dgmo.grammar.terms.d.ts +0 -5
- package/src/editor/dgmo.grammar.terms.js +0 -35
- package/src/editor/highlight-api.ts +0 -596
- package/src/editor/highlight.ts +0 -36
- package/src/editor/index.ts +0 -27
- package/src/editor/keywords.ts +0 -128
- package/src/editor/tokens.ts +0 -30
- package/src/element/index.ts +0 -432
- package/src/embed/css.ts +0 -312
- package/src/embed/escape.ts +0 -15
- package/src/embed/index.ts +0 -280
- package/src/er/classify.ts +0 -228
- package/src/er/layout.ts +0 -530
- package/src/er/parser.ts +0 -750
- package/src/er/renderer.ts +0 -725
- package/src/er/types.ts +0 -52
- package/src/error-card.ts +0 -269
- package/src/event-line/diagnostics.ts +0 -117
- package/src/event-line/parser.ts +0 -643
- package/src/event-line/renderer.ts +0 -1742
- package/src/event-line/types.ts +0 -74
- package/src/fonts.ts +0 -3
- package/src/gantt/parser.ts +0 -1641
- package/src/gantt/renderer.ts +0 -3787
- package/src/gantt/resolver.ts +0 -172
- package/src/gantt/types.ts +0 -206
- package/src/graph/diagnostics.ts +0 -64
- package/src/graph/edge-spline.ts +0 -29
- package/src/graph/flowchart-parser.ts +0 -759
- package/src/graph/flowchart-renderer.ts +0 -817
- package/src/graph/layout.ts +0 -472
- package/src/graph/notes.ts +0 -21
- package/src/graph/state-collapse.ts +0 -78
- package/src/graph/state-parser.ts +0 -574
- package/src/graph/state-renderer.ts +0 -653
- package/src/graph/types.ts +0 -65
- package/src/index.ts +0 -261
- package/src/infra/compute.ts +0 -1385
- package/src/infra/layout.ts +0 -888
- package/src/infra/parser.ts +0 -1234
- package/src/infra/renderer.ts +0 -2534
- package/src/infra/roles.ts +0 -75
- package/src/infra/types.ts +0 -216
- package/src/infra/validation.ts +0 -201
- package/src/journey-map/layout.ts +0 -490
- package/src/journey-map/parser.ts +0 -557
- package/src/journey-map/renderer.ts +0 -1580
- package/src/journey-map/types.ts +0 -47
- package/src/kanban/mutations.ts +0 -175
- package/src/kanban/parser.ts +0 -509
- package/src/kanban/renderer.ts +0 -1449
- package/src/kanban/types.ts +0 -39
- package/src/label-layout.ts +0 -368
- package/src/map/colorize.ts +0 -54
- package/src/map/completion.ts +0 -316
- package/src/map/context-labels.ts +0 -609
- package/src/map/data/PROVENANCE.json +0 -1
- package/src/map/data/README.md +0 -40
- package/src/map/data/airport-collisions.json +0 -1
- package/src/map/data/airports.json +0 -1
- package/src/map/data/gazetteer.json +0 -1
- package/src/map/data/lakes.json +0 -1
- package/src/map/data/mountain-ranges.json +0 -1
- package/src/map/data/na-lakes.json +0 -1
- package/src/map/data/na-land.json +0 -1
- package/src/map/data/region-names.json +0 -1
- package/src/map/data/rivers.json +0 -1
- package/src/map/data/types.ts +0 -121
- package/src/map/data/us-states.json +0 -1
- package/src/map/data/water-bodies.json +0 -1
- package/src/map/data/world-coarse.json +0 -1
- package/src/map/data/world-detail.json +0 -1
- package/src/map/diagnostics.ts +0 -134
- package/src/map/dimensions.ts +0 -150
- package/src/map/geo-query.ts +0 -295
- package/src/map/geo.ts +0 -438
- package/src/map/invert.ts +0 -111
- package/src/map/layout.ts +0 -5069
- package/src/map/legend-band.ts +0 -127
- package/src/map/load-data.ts +0 -186
- package/src/map/parser.ts +0 -783
- package/src/map/renderer.ts +0 -1445
- package/src/map/resolved-types.ts +0 -160
- package/src/map/resolver.ts +0 -1146
- package/src/map/types.ts +0 -194
- package/src/mindmap/collapse.ts +0 -67
- package/src/mindmap/layout.ts +0 -649
- package/src/mindmap/parser.ts +0 -430
- package/src/mindmap/renderer.ts +0 -637
- package/src/mindmap/text-wrap.ts +0 -218
- package/src/mindmap/types.ts +0 -56
- package/src/org/collapse.ts +0 -151
- package/src/org/layout.ts +0 -1262
- package/src/org/parser.ts +0 -519
- package/src/org/renderer.ts +0 -866
- package/src/org/resolver.ts +0 -636
- package/src/palettes/atlas.ts +0 -77
- package/src/palettes/blueprint.ts +0 -73
- package/src/palettes/catppuccin.ts +0 -84
- package/src/palettes/color-utils.ts +0 -443
- package/src/palettes/index.ts +0 -67
- package/src/palettes/nord.ts +0 -67
- package/src/palettes/registry.ts +0 -114
- package/src/palettes/slate.ts +0 -73
- package/src/palettes/tidewater.ts +0 -73
- package/src/palettes/tokyo-night.ts +0 -86
- package/src/palettes/types.ts +0 -88
- package/src/pert/analyzer.ts +0 -1183
- package/src/pert/diagnostics.ts +0 -110
- package/src/pert/index.ts +0 -43
- package/src/pert/internal.ts +0 -252
- package/src/pert/layout.ts +0 -971
- package/src/pert/monte-carlo.ts +0 -446
- package/src/pert/parser.ts +0 -1606
- package/src/pert/renderer.ts +0 -4009
- package/src/pert/share-normalize.ts +0 -62
- package/src/pert/types.ts +0 -604
- package/src/pyramid/parser.ts +0 -185
- package/src/pyramid/renderer.ts +0 -768
- package/src/pyramid/types.ts +0 -28
- package/src/quadrant/parser.ts +0 -18
- package/src/quadrant/renderer.ts +0 -638
- package/src/raci/diagnostics.ts +0 -213
- package/src/raci/index.ts +0 -23
- package/src/raci/mutations.ts +0 -321
- package/src/raci/parser.ts +0 -699
- package/src/raci/renderer.ts +0 -1627
- package/src/raci/types.ts +0 -82
- package/src/raci/variants.ts +0 -320
- package/src/render.ts +0 -268
- package/src/ring/parser.ts +0 -212
- package/src/ring/renderer.ts +0 -490
- package/src/ring/types.ts +0 -27
- package/src/sequence/collapse.ts +0 -174
- package/src/sequence/parser.ts +0 -1596
- package/src/sequence/participant-inference.ts +0 -152
- package/src/sequence/renderer.ts +0 -3168
- package/src/sequence/tag-resolution.ts +0 -164
- package/src/sharing.ts +0 -211
- package/src/sitemap/collapse.ts +0 -100
- package/src/sitemap/layout.ts +0 -925
- package/src/sitemap/parser.ts +0 -755
- package/src/sitemap/renderer.ts +0 -828
- package/src/sitemap/types.ts +0 -42
- package/src/slope/parser.ts +0 -18
- package/src/slope/renderer.ts +0 -378
- package/src/swimlane/diagnostics.ts +0 -94
- package/src/swimlane/layout.ts +0 -597
- package/src/swimlane/parser.ts +0 -613
- package/src/swimlane/renderer.ts +0 -681
- package/src/swimlane/types.ts +0 -151
- package/src/tech-radar/interactive.ts +0 -1129
- package/src/tech-radar/layout.ts +0 -197
- package/src/tech-radar/parser.ts +0 -436
- package/src/tech-radar/renderer.ts +0 -1239
- package/src/tech-radar/shared.ts +0 -151
- package/src/tech-radar/types.ts +0 -86
- package/src/themes.ts +0 -22
- package/src/timeline/parser.ts +0 -472
- package/src/timeline/renderer.ts +0 -3024
- package/src/timeline/types.ts +0 -33
- package/src/timeline/viz-parser.ts +0 -21
- package/src/treemap/diagnostics.ts +0 -62
- package/src/treemap/layout-radial.ts +0 -233
- package/src/treemap/layout.ts +0 -189
- package/src/treemap/parser.ts +0 -463
- package/src/treemap/renderer-radial.ts +0 -438
- package/src/treemap/renderer.ts +0 -572
- package/src/treemap/treemap-shared.ts +0 -309
- package/src/treemap/types.ts +0 -60
- package/src/utils/arrow-markers.ts +0 -51
- package/src/utils/arrows-diagnostics.ts +0 -62
- package/src/utils/arrows.ts +0 -227
- package/src/utils/brand.ts +0 -61
- package/src/utils/card.ts +0 -183
- package/src/utils/collapse-engine/tree-edges.ts +0 -115
- package/src/utils/collapse-engine/tree.ts +0 -117
- package/src/utils/d3-helpers.ts +0 -219
- package/src/utils/d3-types.ts +0 -9
- package/src/utils/description-helpers.ts +0 -38
- package/src/utils/duration.ts +0 -312
- package/src/utils/export-container.ts +0 -41
- package/src/utils/expr-eval.ts +0 -27
- package/src/utils/fit-canvas.ts +0 -64
- package/src/utils/hover-styles.ts +0 -673
- package/src/utils/inline-markdown.ts +0 -96
- package/src/utils/legend-constants.ts +0 -48
- package/src/utils/legend-d3.ts +0 -618
- package/src/utils/legend-integration.ts +0 -107
- package/src/utils/legend-layout.ts +0 -776
- package/src/utils/legend-svg.ts +0 -214
- package/src/utils/legend-types.ts +0 -272
- package/src/utils/name-normalize.ts +0 -163
- package/src/utils/note-box/constants.ts +0 -25
- package/src/utils/note-box/index.ts +0 -11
- package/src/utils/note-box/metrics.ts +0 -90
- package/src/utils/note-box/svg.ts +0 -331
- package/src/utils/notes/bounds.ts +0 -30
- package/src/utils/notes/build.ts +0 -131
- package/src/utils/notes/index.ts +0 -18
- package/src/utils/notes/model.ts +0 -19
- package/src/utils/notes/parse.ts +0 -131
- package/src/utils/notes/place.ts +0 -177
- package/src/utils/notes/resolve.ts +0 -88
- package/src/utils/number-format.ts +0 -36
- package/src/utils/parsing.ts +0 -953
- package/src/utils/reserved-key-registry.ts +0 -239
- package/src/utils/safe-href.ts +0 -38
- package/src/utils/scaling.ts +0 -97
- package/src/utils/svg-embed.ts +0 -251
- package/src/utils/tag-groups.ts +0 -607
- package/src/utils/text-measure.ts +0 -122
- package/src/utils/title-constants.ts +0 -24
- package/src/utils/visual-conventions.ts +0 -61
- package/src/utils/wrapped-desc.ts +0 -87
- package/src/venn/parser.ts +0 -18
- package/src/venn/renderer.ts +0 -1047
- package/src/version-control/diagnostics.ts +0 -67
- package/src/version-control/parser.ts +0 -544
- package/src/version-control/renderer.ts +0 -724
- package/src/version-control/types.ts +0 -100
- package/src/visualizations/parse.ts +0 -1560
- package/src/visualizations/types.ts +0 -262
- package/src/wireframe/layout.ts +0 -479
- package/src/wireframe/parser.ts +0 -1114
- package/src/wireframe/renderer.ts +0 -1318
- package/src/wireframe/types.ts +0 -110
- package/src/wordcloud/parser.ts +0 -18
- package/src/wordcloud/renderer.ts +0 -357
|
@@ -0,0 +1,3950 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computeQuadrantPointLabels
|
|
3
|
+
} from "./chunk-PBAHEPLQ.js";
|
|
4
|
+
import {
|
|
5
|
+
renderDataChartD3
|
|
6
|
+
} from "./chunk-OJR3MQYV.js";
|
|
7
|
+
import {
|
|
8
|
+
renderTimeline
|
|
9
|
+
} from "./chunk-UWN33VNX.js";
|
|
10
|
+
import {
|
|
11
|
+
EXPORT_HEIGHT,
|
|
12
|
+
EXPORT_WIDTH,
|
|
13
|
+
createExportContainer,
|
|
14
|
+
createTooltip,
|
|
15
|
+
finalizeSvgExport,
|
|
16
|
+
hideTooltip,
|
|
17
|
+
initD3Chart,
|
|
18
|
+
renderChartTitle,
|
|
19
|
+
resolveExportPalette,
|
|
20
|
+
showTooltip
|
|
21
|
+
} from "./chunk-U6EQI3U6.js";
|
|
22
|
+
import {
|
|
23
|
+
ScaleContext
|
|
24
|
+
} from "./chunk-YYVTBZZC.js";
|
|
25
|
+
import {
|
|
26
|
+
FONT_FAMILY
|
|
27
|
+
} from "./chunk-6KFWC4CO.js";
|
|
28
|
+
import {
|
|
29
|
+
measureText,
|
|
30
|
+
wrapTextToWidth
|
|
31
|
+
} from "./chunk-WCT3NYCV.js";
|
|
32
|
+
import {
|
|
33
|
+
getRenderCategory,
|
|
34
|
+
parseArc,
|
|
35
|
+
parseDgmo,
|
|
36
|
+
parseQuadrant,
|
|
37
|
+
parseSlope,
|
|
38
|
+
parseTimeline,
|
|
39
|
+
parseVenn,
|
|
40
|
+
parseVisualization,
|
|
41
|
+
parseWordcloud
|
|
42
|
+
} from "./chunk-6FZYCKXB.js";
|
|
43
|
+
import {
|
|
44
|
+
getPalette
|
|
45
|
+
} from "./chunk-R3XI2VC7.js";
|
|
46
|
+
import {
|
|
47
|
+
getSeriesColors,
|
|
48
|
+
mix,
|
|
49
|
+
themeBaseBg
|
|
50
|
+
} from "./chunk-P6T66CCZ.js";
|
|
51
|
+
import {
|
|
52
|
+
resolveActiveTagGroup,
|
|
53
|
+
tagAttrKey
|
|
54
|
+
} from "./chunk-QLCCJEJI.js";
|
|
55
|
+
import {
|
|
56
|
+
__commonJS,
|
|
57
|
+
__toESM
|
|
58
|
+
} from "./chunk-5WRI5ZAA.js";
|
|
59
|
+
|
|
60
|
+
// node_modules/.pnpm/lz-string@1.5.0/node_modules/lz-string/libs/lz-string.js
|
|
61
|
+
var require_lz_string = __commonJS({
|
|
62
|
+
"node_modules/.pnpm/lz-string@1.5.0/node_modules/lz-string/libs/lz-string.js"(exports, module) {
|
|
63
|
+
"use strict";
|
|
64
|
+
var LZString = (function() {
|
|
65
|
+
var f = String.fromCharCode;
|
|
66
|
+
var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
|
67
|
+
var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$";
|
|
68
|
+
var baseReverseDic = {};
|
|
69
|
+
function getBaseValue(alphabet, character) {
|
|
70
|
+
if (!baseReverseDic[alphabet]) {
|
|
71
|
+
baseReverseDic[alphabet] = {};
|
|
72
|
+
for (var i = 0; i < alphabet.length; i++) {
|
|
73
|
+
baseReverseDic[alphabet][alphabet.charAt(i)] = i;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return baseReverseDic[alphabet][character];
|
|
77
|
+
}
|
|
78
|
+
var LZString2 = {
|
|
79
|
+
compressToBase64: function(input) {
|
|
80
|
+
if (input == null) return "";
|
|
81
|
+
var res = LZString2._compress(input, 6, function(a) {
|
|
82
|
+
return keyStrBase64.charAt(a);
|
|
83
|
+
});
|
|
84
|
+
switch (res.length % 4) {
|
|
85
|
+
// To produce valid Base64
|
|
86
|
+
default:
|
|
87
|
+
// When could this happen ?
|
|
88
|
+
case 0:
|
|
89
|
+
return res;
|
|
90
|
+
case 1:
|
|
91
|
+
return res + "===";
|
|
92
|
+
case 2:
|
|
93
|
+
return res + "==";
|
|
94
|
+
case 3:
|
|
95
|
+
return res + "=";
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
decompressFromBase64: function(input) {
|
|
99
|
+
if (input == null) return "";
|
|
100
|
+
if (input == "") return null;
|
|
101
|
+
return LZString2._decompress(input.length, 32, function(index) {
|
|
102
|
+
return getBaseValue(keyStrBase64, input.charAt(index));
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
compressToUTF16: function(input) {
|
|
106
|
+
if (input == null) return "";
|
|
107
|
+
return LZString2._compress(input, 15, function(a) {
|
|
108
|
+
return f(a + 32);
|
|
109
|
+
}) + " ";
|
|
110
|
+
},
|
|
111
|
+
decompressFromUTF16: function(compressed) {
|
|
112
|
+
if (compressed == null) return "";
|
|
113
|
+
if (compressed == "") return null;
|
|
114
|
+
return LZString2._decompress(compressed.length, 16384, function(index) {
|
|
115
|
+
return compressed.charCodeAt(index) - 32;
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
//compress into uint8array (UCS-2 big endian format)
|
|
119
|
+
compressToUint8Array: function(uncompressed) {
|
|
120
|
+
var compressed = LZString2.compress(uncompressed);
|
|
121
|
+
var buf = new Uint8Array(compressed.length * 2);
|
|
122
|
+
for (var i = 0, TotalLen = compressed.length; i < TotalLen; i++) {
|
|
123
|
+
var current_value = compressed.charCodeAt(i);
|
|
124
|
+
buf[i * 2] = current_value >>> 8;
|
|
125
|
+
buf[i * 2 + 1] = current_value % 256;
|
|
126
|
+
}
|
|
127
|
+
return buf;
|
|
128
|
+
},
|
|
129
|
+
//decompress from uint8array (UCS-2 big endian format)
|
|
130
|
+
decompressFromUint8Array: function(compressed) {
|
|
131
|
+
if (compressed === null || compressed === void 0) {
|
|
132
|
+
return LZString2.decompress(compressed);
|
|
133
|
+
} else {
|
|
134
|
+
var buf = new Array(compressed.length / 2);
|
|
135
|
+
for (var i = 0, TotalLen = buf.length; i < TotalLen; i++) {
|
|
136
|
+
buf[i] = compressed[i * 2] * 256 + compressed[i * 2 + 1];
|
|
137
|
+
}
|
|
138
|
+
var result = [];
|
|
139
|
+
buf.forEach(function(c) {
|
|
140
|
+
result.push(f(c));
|
|
141
|
+
});
|
|
142
|
+
return LZString2.decompress(result.join(""));
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
//compress into a string that is already URI encoded
|
|
146
|
+
compressToEncodedURIComponent: function(input) {
|
|
147
|
+
if (input == null) return "";
|
|
148
|
+
return LZString2._compress(input, 6, function(a) {
|
|
149
|
+
return keyStrUriSafe.charAt(a);
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
//decompress from an output of compressToEncodedURIComponent
|
|
153
|
+
decompressFromEncodedURIComponent: function(input) {
|
|
154
|
+
if (input == null) return "";
|
|
155
|
+
if (input == "") return null;
|
|
156
|
+
input = input.replace(/ /g, "+");
|
|
157
|
+
return LZString2._decompress(input.length, 32, function(index) {
|
|
158
|
+
return getBaseValue(keyStrUriSafe, input.charAt(index));
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
compress: function(uncompressed) {
|
|
162
|
+
return LZString2._compress(uncompressed, 16, function(a) {
|
|
163
|
+
return f(a);
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
_compress: function(uncompressed, bitsPerChar, getCharFromInt) {
|
|
167
|
+
if (uncompressed == null) return "";
|
|
168
|
+
var i, value, context_dictionary = {}, context_dictionaryToCreate = {}, context_c = "", context_wc = "", context_w = "", context_enlargeIn = 2, context_dictSize = 3, context_numBits = 2, context_data = [], context_data_val = 0, context_data_position = 0, ii;
|
|
169
|
+
for (ii = 0; ii < uncompressed.length; ii += 1) {
|
|
170
|
+
context_c = uncompressed.charAt(ii);
|
|
171
|
+
if (!Object.prototype.hasOwnProperty.call(context_dictionary, context_c)) {
|
|
172
|
+
context_dictionary[context_c] = context_dictSize++;
|
|
173
|
+
context_dictionaryToCreate[context_c] = true;
|
|
174
|
+
}
|
|
175
|
+
context_wc = context_w + context_c;
|
|
176
|
+
if (Object.prototype.hasOwnProperty.call(context_dictionary, context_wc)) {
|
|
177
|
+
context_w = context_wc;
|
|
178
|
+
} else {
|
|
179
|
+
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
|
|
180
|
+
if (context_w.charCodeAt(0) < 256) {
|
|
181
|
+
for (i = 0; i < context_numBits; i++) {
|
|
182
|
+
context_data_val = context_data_val << 1;
|
|
183
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
184
|
+
context_data_position = 0;
|
|
185
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
186
|
+
context_data_val = 0;
|
|
187
|
+
} else {
|
|
188
|
+
context_data_position++;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
value = context_w.charCodeAt(0);
|
|
192
|
+
for (i = 0; i < 8; i++) {
|
|
193
|
+
context_data_val = context_data_val << 1 | value & 1;
|
|
194
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
195
|
+
context_data_position = 0;
|
|
196
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
197
|
+
context_data_val = 0;
|
|
198
|
+
} else {
|
|
199
|
+
context_data_position++;
|
|
200
|
+
}
|
|
201
|
+
value = value >> 1;
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
value = 1;
|
|
205
|
+
for (i = 0; i < context_numBits; i++) {
|
|
206
|
+
context_data_val = context_data_val << 1 | value;
|
|
207
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
208
|
+
context_data_position = 0;
|
|
209
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
210
|
+
context_data_val = 0;
|
|
211
|
+
} else {
|
|
212
|
+
context_data_position++;
|
|
213
|
+
}
|
|
214
|
+
value = 0;
|
|
215
|
+
}
|
|
216
|
+
value = context_w.charCodeAt(0);
|
|
217
|
+
for (i = 0; i < 16; i++) {
|
|
218
|
+
context_data_val = context_data_val << 1 | value & 1;
|
|
219
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
220
|
+
context_data_position = 0;
|
|
221
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
222
|
+
context_data_val = 0;
|
|
223
|
+
} else {
|
|
224
|
+
context_data_position++;
|
|
225
|
+
}
|
|
226
|
+
value = value >> 1;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
context_enlargeIn--;
|
|
230
|
+
if (context_enlargeIn == 0) {
|
|
231
|
+
context_enlargeIn = Math.pow(2, context_numBits);
|
|
232
|
+
context_numBits++;
|
|
233
|
+
}
|
|
234
|
+
delete context_dictionaryToCreate[context_w];
|
|
235
|
+
} else {
|
|
236
|
+
value = context_dictionary[context_w];
|
|
237
|
+
for (i = 0; i < context_numBits; i++) {
|
|
238
|
+
context_data_val = context_data_val << 1 | value & 1;
|
|
239
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
240
|
+
context_data_position = 0;
|
|
241
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
242
|
+
context_data_val = 0;
|
|
243
|
+
} else {
|
|
244
|
+
context_data_position++;
|
|
245
|
+
}
|
|
246
|
+
value = value >> 1;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
context_enlargeIn--;
|
|
250
|
+
if (context_enlargeIn == 0) {
|
|
251
|
+
context_enlargeIn = Math.pow(2, context_numBits);
|
|
252
|
+
context_numBits++;
|
|
253
|
+
}
|
|
254
|
+
context_dictionary[context_wc] = context_dictSize++;
|
|
255
|
+
context_w = String(context_c);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (context_w !== "") {
|
|
259
|
+
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate, context_w)) {
|
|
260
|
+
if (context_w.charCodeAt(0) < 256) {
|
|
261
|
+
for (i = 0; i < context_numBits; i++) {
|
|
262
|
+
context_data_val = context_data_val << 1;
|
|
263
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
264
|
+
context_data_position = 0;
|
|
265
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
266
|
+
context_data_val = 0;
|
|
267
|
+
} else {
|
|
268
|
+
context_data_position++;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
value = context_w.charCodeAt(0);
|
|
272
|
+
for (i = 0; i < 8; i++) {
|
|
273
|
+
context_data_val = context_data_val << 1 | value & 1;
|
|
274
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
275
|
+
context_data_position = 0;
|
|
276
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
277
|
+
context_data_val = 0;
|
|
278
|
+
} else {
|
|
279
|
+
context_data_position++;
|
|
280
|
+
}
|
|
281
|
+
value = value >> 1;
|
|
282
|
+
}
|
|
283
|
+
} else {
|
|
284
|
+
value = 1;
|
|
285
|
+
for (i = 0; i < context_numBits; i++) {
|
|
286
|
+
context_data_val = context_data_val << 1 | value;
|
|
287
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
288
|
+
context_data_position = 0;
|
|
289
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
290
|
+
context_data_val = 0;
|
|
291
|
+
} else {
|
|
292
|
+
context_data_position++;
|
|
293
|
+
}
|
|
294
|
+
value = 0;
|
|
295
|
+
}
|
|
296
|
+
value = context_w.charCodeAt(0);
|
|
297
|
+
for (i = 0; i < 16; i++) {
|
|
298
|
+
context_data_val = context_data_val << 1 | value & 1;
|
|
299
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
300
|
+
context_data_position = 0;
|
|
301
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
302
|
+
context_data_val = 0;
|
|
303
|
+
} else {
|
|
304
|
+
context_data_position++;
|
|
305
|
+
}
|
|
306
|
+
value = value >> 1;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
context_enlargeIn--;
|
|
310
|
+
if (context_enlargeIn == 0) {
|
|
311
|
+
context_enlargeIn = Math.pow(2, context_numBits);
|
|
312
|
+
context_numBits++;
|
|
313
|
+
}
|
|
314
|
+
delete context_dictionaryToCreate[context_w];
|
|
315
|
+
} else {
|
|
316
|
+
value = context_dictionary[context_w];
|
|
317
|
+
for (i = 0; i < context_numBits; i++) {
|
|
318
|
+
context_data_val = context_data_val << 1 | value & 1;
|
|
319
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
320
|
+
context_data_position = 0;
|
|
321
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
322
|
+
context_data_val = 0;
|
|
323
|
+
} else {
|
|
324
|
+
context_data_position++;
|
|
325
|
+
}
|
|
326
|
+
value = value >> 1;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
context_enlargeIn--;
|
|
330
|
+
if (context_enlargeIn == 0) {
|
|
331
|
+
context_enlargeIn = Math.pow(2, context_numBits);
|
|
332
|
+
context_numBits++;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
value = 2;
|
|
336
|
+
for (i = 0; i < context_numBits; i++) {
|
|
337
|
+
context_data_val = context_data_val << 1 | value & 1;
|
|
338
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
339
|
+
context_data_position = 0;
|
|
340
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
341
|
+
context_data_val = 0;
|
|
342
|
+
} else {
|
|
343
|
+
context_data_position++;
|
|
344
|
+
}
|
|
345
|
+
value = value >> 1;
|
|
346
|
+
}
|
|
347
|
+
while (true) {
|
|
348
|
+
context_data_val = context_data_val << 1;
|
|
349
|
+
if (context_data_position == bitsPerChar - 1) {
|
|
350
|
+
context_data.push(getCharFromInt(context_data_val));
|
|
351
|
+
break;
|
|
352
|
+
} else context_data_position++;
|
|
353
|
+
}
|
|
354
|
+
return context_data.join("");
|
|
355
|
+
},
|
|
356
|
+
decompress: function(compressed) {
|
|
357
|
+
if (compressed == null) return "";
|
|
358
|
+
if (compressed == "") return null;
|
|
359
|
+
return LZString2._decompress(compressed.length, 32768, function(index) {
|
|
360
|
+
return compressed.charCodeAt(index);
|
|
361
|
+
});
|
|
362
|
+
},
|
|
363
|
+
_decompress: function(length, resetValue, getNextValue) {
|
|
364
|
+
var dictionary = [], next, enlargeIn = 4, dictSize = 4, numBits = 3, entry = "", result = [], i, w, bits, resb, maxpower, power, c, data = { val: getNextValue(0), position: resetValue, index: 1 };
|
|
365
|
+
for (i = 0; i < 3; i += 1) {
|
|
366
|
+
dictionary[i] = i;
|
|
367
|
+
}
|
|
368
|
+
bits = 0;
|
|
369
|
+
maxpower = Math.pow(2, 2);
|
|
370
|
+
power = 1;
|
|
371
|
+
while (power != maxpower) {
|
|
372
|
+
resb = data.val & data.position;
|
|
373
|
+
data.position >>= 1;
|
|
374
|
+
if (data.position == 0) {
|
|
375
|
+
data.position = resetValue;
|
|
376
|
+
data.val = getNextValue(data.index++);
|
|
377
|
+
}
|
|
378
|
+
bits |= (resb > 0 ? 1 : 0) * power;
|
|
379
|
+
power <<= 1;
|
|
380
|
+
}
|
|
381
|
+
switch (next = bits) {
|
|
382
|
+
case 0:
|
|
383
|
+
bits = 0;
|
|
384
|
+
maxpower = Math.pow(2, 8);
|
|
385
|
+
power = 1;
|
|
386
|
+
while (power != maxpower) {
|
|
387
|
+
resb = data.val & data.position;
|
|
388
|
+
data.position >>= 1;
|
|
389
|
+
if (data.position == 0) {
|
|
390
|
+
data.position = resetValue;
|
|
391
|
+
data.val = getNextValue(data.index++);
|
|
392
|
+
}
|
|
393
|
+
bits |= (resb > 0 ? 1 : 0) * power;
|
|
394
|
+
power <<= 1;
|
|
395
|
+
}
|
|
396
|
+
c = f(bits);
|
|
397
|
+
break;
|
|
398
|
+
case 1:
|
|
399
|
+
bits = 0;
|
|
400
|
+
maxpower = Math.pow(2, 16);
|
|
401
|
+
power = 1;
|
|
402
|
+
while (power != maxpower) {
|
|
403
|
+
resb = data.val & data.position;
|
|
404
|
+
data.position >>= 1;
|
|
405
|
+
if (data.position == 0) {
|
|
406
|
+
data.position = resetValue;
|
|
407
|
+
data.val = getNextValue(data.index++);
|
|
408
|
+
}
|
|
409
|
+
bits |= (resb > 0 ? 1 : 0) * power;
|
|
410
|
+
power <<= 1;
|
|
411
|
+
}
|
|
412
|
+
c = f(bits);
|
|
413
|
+
break;
|
|
414
|
+
case 2:
|
|
415
|
+
return "";
|
|
416
|
+
}
|
|
417
|
+
dictionary[3] = c;
|
|
418
|
+
w = c;
|
|
419
|
+
result.push(c);
|
|
420
|
+
while (true) {
|
|
421
|
+
if (data.index > length) {
|
|
422
|
+
return "";
|
|
423
|
+
}
|
|
424
|
+
bits = 0;
|
|
425
|
+
maxpower = Math.pow(2, numBits);
|
|
426
|
+
power = 1;
|
|
427
|
+
while (power != maxpower) {
|
|
428
|
+
resb = data.val & data.position;
|
|
429
|
+
data.position >>= 1;
|
|
430
|
+
if (data.position == 0) {
|
|
431
|
+
data.position = resetValue;
|
|
432
|
+
data.val = getNextValue(data.index++);
|
|
433
|
+
}
|
|
434
|
+
bits |= (resb > 0 ? 1 : 0) * power;
|
|
435
|
+
power <<= 1;
|
|
436
|
+
}
|
|
437
|
+
switch (c = bits) {
|
|
438
|
+
case 0:
|
|
439
|
+
bits = 0;
|
|
440
|
+
maxpower = Math.pow(2, 8);
|
|
441
|
+
power = 1;
|
|
442
|
+
while (power != maxpower) {
|
|
443
|
+
resb = data.val & data.position;
|
|
444
|
+
data.position >>= 1;
|
|
445
|
+
if (data.position == 0) {
|
|
446
|
+
data.position = resetValue;
|
|
447
|
+
data.val = getNextValue(data.index++);
|
|
448
|
+
}
|
|
449
|
+
bits |= (resb > 0 ? 1 : 0) * power;
|
|
450
|
+
power <<= 1;
|
|
451
|
+
}
|
|
452
|
+
dictionary[dictSize++] = f(bits);
|
|
453
|
+
c = dictSize - 1;
|
|
454
|
+
enlargeIn--;
|
|
455
|
+
break;
|
|
456
|
+
case 1:
|
|
457
|
+
bits = 0;
|
|
458
|
+
maxpower = Math.pow(2, 16);
|
|
459
|
+
power = 1;
|
|
460
|
+
while (power != maxpower) {
|
|
461
|
+
resb = data.val & data.position;
|
|
462
|
+
data.position >>= 1;
|
|
463
|
+
if (data.position == 0) {
|
|
464
|
+
data.position = resetValue;
|
|
465
|
+
data.val = getNextValue(data.index++);
|
|
466
|
+
}
|
|
467
|
+
bits |= (resb > 0 ? 1 : 0) * power;
|
|
468
|
+
power <<= 1;
|
|
469
|
+
}
|
|
470
|
+
dictionary[dictSize++] = f(bits);
|
|
471
|
+
c = dictSize - 1;
|
|
472
|
+
enlargeIn--;
|
|
473
|
+
break;
|
|
474
|
+
case 2:
|
|
475
|
+
return result.join("");
|
|
476
|
+
}
|
|
477
|
+
if (enlargeIn == 0) {
|
|
478
|
+
enlargeIn = Math.pow(2, numBits);
|
|
479
|
+
numBits++;
|
|
480
|
+
}
|
|
481
|
+
if (dictionary[c]) {
|
|
482
|
+
entry = dictionary[c];
|
|
483
|
+
} else {
|
|
484
|
+
if (c === dictSize) {
|
|
485
|
+
entry = w + w.charAt(0);
|
|
486
|
+
} else {
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
result.push(entry);
|
|
491
|
+
dictionary[dictSize++] = w + entry.charAt(0);
|
|
492
|
+
enlargeIn--;
|
|
493
|
+
w = entry;
|
|
494
|
+
if (enlargeIn == 0) {
|
|
495
|
+
enlargeIn = Math.pow(2, numBits);
|
|
496
|
+
numBits++;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
return LZString2;
|
|
502
|
+
})();
|
|
503
|
+
if (typeof define === "function" && define.amd) {
|
|
504
|
+
define(function() {
|
|
505
|
+
return LZString;
|
|
506
|
+
});
|
|
507
|
+
} else if (typeof module !== "undefined" && module != null) {
|
|
508
|
+
module.exports = LZString;
|
|
509
|
+
} else if (typeof angular !== "undefined" && angular != null) {
|
|
510
|
+
angular.module("LZString", []).factory("LZString", function() {
|
|
511
|
+
return LZString;
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// src/slope/renderer.ts
|
|
518
|
+
import * as d3Scale from "d3-scale";
|
|
519
|
+
import * as d3Shape from "d3-shape";
|
|
520
|
+
import * as d3Array from "d3-array";
|
|
521
|
+
function resolveVerticalCollisions(items, minGap, maxY) {
|
|
522
|
+
if (items.length === 0) return [];
|
|
523
|
+
const sorted = items.map((it, i) => ({ ...it, idx: i })).sort((a, b) => a.naturalY - b.naturalY);
|
|
524
|
+
const adjustedY = new Array(items.length);
|
|
525
|
+
let prevBottom = -Infinity;
|
|
526
|
+
for (const item of sorted) {
|
|
527
|
+
const halfH = item.height / 2;
|
|
528
|
+
let top = Math.max(item.naturalY - halfH, prevBottom + minGap);
|
|
529
|
+
if (maxY !== void 0) {
|
|
530
|
+
top = Math.min(top, maxY - item.height);
|
|
531
|
+
}
|
|
532
|
+
adjustedY[item.idx] = top + halfH;
|
|
533
|
+
prevBottom = top + item.height;
|
|
534
|
+
}
|
|
535
|
+
return adjustedY;
|
|
536
|
+
}
|
|
537
|
+
var SLOPE_MARGIN = { top: 80, bottom: 40, left: 80 };
|
|
538
|
+
var SLOPE_LABEL_FONT_SIZE = 14;
|
|
539
|
+
function renderSlopeChart(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
540
|
+
const { periods, data } = parsed;
|
|
541
|
+
const title = parsed.noTitle ? null : parsed.title;
|
|
542
|
+
if (data.length === 0 || periods.length < 2) return;
|
|
543
|
+
const init = initD3Chart(container, palette, exportDims);
|
|
544
|
+
if (!init) return;
|
|
545
|
+
const { svg, width, height, textColor, mutedColor, bgColor, colors } = init;
|
|
546
|
+
const idealWidth = SLOPE_MARGIN.left + periods.length * 100 + 150;
|
|
547
|
+
const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
|
|
548
|
+
const sMarginTop = ctx.aesthetic(SLOPE_MARGIN.top);
|
|
549
|
+
const sMarginBottom = ctx.aesthetic(SLOPE_MARGIN.bottom);
|
|
550
|
+
const sMarginLeft = ctx.aesthetic(SLOPE_MARGIN.left);
|
|
551
|
+
const sLabelFontSize = ctx.text(SLOPE_LABEL_FONT_SIZE);
|
|
552
|
+
const sPeriodFont = ctx.text(18);
|
|
553
|
+
const sValueFont = ctx.text(16);
|
|
554
|
+
const sPeriodHeaderY = ctx.structural(15);
|
|
555
|
+
const sDash = `${ctx.structural(4)},${ctx.structural(4)}`;
|
|
556
|
+
const sLineStroke = ctx.structural(2.5);
|
|
557
|
+
const sHitWidth = ctx.structural(14);
|
|
558
|
+
const sPointR = ctx.structural(4);
|
|
559
|
+
const sPointStroke = ctx.structural(1.5);
|
|
560
|
+
const sValueLabelXOff = ctx.structural(10);
|
|
561
|
+
const sLabelGap = ctx.structural(10);
|
|
562
|
+
svg.attr("preserveAspectRatio", "xMidYMin meet");
|
|
563
|
+
if (ctx.isBelowFloor) {
|
|
564
|
+
svg.attr("width", "100%");
|
|
565
|
+
}
|
|
566
|
+
const maxLabelText = data.reduce((longest, item) => {
|
|
567
|
+
const text = `${item.values[item.values.length - 1]} \u2014 ${item.label}`;
|
|
568
|
+
return text.length > longest.length ? text : longest;
|
|
569
|
+
}, "");
|
|
570
|
+
const estimatedLabelWidth = measureText(maxLabelText, sLabelFontSize);
|
|
571
|
+
const maxRightMargin = Math.floor(width * 0.35);
|
|
572
|
+
const rightMargin = Math.min(
|
|
573
|
+
Math.max(estimatedLabelWidth + ctx.aesthetic(30), ctx.aesthetic(120)),
|
|
574
|
+
maxRightMargin
|
|
575
|
+
);
|
|
576
|
+
const innerWidth = width - sMarginLeft - rightMargin;
|
|
577
|
+
const innerHeight = height - sMarginTop - sMarginBottom;
|
|
578
|
+
const allValues = data.flatMap((d) => d.values);
|
|
579
|
+
const [minVal, maxVal] = d3Array.extent(allValues);
|
|
580
|
+
const valuePadding = (maxVal - minVal) * 0.1 || 1;
|
|
581
|
+
const yScale = d3Scale.scaleLinear().domain([minVal - valuePadding, maxVal + valuePadding]).range([innerHeight, 0]);
|
|
582
|
+
const xScale = d3Scale.scalePoint().domain(periods).range([0, innerWidth]).padding(0);
|
|
583
|
+
const g = svg.append("g").attr("transform", `translate(${sMarginLeft},${sMarginTop})`);
|
|
584
|
+
const tooltip = createTooltip(container, palette, isDark);
|
|
585
|
+
renderChartTitle(
|
|
586
|
+
svg,
|
|
587
|
+
title,
|
|
588
|
+
parsed.titleLineNumber,
|
|
589
|
+
width,
|
|
590
|
+
textColor,
|
|
591
|
+
onClickItem
|
|
592
|
+
);
|
|
593
|
+
for (const period of periods) {
|
|
594
|
+
const x = xScale(period);
|
|
595
|
+
g.append("text").attr("x", x).attr("y", -sPeriodHeaderY).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", `${sPeriodFont}px`).attr("font-weight", "600").text(period);
|
|
596
|
+
g.append("line").attr("x1", x).attr("y1", 0).attr("x2", x).attr("y2", innerHeight).attr("stroke", mutedColor).attr("stroke-width", 1).attr("stroke-dasharray", sDash);
|
|
597
|
+
}
|
|
598
|
+
const lineGen = d3Shape.line().x((_d, i) => xScale(periods[i])).y((d) => yScale(d));
|
|
599
|
+
const seriesInfo = data.map((item, idx) => {
|
|
600
|
+
const color = item.color ?? colors[idx % colors.length];
|
|
601
|
+
const firstVal = item.values[0];
|
|
602
|
+
const lastVal = item.values[item.values.length - 1];
|
|
603
|
+
const absChange = lastVal - firstVal;
|
|
604
|
+
const pctChange = firstVal !== 0 ? absChange / firstVal * 100 : null;
|
|
605
|
+
const sign = absChange > 0 ? "+" : "";
|
|
606
|
+
const tipLines = [`${sign}${parseFloat(absChange.toFixed(2))}`];
|
|
607
|
+
if (pctChange !== null) tipLines.push(`${sign}${pctChange.toFixed(1)}%`);
|
|
608
|
+
const tipHtml = tipLines.join("<br>");
|
|
609
|
+
const lastX = xScale(periods[periods.length - 1]);
|
|
610
|
+
const labelText = `${lastVal} \u2014 ${item.label}`;
|
|
611
|
+
const availableWidth = rightMargin - ctx.aesthetic(15);
|
|
612
|
+
let labelLineCount = 1;
|
|
613
|
+
let wrappedLines = null;
|
|
614
|
+
if (measureText(labelText, sLabelFontSize) > availableWidth) {
|
|
615
|
+
const lines = wrapTextToWidth(labelText, sLabelFontSize, availableWidth);
|
|
616
|
+
labelLineCount = lines.length;
|
|
617
|
+
wrappedLines = lines;
|
|
618
|
+
}
|
|
619
|
+
const lineHeight = sLabelFontSize * 1.2;
|
|
620
|
+
const labelHeight = labelLineCount === 1 ? sLabelFontSize : labelLineCount * lineHeight;
|
|
621
|
+
return {
|
|
622
|
+
item,
|
|
623
|
+
idx,
|
|
624
|
+
color,
|
|
625
|
+
firstVal,
|
|
626
|
+
lastVal,
|
|
627
|
+
tipHtml,
|
|
628
|
+
lastX,
|
|
629
|
+
labelText,
|
|
630
|
+
wrappedLines,
|
|
631
|
+
labelHeight
|
|
632
|
+
};
|
|
633
|
+
});
|
|
634
|
+
const leftLabelHeight = sValueFont * 1.25;
|
|
635
|
+
const leftLabelCollisions = /* @__PURE__ */ new Map();
|
|
636
|
+
for (let pi = 0; pi < periods.length - 1; pi++) {
|
|
637
|
+
const entries = data.map((item) => ({
|
|
638
|
+
// pi is in-bounds by loop guard against periods.length, and each data row has periods.length values.
|
|
639
|
+
naturalY: yScale(item.values[pi]),
|
|
640
|
+
height: leftLabelHeight
|
|
641
|
+
}));
|
|
642
|
+
leftLabelCollisions.set(
|
|
643
|
+
pi,
|
|
644
|
+
resolveVerticalCollisions(entries, 4, innerHeight)
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
const rightEntries = seriesInfo.map((si) => ({
|
|
648
|
+
naturalY: yScale(si.lastVal),
|
|
649
|
+
height: Math.max(si.labelHeight, sLabelFontSize * 1.4)
|
|
650
|
+
}));
|
|
651
|
+
const rightAdjustedY = resolveVerticalCollisions(
|
|
652
|
+
rightEntries,
|
|
653
|
+
4,
|
|
654
|
+
innerHeight
|
|
655
|
+
);
|
|
656
|
+
data.forEach((item, idx) => {
|
|
657
|
+
const si = seriesInfo[idx];
|
|
658
|
+
const color = si.color;
|
|
659
|
+
const seriesG = g.append("g").attr("class", "slope-series").attr("data-line-number", String(item.lineNumber));
|
|
660
|
+
seriesG.append("path").datum(item.values).attr("fill", "none").attr("stroke", color).attr("stroke-width", sLineStroke).attr("d", lineGen);
|
|
661
|
+
seriesG.append("path").datum(item.values).attr("fill", "none").attr("stroke", "transparent").attr("stroke-width", sHitWidth).attr("d", lineGen).style("cursor", onClickItem ? "pointer" : "default").on(
|
|
662
|
+
"mouseenter",
|
|
663
|
+
(event) => showTooltip(tooltip, si.tipHtml, event)
|
|
664
|
+
).on(
|
|
665
|
+
"mousemove",
|
|
666
|
+
(event) => showTooltip(tooltip, si.tipHtml, event)
|
|
667
|
+
).on("mouseleave", () => hideTooltip(tooltip)).on("click", () => {
|
|
668
|
+
if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
|
|
669
|
+
});
|
|
670
|
+
item.values.forEach((val, i) => {
|
|
671
|
+
const x = xScale(periods[i]);
|
|
672
|
+
const y = yScale(val);
|
|
673
|
+
seriesG.append("circle").attr("cx", x).attr("cy", y).attr("r", sPointR).attr("fill", color).attr("stroke", bgColor).attr("stroke-width", sPointStroke).style("cursor", onClickItem ? "pointer" : "default").on(
|
|
674
|
+
"mouseenter",
|
|
675
|
+
(event) => showTooltip(tooltip, si.tipHtml, event)
|
|
676
|
+
).on(
|
|
677
|
+
"mousemove",
|
|
678
|
+
(event) => showTooltip(tooltip, si.tipHtml, event)
|
|
679
|
+
).on("mouseleave", () => hideTooltip(tooltip)).on("click", () => {
|
|
680
|
+
if (onClickItem && item.lineNumber) onClickItem(item.lineNumber);
|
|
681
|
+
});
|
|
682
|
+
const isFirst = i === 0;
|
|
683
|
+
const isLast = i === periods.length - 1;
|
|
684
|
+
if (!isLast) {
|
|
685
|
+
const adjustedY = leftLabelCollisions.get(i)[idx];
|
|
686
|
+
seriesG.append("text").attr("x", isFirst ? x - sValueLabelXOff : x).attr("y", adjustedY).attr("dy", "0.35em").attr("text-anchor", isFirst ? "end" : "middle").attr("fill", color).attr("font-size", `${sValueFont}px`).text(val.toString());
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
const adjustedLastY = rightAdjustedY[idx];
|
|
690
|
+
const labelEl = seriesG.append("text").attr("x", si.lastX + sLabelGap).attr("y", adjustedLastY).attr("text-anchor", "start").attr("fill", color).attr("font-size", `${sLabelFontSize}px`).attr("font-weight", "500");
|
|
691
|
+
if (!si.wrappedLines) {
|
|
692
|
+
labelEl.attr("dy", "0.35em").text(si.labelText);
|
|
693
|
+
} else {
|
|
694
|
+
const lineHeight = sLabelFontSize * 1.2;
|
|
695
|
+
const totalHeight = (si.wrappedLines.length - 1) * lineHeight;
|
|
696
|
+
const startDy = -totalHeight / 2;
|
|
697
|
+
si.wrappedLines.forEach((line2, li) => {
|
|
698
|
+
labelEl.append("tspan").attr("x", si.lastX + sLabelGap).attr(
|
|
699
|
+
"dy",
|
|
700
|
+
li === 0 ? `${startDy + sLabelFontSize * 0.35}px` : `${lineHeight}px`
|
|
701
|
+
).text(line2);
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// src/arc/renderer.ts
|
|
708
|
+
import * as d3Scale2 from "d3-scale";
|
|
709
|
+
import * as d3Selection from "d3-selection";
|
|
710
|
+
import * as d3Array2 from "d3-array";
|
|
711
|
+
function orderArcNodes(links, order, groups) {
|
|
712
|
+
const nodeSet = /* @__PURE__ */ new Set();
|
|
713
|
+
for (const link of links) {
|
|
714
|
+
nodeSet.add(link.source);
|
|
715
|
+
nodeSet.add(link.target);
|
|
716
|
+
}
|
|
717
|
+
const allNodes = Array.from(nodeSet);
|
|
718
|
+
if (order === "name") {
|
|
719
|
+
return allNodes.slice().sort((a, b) => a.localeCompare(b));
|
|
720
|
+
}
|
|
721
|
+
if (order === "degree") {
|
|
722
|
+
const degree = /* @__PURE__ */ new Map();
|
|
723
|
+
for (const node of allNodes) degree.set(node, 0);
|
|
724
|
+
for (const link of links) {
|
|
725
|
+
degree.set(link.source, degree.get(link.source) + link.value);
|
|
726
|
+
degree.set(link.target, degree.get(link.target) + link.value);
|
|
727
|
+
}
|
|
728
|
+
return allNodes.slice().sort((a, b) => {
|
|
729
|
+
const diff = degree.get(b) - degree.get(a);
|
|
730
|
+
return diff !== 0 ? diff : a.localeCompare(b);
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
if (order === "group") {
|
|
734
|
+
if (groups.length > 0) {
|
|
735
|
+
const ordered = [];
|
|
736
|
+
const placed = /* @__PURE__ */ new Set();
|
|
737
|
+
for (const group of groups) {
|
|
738
|
+
for (const node of group.nodes) {
|
|
739
|
+
if (!placed.has(node)) {
|
|
740
|
+
ordered.push(node);
|
|
741
|
+
placed.add(node);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
for (const node of allNodes) {
|
|
746
|
+
if (!placed.has(node)) {
|
|
747
|
+
ordered.push(node);
|
|
748
|
+
placed.add(node);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return ordered;
|
|
752
|
+
}
|
|
753
|
+
const adj = /* @__PURE__ */ new Map();
|
|
754
|
+
for (const node of allNodes) adj.set(node, /* @__PURE__ */ new Set());
|
|
755
|
+
for (const link of links) {
|
|
756
|
+
adj.get(link.source).add(link.target);
|
|
757
|
+
adj.get(link.target).add(link.source);
|
|
758
|
+
}
|
|
759
|
+
const degree = /* @__PURE__ */ new Map();
|
|
760
|
+
for (const node of allNodes) degree.set(node, 0);
|
|
761
|
+
for (const link of links) {
|
|
762
|
+
degree.set(link.source, degree.get(link.source) + link.value);
|
|
763
|
+
degree.set(link.target, degree.get(link.target) + link.value);
|
|
764
|
+
}
|
|
765
|
+
const visited = /* @__PURE__ */ new Set();
|
|
766
|
+
const components = [];
|
|
767
|
+
const remaining = new Set(allNodes);
|
|
768
|
+
while (remaining.size > 0) {
|
|
769
|
+
let root = "";
|
|
770
|
+
let maxDeg = -1;
|
|
771
|
+
for (const node of remaining) {
|
|
772
|
+
if (degree.get(node) > maxDeg) {
|
|
773
|
+
maxDeg = degree.get(node);
|
|
774
|
+
root = node;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
const component = [];
|
|
778
|
+
const queue = [root];
|
|
779
|
+
visited.add(root);
|
|
780
|
+
remaining.delete(root);
|
|
781
|
+
while (queue.length > 0) {
|
|
782
|
+
const curr = queue.shift();
|
|
783
|
+
component.push(curr);
|
|
784
|
+
for (const neighbor of adj.get(curr)) {
|
|
785
|
+
if (!visited.has(neighbor)) {
|
|
786
|
+
visited.add(neighbor);
|
|
787
|
+
remaining.delete(neighbor);
|
|
788
|
+
queue.push(neighbor);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
components.push(component);
|
|
793
|
+
}
|
|
794
|
+
components.sort((a, b) => b.length - a.length);
|
|
795
|
+
return components.flat();
|
|
796
|
+
}
|
|
797
|
+
return allNodes;
|
|
798
|
+
}
|
|
799
|
+
var ARC_MARGIN_TOP = 60;
|
|
800
|
+
var ARC_MARGIN_RIGHT = 40;
|
|
801
|
+
var ARC_MARGIN_BOTTOM = 60;
|
|
802
|
+
var ARC_MARGIN_LEFT = 40;
|
|
803
|
+
var ARC_MARGIN_LEFT_VERTICAL = 120;
|
|
804
|
+
var ARC_NODE_RADIUS = 5;
|
|
805
|
+
var ARC_NODE_STROKE_WIDTH = 1.5;
|
|
806
|
+
var ARC_NODE_LABEL_FONT = 11;
|
|
807
|
+
var ARC_GROUP_LABEL_FONT = 12;
|
|
808
|
+
var ARC_BAND_HALF_W = 60;
|
|
809
|
+
var ARC_BAND_HALF_H = 40;
|
|
810
|
+
var ARC_BAND_RADIUS = 4;
|
|
811
|
+
var ARC_BAND_LABEL_X_OFFSET = 6;
|
|
812
|
+
var ARC_BAND_LABEL_Y_OFFSET = 14;
|
|
813
|
+
var ARC_BAND_LABEL_BOTTOM_OFFSET = 4;
|
|
814
|
+
var ARC_NODE_LABEL_X_OFFSET = 14;
|
|
815
|
+
var ARC_NODE_LABEL_Y_OFFSET = 20;
|
|
816
|
+
var ARC_STROKE_MIN = 1.5;
|
|
817
|
+
var ARC_STROKE_MAX = 6;
|
|
818
|
+
var ARC_BASELINE_STROKE_WIDTH = 1;
|
|
819
|
+
function renderArcDiagram(container, parsed, palette, _isDark, onClickItem, exportDims) {
|
|
820
|
+
const { links, orientation, arcOrder, arcNodeGroups } = parsed;
|
|
821
|
+
const title = parsed.noTitle ? null : parsed.title;
|
|
822
|
+
if (links.length === 0) return;
|
|
823
|
+
const init = initD3Chart(container, palette, exportDims);
|
|
824
|
+
if (!init) return;
|
|
825
|
+
const { svg, width, height, textColor, mutedColor, bgColor, colors } = init;
|
|
826
|
+
const isVertical = orientation === "vertical";
|
|
827
|
+
const nodes = orderArcNodes(links, arcOrder, arcNodeGroups);
|
|
828
|
+
const idealWidth = isVertical ? ARC_MARGIN_LEFT_VERTICAL + ARC_MARGIN_RIGHT + ARC_BAND_HALF_W * 2 + 100 : nodes.length * 20 + ARC_MARGIN_LEFT + ARC_MARGIN_RIGHT;
|
|
829
|
+
const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
|
|
830
|
+
const sMarginTop = ctx.aesthetic(ARC_MARGIN_TOP);
|
|
831
|
+
const sMarginRight = ctx.aesthetic(ARC_MARGIN_RIGHT);
|
|
832
|
+
const sMarginBottom = ctx.aesthetic(ARC_MARGIN_BOTTOM);
|
|
833
|
+
const sMarginLeft = isVertical ? ctx.aesthetic(ARC_MARGIN_LEFT_VERTICAL) : ctx.aesthetic(ARC_MARGIN_LEFT);
|
|
834
|
+
const sNodeRadius = ctx.structural(ARC_NODE_RADIUS);
|
|
835
|
+
const sNodeStrokeWidth = ctx.structural(ARC_NODE_STROKE_WIDTH);
|
|
836
|
+
const sNodeLabelFont = ctx.text(ARC_NODE_LABEL_FONT);
|
|
837
|
+
const sGroupLabelFont = ctx.text(ARC_GROUP_LABEL_FONT);
|
|
838
|
+
const sBandHalfW = ctx.aesthetic(ARC_BAND_HALF_W);
|
|
839
|
+
const sBandHalfH = ctx.aesthetic(ARC_BAND_HALF_H);
|
|
840
|
+
const sBandRadius = ctx.structural(ARC_BAND_RADIUS);
|
|
841
|
+
const sBandLabelXOffset = ctx.structural(ARC_BAND_LABEL_X_OFFSET);
|
|
842
|
+
const sBandLabelYOffset = ctx.structural(ARC_BAND_LABEL_Y_OFFSET);
|
|
843
|
+
const sBandLabelBottomOffset = ctx.structural(ARC_BAND_LABEL_BOTTOM_OFFSET);
|
|
844
|
+
const sNodeLabelXOffset = ctx.structural(ARC_NODE_LABEL_X_OFFSET);
|
|
845
|
+
const sNodeLabelYOffset = ctx.structural(ARC_NODE_LABEL_Y_OFFSET);
|
|
846
|
+
const sStrokeMin = ctx.structural(ARC_STROKE_MIN);
|
|
847
|
+
const sStrokeMax = ctx.structural(ARC_STROKE_MAX);
|
|
848
|
+
const sBaselineDash = `${ctx.structural(4)},${ctx.structural(4)}`;
|
|
849
|
+
const sBaselineStrokeWidth = ctx.structural(ARC_BASELINE_STROKE_WIDTH);
|
|
850
|
+
svg.attr("preserveAspectRatio", "xMidYMin meet");
|
|
851
|
+
if (ctx.isBelowFloor) {
|
|
852
|
+
svg.attr("width", "100%");
|
|
853
|
+
}
|
|
854
|
+
const margin = {
|
|
855
|
+
top: sMarginTop,
|
|
856
|
+
right: sMarginRight,
|
|
857
|
+
bottom: sMarginBottom,
|
|
858
|
+
left: sMarginLeft
|
|
859
|
+
};
|
|
860
|
+
const innerWidth = width - margin.left - margin.right;
|
|
861
|
+
const innerHeight = height - margin.top - margin.bottom;
|
|
862
|
+
const nodeColorMap = /* @__PURE__ */ new Map();
|
|
863
|
+
for (const group of arcNodeGroups) {
|
|
864
|
+
if (group.color) {
|
|
865
|
+
for (const node of group.nodes) {
|
|
866
|
+
if (!nodeColorMap.has(node)) {
|
|
867
|
+
nodeColorMap.set(node, group.color);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
const groupNodeSets = /* @__PURE__ */ new Map();
|
|
873
|
+
for (const group of arcNodeGroups) {
|
|
874
|
+
groupNodeSets.set(group.name, new Set(group.nodes));
|
|
875
|
+
}
|
|
876
|
+
const values = links.map((l) => l.value);
|
|
877
|
+
const [minVal, maxVal] = d3Array2.extent(values);
|
|
878
|
+
const strokeScale = d3Scale2.scaleLinear().domain([minVal, maxVal]).range([sStrokeMin, sStrokeMax]);
|
|
879
|
+
const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
880
|
+
renderChartTitle(
|
|
881
|
+
svg,
|
|
882
|
+
title,
|
|
883
|
+
parsed.titleLineNumber,
|
|
884
|
+
width,
|
|
885
|
+
textColor,
|
|
886
|
+
onClickItem
|
|
887
|
+
);
|
|
888
|
+
const neighbors = /* @__PURE__ */ new Map();
|
|
889
|
+
for (const node of nodes) neighbors.set(node, /* @__PURE__ */ new Set());
|
|
890
|
+
for (const link of links) {
|
|
891
|
+
neighbors.get(link.source).add(link.target);
|
|
892
|
+
neighbors.get(link.target).add(link.source);
|
|
893
|
+
}
|
|
894
|
+
const FADE_OPACITY = 0.1;
|
|
895
|
+
function handleMouseEnter(hovered) {
|
|
896
|
+
const connected = neighbors.get(hovered);
|
|
897
|
+
g.selectAll(".arc-link").each(function() {
|
|
898
|
+
const el = d3Selection.select(this);
|
|
899
|
+
const src = el.attr("data-source");
|
|
900
|
+
const tgt = el.attr("data-target");
|
|
901
|
+
const isRelated = src === hovered || tgt === hovered;
|
|
902
|
+
el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
|
|
903
|
+
});
|
|
904
|
+
g.selectAll(".arc-node").each(function() {
|
|
905
|
+
const el = d3Selection.select(this);
|
|
906
|
+
const name = el.attr("data-node");
|
|
907
|
+
const isRelated = name === hovered || connected.has(name);
|
|
908
|
+
el.attr("opacity", isRelated ? 1 : FADE_OPACITY);
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
function handleMouseLeave() {
|
|
912
|
+
g.selectAll(".arc-link").attr(
|
|
913
|
+
"stroke-opacity",
|
|
914
|
+
0.7
|
|
915
|
+
);
|
|
916
|
+
g.selectAll(".arc-node").attr("opacity", 1);
|
|
917
|
+
g.selectAll(".arc-group-band").attr(
|
|
918
|
+
"fill-opacity",
|
|
919
|
+
0.06
|
|
920
|
+
);
|
|
921
|
+
g.selectAll(".arc-group-label").attr(
|
|
922
|
+
"fill-opacity",
|
|
923
|
+
0.5
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
function handleGroupEnter(groupName) {
|
|
927
|
+
const members = groupNodeSets.get(groupName);
|
|
928
|
+
if (!members) return;
|
|
929
|
+
g.selectAll(".arc-link").each(function() {
|
|
930
|
+
const el = d3Selection.select(this);
|
|
931
|
+
const isRelated = members.has(el.attr("data-source")) || members.has(el.attr("data-target"));
|
|
932
|
+
el.attr("stroke-opacity", isRelated ? 0.85 : FADE_OPACITY);
|
|
933
|
+
});
|
|
934
|
+
g.selectAll(".arc-node").each(function() {
|
|
935
|
+
const el = d3Selection.select(this);
|
|
936
|
+
el.attr("opacity", members.has(el.attr("data-node")) ? 1 : FADE_OPACITY);
|
|
937
|
+
});
|
|
938
|
+
g.selectAll(".arc-group-band").each(function() {
|
|
939
|
+
const el = d3Selection.select(this);
|
|
940
|
+
el.attr(
|
|
941
|
+
"fill-opacity",
|
|
942
|
+
el.attr("data-group") === groupName ? 0.18 : 0.03
|
|
943
|
+
);
|
|
944
|
+
});
|
|
945
|
+
g.selectAll(".arc-group-label").each(function() {
|
|
946
|
+
const el = d3Selection.select(this);
|
|
947
|
+
el.attr("fill-opacity", el.attr("data-group") === groupName ? 1 : 0.2);
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
if (isVertical) {
|
|
951
|
+
const yScale = d3Scale2.scalePoint().domain(nodes).range([0, innerHeight]).padding(0.5);
|
|
952
|
+
const baseX = innerWidth / 2;
|
|
953
|
+
if (arcNodeGroups.length > 0) {
|
|
954
|
+
const bandPad = (yScale.step?.() ?? 20) * 0.4;
|
|
955
|
+
for (const group of arcNodeGroups) {
|
|
956
|
+
const groupNodes = group.nodes.filter((n) => nodes.includes(n));
|
|
957
|
+
if (groupNodes.length === 0) continue;
|
|
958
|
+
const positions = groupNodes.map((n) => yScale(n));
|
|
959
|
+
const minY = Math.min(...positions) - bandPad;
|
|
960
|
+
const maxY = Math.max(...positions) + bandPad;
|
|
961
|
+
g.append("rect").attr("class", "arc-group-band").attr("data-group", group.name).attr("data-line-number", String(group.lineNumber)).attr("x", baseX - sBandHalfW).attr("y", minY).attr("width", sBandHalfW * 2).attr("height", maxY - minY).attr("rx", sBandRadius).attr("fill", textColor).attr("fill-opacity", 0.06).style("cursor", "pointer").on("mouseenter", () => handleGroupEnter(group.name)).on("mouseleave", handleMouseLeave).on("click", () => {
|
|
962
|
+
if (onClickItem) onClickItem(group.lineNumber);
|
|
963
|
+
});
|
|
964
|
+
g.append("text").attr("class", "arc-group-label").attr("data-group", group.name).attr("data-line-number", String(group.lineNumber)).attr("x", baseX - sBandHalfW + sBandLabelXOffset).attr("y", minY + sBandLabelYOffset).attr("fill", textColor).attr("font-size", `${sGroupLabelFont}px`).attr("font-weight", "600").attr("fill-opacity", 0.5).style("cursor", onClickItem ? "pointer" : "default").text(group.name).on("mouseenter", () => handleGroupEnter(group.name)).on("mouseleave", handleMouseLeave).on("click", () => {
|
|
965
|
+
if (onClickItem) onClickItem(group.lineNumber);
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
g.append("line").attr("x1", baseX).attr("y1", 0).attr("x2", baseX).attr("y2", innerHeight).attr("stroke", mutedColor).attr("stroke-width", sBaselineStrokeWidth).attr("stroke-dasharray", sBaselineDash);
|
|
970
|
+
links.forEach((link, idx) => {
|
|
971
|
+
const y1 = yScale(link.source);
|
|
972
|
+
const y2 = yScale(link.target);
|
|
973
|
+
const midY = (y1 + y2) / 2;
|
|
974
|
+
const distance = Math.abs(y2 - y1);
|
|
975
|
+
const controlX = baseX + distance * 0.4;
|
|
976
|
+
const color = link.color ?? colors[idx % colors.length];
|
|
977
|
+
g.append("path").attr("class", "arc-link").attr("data-source", link.source).attr("data-target", link.target).attr("data-line-number", String(link.lineNumber)).attr("d", `M ${baseX},${y1} Q ${controlX},${midY} ${baseX},${y2}`).attr("fill", "none").attr("stroke", color).attr("stroke-width", strokeScale(link.value)).attr("stroke-opacity", 0.7).style("cursor", onClickItem ? "pointer" : "default").on("click", () => {
|
|
978
|
+
if (onClickItem && link.lineNumber) onClickItem(link.lineNumber);
|
|
979
|
+
});
|
|
980
|
+
});
|
|
981
|
+
for (const node of nodes) {
|
|
982
|
+
const y = yScale(node);
|
|
983
|
+
const nodeColor = nodeColorMap.get(node) ?? textColor;
|
|
984
|
+
const nodeLink = links.find(
|
|
985
|
+
(l) => l.source === node || l.target === node
|
|
986
|
+
);
|
|
987
|
+
const nodeG = g.append("g").attr("class", "arc-node").attr("data-node", node).attr(
|
|
988
|
+
"data-line-number",
|
|
989
|
+
nodeLink?.lineNumber ? String(nodeLink.lineNumber) : null
|
|
990
|
+
).style("cursor", "pointer").on("mouseenter", () => handleMouseEnter(node)).on("mouseleave", handleMouseLeave).on("click", () => {
|
|
991
|
+
if (onClickItem && nodeLink?.lineNumber)
|
|
992
|
+
onClickItem(nodeLink.lineNumber);
|
|
993
|
+
});
|
|
994
|
+
nodeG.append("circle").attr("cx", baseX).attr("cy", y).attr("r", sNodeRadius).attr("fill", nodeColor).attr("stroke", bgColor).attr("stroke-width", sNodeStrokeWidth);
|
|
995
|
+
nodeG.append("text").attr("x", baseX - sNodeLabelXOffset).attr("y", y).attr("dy", "0.35em").attr("text-anchor", "end").attr("fill", textColor).attr("font-size", `${sNodeLabelFont}px`).text(node);
|
|
996
|
+
}
|
|
997
|
+
} else {
|
|
998
|
+
const xScale = d3Scale2.scalePoint().domain(nodes).range([0, innerWidth]).padding(0.5);
|
|
999
|
+
let baseY = innerHeight / 2;
|
|
1000
|
+
if (exportDims) {
|
|
1001
|
+
let maxDist = 0;
|
|
1002
|
+
for (const l of links) {
|
|
1003
|
+
const a = xScale(l.source);
|
|
1004
|
+
const b = xScale(l.target);
|
|
1005
|
+
if (a != null && b != null)
|
|
1006
|
+
maxDist = Math.max(maxDist, Math.abs(b - a));
|
|
1007
|
+
}
|
|
1008
|
+
const above = maxDist * 0.2 + sNodeRadius;
|
|
1009
|
+
const below = Math.max(sBandHalfH, sNodeLabelYOffset + sNodeLabelFont);
|
|
1010
|
+
baseY = above;
|
|
1011
|
+
const tightHeight = margin.top + above + below + margin.bottom;
|
|
1012
|
+
svg.attr("height", tightHeight).attr("viewBox", `0 0 ${width} ${tightHeight}`);
|
|
1013
|
+
}
|
|
1014
|
+
if (arcNodeGroups.length > 0) {
|
|
1015
|
+
const bandPad = (xScale.step?.() ?? 20) * 0.4;
|
|
1016
|
+
for (const group of arcNodeGroups) {
|
|
1017
|
+
const groupNodes = group.nodes.filter((n) => nodes.includes(n));
|
|
1018
|
+
if (groupNodes.length === 0) continue;
|
|
1019
|
+
const positions = groupNodes.map((n) => xScale(n));
|
|
1020
|
+
const minX = Math.min(...positions) - bandPad;
|
|
1021
|
+
const maxX = Math.max(...positions) + bandPad;
|
|
1022
|
+
g.append("rect").attr("class", "arc-group-band").attr("data-group", group.name).attr("data-line-number", String(group.lineNumber)).attr("x", minX).attr("y", baseY - sBandHalfH).attr("width", maxX - minX).attr("height", sBandHalfH * 2).attr("rx", sBandRadius).attr("fill", textColor).attr("fill-opacity", 0.06).style("cursor", "pointer").on("mouseenter", () => handleGroupEnter(group.name)).on("mouseleave", handleMouseLeave).on("click", () => {
|
|
1023
|
+
if (onClickItem) onClickItem(group.lineNumber);
|
|
1024
|
+
});
|
|
1025
|
+
g.append("text").attr("class", "arc-group-label").attr("data-group", group.name).attr("data-line-number", String(group.lineNumber)).attr("x", (minX + maxX) / 2).attr("y", baseY + sBandHalfH - sBandLabelBottomOffset).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", `${sGroupLabelFont}px`).attr("font-weight", "600").attr("fill-opacity", 0.5).style("cursor", onClickItem ? "pointer" : "default").text(group.name).on("mouseenter", () => handleGroupEnter(group.name)).on("mouseleave", handleMouseLeave).on("click", () => {
|
|
1026
|
+
if (onClickItem) onClickItem(group.lineNumber);
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
g.append("line").attr("x1", 0).attr("y1", baseY).attr("x2", innerWidth).attr("y2", baseY).attr("stroke", mutedColor).attr("stroke-width", sBaselineStrokeWidth).attr("stroke-dasharray", sBaselineDash);
|
|
1031
|
+
links.forEach((link, idx) => {
|
|
1032
|
+
const x1 = xScale(link.source);
|
|
1033
|
+
const x2 = xScale(link.target);
|
|
1034
|
+
const midX = (x1 + x2) / 2;
|
|
1035
|
+
const distance = Math.abs(x2 - x1);
|
|
1036
|
+
const controlY = baseY - distance * 0.4;
|
|
1037
|
+
const color = link.color ?? colors[idx % colors.length];
|
|
1038
|
+
g.append("path").attr("class", "arc-link").attr("data-source", link.source).attr("data-target", link.target).attr("data-line-number", String(link.lineNumber)).attr("d", `M ${x1},${baseY} Q ${midX},${controlY} ${x2},${baseY}`).attr("fill", "none").attr("stroke", color).attr("stroke-width", strokeScale(link.value)).attr("stroke-opacity", 0.7).style("cursor", onClickItem ? "pointer" : "default").on("click", () => {
|
|
1039
|
+
if (onClickItem && link.lineNumber) onClickItem(link.lineNumber);
|
|
1040
|
+
});
|
|
1041
|
+
});
|
|
1042
|
+
for (const node of nodes) {
|
|
1043
|
+
const x = xScale(node);
|
|
1044
|
+
const nodeColor = nodeColorMap.get(node) ?? textColor;
|
|
1045
|
+
const nodeLink = links.find(
|
|
1046
|
+
(l) => l.source === node || l.target === node
|
|
1047
|
+
);
|
|
1048
|
+
const nodeG = g.append("g").attr("class", "arc-node").attr("data-node", node).attr(
|
|
1049
|
+
"data-line-number",
|
|
1050
|
+
nodeLink?.lineNumber ? String(nodeLink.lineNumber) : null
|
|
1051
|
+
).style("cursor", "pointer").on("mouseenter", () => handleMouseEnter(node)).on("mouseleave", handleMouseLeave).on("click", () => {
|
|
1052
|
+
if (onClickItem && nodeLink?.lineNumber)
|
|
1053
|
+
onClickItem(nodeLink.lineNumber);
|
|
1054
|
+
});
|
|
1055
|
+
nodeG.append("circle").attr("cx", x).attr("cy", baseY).attr("r", sNodeRadius).attr("fill", nodeColor).attr("stroke", bgColor).attr("stroke-width", sNodeStrokeWidth);
|
|
1056
|
+
nodeG.append("text").attr("x", x).attr("y", baseY + sNodeLabelYOffset).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", `${sNodeLabelFont}px`).text(node);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// src/wordcloud/renderer.ts
|
|
1062
|
+
import * as d3Selection2 from "d3-selection";
|
|
1063
|
+
import cloud from "d3-cloud";
|
|
1064
|
+
function getRotateFn(mode) {
|
|
1065
|
+
if (mode === "mixed") return () => Math.random() > 0.5 ? 0 : 90;
|
|
1066
|
+
if (mode === "angled") return () => Math.round(Math.random() * 30 - 15);
|
|
1067
|
+
return () => 0;
|
|
1068
|
+
}
|
|
1069
|
+
function hasCanvas2d() {
|
|
1070
|
+
try {
|
|
1071
|
+
if (typeof document === "undefined") return false;
|
|
1072
|
+
const canvas = document.createElement("canvas");
|
|
1073
|
+
return typeof canvas.getContext === "function" && !!canvas.getContext("2d");
|
|
1074
|
+
} catch {
|
|
1075
|
+
return false;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
function estimateWordWidth(text, size) {
|
|
1079
|
+
return measureText(text, size);
|
|
1080
|
+
}
|
|
1081
|
+
function layoutWordsNoCanvas(words, width, height, padding, rotateFn) {
|
|
1082
|
+
const sorted = [...words].sort((a, b) => b.size - a.size);
|
|
1083
|
+
const placed = [];
|
|
1084
|
+
const maxR = Math.sqrt(width * width + height * height) / 2;
|
|
1085
|
+
const aspect = width > 0 ? height / width : 1;
|
|
1086
|
+
for (const w of sorted) {
|
|
1087
|
+
const rotate = rotateFn();
|
|
1088
|
+
const rawW = estimateWordWidth(w.text, w.size) + padding * 2;
|
|
1089
|
+
const rawH = w.size + padding * 2;
|
|
1090
|
+
const rad = rotate * Math.PI / 180;
|
|
1091
|
+
const cos = Math.abs(Math.cos(rad));
|
|
1092
|
+
const sin = Math.abs(Math.sin(rad));
|
|
1093
|
+
const halfW = (rawW * cos + rawH * sin) / 2;
|
|
1094
|
+
const halfH = (rawW * sin + rawH * cos) / 2;
|
|
1095
|
+
let spot = null;
|
|
1096
|
+
for (let t = 0; t < 4e3; t++) {
|
|
1097
|
+
const a = t * 0.25;
|
|
1098
|
+
const r = a * 1.4;
|
|
1099
|
+
if (r > maxR) break;
|
|
1100
|
+
const x = Math.cos(a) * r;
|
|
1101
|
+
const y = Math.sin(a) * r * aspect;
|
|
1102
|
+
if (x - halfW < -width / 2 || x + halfW > width / 2 || y - halfH < -height / 2 || y + halfH > height / 2) {
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
let collides = false;
|
|
1106
|
+
for (const p of placed) {
|
|
1107
|
+
if (Math.abs(x - p.x) < halfW + p.halfW && Math.abs(y - p.y) < halfH + p.halfH) {
|
|
1108
|
+
collides = true;
|
|
1109
|
+
break;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
if (!collides) {
|
|
1113
|
+
spot = { x, y };
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
if (!spot) continue;
|
|
1118
|
+
placed.push({ ...w, rotate, x: spot.x, y: spot.y, halfW, halfH });
|
|
1119
|
+
}
|
|
1120
|
+
return placed;
|
|
1121
|
+
}
|
|
1122
|
+
function renderWordCloud(container, parsed, palette, _isDark, onClickItem, exportDims) {
|
|
1123
|
+
const { words, cloudOptions } = parsed;
|
|
1124
|
+
const title = parsed.noTitle ? null : parsed.title;
|
|
1125
|
+
if (words.length === 0) return;
|
|
1126
|
+
const init = initD3Chart(container, palette, exportDims);
|
|
1127
|
+
if (!init) return;
|
|
1128
|
+
const { svg, width, height, textColor, colors } = init;
|
|
1129
|
+
const idealWidth = Math.max(400, words.length * 30);
|
|
1130
|
+
const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
|
|
1131
|
+
const sTitleHeight = title ? ctx.aesthetic(40) : 0;
|
|
1132
|
+
const cloudHeight = height - sTitleHeight;
|
|
1133
|
+
const sPadding = ctx.structural(2);
|
|
1134
|
+
svg.attr("preserveAspectRatio", "xMidYMid meet");
|
|
1135
|
+
if (ctx.isBelowFloor) {
|
|
1136
|
+
svg.attr("width", "100%");
|
|
1137
|
+
}
|
|
1138
|
+
const { minSize, maxSize } = cloudOptions;
|
|
1139
|
+
const sMinSize = ctx.text(minSize);
|
|
1140
|
+
const sMaxSize = ctx.text(maxSize);
|
|
1141
|
+
const weights = words.map((w) => w.weight);
|
|
1142
|
+
const minWeight = Math.min(...weights);
|
|
1143
|
+
const maxWeight = Math.max(...weights);
|
|
1144
|
+
const range = maxWeight - minWeight || 1;
|
|
1145
|
+
const fontSize = (weight) => {
|
|
1146
|
+
const t = (weight - minWeight) / range;
|
|
1147
|
+
return sMinSize + Math.sqrt(t) * (sMaxSize - sMinSize);
|
|
1148
|
+
};
|
|
1149
|
+
const rotateFn = getRotateFn(cloudOptions.rotate);
|
|
1150
|
+
renderChartTitle(
|
|
1151
|
+
svg,
|
|
1152
|
+
title,
|
|
1153
|
+
parsed.titleLineNumber,
|
|
1154
|
+
width,
|
|
1155
|
+
textColor,
|
|
1156
|
+
onClickItem
|
|
1157
|
+
);
|
|
1158
|
+
const g = svg.append("g").attr(
|
|
1159
|
+
"transform",
|
|
1160
|
+
`translate(${width / 2},${sTitleHeight + cloudHeight / 2})`
|
|
1161
|
+
);
|
|
1162
|
+
const sized = words.map((w) => ({ ...w, size: fontSize(w.weight) }));
|
|
1163
|
+
const draw = (layoutWords) => {
|
|
1164
|
+
g.selectAll("text").data(layoutWords).join("text").style("font-size", (d) => `${d.size}px`).style("font-family", FONT_FAMILY).style("font-weight", "600").style("fill", (_d, i) => colors[i % colors.length]).style(
|
|
1165
|
+
"cursor",
|
|
1166
|
+
(d) => onClickItem && d.lineNumber ? "pointer" : "default"
|
|
1167
|
+
).attr("text-anchor", "middle").attr("transform", (d) => `translate(${d.x},${d.y}) rotate(${d.rotate})`).attr("data-line-number", (d) => {
|
|
1168
|
+
const ln = d.lineNumber;
|
|
1169
|
+
return ln ? String(ln) : null;
|
|
1170
|
+
}).text((d) => d.text).on("click", (_event, d) => {
|
|
1171
|
+
const ln = d.lineNumber;
|
|
1172
|
+
if (onClickItem && ln) onClickItem(ln);
|
|
1173
|
+
});
|
|
1174
|
+
};
|
|
1175
|
+
if (!hasCanvas2d()) {
|
|
1176
|
+
draw(layoutWordsNoCanvas(sized, width, cloudHeight, sPadding, rotateFn));
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
cloud().size([width, cloudHeight]).words(sized).padding(sPadding).rotate(rotateFn).fontSize((d) => d.size).font(FONT_FAMILY).on("end", draw).start();
|
|
1180
|
+
}
|
|
1181
|
+
function renderWordCloudAsync(container, parsed, palette, _isDark, exportDims) {
|
|
1182
|
+
return new Promise((resolve) => {
|
|
1183
|
+
d3Selection2.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
1184
|
+
const { words, cloudOptions } = parsed;
|
|
1185
|
+
const title = parsed.noTitle ? null : parsed.title;
|
|
1186
|
+
if (words.length === 0) {
|
|
1187
|
+
resolve();
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
const width = exportDims?.width ?? container.clientWidth;
|
|
1191
|
+
const height = exportDims?.height ?? container.clientHeight;
|
|
1192
|
+
if (width <= 0 || height <= 0) {
|
|
1193
|
+
resolve();
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
const titleHeight = title ? 40 : 0;
|
|
1197
|
+
const cloudHeight = height - titleHeight;
|
|
1198
|
+
const textColor = palette.text;
|
|
1199
|
+
const bgColor = palette.bg;
|
|
1200
|
+
const colors = getSeriesColors(palette);
|
|
1201
|
+
const { minSize, maxSize } = cloudOptions;
|
|
1202
|
+
const weights = words.map((w) => w.weight);
|
|
1203
|
+
const minWeight = Math.min(...weights);
|
|
1204
|
+
const maxWeight = Math.max(...weights);
|
|
1205
|
+
const range = maxWeight - minWeight || 1;
|
|
1206
|
+
const fontSize = (weight) => {
|
|
1207
|
+
const t = (weight - minWeight) / range;
|
|
1208
|
+
return minSize + Math.sqrt(t) * (maxSize - minSize);
|
|
1209
|
+
};
|
|
1210
|
+
const rotateFn = getRotateFn(cloudOptions.rotate);
|
|
1211
|
+
const svg = d3Selection2.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMid meet").style("background", bgColor);
|
|
1212
|
+
renderChartTitle(svg, title, parsed.titleLineNumber, width, textColor);
|
|
1213
|
+
const g = svg.append("g").attr(
|
|
1214
|
+
"transform",
|
|
1215
|
+
`translate(${width / 2},${titleHeight + cloudHeight / 2})`
|
|
1216
|
+
);
|
|
1217
|
+
const sized = words.map((w) => ({ ...w, size: fontSize(w.weight) }));
|
|
1218
|
+
const draw = (layoutWords) => {
|
|
1219
|
+
g.selectAll("text").data(layoutWords).join("text").style("font-size", (d) => `${d.size}px`).style("font-family", FONT_FAMILY).style("font-weight", "600").style("fill", (_d, i) => colors[i % colors.length]).attr("text-anchor", "middle").attr(
|
|
1220
|
+
"transform",
|
|
1221
|
+
(d) => `translate(${d.x},${d.y}) rotate(${d.rotate})`
|
|
1222
|
+
).text((d) => d.text);
|
|
1223
|
+
resolve();
|
|
1224
|
+
};
|
|
1225
|
+
if (!hasCanvas2d()) {
|
|
1226
|
+
draw(layoutWordsNoCanvas(sized, width, cloudHeight, 2, rotateFn));
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
cloud().size([width, cloudHeight]).words(sized).padding(2).rotate(rotateFn).fontSize((d) => d.size).font(FONT_FAMILY).on("end", draw).start();
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// src/venn/renderer.ts
|
|
1234
|
+
function fitCirclesToContainerAsymmetric(circles, w, h, mLeft, mRight, mTop, mBottom) {
|
|
1235
|
+
if (circles.length === 0) return [];
|
|
1236
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
1237
|
+
for (const c of circles) {
|
|
1238
|
+
minX = Math.min(minX, c.x - c.r);
|
|
1239
|
+
maxX = Math.max(maxX, c.x + c.r);
|
|
1240
|
+
minY = Math.min(minY, c.y - c.r);
|
|
1241
|
+
maxY = Math.max(maxY, c.y + c.r);
|
|
1242
|
+
}
|
|
1243
|
+
const bw = maxX - minX;
|
|
1244
|
+
const bh = maxY - minY;
|
|
1245
|
+
const availW = w - mLeft - mRight;
|
|
1246
|
+
const availH = h - mTop - mBottom;
|
|
1247
|
+
const scale = Math.min(availW / bw, availH / bh);
|
|
1248
|
+
const cx = (minX + maxX) / 2;
|
|
1249
|
+
const cy = (minY + maxY) / 2;
|
|
1250
|
+
const tx = mLeft + availW / 2;
|
|
1251
|
+
const ty = mTop + availH / 2;
|
|
1252
|
+
return circles.map((c) => ({
|
|
1253
|
+
x: (c.x - cx) * scale + tx,
|
|
1254
|
+
y: (c.y - cy) * scale + ty,
|
|
1255
|
+
r: c.r * scale
|
|
1256
|
+
}));
|
|
1257
|
+
}
|
|
1258
|
+
function pointInCircle(p, c) {
|
|
1259
|
+
const dx = p.x - c.x;
|
|
1260
|
+
const dy = p.y - c.y;
|
|
1261
|
+
return dx * dx + dy * dy <= c.r * c.r + 1e-6;
|
|
1262
|
+
}
|
|
1263
|
+
function regionCentroid(circles, inside) {
|
|
1264
|
+
const GRID = 50;
|
|
1265
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
1266
|
+
for (const c of circles) {
|
|
1267
|
+
minX = Math.min(minX, c.x - c.r);
|
|
1268
|
+
maxX = Math.max(maxX, c.x + c.r);
|
|
1269
|
+
minY = Math.min(minY, c.y - c.r);
|
|
1270
|
+
maxY = Math.max(maxY, c.y + c.r);
|
|
1271
|
+
}
|
|
1272
|
+
const stepX = (maxX - minX) / GRID;
|
|
1273
|
+
const stepY = (maxY - minY) / GRID;
|
|
1274
|
+
let sx = 0, sy = 0, count = 0;
|
|
1275
|
+
for (let gi = 0; gi <= GRID; gi++) {
|
|
1276
|
+
const x = minX + gi * stepX;
|
|
1277
|
+
for (let gj = 0; gj <= GRID; gj++) {
|
|
1278
|
+
const y = minY + gj * stepY;
|
|
1279
|
+
let match = true;
|
|
1280
|
+
for (let j = 0; j < circles.length; j++) {
|
|
1281
|
+
const isIn = pointInCircle({ x, y }, circles[j]);
|
|
1282
|
+
if (isIn !== inside[j]) {
|
|
1283
|
+
match = false;
|
|
1284
|
+
break;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
if (match) {
|
|
1288
|
+
sx += x;
|
|
1289
|
+
sy += y;
|
|
1290
|
+
count++;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
if (count === 0) {
|
|
1295
|
+
let fx = 0, fy = 0, fc = 0;
|
|
1296
|
+
for (let j = 0; j < circles.length; j++) {
|
|
1297
|
+
if (inside[j]) {
|
|
1298
|
+
fx += circles[j].x;
|
|
1299
|
+
fy += circles[j].y;
|
|
1300
|
+
fc++;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
return { x: fx / (fc || 1), y: fy / (fc || 1) };
|
|
1304
|
+
}
|
|
1305
|
+
return { x: sx / count, y: sy / count };
|
|
1306
|
+
}
|
|
1307
|
+
function renderVenn(container, parsed, palette, _isDark, onClickItem, exportDims) {
|
|
1308
|
+
const { vennSets, vennOverlaps } = parsed;
|
|
1309
|
+
const solid = parsed.solidFill === true;
|
|
1310
|
+
const title = parsed.noTitle ? null : parsed.title;
|
|
1311
|
+
if (vennSets.length < 2 || vennSets.length > 3) return;
|
|
1312
|
+
const init = initD3Chart(container, palette, exportDims);
|
|
1313
|
+
if (!init) return;
|
|
1314
|
+
const { svg, width, height, textColor, colors } = init;
|
|
1315
|
+
const n = vennSets.length;
|
|
1316
|
+
const idealWidth = n === 2 ? 500 : 600;
|
|
1317
|
+
const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
|
|
1318
|
+
const sTitleHeight = title ? ctx.aesthetic(40) : 0;
|
|
1319
|
+
svg.attr("preserveAspectRatio", "xMidYMid meet");
|
|
1320
|
+
if (ctx.isBelowFloor) {
|
|
1321
|
+
svg.attr("width", "100%");
|
|
1322
|
+
}
|
|
1323
|
+
const BASE_R = 100;
|
|
1324
|
+
const OVERLAP_DISTANCE = BASE_R * 1.4;
|
|
1325
|
+
let rawCircles;
|
|
1326
|
+
if (n === 2) {
|
|
1327
|
+
rawCircles = [
|
|
1328
|
+
{ x: -OVERLAP_DISTANCE / 2, y: 0, r: BASE_R },
|
|
1329
|
+
{ x: OVERLAP_DISTANCE / 2, y: 0, r: BASE_R }
|
|
1330
|
+
];
|
|
1331
|
+
} else {
|
|
1332
|
+
const s = OVERLAP_DISTANCE;
|
|
1333
|
+
const h = Math.sqrt(3) / 2 * s;
|
|
1334
|
+
rawCircles = [
|
|
1335
|
+
{ x: -s / 2, y: h / 3, r: BASE_R },
|
|
1336
|
+
{ x: s / 2, y: h / 3, r: BASE_R },
|
|
1337
|
+
{ x: 0, y: -(2 * h) / 3, r: BASE_R }
|
|
1338
|
+
];
|
|
1339
|
+
}
|
|
1340
|
+
const setColors = vennSets.map(
|
|
1341
|
+
// colors is non-empty; modulo guarantees in-bounds.
|
|
1342
|
+
(s, i) => s.color ?? colors[i % colors.length]
|
|
1343
|
+
);
|
|
1344
|
+
const clusterCx = rawCircles.reduce((s, c) => s + c.x, 0) / n;
|
|
1345
|
+
const clusterCy = rawCircles.reduce((s, c) => s + c.y, 0) / n;
|
|
1346
|
+
let marginLeft = ctx.aesthetic(30), marginRight = ctx.aesthetic(30), marginTop = ctx.aesthetic(30), marginBottom = ctx.aesthetic(30);
|
|
1347
|
+
const stubLen = ctx.structural(20);
|
|
1348
|
+
const edgePad = ctx.aesthetic(8);
|
|
1349
|
+
const labelTextPad = ctx.aesthetic(4);
|
|
1350
|
+
const sSetLabelFont = ctx.text(14);
|
|
1351
|
+
for (let i = 0; i < n; i++) {
|
|
1352
|
+
const estimatedWidth = measureText(vennSets[i].name, sSetLabelFont) + stubLen + edgePad + labelTextPad;
|
|
1353
|
+
const dx = rawCircles[i].x - clusterCx;
|
|
1354
|
+
const dy = rawCircles[i].y - clusterCy;
|
|
1355
|
+
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
1356
|
+
if (dx >= 0) marginRight = Math.max(marginRight, estimatedWidth);
|
|
1357
|
+
else marginLeft = Math.max(marginLeft, estimatedWidth);
|
|
1358
|
+
} else {
|
|
1359
|
+
const halfEstimate = estimatedWidth * 0.5;
|
|
1360
|
+
if (dy >= 0)
|
|
1361
|
+
marginBottom = Math.max(marginBottom, halfEstimate + ctx.aesthetic(20));
|
|
1362
|
+
else marginTop = Math.max(marginTop, halfEstimate + ctx.aesthetic(20));
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
const OVERLAP_FONT = ctx.text(13);
|
|
1366
|
+
const OVERLAP_LINE_H = ctx.structural(16);
|
|
1367
|
+
const OVERLAP_LEADER_PAD = ctx.structural(18);
|
|
1368
|
+
const OVERLAP_TEXT_GAP = ctx.aesthetic(6);
|
|
1369
|
+
const OVERLAP_MARGIN_PAD = ctx.aesthetic(12);
|
|
1370
|
+
const OVERLAP_WRAP_TARGET_W = Math.max(
|
|
1371
|
+
ctx.structural(80),
|
|
1372
|
+
Math.min(ctx.structural(170), width * 0.18)
|
|
1373
|
+
);
|
|
1374
|
+
function predictOverlapDirRaw(idxs) {
|
|
1375
|
+
const excluded = rawCircles.map((_, j) => j).filter((j) => !idxs.includes(j));
|
|
1376
|
+
if (excluded.length > 0) {
|
|
1377
|
+
let sx = 0, sy = 0;
|
|
1378
|
+
for (const ei of excluded) {
|
|
1379
|
+
sx += rawCircles[ei].x;
|
|
1380
|
+
sy += rawCircles[ei].y;
|
|
1381
|
+
}
|
|
1382
|
+
sx /= excluded.length;
|
|
1383
|
+
sy /= excluded.length;
|
|
1384
|
+
let cx = 0, cy = 0;
|
|
1385
|
+
for (const ci of idxs) {
|
|
1386
|
+
cx += rawCircles[ci].x;
|
|
1387
|
+
cy += rawCircles[ci].y;
|
|
1388
|
+
}
|
|
1389
|
+
cx /= idxs.length;
|
|
1390
|
+
cy /= idxs.length;
|
|
1391
|
+
const dx = cx - sx;
|
|
1392
|
+
const dy = cy - sy;
|
|
1393
|
+
const m = Math.sqrt(dx * dx + dy * dy);
|
|
1394
|
+
if (m >= 1e-6) return { x: dx / m, y: dy / m };
|
|
1395
|
+
}
|
|
1396
|
+
if (n === 3) return { x: 0, y: -1 };
|
|
1397
|
+
return { x: 0, y: 1 };
|
|
1398
|
+
}
|
|
1399
|
+
const wrappedOverlapLabels = /* @__PURE__ */ new Map();
|
|
1400
|
+
for (const ov of vennOverlaps) {
|
|
1401
|
+
if (!ov.label) continue;
|
|
1402
|
+
const idxs = ov.sets.map((s) => vennSets.findIndex((vs) => vs.name === s));
|
|
1403
|
+
if (idxs.some((idx) => idx < 0)) continue;
|
|
1404
|
+
const lines = wrapTextToWidth(
|
|
1405
|
+
ov.label,
|
|
1406
|
+
OVERLAP_FONT,
|
|
1407
|
+
OVERLAP_WRAP_TARGET_W
|
|
1408
|
+
);
|
|
1409
|
+
wrappedOverlapLabels.set(ov, lines);
|
|
1410
|
+
const dir = predictOverlapDirRaw(idxs);
|
|
1411
|
+
const labelW = lines.reduce(
|
|
1412
|
+
(m, l) => Math.max(m, measureText(l, OVERLAP_FONT)),
|
|
1413
|
+
0
|
|
1414
|
+
);
|
|
1415
|
+
const labelH = lines.length * OVERLAP_LINE_H;
|
|
1416
|
+
const baseLeader = OVERLAP_LEADER_PAD + OVERLAP_TEXT_GAP + OVERLAP_MARGIN_PAD;
|
|
1417
|
+
if (Math.abs(dir.x) >= Math.abs(dir.y)) {
|
|
1418
|
+
const need = labelW + baseLeader;
|
|
1419
|
+
if (dir.x >= 0) marginRight = Math.max(marginRight, need);
|
|
1420
|
+
else marginLeft = Math.max(marginLeft, need);
|
|
1421
|
+
const halfH = labelH / 2;
|
|
1422
|
+
if (dir.y >= 0) marginBottom = Math.max(marginBottom, halfH + 8);
|
|
1423
|
+
else marginTop = Math.max(marginTop, halfH + 8);
|
|
1424
|
+
} else {
|
|
1425
|
+
const isStackedTriple = idxs.length === 3 && n === 3 && dir.y < 0;
|
|
1426
|
+
const padBoost = isStackedTriple ? 32 : 0;
|
|
1427
|
+
const need = labelH + baseLeader + padBoost;
|
|
1428
|
+
if (dir.y >= 0) marginBottom = Math.max(marginBottom, need);
|
|
1429
|
+
else marginTop = Math.max(marginTop, need);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
const drawH = height - sTitleHeight;
|
|
1433
|
+
const maxSideMarginX = width * 0.32;
|
|
1434
|
+
const maxSideMarginY = drawH * 0.4;
|
|
1435
|
+
marginLeft = Math.min(marginLeft, maxSideMarginX);
|
|
1436
|
+
marginRight = Math.min(marginRight, maxSideMarginX);
|
|
1437
|
+
marginTop = Math.min(marginTop, maxSideMarginY);
|
|
1438
|
+
marginBottom = Math.min(marginBottom, maxSideMarginY);
|
|
1439
|
+
const circles = fitCirclesToContainerAsymmetric(
|
|
1440
|
+
rawCircles,
|
|
1441
|
+
width,
|
|
1442
|
+
drawH,
|
|
1443
|
+
marginLeft,
|
|
1444
|
+
marginRight,
|
|
1445
|
+
marginTop,
|
|
1446
|
+
marginBottom
|
|
1447
|
+
).map((c) => ({ ...c, y: c.y + sTitleHeight }));
|
|
1448
|
+
const scaledR = circles[0].r;
|
|
1449
|
+
svg.append("style").text(
|
|
1450
|
+
"circle:focus, circle:focus-visible { outline-solid: none !important; }"
|
|
1451
|
+
);
|
|
1452
|
+
renderChartTitle(
|
|
1453
|
+
svg,
|
|
1454
|
+
title,
|
|
1455
|
+
parsed.titleLineNumber,
|
|
1456
|
+
width,
|
|
1457
|
+
textColor,
|
|
1458
|
+
onClickItem
|
|
1459
|
+
);
|
|
1460
|
+
const circleEls = [];
|
|
1461
|
+
const circleGroup = svg.append("g");
|
|
1462
|
+
circles.forEach((c, i) => {
|
|
1463
|
+
const el = circleGroup.append("circle").attr("cx", c.x).attr("cy", c.y).attr("r", c.r).attr("fill", setColors[i]).attr("fill-opacity", solid ? 0.6 : 0.35).attr("stroke", setColors[i]).attr("stroke-width", ctx.structural(2)).style("pointer-events", "none");
|
|
1464
|
+
circleEls.push(el);
|
|
1465
|
+
});
|
|
1466
|
+
const defs = svg.append("defs");
|
|
1467
|
+
circles.forEach((c, i) => {
|
|
1468
|
+
defs.append("clipPath").attr("id", `vcp-${i}`).append("circle").attr("cx", c.x).attr("cy", c.y).attr("r", c.r);
|
|
1469
|
+
});
|
|
1470
|
+
const regionIdxSets = circles.map((_, i) => [i]);
|
|
1471
|
+
if (n === 2) {
|
|
1472
|
+
regionIdxSets.push([0, 1]);
|
|
1473
|
+
} else {
|
|
1474
|
+
regionIdxSets.push([0, 1], [0, 2], [1, 2], [0, 1, 2]);
|
|
1475
|
+
}
|
|
1476
|
+
const overlayGroup = svg.append("g").style("pointer-events", "none");
|
|
1477
|
+
const overlayEls = /* @__PURE__ */ new Map();
|
|
1478
|
+
for (const idxs of regionIdxSets) {
|
|
1479
|
+
const key = idxs.join("-");
|
|
1480
|
+
const excluded = Array.from({ length: n }, (_, j) => j).filter(
|
|
1481
|
+
(j) => !idxs.includes(j)
|
|
1482
|
+
);
|
|
1483
|
+
let clipId = `vcp-${idxs[0]}`;
|
|
1484
|
+
for (let k = 1; k < idxs.length; k++) {
|
|
1485
|
+
const nestedId = `vcp-n-${idxs.slice(0, k + 1).join("-")}`;
|
|
1486
|
+
const ci = idxs[k];
|
|
1487
|
+
defs.append("clipPath").attr("id", nestedId).append("circle").attr("cx", circles[ci].x).attr("cy", circles[ci].y).attr("r", circles[ci].r).attr("clip-path", `url(#${clipId})`);
|
|
1488
|
+
clipId = nestedId;
|
|
1489
|
+
}
|
|
1490
|
+
let regionLineNumber = null;
|
|
1491
|
+
if (idxs.length === 1) {
|
|
1492
|
+
regionLineNumber = vennSets[idxs[0]].lineNumber;
|
|
1493
|
+
} else {
|
|
1494
|
+
const sortedNames = idxs.map((i) => vennSets[i].name).sort();
|
|
1495
|
+
const ov = vennOverlaps.find(
|
|
1496
|
+
(o) => o.sets.length === sortedNames.length && o.sets.every((s, k) => s === sortedNames[k])
|
|
1497
|
+
);
|
|
1498
|
+
regionLineNumber = ov?.lineNumber ?? null;
|
|
1499
|
+
}
|
|
1500
|
+
const el = overlayGroup.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "white").attr("fill-opacity", 0).attr("class", "venn-region-overlay").attr(
|
|
1501
|
+
"data-line-number",
|
|
1502
|
+
regionLineNumber != null ? String(regionLineNumber) : "0"
|
|
1503
|
+
).attr("clip-path", `url(#${clipId})`);
|
|
1504
|
+
if (excluded.length > 0) {
|
|
1505
|
+
const maskId = `vvm-${key}`;
|
|
1506
|
+
const mask = defs.append("mask").attr("id", maskId);
|
|
1507
|
+
mask.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "white");
|
|
1508
|
+
for (const j of excluded) {
|
|
1509
|
+
mask.append("circle").attr("cx", circles[j].x).attr("cy", circles[j].y).attr("r", circles[j].r).attr("fill", "black");
|
|
1510
|
+
}
|
|
1511
|
+
el.attr("mask", `url(#${maskId})`);
|
|
1512
|
+
}
|
|
1513
|
+
overlayEls.set(key, el);
|
|
1514
|
+
}
|
|
1515
|
+
const labelEls = /* @__PURE__ */ new Map();
|
|
1516
|
+
function registerLabel(key, el) {
|
|
1517
|
+
if (!labelEls.has(key)) labelEls.set(key, []);
|
|
1518
|
+
labelEls.get(key).push(el);
|
|
1519
|
+
}
|
|
1520
|
+
function dimLabelsExcept(matchKey) {
|
|
1521
|
+
labelEls.forEach((els, k) => {
|
|
1522
|
+
const op = matchKey === null || k === matchKey ? 1 : 0.2;
|
|
1523
|
+
els.forEach((el) => el.attr("opacity", op));
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
const showRegionOverlay = (idxs) => {
|
|
1527
|
+
const key = [...idxs].sort((a, b) => a - b).join("-");
|
|
1528
|
+
overlayEls.forEach(
|
|
1529
|
+
(el, k) => el.attr("fill-opacity", k === key ? 0 : 0.55)
|
|
1530
|
+
);
|
|
1531
|
+
dimLabelsExcept(key);
|
|
1532
|
+
};
|
|
1533
|
+
const hideAllOverlays = () => {
|
|
1534
|
+
overlayEls.forEach((el) => el.attr("fill-opacity", 0));
|
|
1535
|
+
dimLabelsExcept(null);
|
|
1536
|
+
};
|
|
1537
|
+
const gcx = circles.reduce((s, c) => s + c.x, 0) / n;
|
|
1538
|
+
const gcy = circles.reduce((s, c) => s + c.y, 0) / n;
|
|
1539
|
+
function exclusiveHSpan(_px, py, ci) {
|
|
1540
|
+
const cci = circles[ci];
|
|
1541
|
+
const dy = py - cci.y;
|
|
1542
|
+
const halfChord = Math.sqrt(Math.max(0, cci.r * cci.r - dy * dy));
|
|
1543
|
+
let left = cci.x - halfChord;
|
|
1544
|
+
let right = cci.x + halfChord;
|
|
1545
|
+
for (let j = 0; j < n; j++) {
|
|
1546
|
+
if (j === ci) continue;
|
|
1547
|
+
const cj = circles[j];
|
|
1548
|
+
const djy = py - cj.y;
|
|
1549
|
+
if (Math.abs(djy) >= cj.r) continue;
|
|
1550
|
+
const hc = Math.sqrt(cj.r * cj.r - djy * djy);
|
|
1551
|
+
const jLeft = cj.x - hc;
|
|
1552
|
+
const jRight = cj.x + hc;
|
|
1553
|
+
if (jLeft <= left && jRight >= right) return 0;
|
|
1554
|
+
if (jLeft <= left && jRight > left) left = jRight;
|
|
1555
|
+
if (jRight >= right && jLeft < right) right = jLeft;
|
|
1556
|
+
}
|
|
1557
|
+
return Math.max(0, right - left);
|
|
1558
|
+
}
|
|
1559
|
+
const MIN_FONT = ctx.text(10);
|
|
1560
|
+
const MAX_FONT = ctx.text(22);
|
|
1561
|
+
const INTERNAL_PAD = ctx.aesthetic(12);
|
|
1562
|
+
const labelGroup = svg.append("g");
|
|
1563
|
+
const setLabelBBoxes = circles.map(() => null);
|
|
1564
|
+
circles.forEach((c, i) => {
|
|
1565
|
+
const text = vennSets[i].name;
|
|
1566
|
+
const inside = circles.map((_, j) => j === i);
|
|
1567
|
+
const centroid = regionCentroid(circles, inside);
|
|
1568
|
+
const availW = exclusiveHSpan(centroid.x, centroid.y, i);
|
|
1569
|
+
const textWidthPerPx = measureText(text, 1);
|
|
1570
|
+
const fitFont = Math.min(
|
|
1571
|
+
MAX_FONT,
|
|
1572
|
+
Math.max(MIN_FONT, (availW - INTERNAL_PAD * 2) / textWidthPerPx)
|
|
1573
|
+
);
|
|
1574
|
+
const estTextW = measureText(text, fitFont);
|
|
1575
|
+
const fitsInside = estTextW + INTERNAL_PAD * 2 < availW && pointInCircle({ x: centroid.x, y: centroid.y - fitFont / 2 }, c) && pointInCircle({ x: centroid.x, y: centroid.y + fitFont / 2 }, c);
|
|
1576
|
+
const setKey = String(i);
|
|
1577
|
+
const labelG = labelGroup.append("g").style("cursor", "default").on("mouseenter", () => showRegionOverlay([i])).on("mouseleave", () => hideAllOverlays());
|
|
1578
|
+
registerLabel(setKey, labelG);
|
|
1579
|
+
if (fitsInside) {
|
|
1580
|
+
labelG.append("text").attr("x", centroid.x).attr("y", centroid.y).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", textColor).attr("font-size", `${Math.round(fitFont)}px`).attr("font-weight", "bold").text(text);
|
|
1581
|
+
setLabelBBoxes[i] = {
|
|
1582
|
+
x: centroid.x - estTextW / 2,
|
|
1583
|
+
y: centroid.y - fitFont / 2,
|
|
1584
|
+
w: estTextW,
|
|
1585
|
+
h: fitFont
|
|
1586
|
+
};
|
|
1587
|
+
} else {
|
|
1588
|
+
let dx = c.x - gcx;
|
|
1589
|
+
let dy = c.y - gcy;
|
|
1590
|
+
const mag = Math.sqrt(dx * dx + dy * dy);
|
|
1591
|
+
if (mag < 1e-6) {
|
|
1592
|
+
dx = 1;
|
|
1593
|
+
dy = 0;
|
|
1594
|
+
} else {
|
|
1595
|
+
dx /= mag;
|
|
1596
|
+
dy /= mag;
|
|
1597
|
+
}
|
|
1598
|
+
const exitX = c.x + dx * c.r;
|
|
1599
|
+
const exitY = c.y + dy * c.r;
|
|
1600
|
+
const edgeX = exitX + dx * edgePad;
|
|
1601
|
+
const edgeY = exitY + dy * edgePad;
|
|
1602
|
+
const stubEndX = edgeX + dx * stubLen;
|
|
1603
|
+
const stubEndY = edgeY + dy * stubLen;
|
|
1604
|
+
labelG.append("line").attr("x1", edgeX).attr("y1", edgeY).attr("x2", stubEndX).attr("y2", stubEndY).attr("stroke", textColor).attr("stroke-width", ctx.structural(1));
|
|
1605
|
+
const isRight = stubEndX >= gcx;
|
|
1606
|
+
const textAnchor = isRight ? "start" : "end";
|
|
1607
|
+
let textX = stubEndX + (isRight ? labelTextPad : -labelTextPad);
|
|
1608
|
+
const textY = stubEndY;
|
|
1609
|
+
const estW = measureText(text, sSetLabelFont);
|
|
1610
|
+
if (isRight) textX = Math.min(textX, width - estW - 4);
|
|
1611
|
+
else textX = Math.max(textX, estW + 4);
|
|
1612
|
+
const renderedTextY = Math.max(
|
|
1613
|
+
sSetLabelFont,
|
|
1614
|
+
Math.min(height - 4, textY)
|
|
1615
|
+
);
|
|
1616
|
+
labelG.append("text").attr("x", textX).attr("y", renderedTextY).attr("text-anchor", textAnchor).attr("dominant-baseline", "central").attr("fill", textColor).attr("font-size", `${sSetLabelFont}px`).attr("font-weight", "bold").text(text);
|
|
1617
|
+
const externalEstW = measureText(text, sSetLabelFont);
|
|
1618
|
+
setLabelBBoxes[i] = {
|
|
1619
|
+
x: isRight ? textX : textX - externalEstW,
|
|
1620
|
+
y: renderedTextY - sSetLabelFont / 2,
|
|
1621
|
+
w: externalEstW,
|
|
1622
|
+
h: sSetLabelFont
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
});
|
|
1626
|
+
function clipLineByRects(x1, y1, x2, y2, rects, pad = 4) {
|
|
1627
|
+
const dx = x2 - x1;
|
|
1628
|
+
const dy = y2 - y1;
|
|
1629
|
+
const skips = [];
|
|
1630
|
+
for (const raw of rects) {
|
|
1631
|
+
const rx = raw.x - pad;
|
|
1632
|
+
const ry = raw.y - pad;
|
|
1633
|
+
const rw = raw.w + 2 * pad;
|
|
1634
|
+
const rh = raw.h + 2 * pad;
|
|
1635
|
+
let tMin = 0;
|
|
1636
|
+
let tMax = 1;
|
|
1637
|
+
if (Math.abs(dx) < 1e-9) {
|
|
1638
|
+
if (x1 < rx || x1 > rx + rw) continue;
|
|
1639
|
+
} else {
|
|
1640
|
+
const t1 = (rx - x1) / dx;
|
|
1641
|
+
const t2 = (rx + rw - x1) / dx;
|
|
1642
|
+
tMin = Math.max(tMin, Math.min(t1, t2));
|
|
1643
|
+
tMax = Math.min(tMax, Math.max(t1, t2));
|
|
1644
|
+
}
|
|
1645
|
+
if (Math.abs(dy) < 1e-9) {
|
|
1646
|
+
if (y1 < ry || y1 > ry + rh) continue;
|
|
1647
|
+
} else {
|
|
1648
|
+
const t1 = (ry - y1) / dy;
|
|
1649
|
+
const t2 = (ry + rh - y1) / dy;
|
|
1650
|
+
tMin = Math.max(tMin, Math.min(t1, t2));
|
|
1651
|
+
tMax = Math.min(tMax, Math.max(t1, t2));
|
|
1652
|
+
}
|
|
1653
|
+
if (tMin < tMax) skips.push([Math.max(0, tMin), Math.min(1, tMax)]);
|
|
1654
|
+
}
|
|
1655
|
+
skips.sort((a, b) => a[0] - b[0]);
|
|
1656
|
+
const merged = [];
|
|
1657
|
+
for (const s of skips) {
|
|
1658
|
+
const last = merged[merged.length - 1];
|
|
1659
|
+
if (!last || s[0] > last[1]) merged.push([s[0], s[1]]);
|
|
1660
|
+
else last[1] = Math.max(last[1], s[1]);
|
|
1661
|
+
}
|
|
1662
|
+
const segs = [];
|
|
1663
|
+
let cursor = 0;
|
|
1664
|
+
for (const [s, e] of merged) {
|
|
1665
|
+
if (s > cursor)
|
|
1666
|
+
segs.push({
|
|
1667
|
+
x1: x1 + dx * cursor,
|
|
1668
|
+
y1: y1 + dy * cursor,
|
|
1669
|
+
x2: x1 + dx * s,
|
|
1670
|
+
y2: y1 + dy * s
|
|
1671
|
+
});
|
|
1672
|
+
cursor = Math.max(cursor, e);
|
|
1673
|
+
}
|
|
1674
|
+
if (cursor < 1)
|
|
1675
|
+
segs.push({
|
|
1676
|
+
x1: x1 + dx * cursor,
|
|
1677
|
+
y1: y1 + dy * cursor,
|
|
1678
|
+
x2,
|
|
1679
|
+
y2
|
|
1680
|
+
});
|
|
1681
|
+
return segs;
|
|
1682
|
+
}
|
|
1683
|
+
function overlapOutwardDir(centroid, idxs) {
|
|
1684
|
+
const excluded = circles.map((_, j) => j).filter((j) => !idxs.includes(j));
|
|
1685
|
+
if (excluded.length > 0) {
|
|
1686
|
+
let sx = 0, sy = 0;
|
|
1687
|
+
for (const ei of excluded) {
|
|
1688
|
+
sx += circles[ei].x;
|
|
1689
|
+
sy += circles[ei].y;
|
|
1690
|
+
}
|
|
1691
|
+
sx /= excluded.length;
|
|
1692
|
+
sy /= excluded.length;
|
|
1693
|
+
const dx = centroid.x - sx;
|
|
1694
|
+
const dy = centroid.y - sy;
|
|
1695
|
+
const m = Math.sqrt(dx * dx + dy * dy);
|
|
1696
|
+
if (m >= 1e-6) {
|
|
1697
|
+
const nx = Math.abs(dx / m) < 1e-9 ? 0 : dx / m;
|
|
1698
|
+
const ny = Math.abs(dy / m) < 1e-9 ? 0 : dy / m;
|
|
1699
|
+
return { x: nx, y: ny };
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
if (n === 3) return { x: 0, y: -1 };
|
|
1703
|
+
return { x: 0, y: 1 };
|
|
1704
|
+
}
|
|
1705
|
+
function lensExit(c0, dir, idxs) {
|
|
1706
|
+
let minT = Infinity;
|
|
1707
|
+
for (const i of idxs) {
|
|
1708
|
+
const c = circles[i];
|
|
1709
|
+
const dx = c0.x - c.x;
|
|
1710
|
+
const dy = c0.y - c.y;
|
|
1711
|
+
const B = dx * dir.x + dy * dir.y;
|
|
1712
|
+
const C = dx * dx + dy * dy - c.r * c.r;
|
|
1713
|
+
const disc = B * B - C;
|
|
1714
|
+
if (disc < 0) continue;
|
|
1715
|
+
const t = -B + Math.sqrt(disc);
|
|
1716
|
+
if (t > 0 && t < minT) minT = t;
|
|
1717
|
+
}
|
|
1718
|
+
if (!isFinite(minT)) return { x: c0.x, y: c0.y };
|
|
1719
|
+
return { x: c0.x + dir.x * minT, y: c0.y + dir.y * minT };
|
|
1720
|
+
}
|
|
1721
|
+
function unionExit(c0, dir, idxs) {
|
|
1722
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
1723
|
+
for (const c of circles) {
|
|
1724
|
+
minX = Math.min(minX, c.x - c.r);
|
|
1725
|
+
maxX = Math.max(maxX, c.x + c.r);
|
|
1726
|
+
minY = Math.min(minY, c.y - c.r);
|
|
1727
|
+
maxY = Math.max(maxY, c.y + c.r);
|
|
1728
|
+
}
|
|
1729
|
+
const STEP = 3;
|
|
1730
|
+
const MAX_ITERS = 400;
|
|
1731
|
+
const axisAligned = dir.x === 0 || dir.y === 0;
|
|
1732
|
+
let p = { x: c0.x, y: c0.y };
|
|
1733
|
+
let leftOverlap = false;
|
|
1734
|
+
for (let i = 0; i < MAX_ITERS; i++) {
|
|
1735
|
+
const next = { x: p.x + dir.x * STEP, y: p.y + dir.y * STEP };
|
|
1736
|
+
p = next;
|
|
1737
|
+
if (!leftOverlap) {
|
|
1738
|
+
leftOverlap = !idxs.every((ci) => pointInCircle(next, circles[ci]));
|
|
1739
|
+
if (!leftOverlap) continue;
|
|
1740
|
+
}
|
|
1741
|
+
const insideAny = circles.some((c) => pointInCircle(next, c));
|
|
1742
|
+
if (insideAny) continue;
|
|
1743
|
+
if (axisAligned) {
|
|
1744
|
+
const passedX = dir.x > 0 ? next.x >= maxX : next.x <= minX;
|
|
1745
|
+
const passedY = dir.y > 0 ? next.y >= maxY : next.y <= minY;
|
|
1746
|
+
if (dir.x !== 0 && !passedX) continue;
|
|
1747
|
+
if (dir.y !== 0 && !passedY) continue;
|
|
1748
|
+
}
|
|
1749
|
+
break;
|
|
1750
|
+
}
|
|
1751
|
+
return p;
|
|
1752
|
+
}
|
|
1753
|
+
for (const ov of vennOverlaps) {
|
|
1754
|
+
if (!ov.label) continue;
|
|
1755
|
+
const idxs = ov.sets.map((s) => vennSets.findIndex((vs) => vs.name === s));
|
|
1756
|
+
if (idxs.some((idx) => idx < 0)) continue;
|
|
1757
|
+
const lines = wrappedOverlapLabels.get(ov) ?? [ov.label];
|
|
1758
|
+
const inside = circles.map((_, j) => idxs.includes(j));
|
|
1759
|
+
const centroid = regionCentroid(circles, inside);
|
|
1760
|
+
const dir = overlapOutwardDir(centroid, idxs);
|
|
1761
|
+
const isTriple = idxs.length === 3 && n === 3;
|
|
1762
|
+
const padBoost = isTriple && dir.y < 0 ? 32 : 0;
|
|
1763
|
+
const leaderPad = OVERLAP_LEADER_PAD + padBoost;
|
|
1764
|
+
const lensPt = lensExit(centroid, dir, idxs);
|
|
1765
|
+
const farExit = unionExit(centroid, dir, idxs);
|
|
1766
|
+
const stubEndX = farExit.x + dir.x * leaderPad;
|
|
1767
|
+
const stubEndY = farExit.y + dir.y * leaderPad;
|
|
1768
|
+
const horizontal = Math.abs(dir.x) >= Math.abs(dir.y);
|
|
1769
|
+
let textAnchor;
|
|
1770
|
+
let baseline = "central";
|
|
1771
|
+
if (horizontal) {
|
|
1772
|
+
textAnchor = dir.x >= 0 ? "start" : "end";
|
|
1773
|
+
} else {
|
|
1774
|
+
textAnchor = "middle";
|
|
1775
|
+
baseline = dir.y >= 0 ? "hanging" : "auto";
|
|
1776
|
+
}
|
|
1777
|
+
let textX, textY;
|
|
1778
|
+
if (horizontal) {
|
|
1779
|
+
const sign = dir.x >= 0 ? 1 : -1;
|
|
1780
|
+
textX = stubEndX + sign * OVERLAP_TEXT_GAP;
|
|
1781
|
+
textY = stubEndY;
|
|
1782
|
+
} else {
|
|
1783
|
+
const sign = dir.y >= 0 ? 1 : -1;
|
|
1784
|
+
textX = stubEndX;
|
|
1785
|
+
textY = stubEndY + sign * OVERLAP_TEXT_GAP;
|
|
1786
|
+
}
|
|
1787
|
+
const blockW = lines.reduce(
|
|
1788
|
+
(m, l) => Math.max(m, measureText(l, OVERLAP_FONT)),
|
|
1789
|
+
0
|
|
1790
|
+
);
|
|
1791
|
+
const blockH = lines.length * OVERLAP_LINE_H;
|
|
1792
|
+
if (textAnchor === "start") textX = Math.min(textX, width - blockW - 4);
|
|
1793
|
+
else if (textAnchor === "end") textX = Math.max(textX, blockW + 4);
|
|
1794
|
+
else
|
|
1795
|
+
textX = Math.max(blockW / 2 + 4, Math.min(width - blockW / 2 - 4, textX));
|
|
1796
|
+
let topY, bottomY;
|
|
1797
|
+
if (baseline === "hanging") {
|
|
1798
|
+
topY = textY;
|
|
1799
|
+
bottomY = textY + blockH;
|
|
1800
|
+
} else if (baseline === "auto") {
|
|
1801
|
+
bottomY = textY;
|
|
1802
|
+
topY = textY - blockH;
|
|
1803
|
+
} else {
|
|
1804
|
+
topY = textY - blockH / 2;
|
|
1805
|
+
bottomY = textY + blockH / 2;
|
|
1806
|
+
}
|
|
1807
|
+
if (topY < sTitleHeight + 6) textY += sTitleHeight + 6 - topY;
|
|
1808
|
+
else if (bottomY > height - 4) textY -= bottomY - (height - 4);
|
|
1809
|
+
const startY = baseline === "hanging" ? textY : baseline === "auto" ? textY - (lines.length - 1) * OVERLAP_LINE_H : textY - (lines.length - 1) * OVERLAP_LINE_H / 2;
|
|
1810
|
+
const leaderStartX = isTriple ? centroid.x : lensPt.x;
|
|
1811
|
+
const leaderStartY = isTriple ? centroid.y : lensPt.y;
|
|
1812
|
+
let tinted = setColors[idxs[0]];
|
|
1813
|
+
for (let k = 1; k < idxs.length; k++) {
|
|
1814
|
+
const pct = k / (k + 1) * 100;
|
|
1815
|
+
tinted = mix(tinted, setColors[idxs[k]], pct);
|
|
1816
|
+
}
|
|
1817
|
+
const overlapColor = mix(tinted, textColor, 90);
|
|
1818
|
+
const ovKey = [...idxs].sort((a, b) => a - b).join("-");
|
|
1819
|
+
const ovLabelG = labelGroup.append("g").style("cursor", "default").on("mouseenter", () => showRegionOverlay(idxs)).on("mouseleave", () => hideAllOverlays());
|
|
1820
|
+
registerLabel(ovKey, ovLabelG);
|
|
1821
|
+
const labelRects = setLabelBBoxes.filter((b) => b !== null);
|
|
1822
|
+
const segments = clipLineByRects(
|
|
1823
|
+
leaderStartX,
|
|
1824
|
+
leaderStartY,
|
|
1825
|
+
stubEndX,
|
|
1826
|
+
stubEndY,
|
|
1827
|
+
labelRects,
|
|
1828
|
+
4
|
|
1829
|
+
);
|
|
1830
|
+
for (const seg of segments) {
|
|
1831
|
+
ovLabelG.append("line").attr("x1", seg.x1).attr("y1", seg.y1).attr("x2", seg.x2).attr("y2", seg.y2).attr("stroke", overlapColor).attr("stroke-width", ctx.structural(1.25)).attr("opacity", 0.85);
|
|
1832
|
+
}
|
|
1833
|
+
const textEl = ovLabelG.append("text").attr("text-anchor", textAnchor).attr("dominant-baseline", baseline).attr("fill", overlapColor).attr("font-size", `${OVERLAP_FONT}px`).attr("font-weight", "600");
|
|
1834
|
+
lines.forEach((line2, i) => {
|
|
1835
|
+
const tspan = textEl.append("tspan").attr("x", textX);
|
|
1836
|
+
if (i === 0) tspan.attr("y", startY);
|
|
1837
|
+
else tspan.attr("dy", OVERLAP_LINE_H);
|
|
1838
|
+
tspan.text(line2);
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
const hoverGroup = svg.append("g");
|
|
1842
|
+
circles.forEach((c, i) => {
|
|
1843
|
+
hoverGroup.append("circle").attr("cx", c.x).attr("cy", c.y).attr("r", c.r).attr("fill", "transparent").attr("stroke", "none").attr("class", "venn-hit-target").attr("data-line-number", String(vennSets[i].lineNumber)).style("cursor", onClickItem ? "pointer" : "default").style("outline-solid", "none").on("mouseenter", () => {
|
|
1844
|
+
showRegionOverlay([i]);
|
|
1845
|
+
}).on("mouseleave", () => {
|
|
1846
|
+
hideAllOverlays();
|
|
1847
|
+
}).on("click", function() {
|
|
1848
|
+
this.blur?.();
|
|
1849
|
+
if (onClickItem && vennSets[i].lineNumber)
|
|
1850
|
+
onClickItem(vennSets[i].lineNumber);
|
|
1851
|
+
});
|
|
1852
|
+
});
|
|
1853
|
+
const overlayR = scaledR * 0.35;
|
|
1854
|
+
const subsets = [];
|
|
1855
|
+
if (n === 2) {
|
|
1856
|
+
subsets.push({
|
|
1857
|
+
idxs: [0, 1],
|
|
1858
|
+
sets: [vennSets[0].name, vennSets[1].name].sort()
|
|
1859
|
+
});
|
|
1860
|
+
} else {
|
|
1861
|
+
for (let a = 0; a < n; a++) {
|
|
1862
|
+
for (let b = a + 1; b < n; b++) {
|
|
1863
|
+
subsets.push({
|
|
1864
|
+
idxs: [a, b],
|
|
1865
|
+
sets: [vennSets[a].name, vennSets[b].name].sort()
|
|
1866
|
+
});
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
subsets.push({
|
|
1870
|
+
idxs: [0, 1, 2],
|
|
1871
|
+
sets: [vennSets[0].name, vennSets[1].name, vennSets[2].name].sort()
|
|
1872
|
+
});
|
|
1873
|
+
}
|
|
1874
|
+
for (const subset of subsets) {
|
|
1875
|
+
const { idxs, sets } = subset;
|
|
1876
|
+
const inside = circles.map((_, j) => idxs.includes(j));
|
|
1877
|
+
const centroid = regionCentroid(circles, inside);
|
|
1878
|
+
const declaredOv = vennOverlaps.find(
|
|
1879
|
+
(ov) => ov.sets.length === sets.length && ov.sets.every((s, k) => s === sets[k])
|
|
1880
|
+
);
|
|
1881
|
+
hoverGroup.append("circle").attr("cx", centroid.x).attr("cy", centroid.y).attr("r", overlayR).attr("fill", "transparent").attr("stroke", "none").attr("class", "venn-hit-target").attr("data-line-number", declaredOv ? String(declaredOv.lineNumber) : "").style("cursor", onClickItem && declaredOv ? "pointer" : "default").style("outline-solid", "none").on("mouseenter", () => {
|
|
1882
|
+
showRegionOverlay(idxs);
|
|
1883
|
+
}).on("mouseleave", () => {
|
|
1884
|
+
hideAllOverlays();
|
|
1885
|
+
}).on("click", function() {
|
|
1886
|
+
this.blur?.();
|
|
1887
|
+
if (onClickItem && declaredOv) onClickItem(declaredOv.lineNumber);
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
// src/quadrant/renderer.ts
|
|
1893
|
+
import * as d3Scale3 from "d3-scale";
|
|
1894
|
+
import * as d3Selection3 from "d3-selection";
|
|
1895
|
+
function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportDims) {
|
|
1896
|
+
const {
|
|
1897
|
+
quadrantLabels,
|
|
1898
|
+
quadrantPoints,
|
|
1899
|
+
quadrantXAxis,
|
|
1900
|
+
quadrantYAxis,
|
|
1901
|
+
quadrantTitleLineNumber,
|
|
1902
|
+
quadrantXAxisLineNumber,
|
|
1903
|
+
quadrantYAxisLineNumber
|
|
1904
|
+
} = parsed;
|
|
1905
|
+
const title = parsed.noTitle ? null : parsed.title;
|
|
1906
|
+
if (quadrantPoints.length === 0) return;
|
|
1907
|
+
const init = initD3Chart(container, palette, exportDims);
|
|
1908
|
+
if (!init) return;
|
|
1909
|
+
const { svg, width, height, textColor } = init;
|
|
1910
|
+
const borderColor = palette.border;
|
|
1911
|
+
const idealWidth = 600;
|
|
1912
|
+
const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
|
|
1913
|
+
svg.attr("preserveAspectRatio", "xMidYMid meet");
|
|
1914
|
+
if (ctx.isBelowFloor) {
|
|
1915
|
+
svg.attr("width", "100%");
|
|
1916
|
+
}
|
|
1917
|
+
const defaultColors = [
|
|
1918
|
+
palette.colors.blue,
|
|
1919
|
+
palette.colors.green,
|
|
1920
|
+
palette.colors.yellow,
|
|
1921
|
+
palette.colors.purple
|
|
1922
|
+
];
|
|
1923
|
+
const hasXAxis = !!quadrantXAxis;
|
|
1924
|
+
const hasYAxis = !!quadrantYAxis;
|
|
1925
|
+
const margin = {
|
|
1926
|
+
top: title ? ctx.aesthetic(60) : ctx.aesthetic(30),
|
|
1927
|
+
right: ctx.aesthetic(30),
|
|
1928
|
+
bottom: hasXAxis ? ctx.aesthetic(70) : ctx.aesthetic(40),
|
|
1929
|
+
left: hasYAxis ? ctx.aesthetic(80) : ctx.aesthetic(40)
|
|
1930
|
+
};
|
|
1931
|
+
const chartWidth = width - margin.left - margin.right;
|
|
1932
|
+
const chartHeight = height - margin.top - margin.bottom;
|
|
1933
|
+
const xScale = d3Scale3.scaleLinear().domain([0, 1]).range([0, chartWidth]);
|
|
1934
|
+
const yScale = d3Scale3.scaleLinear().domain([0, 1]).range([chartHeight, 0]);
|
|
1935
|
+
renderChartTitle(
|
|
1936
|
+
svg,
|
|
1937
|
+
title,
|
|
1938
|
+
quadrantTitleLineNumber,
|
|
1939
|
+
width,
|
|
1940
|
+
textColor,
|
|
1941
|
+
onClickItem
|
|
1942
|
+
);
|
|
1943
|
+
const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
|
|
1944
|
+
const bg = themeBaseBg(palette, isDark);
|
|
1945
|
+
const getQuadrantColor = (label, defaultIdx) => {
|
|
1946
|
+
return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
|
|
1947
|
+
};
|
|
1948
|
+
const getQuadrantFill = (label, defaultIdx) => {
|
|
1949
|
+
return mix(getQuadrantColor(label, defaultIdx), bg, 30);
|
|
1950
|
+
};
|
|
1951
|
+
const quadrantDefs = [
|
|
1952
|
+
{
|
|
1953
|
+
position: "top-left",
|
|
1954
|
+
x: 0,
|
|
1955
|
+
y: 0,
|
|
1956
|
+
w: chartWidth / 2,
|
|
1957
|
+
h: chartHeight / 2,
|
|
1958
|
+
labelX: chartWidth / 4,
|
|
1959
|
+
labelY: chartHeight / 4,
|
|
1960
|
+
label: quadrantLabels.topLeft,
|
|
1961
|
+
colorIdx: 1
|
|
1962
|
+
// green
|
|
1963
|
+
},
|
|
1964
|
+
{
|
|
1965
|
+
position: "top-right",
|
|
1966
|
+
x: chartWidth / 2,
|
|
1967
|
+
y: 0,
|
|
1968
|
+
w: chartWidth / 2,
|
|
1969
|
+
h: chartHeight / 2,
|
|
1970
|
+
labelX: chartWidth * 3 / 4,
|
|
1971
|
+
labelY: chartHeight / 4,
|
|
1972
|
+
label: quadrantLabels.topRight,
|
|
1973
|
+
colorIdx: 0
|
|
1974
|
+
// blue
|
|
1975
|
+
},
|
|
1976
|
+
{
|
|
1977
|
+
position: "bottom-left",
|
|
1978
|
+
x: 0,
|
|
1979
|
+
y: chartHeight / 2,
|
|
1980
|
+
w: chartWidth / 2,
|
|
1981
|
+
h: chartHeight / 2,
|
|
1982
|
+
labelX: chartWidth / 4,
|
|
1983
|
+
labelY: chartHeight * 3 / 4,
|
|
1984
|
+
label: quadrantLabels.bottomLeft,
|
|
1985
|
+
colorIdx: 2
|
|
1986
|
+
// yellow
|
|
1987
|
+
},
|
|
1988
|
+
{
|
|
1989
|
+
position: "bottom-right",
|
|
1990
|
+
x: chartWidth / 2,
|
|
1991
|
+
y: chartHeight / 2,
|
|
1992
|
+
w: chartWidth / 2,
|
|
1993
|
+
h: chartHeight / 2,
|
|
1994
|
+
labelX: chartWidth * 3 / 4,
|
|
1995
|
+
labelY: chartHeight * 3 / 4,
|
|
1996
|
+
label: quadrantLabels.bottomRight,
|
|
1997
|
+
colorIdx: 3
|
|
1998
|
+
// purple
|
|
1999
|
+
}
|
|
2000
|
+
];
|
|
2001
|
+
const quadrantRects = chartG.selectAll("rect.quadrant").data(quadrantDefs).enter().append("rect").attr("class", "quadrant").attr("x", (d) => d.x).attr("y", (d) => d.y).attr("width", (d) => d.w).attr("height", (d) => d.h).attr("fill", (d) => getQuadrantFill(d.label, d.colorIdx)).attr("stroke", (d) => getQuadrantColor(d.label, d.colorIdx)).attr("stroke-width", ctx.structural(2));
|
|
2002
|
+
const shadowColor = "rgba(0,0,0,0.4)";
|
|
2003
|
+
const quadrantLabelColor = mix(textColor, bg, 35);
|
|
2004
|
+
const LABEL_MAX_FONT = ctx.text(48);
|
|
2005
|
+
const LABEL_MIN_FONT = ctx.text(14);
|
|
2006
|
+
const LABEL_PAD = ctx.aesthetic(40);
|
|
2007
|
+
const quadrantLabelLayout = (text, qw2, qh2) => {
|
|
2008
|
+
const availW = qw2 - LABEL_PAD;
|
|
2009
|
+
const availH = qh2 - LABEL_PAD;
|
|
2010
|
+
if (measureText(text, LABEL_MAX_FONT) <= availW) {
|
|
2011
|
+
const fs = Math.min(LABEL_MAX_FONT, availH);
|
|
2012
|
+
return {
|
|
2013
|
+
lines: [text],
|
|
2014
|
+
fontSize: Math.max(LABEL_MIN_FONT, Math.round(fs))
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
const wrapLines = (fs) => wrapTextToWidth(text, fs, availW);
|
|
2018
|
+
let lo = LABEL_MIN_FONT;
|
|
2019
|
+
let hi = LABEL_MAX_FONT;
|
|
2020
|
+
let bestLines = wrapLines(lo);
|
|
2021
|
+
let bestFs = lo;
|
|
2022
|
+
while (lo <= hi) {
|
|
2023
|
+
const mid = Math.round((lo + hi) / 2);
|
|
2024
|
+
const lines = wrapLines(mid);
|
|
2025
|
+
const totalH = lines.length * mid * 1.2;
|
|
2026
|
+
const maxLineW = Math.max(...lines.map((l) => measureText(l, mid)));
|
|
2027
|
+
if (maxLineW <= availW && totalH <= availH) {
|
|
2028
|
+
bestFs = mid;
|
|
2029
|
+
bestLines = lines;
|
|
2030
|
+
lo = mid + 1;
|
|
2031
|
+
} else {
|
|
2032
|
+
hi = mid - 1;
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
return { lines: bestLines, fontSize: Math.max(LABEL_MIN_FONT, bestFs) };
|
|
2036
|
+
};
|
|
2037
|
+
const qw = chartWidth / 2;
|
|
2038
|
+
const qh = chartHeight / 2;
|
|
2039
|
+
const quadrantDefsWithLabel = quadrantDefs.filter((d) => d.label !== null);
|
|
2040
|
+
const labelLayouts = new Map(
|
|
2041
|
+
quadrantDefsWithLabel.map((d) => [
|
|
2042
|
+
d.label.text,
|
|
2043
|
+
quadrantLabelLayout(d.label.text, qw, qh)
|
|
2044
|
+
])
|
|
2045
|
+
);
|
|
2046
|
+
const renderWatermarkOriginal = (textEl, d) => {
|
|
2047
|
+
const layout = labelLayouts.get(d.label.text);
|
|
2048
|
+
const el = d3Selection3.select(textEl);
|
|
2049
|
+
el.text(null).attr("font-size", `${layout.fontSize}px`).attr("font-weight", "700");
|
|
2050
|
+
if (layout.lines.length === 1) {
|
|
2051
|
+
el.text(layout.lines[0]);
|
|
2052
|
+
} else {
|
|
2053
|
+
const lineH = layout.fontSize * 1.2;
|
|
2054
|
+
const totalH = layout.lines.length * lineH;
|
|
2055
|
+
const startY = -totalH / 2 + lineH / 2;
|
|
2056
|
+
layout.lines.forEach((line2, i) => {
|
|
2057
|
+
el.append("tspan").attr("x", d.labelX).attr("dy", i === 0 ? `${startY}px` : `${lineH}px`).text(line2);
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
const quadrantLabelTexts = chartG.selectAll("text.quadrant-label").data(quadrantDefsWithLabel).enter().append("text").attr("class", "quadrant-label").attr("x", (d) => d.labelX).attr("y", (d) => d.labelY).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", quadrantLabelColor).attr(
|
|
2062
|
+
"data-line-number",
|
|
2063
|
+
(d) => d.label?.lineNumber ? String(d.label.lineNumber) : null
|
|
2064
|
+
).style(
|
|
2065
|
+
"cursor",
|
|
2066
|
+
(d) => onClickItem && d.label?.lineNumber ? "pointer" : "default"
|
|
2067
|
+
).each(function(d) {
|
|
2068
|
+
renderWatermarkOriginal(this, d);
|
|
2069
|
+
});
|
|
2070
|
+
if (onClickItem) {
|
|
2071
|
+
quadrantLabelTexts.on("click", (_, d) => {
|
|
2072
|
+
if (d.label?.lineNumber) onClickItem(d.label.lineNumber);
|
|
2073
|
+
}).on("mouseenter", function() {
|
|
2074
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
2075
|
+
}).on("mouseleave", function() {
|
|
2076
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
const sAxisFont = ctx.text(18);
|
|
2080
|
+
const sAxisPad = ctx.aesthetic(20);
|
|
2081
|
+
const sAxisX = ctx.aesthetic(22);
|
|
2082
|
+
if (quadrantXAxis) {
|
|
2083
|
+
const xLowLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", margin.left + chartWidth / 4).attr("y", height - sAxisPad).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", `${sAxisFont}px`).attr(
|
|
2084
|
+
"data-line-number",
|
|
2085
|
+
quadrantXAxisLineNumber ? String(quadrantXAxisLineNumber) : null
|
|
2086
|
+
).style(
|
|
2087
|
+
"cursor",
|
|
2088
|
+
onClickItem && quadrantXAxisLineNumber ? "pointer" : "default"
|
|
2089
|
+
).text(quadrantXAxis[0]);
|
|
2090
|
+
const xHighLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", margin.left + chartWidth * 3 / 4).attr("y", height - sAxisPad).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", `${sAxisFont}px`).attr(
|
|
2091
|
+
"data-line-number",
|
|
2092
|
+
quadrantXAxisLineNumber ? String(quadrantXAxisLineNumber) : null
|
|
2093
|
+
).style(
|
|
2094
|
+
"cursor",
|
|
2095
|
+
onClickItem && quadrantXAxisLineNumber ? "pointer" : "default"
|
|
2096
|
+
).text(quadrantXAxis[1]);
|
|
2097
|
+
if (onClickItem && quadrantXAxisLineNumber) {
|
|
2098
|
+
[xLowLabel, xHighLabel].forEach((label) => {
|
|
2099
|
+
label.on("click", () => onClickItem(quadrantXAxisLineNumber)).on("mouseenter", function() {
|
|
2100
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
2101
|
+
}).on("mouseleave", function() {
|
|
2102
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
2103
|
+
});
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
if (quadrantYAxis) {
|
|
2108
|
+
const yMidBottom = margin.top + chartHeight * 3 / 4;
|
|
2109
|
+
const yMidTop = margin.top + chartHeight / 4;
|
|
2110
|
+
const yLowLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", sAxisX).attr("y", yMidBottom).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", `${sAxisFont}px`).attr("transform", `rotate(-90, ${sAxisX}, ${yMidBottom})`).attr(
|
|
2111
|
+
"data-line-number",
|
|
2112
|
+
quadrantYAxisLineNumber ? String(quadrantYAxisLineNumber) : null
|
|
2113
|
+
).style(
|
|
2114
|
+
"cursor",
|
|
2115
|
+
onClickItem && quadrantYAxisLineNumber ? "pointer" : "default"
|
|
2116
|
+
).text(quadrantYAxis[0]);
|
|
2117
|
+
const yHighLabel = svg.append("text").attr("class", "quadrant-axis-label").attr("x", sAxisX).attr("y", yMidTop).attr("text-anchor", "middle").attr("fill", textColor).attr("font-size", `${sAxisFont}px`).attr("transform", `rotate(-90, ${sAxisX}, ${yMidTop})`).attr(
|
|
2118
|
+
"data-line-number",
|
|
2119
|
+
quadrantYAxisLineNumber ? String(quadrantYAxisLineNumber) : null
|
|
2120
|
+
).style(
|
|
2121
|
+
"cursor",
|
|
2122
|
+
onClickItem && quadrantYAxisLineNumber ? "pointer" : "default"
|
|
2123
|
+
).text(quadrantYAxis[1]);
|
|
2124
|
+
if (onClickItem && quadrantYAxisLineNumber) {
|
|
2125
|
+
[yLowLabel, yHighLabel].forEach((label) => {
|
|
2126
|
+
label.on("click", () => onClickItem(quadrantYAxisLineNumber)).on("mouseenter", function() {
|
|
2127
|
+
d3Selection3.select(this).attr("opacity", 0.7);
|
|
2128
|
+
}).on("mouseleave", function() {
|
|
2129
|
+
d3Selection3.select(this).attr("opacity", 1);
|
|
2130
|
+
});
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
chartG.append("line").attr("x1", chartWidth / 2).attr("y1", 0).attr("x2", chartWidth / 2).attr("y2", chartHeight).attr("stroke", borderColor).attr("stroke-width", 1);
|
|
2135
|
+
chartG.append("line").attr("x1", 0).attr("y1", chartHeight / 2).attr("x2", chartWidth).attr("y2", chartHeight / 2).attr("stroke", borderColor).attr("stroke-width", 1);
|
|
2136
|
+
const getPointQuadrant = (x, y) => {
|
|
2137
|
+
if (x >= 0.5 && y >= 0.5) return "top-right";
|
|
2138
|
+
if (x < 0.5 && y >= 0.5) return "top-left";
|
|
2139
|
+
if (x < 0.5 && y < 0.5) return "bottom-left";
|
|
2140
|
+
return "bottom-right";
|
|
2141
|
+
};
|
|
2142
|
+
const POINT_RADIUS = ctx.structural(6);
|
|
2143
|
+
const POINT_LABEL_FONT_SIZE = ctx.text(12);
|
|
2144
|
+
const quadrantLabelObstacles = quadrantDefsWithLabel.map((d) => {
|
|
2145
|
+
const layout = labelLayouts.get(d.label.text);
|
|
2146
|
+
const totalW = Math.max(
|
|
2147
|
+
...layout.lines.map((l) => measureText(l, layout.fontSize))
|
|
2148
|
+
);
|
|
2149
|
+
const totalH = layout.lines.length * layout.fontSize * 1.2;
|
|
2150
|
+
return {
|
|
2151
|
+
x: d.labelX - totalW / 2,
|
|
2152
|
+
y: d.labelY - totalH / 2,
|
|
2153
|
+
w: totalW,
|
|
2154
|
+
h: totalH
|
|
2155
|
+
};
|
|
2156
|
+
});
|
|
2157
|
+
const pointPixels = quadrantPoints.map((point) => ({
|
|
2158
|
+
label: point.label,
|
|
2159
|
+
cx: xScale(point.x),
|
|
2160
|
+
cy: yScale(point.y)
|
|
2161
|
+
}));
|
|
2162
|
+
const placedPointLabels = computeQuadrantPointLabels(
|
|
2163
|
+
pointPixels,
|
|
2164
|
+
{ left: 0, top: 0, right: chartWidth, bottom: chartHeight },
|
|
2165
|
+
quadrantLabelObstacles,
|
|
2166
|
+
POINT_RADIUS,
|
|
2167
|
+
POINT_LABEL_FONT_SIZE
|
|
2168
|
+
);
|
|
2169
|
+
const pointsG = chartG.append("g").attr("class", "points");
|
|
2170
|
+
quadrantPoints.forEach((point, i) => {
|
|
2171
|
+
const cx = xScale(point.x);
|
|
2172
|
+
const cy = yScale(point.y);
|
|
2173
|
+
const quadrant = getPointQuadrant(point.x, point.y);
|
|
2174
|
+
const quadDef = quadrantDefs.find((d) => d.position === quadrant);
|
|
2175
|
+
const pointColor = quadDef?.label?.color ?? defaultColors[quadDef?.colorIdx ?? 0];
|
|
2176
|
+
const placed = placedPointLabels[i];
|
|
2177
|
+
const pointG = pointsG.append("g").attr("class", "point-group").attr("data-line-number", String(point.lineNumber)).attr("data-quadrant", quadrant);
|
|
2178
|
+
if (placed.connectorLine) {
|
|
2179
|
+
pointG.append("line").attr("x1", placed.connectorLine.x1).attr("y1", placed.connectorLine.y1).attr("x2", placed.connectorLine.x2).attr("y2", placed.connectorLine.y2).attr("stroke", pointColor).attr("stroke-width", 1).attr("opacity", 0.5);
|
|
2180
|
+
}
|
|
2181
|
+
pointG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", POINT_RADIUS).attr("fill", "#ffffff").attr("stroke", pointColor).attr("stroke-width", ctx.structural(2));
|
|
2182
|
+
const labelText = pointG.append("text").attr("x", placed.x).attr("y", placed.y).attr("text-anchor", placed.anchor).attr("dominant-baseline", "central").attr("fill", textColor).attr("font-size", `${POINT_LABEL_FONT_SIZE}px`).attr("font-weight", "700").style("text-shadow", `0 1px 2px ${shadowColor}`).style("transition", "y 120ms ease-out");
|
|
2183
|
+
labelText.append("tspan").text(point.label);
|
|
2184
|
+
const coordsTspan = labelText.append("tspan").attr("class", "point-coords").attr("x", placed.x).attr("dy", `${POINT_LABEL_FONT_SIZE}px`).attr("font-size", `${ctx.text(10)}px`).attr("font-weight", "500").attr("opacity", 0).text(`${point.x.toFixed(2)}, ${point.y.toFixed(2)}`);
|
|
2185
|
+
const COORDS_LINE_H = ctx.structural(14);
|
|
2186
|
+
const bumpDy = placed.y < cy ? -COORDS_LINE_H : COORDS_LINE_H;
|
|
2187
|
+
pointG.style("cursor", onClickItem ? "pointer" : "default").on("mouseenter", () => {
|
|
2188
|
+
pointG.select("circle").attr("r", ctx.structural(8));
|
|
2189
|
+
labelText.attr("y", placed.y + bumpDy);
|
|
2190
|
+
coordsTspan.attr("opacity", 1);
|
|
2191
|
+
quadrantRects.attr(
|
|
2192
|
+
"opacity",
|
|
2193
|
+
(qd) => qd.position === quadrant ? 1 : 0.3
|
|
2194
|
+
);
|
|
2195
|
+
quadrantLabelTexts.attr(
|
|
2196
|
+
"opacity",
|
|
2197
|
+
(qd) => qd.position === quadrant ? 1 : 0.3
|
|
2198
|
+
);
|
|
2199
|
+
pointsG.selectAll("g.point-group").filter((_, j) => j !== i).attr("opacity", 0.3);
|
|
2200
|
+
}).on("mouseleave", () => {
|
|
2201
|
+
pointG.select("circle").attr("r", POINT_RADIUS);
|
|
2202
|
+
labelText.attr("y", placed.y);
|
|
2203
|
+
coordsTspan.attr("opacity", 0);
|
|
2204
|
+
quadrantRects.attr("opacity", 1);
|
|
2205
|
+
quadrantLabelTexts.attr("opacity", 1);
|
|
2206
|
+
pointsG.selectAll("g.point-group").attr("opacity", 1);
|
|
2207
|
+
}).on("click", () => {
|
|
2208
|
+
if (onClickItem && point.lineNumber) onClickItem(point.lineNumber);
|
|
2209
|
+
});
|
|
2210
|
+
});
|
|
2211
|
+
quadrantRects.style("cursor", onClickItem ? "pointer" : "default").on("mouseenter", function(_, d) {
|
|
2212
|
+
quadrantRects.attr(
|
|
2213
|
+
"opacity",
|
|
2214
|
+
(qd) => qd.position === d.position ? 1 : 0.3
|
|
2215
|
+
);
|
|
2216
|
+
quadrantLabelTexts.attr(
|
|
2217
|
+
"opacity",
|
|
2218
|
+
(qd) => qd.position === d.position ? 1 : 0.3
|
|
2219
|
+
);
|
|
2220
|
+
pointsG.selectAll("g.point-group").each(function(_2, i) {
|
|
2221
|
+
const pt = quadrantPoints[i];
|
|
2222
|
+
const ptQuad = getPointQuadrant(pt.x, pt.y);
|
|
2223
|
+
d3Selection3.select(this).attr("opacity", ptQuad === d.position ? 1 : 0.2);
|
|
2224
|
+
});
|
|
2225
|
+
}).on("mouseleave", () => {
|
|
2226
|
+
quadrantRects.attr("opacity", 1);
|
|
2227
|
+
quadrantLabelTexts.attr("opacity", 1);
|
|
2228
|
+
pointsG.selectAll("g.point-group").attr("opacity", 1);
|
|
2229
|
+
}).on("click", (_, d) => {
|
|
2230
|
+
if (onClickItem && d.label?.lineNumber) {
|
|
2231
|
+
onClickItem(d.label.lineNumber);
|
|
2232
|
+
}
|
|
2233
|
+
});
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
// src/d3.ts
|
|
2237
|
+
var DIAGRAM_EXPORT_HANDLERS = {
|
|
2238
|
+
org: exportOrg,
|
|
2239
|
+
sitemap: exportSitemap,
|
|
2240
|
+
kanban: exportKanban,
|
|
2241
|
+
class: exportClass,
|
|
2242
|
+
er: exportEr,
|
|
2243
|
+
"boxes-and-lines": exportBoxesAndLines,
|
|
2244
|
+
swimlane: exportSwimlane,
|
|
2245
|
+
"version-control": exportVersionControl,
|
|
2246
|
+
mindmap: exportMindmap,
|
|
2247
|
+
wireframe: exportWireframe,
|
|
2248
|
+
c4: exportC4,
|
|
2249
|
+
flowchart: exportFlowchart,
|
|
2250
|
+
infra: exportInfra,
|
|
2251
|
+
pert: exportPert,
|
|
2252
|
+
gantt: exportGantt,
|
|
2253
|
+
state: exportState,
|
|
2254
|
+
"tech-radar": exportTechRadar,
|
|
2255
|
+
"journey-map": exportJourneyMap,
|
|
2256
|
+
cycle: exportCycle,
|
|
2257
|
+
map: exportMap,
|
|
2258
|
+
pyramid: exportPyramid,
|
|
2259
|
+
ring: exportRing,
|
|
2260
|
+
treemap: exportTreemap,
|
|
2261
|
+
block: exportBlock,
|
|
2262
|
+
raci: exportRaci,
|
|
2263
|
+
// D3 visualizations — own handler per type (Story 109.2). Only `sequence`
|
|
2264
|
+
// still falls through to exportVisualization (no chart-type of its own).
|
|
2265
|
+
slope: exportSlope,
|
|
2266
|
+
arc: exportArc,
|
|
2267
|
+
timeline: exportTimeline,
|
|
2268
|
+
"event-line": exportEventLine,
|
|
2269
|
+
wordcloud: exportWordcloud,
|
|
2270
|
+
venn: exportVenn,
|
|
2271
|
+
quadrant: exportQuadrant
|
|
2272
|
+
};
|
|
2273
|
+
function resolveArcChordOverride(content, detectedType, palette) {
|
|
2274
|
+
if (detectedType === "arc") {
|
|
2275
|
+
const p = parseArc(content, palette);
|
|
2276
|
+
if (!p.error && p.layout === "chord" && p.links.length) {
|
|
2277
|
+
const emitted = [
|
|
2278
|
+
`chord ${p.title ?? ""}`.trimEnd(),
|
|
2279
|
+
...p.links.map((l) => `${l.source} -> ${l.target} ${l.value}`)
|
|
2280
|
+
].join("\n");
|
|
2281
|
+
return { content: emitted, type: "chord" };
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
return null;
|
|
2285
|
+
}
|
|
2286
|
+
async function renderForExport(content, theme, palette, viewState, options) {
|
|
2287
|
+
const exportMode = options?.exportMode ?? false;
|
|
2288
|
+
const { parseDgmoChartType } = await import("./dgmo-router-DZCIDUO5.js");
|
|
2289
|
+
const detectedTypeRaw = parseDgmoChartType(content);
|
|
2290
|
+
const override = resolveArcChordOverride(content, detectedTypeRaw, palette);
|
|
2291
|
+
const renderContent = override?.content ?? content;
|
|
2292
|
+
const detectedType = override?.type ?? detectedTypeRaw;
|
|
2293
|
+
if (detectedType !== null) {
|
|
2294
|
+
const { supportsD3DataChart, renderDataChartD3: renderDataChartD32 } = await import("./charts-d3-QOZHKTCT.js");
|
|
2295
|
+
if (supportsD3DataChart(detectedType)) {
|
|
2296
|
+
return renderDataChartD32(renderContent, theme, palette);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
const ctx = {
|
|
2300
|
+
content: renderContent,
|
|
2301
|
+
theme,
|
|
2302
|
+
palette,
|
|
2303
|
+
viewState,
|
|
2304
|
+
options,
|
|
2305
|
+
exportMode,
|
|
2306
|
+
isDark: theme === "dark"
|
|
2307
|
+
};
|
|
2308
|
+
const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
|
|
2309
|
+
return (handler ?? exportVisualization)(ctx);
|
|
2310
|
+
}
|
|
2311
|
+
function ctxTagOverride(ctx) {
|
|
2312
|
+
return ctx.viewState?.tag ?? ctx.options?.tagGroup;
|
|
2313
|
+
}
|
|
2314
|
+
async function exportEventLine(ctx) {
|
|
2315
|
+
const { content, theme, palette } = ctx;
|
|
2316
|
+
const { parseEventLine } = await import("./parser-JAL2HD4I.js");
|
|
2317
|
+
const { renderEventLineForExport } = await import("./renderer-RPOA5YPZ.js");
|
|
2318
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2319
|
+
const parsed = parseEventLine(content, effectivePalette);
|
|
2320
|
+
if (parsed.error || parsed.events.length === 0) return "";
|
|
2321
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
2322
|
+
renderEventLineForExport(
|
|
2323
|
+
container,
|
|
2324
|
+
parsed,
|
|
2325
|
+
effectivePalette,
|
|
2326
|
+
ctx.isDark,
|
|
2327
|
+
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
|
|
2328
|
+
ctxTagOverride(ctx)
|
|
2329
|
+
);
|
|
2330
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2331
|
+
}
|
|
2332
|
+
async function exportVersionControl(ctx) {
|
|
2333
|
+
const { content, theme, palette } = ctx;
|
|
2334
|
+
const { parseVersionControl } = await import("./parser-TFZFBVWN.js");
|
|
2335
|
+
const { renderVersionControlForExport } = await import("./renderer-GHPZMFU2.js");
|
|
2336
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2337
|
+
const parsed = parseVersionControl(content, effectivePalette);
|
|
2338
|
+
if (parsed.error || parsed.nodes.length === 0) return "";
|
|
2339
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
2340
|
+
renderVersionControlForExport(
|
|
2341
|
+
container,
|
|
2342
|
+
parsed,
|
|
2343
|
+
effectivePalette,
|
|
2344
|
+
ctx.isDark,
|
|
2345
|
+
{
|
|
2346
|
+
width: EXPORT_WIDTH,
|
|
2347
|
+
height: EXPORT_HEIGHT
|
|
2348
|
+
}
|
|
2349
|
+
);
|
|
2350
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2351
|
+
}
|
|
2352
|
+
function unionHiddenAttributes(hideOption, ha) {
|
|
2353
|
+
const sourceHidden = hideOption ? hideOption.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean) : [];
|
|
2354
|
+
const union = /* @__PURE__ */ new Set([...sourceHidden, ...ha ?? []]);
|
|
2355
|
+
return union.size > 0 ? union : void 0;
|
|
2356
|
+
}
|
|
2357
|
+
async function exportOrg(ctx) {
|
|
2358
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
2359
|
+
const { parseOrg } = await import("./parser-6BTRLKRZ.js");
|
|
2360
|
+
const { layoutOrg } = await import("./layout-OB3RI63S.js");
|
|
2361
|
+
const { collapseOrgTree } = await import("./collapse-CLPJD2RL.js");
|
|
2362
|
+
const { renderOrg } = await import("./renderer-T7YKC5VP.js");
|
|
2363
|
+
const isDark = ctx.isDark;
|
|
2364
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2365
|
+
const orgParsed = parseOrg(content, effectivePalette);
|
|
2366
|
+
if (orgParsed.error) return "";
|
|
2367
|
+
const collapsedNodes = viewState?.cg ? new Set(viewState.cg) : void 0;
|
|
2368
|
+
const activeTagGroup = resolveActiveTagGroup(
|
|
2369
|
+
orgParsed.tagGroups,
|
|
2370
|
+
orgParsed.options["active-tag"],
|
|
2371
|
+
ctxTagOverride(ctx)
|
|
2372
|
+
);
|
|
2373
|
+
const hiddenAttributes = unionHiddenAttributes(
|
|
2374
|
+
orgParsed.options["hide"],
|
|
2375
|
+
viewState?.ha
|
|
2376
|
+
);
|
|
2377
|
+
const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
|
|
2378
|
+
const orgLayout = layoutOrg(
|
|
2379
|
+
effectiveParsed,
|
|
2380
|
+
hiddenCounts.size > 0 ? hiddenCounts : void 0,
|
|
2381
|
+
activeTagGroup,
|
|
2382
|
+
hiddenAttributes,
|
|
2383
|
+
false
|
|
2384
|
+
// expandAllLegend off — collapsed-by-default per §1.3
|
|
2385
|
+
);
|
|
2386
|
+
const PADDING = 20;
|
|
2387
|
+
const titleOffset = effectiveParsed.title ? 30 : 0;
|
|
2388
|
+
const exportWidth = orgLayout.width + PADDING * 2;
|
|
2389
|
+
const exportHeight = orgLayout.height + PADDING * 2 + titleOffset;
|
|
2390
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
2391
|
+
renderOrg(
|
|
2392
|
+
container,
|
|
2393
|
+
effectiveParsed,
|
|
2394
|
+
orgLayout,
|
|
2395
|
+
effectivePalette,
|
|
2396
|
+
isDark,
|
|
2397
|
+
void 0,
|
|
2398
|
+
{ width: exportWidth, height: exportHeight },
|
|
2399
|
+
activeTagGroup,
|
|
2400
|
+
hiddenAttributes,
|
|
2401
|
+
void 0,
|
|
2402
|
+
exportMode
|
|
2403
|
+
);
|
|
2404
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2405
|
+
}
|
|
2406
|
+
async function exportSitemap(ctx) {
|
|
2407
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
2408
|
+
const { parseSitemap } = await import("./parser-NGU3ETO6.js");
|
|
2409
|
+
const { layoutSitemap } = await import("./layout-5T6RH5M6.js");
|
|
2410
|
+
const { collapseSitemapTree } = await import("./collapse-ZWUFFCK2.js");
|
|
2411
|
+
const { renderSitemap } = await import("./renderer-5NDVNDAZ.js");
|
|
2412
|
+
const isDark = ctx.isDark;
|
|
2413
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2414
|
+
const sitemapParsed = parseSitemap(content, effectivePalette);
|
|
2415
|
+
if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
|
|
2416
|
+
const collapsedNodes = viewState?.cg ? new Set(viewState.cg) : void 0;
|
|
2417
|
+
const activeTagGroup = resolveActiveTagGroup(
|
|
2418
|
+
sitemapParsed.tagGroups,
|
|
2419
|
+
sitemapParsed.options["active-tag"],
|
|
2420
|
+
ctxTagOverride(ctx)
|
|
2421
|
+
);
|
|
2422
|
+
const hiddenAttributes = unionHiddenAttributes(
|
|
2423
|
+
sitemapParsed.options["hide"],
|
|
2424
|
+
viewState?.ha
|
|
2425
|
+
);
|
|
2426
|
+
const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
|
|
2427
|
+
const sitemapLayout = layoutSitemap(
|
|
2428
|
+
effectiveParsed,
|
|
2429
|
+
hiddenCounts.size > 0 ? hiddenCounts : void 0,
|
|
2430
|
+
activeTagGroup,
|
|
2431
|
+
hiddenAttributes,
|
|
2432
|
+
true
|
|
2433
|
+
);
|
|
2434
|
+
const PADDING = 20;
|
|
2435
|
+
const titleOffset = effectiveParsed.title ? 30 : 0;
|
|
2436
|
+
const exportWidth = sitemapLayout.width + PADDING * 2;
|
|
2437
|
+
const exportHeight = sitemapLayout.height + PADDING * 2 + titleOffset;
|
|
2438
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
2439
|
+
renderSitemap(
|
|
2440
|
+
container,
|
|
2441
|
+
effectiveParsed,
|
|
2442
|
+
sitemapLayout,
|
|
2443
|
+
effectivePalette,
|
|
2444
|
+
isDark,
|
|
2445
|
+
void 0,
|
|
2446
|
+
{ width: exportWidth, height: exportHeight },
|
|
2447
|
+
activeTagGroup,
|
|
2448
|
+
hiddenAttributes,
|
|
2449
|
+
exportMode
|
|
2450
|
+
);
|
|
2451
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2452
|
+
}
|
|
2453
|
+
async function exportKanban(ctx) {
|
|
2454
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
2455
|
+
const { parseKanban } = await import("./parser-XDVELHRK.js");
|
|
2456
|
+
const { renderKanban } = await import("./renderer-6EEZHMH4.js");
|
|
2457
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2458
|
+
const kanbanParsed = parseKanban(content, effectivePalette);
|
|
2459
|
+
if (kanbanParsed.error || kanbanParsed.columns.length === 0) return "";
|
|
2460
|
+
const container = document.createElement("div");
|
|
2461
|
+
container.style.position = "absolute";
|
|
2462
|
+
container.style.left = "-9999px";
|
|
2463
|
+
document.body.appendChild(container);
|
|
2464
|
+
const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
|
|
2465
|
+
const sourceCollapsedColumns = kanbanParsed.columns.filter((c) => c.collapsed).map((c) => c.id);
|
|
2466
|
+
const kanbanCollapsedColumns = viewState?.cc || sourceCollapsedColumns.length > 0 ? /* @__PURE__ */ new Set([...sourceCollapsedColumns, ...viewState?.cc ?? []]) : void 0;
|
|
2467
|
+
renderKanban(container, kanbanParsed, effectivePalette, ctx.isDark, {
|
|
2468
|
+
activeTagGroup: resolveActiveTagGroup(
|
|
2469
|
+
kanbanParsed.tagGroups,
|
|
2470
|
+
kanbanParsed.options["active-tag"],
|
|
2471
|
+
ctxTagOverride(ctx)
|
|
2472
|
+
),
|
|
2473
|
+
currentSwimlaneGroup: viewState?.swim ?? kanbanParsed.options["lane-by"] ?? null,
|
|
2474
|
+
...kanbanCollapsedLanes !== void 0 && {
|
|
2475
|
+
collapsedLanes: kanbanCollapsedLanes
|
|
2476
|
+
},
|
|
2477
|
+
...kanbanCollapsedColumns !== void 0 && {
|
|
2478
|
+
collapsedColumns: kanbanCollapsedColumns
|
|
2479
|
+
},
|
|
2480
|
+
...viewState?.cm !== void 0 && { compactMeta: viewState.cm },
|
|
2481
|
+
exportMode
|
|
2482
|
+
});
|
|
2483
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2484
|
+
}
|
|
2485
|
+
async function exportClass(ctx) {
|
|
2486
|
+
const { content, theme, palette, exportMode } = ctx;
|
|
2487
|
+
const { parseClassDiagram } = await import("./parser-NRAI6O25.js");
|
|
2488
|
+
const { layoutClassDiagram } = await import("./layout-46N4JJYK.js");
|
|
2489
|
+
const { renderClassDiagram } = await import("./renderer-TOCOVLHS.js");
|
|
2490
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2491
|
+
const classParsed = parseClassDiagram(content, effectivePalette);
|
|
2492
|
+
if (classParsed.error || classParsed.classes.length === 0) return "";
|
|
2493
|
+
const classLayout = layoutClassDiagram(classParsed);
|
|
2494
|
+
const PADDING = 20;
|
|
2495
|
+
const titleOffset = classParsed.title ? 40 : 0;
|
|
2496
|
+
const exportWidth = classLayout.width + PADDING * 2;
|
|
2497
|
+
const exportHeight = classLayout.height + PADDING * 2 + titleOffset;
|
|
2498
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
2499
|
+
renderClassDiagram(
|
|
2500
|
+
container,
|
|
2501
|
+
classParsed,
|
|
2502
|
+
classLayout,
|
|
2503
|
+
effectivePalette,
|
|
2504
|
+
ctx.isDark,
|
|
2505
|
+
void 0,
|
|
2506
|
+
{ width: exportWidth, height: exportHeight },
|
|
2507
|
+
void 0,
|
|
2508
|
+
exportMode
|
|
2509
|
+
);
|
|
2510
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2511
|
+
}
|
|
2512
|
+
async function exportEr(ctx) {
|
|
2513
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
2514
|
+
const { parseERDiagram } = await import("./parser-KNSQSKQO.js");
|
|
2515
|
+
const { layoutERDiagram } = await import("./layout-EE6GE46Y.js");
|
|
2516
|
+
const { renderERDiagram } = await import("./renderer-W4M6QLIJ.js");
|
|
2517
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2518
|
+
const erParsed = parseERDiagram(content, effectivePalette);
|
|
2519
|
+
if (erParsed.error || erParsed.tables.length === 0) return "";
|
|
2520
|
+
const erLayout = layoutERDiagram(erParsed);
|
|
2521
|
+
const PADDING = 20;
|
|
2522
|
+
const titleOffset = erParsed.title ? 40 : 0;
|
|
2523
|
+
const exportWidth = erLayout.width + PADDING * 2;
|
|
2524
|
+
const exportHeight = erLayout.height + PADDING * 2 + titleOffset;
|
|
2525
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
2526
|
+
renderERDiagram(
|
|
2527
|
+
container,
|
|
2528
|
+
erParsed,
|
|
2529
|
+
erLayout,
|
|
2530
|
+
effectivePalette,
|
|
2531
|
+
ctx.isDark,
|
|
2532
|
+
void 0,
|
|
2533
|
+
{ width: exportWidth, height: exportHeight },
|
|
2534
|
+
resolveActiveTagGroup(
|
|
2535
|
+
erParsed.tagGroups,
|
|
2536
|
+
erParsed.options["active-tag"],
|
|
2537
|
+
ctxTagOverride(ctx)
|
|
2538
|
+
),
|
|
2539
|
+
// Semantic colors are on by default; the source `no-semantic-colors` flag
|
|
2540
|
+
// suppresses them. An interactive `viewState.sem` overrides source.
|
|
2541
|
+
viewState?.sem ?? (erParsed.options["no-semantic-colors"] === "on" ? false : void 0),
|
|
2542
|
+
exportMode
|
|
2543
|
+
);
|
|
2544
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2545
|
+
}
|
|
2546
|
+
async function exportBoxesAndLines(ctx) {
|
|
2547
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
2548
|
+
const { parseBoxesAndLines } = await import("./parser-TRTHDFPP.js");
|
|
2549
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2550
|
+
const blParsed = parseBoxesAndLines(content, effectivePalette);
|
|
2551
|
+
if (blParsed.error || blParsed.nodes.length === 0) return "";
|
|
2552
|
+
let blHiddenTagValues;
|
|
2553
|
+
if (viewState?.htv) {
|
|
2554
|
+
blHiddenTagValues = /* @__PURE__ */ new Map();
|
|
2555
|
+
for (const [k, v] of Object.entries(viewState.htv)) {
|
|
2556
|
+
blHiddenTagValues.set(k, new Set(v));
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
const { renderBoxesAndLinesForExport } = await import("./renderer-YY2OSF6E.js");
|
|
2560
|
+
const { layoutBoxesAndLines } = await import("./layout-HRBZPFCN.js");
|
|
2561
|
+
const blLayout = await layoutBoxesAndLines(blParsed);
|
|
2562
|
+
const PADDING = 20;
|
|
2563
|
+
const titleOffset = blParsed.title ? 40 : 0;
|
|
2564
|
+
const exportWidth = blLayout.width + PADDING * 2;
|
|
2565
|
+
const exportHeight = blLayout.height + PADDING * 2 + titleOffset;
|
|
2566
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
2567
|
+
const blActiveTagGroup = ctxTagOverride(ctx);
|
|
2568
|
+
renderBoxesAndLinesForExport(
|
|
2569
|
+
container,
|
|
2570
|
+
blParsed,
|
|
2571
|
+
blLayout,
|
|
2572
|
+
effectivePalette,
|
|
2573
|
+
ctx.isDark,
|
|
2574
|
+
{
|
|
2575
|
+
exportDims: { width: exportWidth, height: exportHeight },
|
|
2576
|
+
...blActiveTagGroup !== void 0 && {
|
|
2577
|
+
activeTagGroup: blActiveTagGroup
|
|
2578
|
+
},
|
|
2579
|
+
...blHiddenTagValues !== void 0 && {
|
|
2580
|
+
hiddenTagValues: blHiddenTagValues
|
|
2581
|
+
},
|
|
2582
|
+
exportMode
|
|
2583
|
+
}
|
|
2584
|
+
);
|
|
2585
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2586
|
+
}
|
|
2587
|
+
async function exportSwimlane(ctx) {
|
|
2588
|
+
const { content, theme, palette } = ctx;
|
|
2589
|
+
const { parseSwimlane } = await import("./parser-ZSDPZYQP.js");
|
|
2590
|
+
const { layoutSwimlane } = await import("./layout-N76NR4ZO.js");
|
|
2591
|
+
const { renderSwimlaneForExport } = await import("./renderer-JGBKUCZD.js");
|
|
2592
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2593
|
+
const swimParsed = parseSwimlane(content, effectivePalette);
|
|
2594
|
+
if (swimParsed.error || swimParsed.nodes.length === 0) return "";
|
|
2595
|
+
const swimLayout = layoutSwimlane(swimParsed);
|
|
2596
|
+
const PADDING = 20;
|
|
2597
|
+
const titleOffset = swimParsed.title ? 40 : 0;
|
|
2598
|
+
const exportWidth = swimLayout.width + PADDING * 2;
|
|
2599
|
+
const exportHeight = swimLayout.height + PADDING * 2 + titleOffset;
|
|
2600
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
2601
|
+
renderSwimlaneForExport(
|
|
2602
|
+
container,
|
|
2603
|
+
swimParsed,
|
|
2604
|
+
swimLayout,
|
|
2605
|
+
effectivePalette,
|
|
2606
|
+
ctx.isDark,
|
|
2607
|
+
{
|
|
2608
|
+
exportDims: { width: exportWidth, height: exportHeight },
|
|
2609
|
+
activeTagGroup: resolveActiveTagGroup(
|
|
2610
|
+
swimParsed.tagGroups,
|
|
2611
|
+
swimParsed.options["active-tag"],
|
|
2612
|
+
ctxTagOverride(ctx)
|
|
2613
|
+
),
|
|
2614
|
+
exportMode: ctx.exportMode
|
|
2615
|
+
}
|
|
2616
|
+
);
|
|
2617
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2618
|
+
}
|
|
2619
|
+
async function exportMindmap(ctx) {
|
|
2620
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
2621
|
+
const { parseMindmap } = await import("./parser-N5O4IHUN.js");
|
|
2622
|
+
const { layoutMindmap } = await import("./layout-UM5F4DJB.js");
|
|
2623
|
+
const { collapseMindmapTree } = await import("./collapse-SLINJQW6.js");
|
|
2624
|
+
const { renderMindmap } = await import("./renderer-2XQQQ6QR.js");
|
|
2625
|
+
const isDark = ctx.isDark;
|
|
2626
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2627
|
+
const mmParsed = parseMindmap(content, effectivePalette);
|
|
2628
|
+
if (mmParsed.error) return "";
|
|
2629
|
+
const collapsedNodes = new Set(viewState?.cg ?? []);
|
|
2630
|
+
const collectCollapsed = (nodes) => {
|
|
2631
|
+
for (const n of nodes) {
|
|
2632
|
+
if (n.collapsed) collapsedNodes.add(n.id);
|
|
2633
|
+
if (n.children.length) collectCollapsed(n.children);
|
|
2634
|
+
}
|
|
2635
|
+
};
|
|
2636
|
+
collectCollapsed(mmParsed.roots);
|
|
2637
|
+
const activeTagGroup = resolveActiveTagGroup(
|
|
2638
|
+
mmParsed.tagGroups,
|
|
2639
|
+
mmParsed.options["active-tag"],
|
|
2640
|
+
ctxTagOverride(ctx)
|
|
2641
|
+
);
|
|
2642
|
+
const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
|
|
2643
|
+
const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
|
|
2644
|
+
const effectiveParsed = { ...mmParsed, roots: effectiveRoots };
|
|
2645
|
+
const mmLayout = layoutMindmap(effectiveParsed, effectivePalette, {
|
|
2646
|
+
interactive: false,
|
|
2647
|
+
...hiddenCounts.size > 0 && { hiddenCounts },
|
|
2648
|
+
activeTagGroup,
|
|
2649
|
+
hideDescriptions
|
|
2650
|
+
});
|
|
2651
|
+
const PADDING = 20;
|
|
2652
|
+
const titleOffset = effectiveParsed.title ? 30 : 0;
|
|
2653
|
+
const exportWidth = mmLayout.width + PADDING * 2;
|
|
2654
|
+
const exportHeight = mmLayout.height + PADDING * 2 + titleOffset;
|
|
2655
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
2656
|
+
const colorByDepth = viewState?.cbd ?? mmParsed.options["color-by-depth"] === "on";
|
|
2657
|
+
renderMindmap(
|
|
2658
|
+
container,
|
|
2659
|
+
effectiveParsed,
|
|
2660
|
+
mmLayout,
|
|
2661
|
+
effectivePalette,
|
|
2662
|
+
isDark,
|
|
2663
|
+
void 0,
|
|
2664
|
+
{ width: exportWidth, height: exportHeight },
|
|
2665
|
+
void 0,
|
|
2666
|
+
hideDescriptions,
|
|
2667
|
+
colorByDepth ? null : activeTagGroup,
|
|
2668
|
+
colorByDepth ? { colorByDepth: true, exportMode } : { exportMode }
|
|
2669
|
+
);
|
|
2670
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2671
|
+
}
|
|
2672
|
+
async function exportWireframe(ctx) {
|
|
2673
|
+
const { content, theme, palette } = ctx;
|
|
2674
|
+
const { parseWireframe } = await import("./parser-IXSZZQHY.js");
|
|
2675
|
+
const { layoutWireframe } = await import("./layout-G23F7LDH.js");
|
|
2676
|
+
const { renderWireframe } = await import("./renderer-QIWVHWZO.js");
|
|
2677
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2678
|
+
const wireframeParsed = parseWireframe(content);
|
|
2679
|
+
if (wireframeParsed.error || wireframeParsed.roots.length === 0 && wireframeParsed.modals.length === 0)
|
|
2680
|
+
return "";
|
|
2681
|
+
const wireframeLayout = layoutWireframe(wireframeParsed);
|
|
2682
|
+
const exportWidth = wireframeLayout.width;
|
|
2683
|
+
const exportHeight = wireframeLayout.height;
|
|
2684
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
2685
|
+
renderWireframe(
|
|
2686
|
+
container,
|
|
2687
|
+
wireframeParsed,
|
|
2688
|
+
wireframeLayout,
|
|
2689
|
+
effectivePalette,
|
|
2690
|
+
ctx.isDark,
|
|
2691
|
+
void 0,
|
|
2692
|
+
{ width: exportWidth, height: exportHeight },
|
|
2693
|
+
theme
|
|
2694
|
+
);
|
|
2695
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2696
|
+
}
|
|
2697
|
+
async function exportC4(ctx) {
|
|
2698
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
2699
|
+
const { parseC4 } = await import("./parser-FUSKFGZN.js");
|
|
2700
|
+
const {
|
|
2701
|
+
layoutC4Context,
|
|
2702
|
+
layoutC4Containers,
|
|
2703
|
+
layoutC4Components,
|
|
2704
|
+
layoutC4Deployment
|
|
2705
|
+
} = await import("./layout-5RA6QWR6.js");
|
|
2706
|
+
const { renderC4Context, renderC4Containers } = await import("./renderer-UWA5XS74.js");
|
|
2707
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2708
|
+
const c4Parsed = parseC4(content, effectivePalette);
|
|
2709
|
+
if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
|
|
2710
|
+
const c4Level = ctx.options?.c4Level ?? viewState?.c4l ?? "context";
|
|
2711
|
+
const c4System = ctx.options?.c4System ?? viewState?.c4s;
|
|
2712
|
+
const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
|
|
2713
|
+
const c4Layout = c4Level === "deployment" ? layoutC4Deployment(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers(c4Parsed, c4System) : layoutC4Context(c4Parsed);
|
|
2714
|
+
if (c4Layout.nodes.length === 0) return "";
|
|
2715
|
+
const PADDING = 20;
|
|
2716
|
+
const titleOffset = c4Parsed.title ? 40 : 0;
|
|
2717
|
+
const exportWidth = c4Layout.width + PADDING * 2;
|
|
2718
|
+
const exportHeight = c4Layout.height + PADDING * 2 + titleOffset;
|
|
2719
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
2720
|
+
const renderFn = c4Level === "deployment" || c4Level === "components" && c4System && c4Container || c4Level === "containers" && c4System ? renderC4Containers : renderC4Context;
|
|
2721
|
+
renderFn(
|
|
2722
|
+
container,
|
|
2723
|
+
c4Parsed,
|
|
2724
|
+
c4Layout,
|
|
2725
|
+
effectivePalette,
|
|
2726
|
+
ctx.isDark,
|
|
2727
|
+
void 0,
|
|
2728
|
+
{ width: exportWidth, height: exportHeight },
|
|
2729
|
+
resolveActiveTagGroup(
|
|
2730
|
+
c4Parsed.tagGroups,
|
|
2731
|
+
c4Parsed.options["active-tag"],
|
|
2732
|
+
ctxTagOverride(ctx)
|
|
2733
|
+
),
|
|
2734
|
+
exportMode
|
|
2735
|
+
);
|
|
2736
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2737
|
+
}
|
|
2738
|
+
async function exportFlowchart(ctx) {
|
|
2739
|
+
const { content, theme, palette } = ctx;
|
|
2740
|
+
const { parseFlowchart } = await import("./flowchart-parser-ZNSKIBVR.js");
|
|
2741
|
+
const { layoutGraph } = await import("./layout-HWDSRX5E.js");
|
|
2742
|
+
const { renderFlowchart } = await import("./flowchart-renderer-ZAEOL4DX.js");
|
|
2743
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2744
|
+
const fcParsed = parseFlowchart(content, effectivePalette);
|
|
2745
|
+
if (fcParsed.error || fcParsed.nodes.length === 0) return "";
|
|
2746
|
+
const layout = layoutGraph(fcParsed);
|
|
2747
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
2748
|
+
renderFlowchart(
|
|
2749
|
+
container,
|
|
2750
|
+
fcParsed,
|
|
2751
|
+
layout,
|
|
2752
|
+
effectivePalette,
|
|
2753
|
+
ctx.isDark,
|
|
2754
|
+
void 0,
|
|
2755
|
+
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
|
|
2756
|
+
);
|
|
2757
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2758
|
+
}
|
|
2759
|
+
async function exportInfra(ctx) {
|
|
2760
|
+
const { content, theme, palette, viewState } = ctx;
|
|
2761
|
+
const { parseInfra } = await import("./parser-OV7NOBW6.js");
|
|
2762
|
+
const { computeInfra } = await import("./compute-G55RG53N.js");
|
|
2763
|
+
const { layoutInfra } = await import("./layout-7QXCNOVS.js");
|
|
2764
|
+
const { renderInfra, computeInfraLegendGroups } = await import("./renderer-BYCNMBF7.js");
|
|
2765
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2766
|
+
const infraParsed = parseInfra(content);
|
|
2767
|
+
if (infraParsed.error || infraParsed.nodes.length === 0) return "";
|
|
2768
|
+
const infraComputed = computeInfra(infraParsed);
|
|
2769
|
+
const infraLayout = layoutInfra(infraComputed);
|
|
2770
|
+
const activeTagGroup = resolveActiveTagGroup(
|
|
2771
|
+
infraParsed.tagGroups,
|
|
2772
|
+
infraParsed.options["active-tag"],
|
|
2773
|
+
ctxTagOverride(ctx)
|
|
2774
|
+
);
|
|
2775
|
+
const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
|
|
2776
|
+
const titleOffset = showInfraTitle ? 40 : 0;
|
|
2777
|
+
const infraTagGroups = [...infraParsed.tagGroups];
|
|
2778
|
+
const legendGroups = computeInfraLegendGroups(
|
|
2779
|
+
infraLayout.nodes,
|
|
2780
|
+
infraTagGroups,
|
|
2781
|
+
effectivePalette
|
|
2782
|
+
);
|
|
2783
|
+
const legendOffset = legendGroups.length > 0 ? 28 : 0;
|
|
2784
|
+
const exportWidth = infraLayout.width;
|
|
2785
|
+
const exportHeight = infraLayout.height + titleOffset + legendOffset;
|
|
2786
|
+
const container = createExportContainer(exportWidth, exportHeight);
|
|
2787
|
+
renderInfra(
|
|
2788
|
+
container,
|
|
2789
|
+
infraLayout,
|
|
2790
|
+
effectivePalette,
|
|
2791
|
+
ctx.isDark,
|
|
2792
|
+
showInfraTitle ? infraParsed.title : null,
|
|
2793
|
+
showInfraTitle ? infraParsed.titleLineNumber : null,
|
|
2794
|
+
infraTagGroups,
|
|
2795
|
+
activeTagGroup,
|
|
2796
|
+
false,
|
|
2797
|
+
null,
|
|
2798
|
+
null,
|
|
2799
|
+
true,
|
|
2800
|
+
viewState?.cg ? new Set(viewState.cg) : null
|
|
2801
|
+
);
|
|
2802
|
+
const infraSvg = container.querySelector("svg");
|
|
2803
|
+
if (infraSvg) {
|
|
2804
|
+
infraSvg.setAttribute("width", String(exportWidth));
|
|
2805
|
+
infraSvg.setAttribute("height", String(exportHeight));
|
|
2806
|
+
}
|
|
2807
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2808
|
+
}
|
|
2809
|
+
async function exportPert(ctx) {
|
|
2810
|
+
const { content, theme, palette, viewState } = ctx;
|
|
2811
|
+
const { parsePert } = await import("./parser-3STE62GB.js");
|
|
2812
|
+
const { analyzePert } = await import("./analyzer-UYKBXAKR.js");
|
|
2813
|
+
const { layoutPert } = await import("./layout-S2QBYSK2.js");
|
|
2814
|
+
const { renderPert, measurePertAnalysisBlock } = await import("./renderer-AWWAF6XD.js");
|
|
2815
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2816
|
+
const pertParsed = parsePert(content, { palette: effectivePalette });
|
|
2817
|
+
if (pertParsed.error || pertParsed.activities.length === 0) return "";
|
|
2818
|
+
const pertResolved = analyzePert(pertParsed);
|
|
2819
|
+
const pertLayout = layoutPert(pertResolved);
|
|
2820
|
+
const titleHeight = pertParsed.title && !pertParsed.options.noTitle ? 80 : 0;
|
|
2821
|
+
const PERT_PADDING = 20;
|
|
2822
|
+
const analysisOn = viewState?.an ?? !pertParsed.options.noAnalysis;
|
|
2823
|
+
const fieldLabelsOn = viewState?.fl === true;
|
|
2824
|
+
const exportW = pertLayout.width + PERT_PADDING * 2;
|
|
2825
|
+
const analysisMeasured = analysisOn || fieldLabelsOn ? measurePertAnalysisBlock(pertResolved, exportW - 2 * PERT_PADDING, {
|
|
2826
|
+
showSummary: false,
|
|
2827
|
+
showTornado: analysisOn,
|
|
2828
|
+
showScurve: analysisOn,
|
|
2829
|
+
showFieldLegend: fieldLabelsOn
|
|
2830
|
+
}) : { width: 0, height: 0 };
|
|
2831
|
+
const exportH = pertLayout.height + PERT_PADDING * 2 + titleHeight + analysisMeasured.height;
|
|
2832
|
+
const container = createExportContainer(exportW, exportH);
|
|
2833
|
+
renderPert(
|
|
2834
|
+
container,
|
|
2835
|
+
pertResolved,
|
|
2836
|
+
pertLayout,
|
|
2837
|
+
effectivePalette,
|
|
2838
|
+
ctx.isDark,
|
|
2839
|
+
{
|
|
2840
|
+
title: pertParsed.title,
|
|
2841
|
+
exportDims: { width: exportW, height: exportH },
|
|
2842
|
+
showSummary: false,
|
|
2843
|
+
showTornado: analysisOn,
|
|
2844
|
+
showScurve: analysisOn,
|
|
2845
|
+
showFieldLegend: fieldLabelsOn
|
|
2846
|
+
}
|
|
2847
|
+
);
|
|
2848
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2849
|
+
}
|
|
2850
|
+
async function exportGantt(ctx) {
|
|
2851
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
2852
|
+
const { parseGantt } = await import("./parser-AQETEVRW.js");
|
|
2853
|
+
const { calculateSchedule } = await import("./calculator-LDYQCDDY.js");
|
|
2854
|
+
const { renderGantt } = await import("./renderer-VACVQYML.js");
|
|
2855
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2856
|
+
const ganttParsed = parseGantt(content, effectivePalette);
|
|
2857
|
+
const resolved = calculateSchedule(ganttParsed);
|
|
2858
|
+
if (resolved.tasks.length === 0) return "";
|
|
2859
|
+
const EXPORT_W = 1200;
|
|
2860
|
+
const EXPORT_H = 800;
|
|
2861
|
+
const container = createExportContainer(EXPORT_W, EXPORT_H);
|
|
2862
|
+
const sourceCollapsedGroups = resolved.groups.filter((g) => g.collapsed).map((g) => g.name);
|
|
2863
|
+
const ganttCollapsedGroups = viewState?.cg || sourceCollapsedGroups.length > 0 ? /* @__PURE__ */ new Set([...sourceCollapsedGroups, ...viewState?.cg ?? []]) : void 0;
|
|
2864
|
+
const sourceSwimlane = resolved.options.sort === "tag" ? resolved.options.defaultSwimlaneGroup ?? void 0 : void 0;
|
|
2865
|
+
const ganttSwimlaneGroup = viewState?.swim ?? sourceSwimlane;
|
|
2866
|
+
const ganttCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
|
|
2867
|
+
renderGantt(
|
|
2868
|
+
container,
|
|
2869
|
+
resolved,
|
|
2870
|
+
effectivePalette,
|
|
2871
|
+
ctx.isDark,
|
|
2872
|
+
{
|
|
2873
|
+
...ganttCollapsedGroups !== void 0 && {
|
|
2874
|
+
collapsedGroups: ganttCollapsedGroups
|
|
2875
|
+
},
|
|
2876
|
+
...ganttSwimlaneGroup !== void 0 && {
|
|
2877
|
+
currentSwimlaneGroup: ganttSwimlaneGroup
|
|
2878
|
+
},
|
|
2879
|
+
...ganttCollapsedLanes !== void 0 && {
|
|
2880
|
+
collapsedLanes: ganttCollapsedLanes
|
|
2881
|
+
},
|
|
2882
|
+
currentActiveGroup: resolveActiveTagGroup(
|
|
2883
|
+
resolved.tagGroups,
|
|
2884
|
+
resolved.options.activeTag ?? void 0,
|
|
2885
|
+
ctxTagOverride(ctx)
|
|
2886
|
+
),
|
|
2887
|
+
exportMode
|
|
2888
|
+
},
|
|
2889
|
+
{ width: EXPORT_W, height: EXPORT_H }
|
|
2890
|
+
);
|
|
2891
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2892
|
+
}
|
|
2893
|
+
async function exportState(ctx) {
|
|
2894
|
+
const { content, theme, palette, viewState } = ctx;
|
|
2895
|
+
const { parseState } = await import("./state-parser-Z2V23ATR.js");
|
|
2896
|
+
const { layoutGraph } = await import("./layout-HWDSRX5E.js");
|
|
2897
|
+
const { renderState } = await import("./state-renderer-RFYHSP4K.js");
|
|
2898
|
+
const { collapseStateGroups } = await import("./state-collapse-HCGD6IVJ.js");
|
|
2899
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2900
|
+
const stateParsed = parseState(content, effectivePalette);
|
|
2901
|
+
if (stateParsed.error || stateParsed.nodes.length === 0) return "";
|
|
2902
|
+
const sourceCollapsed = (stateParsed.groups ?? []).filter((g) => g.collapsed).map((g) => g.id);
|
|
2903
|
+
const collapsedGroups = /* @__PURE__ */ new Set([
|
|
2904
|
+
...sourceCollapsed,
|
|
2905
|
+
...viewState?.cg ?? []
|
|
2906
|
+
]);
|
|
2907
|
+
const {
|
|
2908
|
+
parsed: effectiveParsed,
|
|
2909
|
+
collapsedChildCounts,
|
|
2910
|
+
originalGroups
|
|
2911
|
+
} = collapsedGroups.size > 0 ? collapseStateGroups(stateParsed, collapsedGroups) : {
|
|
2912
|
+
parsed: stateParsed,
|
|
2913
|
+
collapsedChildCounts: /* @__PURE__ */ new Map(),
|
|
2914
|
+
originalGroups: stateParsed.groups ?? []
|
|
2915
|
+
};
|
|
2916
|
+
const layout = layoutGraph(effectiveParsed, {
|
|
2917
|
+
collapsedChildCounts,
|
|
2918
|
+
originalGroups
|
|
2919
|
+
});
|
|
2920
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
2921
|
+
renderState(
|
|
2922
|
+
container,
|
|
2923
|
+
effectiveParsed,
|
|
2924
|
+
layout,
|
|
2925
|
+
effectivePalette,
|
|
2926
|
+
ctx.isDark,
|
|
2927
|
+
void 0,
|
|
2928
|
+
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
|
|
2929
|
+
);
|
|
2930
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2931
|
+
}
|
|
2932
|
+
async function exportTechRadar(ctx) {
|
|
2933
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
2934
|
+
const { parseTechRadar } = await import("./parser-2MFP2H2E.js");
|
|
2935
|
+
const { renderTechRadarForExport } = await import("./renderer-ZGNTUWC4.js");
|
|
2936
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2937
|
+
const radarParsed = parseTechRadar(content);
|
|
2938
|
+
if (radarParsed.error || radarParsed.quadrants.length === 0) return "";
|
|
2939
|
+
const RADAR_EXPORT_W = 1300;
|
|
2940
|
+
const RADAR_EXPORT_H = 1500;
|
|
2941
|
+
const container = createExportContainer(RADAR_EXPORT_W, RADAR_EXPORT_H);
|
|
2942
|
+
renderTechRadarForExport(
|
|
2943
|
+
container,
|
|
2944
|
+
radarParsed,
|
|
2945
|
+
effectivePalette,
|
|
2946
|
+
ctx.isDark,
|
|
2947
|
+
{ width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
|
|
2948
|
+
viewState,
|
|
2949
|
+
exportMode
|
|
2950
|
+
);
|
|
2951
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2952
|
+
}
|
|
2953
|
+
async function exportJourneyMap(ctx) {
|
|
2954
|
+
const { content, theme, palette, exportMode } = ctx;
|
|
2955
|
+
const { parseJourneyMap } = await import("./parser-I6KXEYFK.js");
|
|
2956
|
+
const { renderJourneyMap } = await import("./renderer-CLJHQPBJ.js");
|
|
2957
|
+
const { layoutJourneyMap } = await import("./layout-HYZPWAK5.js");
|
|
2958
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2959
|
+
const jmParsed = parseJourneyMap(content, effectivePalette);
|
|
2960
|
+
if (jmParsed.error || jmParsed.phases.length === 0 && jmParsed.steps.length === 0)
|
|
2961
|
+
return "";
|
|
2962
|
+
const jmLayout = layoutJourneyMap(jmParsed, effectivePalette, {
|
|
2963
|
+
isDark: ctx.isDark
|
|
2964
|
+
});
|
|
2965
|
+
const container = createExportContainer(
|
|
2966
|
+
jmLayout.totalWidth,
|
|
2967
|
+
jmLayout.totalHeight
|
|
2968
|
+
);
|
|
2969
|
+
renderJourneyMap(container, jmParsed, effectivePalette, ctx.isDark, {
|
|
2970
|
+
exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
|
|
2971
|
+
exportMode
|
|
2972
|
+
});
|
|
2973
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2974
|
+
}
|
|
2975
|
+
async function exportCycle(ctx) {
|
|
2976
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
2977
|
+
const { parseCycle } = await import("./parser-VQP2UJ6W.js");
|
|
2978
|
+
const { renderCycleForExport } = await import("./renderer-Z7I3W2PP.js");
|
|
2979
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
2980
|
+
const cycleParsed = parseCycle(content);
|
|
2981
|
+
if (cycleParsed.error || cycleParsed.nodes.length === 0) return "";
|
|
2982
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
2983
|
+
renderCycleForExport(
|
|
2984
|
+
container,
|
|
2985
|
+
cycleParsed,
|
|
2986
|
+
effectivePalette,
|
|
2987
|
+
ctx.isDark,
|
|
2988
|
+
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
|
|
2989
|
+
viewState,
|
|
2990
|
+
exportMode
|
|
2991
|
+
);
|
|
2992
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
2993
|
+
}
|
|
2994
|
+
async function exportMap(ctx) {
|
|
2995
|
+
const { content, theme, palette, options } = ctx;
|
|
2996
|
+
const { parseMap } = await import("./parser-4FL2CMDG.js");
|
|
2997
|
+
const { resolveMap } = await import("./resolver-NOAA7ID7.js");
|
|
2998
|
+
const { renderMapForExport } = await import("./renderer-SMIBCRVB.js");
|
|
2999
|
+
const { mapExportDimensions } = await import("./dimensions-J3FHIMJ6.js");
|
|
3000
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3001
|
+
const mapParsed = parseMap(content, effectivePalette);
|
|
3002
|
+
let mapData = options?.mapData;
|
|
3003
|
+
if (!mapData) {
|
|
3004
|
+
const { loadMapData } = await import("./load-data-EHDDZ5ZS.js");
|
|
3005
|
+
try {
|
|
3006
|
+
mapData = await loadMapData();
|
|
3007
|
+
} catch {
|
|
3008
|
+
return "";
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
const mapResolved = resolveMap(mapParsed, mapData);
|
|
3012
|
+
const dims = mapExportDimensions(
|
|
3013
|
+
mapResolved,
|
|
3014
|
+
mapData,
|
|
3015
|
+
EXPORT_WIDTH,
|
|
3016
|
+
options?.mapAspect
|
|
3017
|
+
);
|
|
3018
|
+
const container = createExportContainer(dims.width, dims.height);
|
|
3019
|
+
renderMapForExport(
|
|
3020
|
+
container,
|
|
3021
|
+
mapResolved,
|
|
3022
|
+
mapData,
|
|
3023
|
+
effectivePalette,
|
|
3024
|
+
ctx.isDark,
|
|
3025
|
+
dims
|
|
3026
|
+
);
|
|
3027
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3028
|
+
}
|
|
3029
|
+
async function exportPyramid(ctx) {
|
|
3030
|
+
const { content, theme, palette } = ctx;
|
|
3031
|
+
const { parsePyramid } = await import("./parser-XKRP7QKP.js");
|
|
3032
|
+
const { renderPyramidForExport } = await import("./renderer-KQB7UGDS.js");
|
|
3033
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3034
|
+
const pyramidParsed = parsePyramid(content);
|
|
3035
|
+
if (pyramidParsed.error || pyramidParsed.layers.length === 0) return "";
|
|
3036
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
3037
|
+
renderPyramidForExport(
|
|
3038
|
+
container,
|
|
3039
|
+
pyramidParsed,
|
|
3040
|
+
effectivePalette,
|
|
3041
|
+
ctx.isDark,
|
|
3042
|
+
{ width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
|
|
3043
|
+
);
|
|
3044
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3045
|
+
}
|
|
3046
|
+
async function exportRing(ctx) {
|
|
3047
|
+
const { content, theme, palette } = ctx;
|
|
3048
|
+
const { parseRing } = await import("./parser-O5J4VBII.js");
|
|
3049
|
+
const { renderRingForExport } = await import("./renderer-ZTR3DER3.js");
|
|
3050
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3051
|
+
const ringParsed = parseRing(content);
|
|
3052
|
+
if (ringParsed.error || ringParsed.layers.length === 0) return "";
|
|
3053
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
3054
|
+
renderRingForExport(container, ringParsed, effectivePalette, ctx.isDark, {
|
|
3055
|
+
width: EXPORT_WIDTH,
|
|
3056
|
+
height: EXPORT_HEIGHT
|
|
3057
|
+
});
|
|
3058
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3059
|
+
}
|
|
3060
|
+
async function exportTreemap(ctx) {
|
|
3061
|
+
const { content, theme, palette } = ctx;
|
|
3062
|
+
const { parseTreemap } = await import("./parser-CFZD5IBL.js");
|
|
3063
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3064
|
+
const treemapParsed = parseTreemap(content, effectivePalette);
|
|
3065
|
+
if (treemapParsed.error || treemapParsed.roots.length === 0) return "";
|
|
3066
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
3067
|
+
const dims = { width: EXPORT_WIDTH, height: EXPORT_HEIGHT };
|
|
3068
|
+
if (treemapParsed.options.radial) {
|
|
3069
|
+
const { renderTreemapRadialForExport } = await import("./renderer-radial-C32GPJQ7.js");
|
|
3070
|
+
renderTreemapRadialForExport(
|
|
3071
|
+
container,
|
|
3072
|
+
treemapParsed,
|
|
3073
|
+
effectivePalette,
|
|
3074
|
+
ctx.isDark,
|
|
3075
|
+
dims
|
|
3076
|
+
);
|
|
3077
|
+
} else {
|
|
3078
|
+
const { renderTreemapForExport } = await import("./renderer-7MVLWWQD.js");
|
|
3079
|
+
renderTreemapForExport(
|
|
3080
|
+
container,
|
|
3081
|
+
treemapParsed,
|
|
3082
|
+
effectivePalette,
|
|
3083
|
+
ctx.isDark,
|
|
3084
|
+
dims
|
|
3085
|
+
);
|
|
3086
|
+
}
|
|
3087
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3088
|
+
}
|
|
3089
|
+
async function exportBlock(ctx) {
|
|
3090
|
+
const { content, theme, palette } = ctx;
|
|
3091
|
+
const { parseBlock } = await import("./parser-4OGOBJJ4.js");
|
|
3092
|
+
const { renderBlockForExport } = await import("./renderer-PGX2TKVS.js");
|
|
3093
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3094
|
+
const blockParsed = parseBlock(content, effectivePalette);
|
|
3095
|
+
if (blockParsed.error || blockParsed.top.rows.length === 0) return "";
|
|
3096
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
3097
|
+
renderBlockForExport(container, blockParsed, effectivePalette, ctx.isDark, {
|
|
3098
|
+
width: EXPORT_WIDTH,
|
|
3099
|
+
height: EXPORT_HEIGHT
|
|
3100
|
+
});
|
|
3101
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3102
|
+
}
|
|
3103
|
+
async function exportRaci(ctx) {
|
|
3104
|
+
const { content, theme, palette } = ctx;
|
|
3105
|
+
const { parseRaci } = await import("./parser-Y6WTBU76.js");
|
|
3106
|
+
const { renderRaciForExport } = await import("./renderer-WZRREQ2P.js");
|
|
3107
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3108
|
+
const raciParsed = parseRaci(content, effectivePalette);
|
|
3109
|
+
if (raciParsed.error) return "";
|
|
3110
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
3111
|
+
renderRaciForExport(container, raciParsed, effectivePalette, ctx.isDark, {
|
|
3112
|
+
width: EXPORT_WIDTH,
|
|
3113
|
+
height: EXPORT_HEIGHT
|
|
3114
|
+
});
|
|
3115
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3116
|
+
}
|
|
3117
|
+
function beginVizExport() {
|
|
3118
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
3119
|
+
const dims = {
|
|
3120
|
+
width: EXPORT_WIDTH,
|
|
3121
|
+
height: EXPORT_HEIGHT
|
|
3122
|
+
};
|
|
3123
|
+
return { container, dims };
|
|
3124
|
+
}
|
|
3125
|
+
async function exportSlope(ctx) {
|
|
3126
|
+
const { content, theme, palette } = ctx;
|
|
3127
|
+
const parsed = parseSlope(content, palette);
|
|
3128
|
+
if (parsed.error || parsed.data.length === 0) return "";
|
|
3129
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3130
|
+
const { container, dims } = beginVizExport();
|
|
3131
|
+
renderSlopeChart(
|
|
3132
|
+
container,
|
|
3133
|
+
parsed,
|
|
3134
|
+
effectivePalette,
|
|
3135
|
+
ctx.isDark,
|
|
3136
|
+
void 0,
|
|
3137
|
+
dims
|
|
3138
|
+
);
|
|
3139
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3140
|
+
}
|
|
3141
|
+
async function exportArc(ctx) {
|
|
3142
|
+
const { content, theme, palette } = ctx;
|
|
3143
|
+
const parsed = parseArc(content, palette);
|
|
3144
|
+
if (parsed.error || parsed.links.length === 0) return "";
|
|
3145
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3146
|
+
const { container, dims } = beginVizExport();
|
|
3147
|
+
renderArcDiagram(
|
|
3148
|
+
container,
|
|
3149
|
+
parsed,
|
|
3150
|
+
effectivePalette,
|
|
3151
|
+
ctx.isDark,
|
|
3152
|
+
void 0,
|
|
3153
|
+
dims
|
|
3154
|
+
);
|
|
3155
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3156
|
+
}
|
|
3157
|
+
async function exportTimeline(ctx) {
|
|
3158
|
+
const { content, theme, palette, viewState, exportMode } = ctx;
|
|
3159
|
+
const parsed = parseTimeline(content, palette);
|
|
3160
|
+
if (parsed.error || parsed.timelineEvents.length === 0) return "";
|
|
3161
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3162
|
+
const { container, dims } = beginVizExport();
|
|
3163
|
+
renderTimeline(
|
|
3164
|
+
container,
|
|
3165
|
+
parsed,
|
|
3166
|
+
effectivePalette,
|
|
3167
|
+
ctx.isDark,
|
|
3168
|
+
void 0,
|
|
3169
|
+
dims,
|
|
3170
|
+
resolveActiveTagGroup(
|
|
3171
|
+
parsed.timelineTagGroups,
|
|
3172
|
+
parsed.timelineActiveTag,
|
|
3173
|
+
ctxTagOverride(ctx)
|
|
3174
|
+
),
|
|
3175
|
+
// Swimlane axis from source (`lane-by <group>` / `sort tag:<group>` →
|
|
3176
|
+
// timelineDefaultSwimlaneTG), with a viewState.swim override.
|
|
3177
|
+
viewState?.swim ?? parsed.timelineDefaultSwimlaneTG,
|
|
3178
|
+
void 0,
|
|
3179
|
+
void 0,
|
|
3180
|
+
exportMode
|
|
3181
|
+
);
|
|
3182
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3183
|
+
}
|
|
3184
|
+
async function exportWordcloud(ctx) {
|
|
3185
|
+
const { content, theme, palette } = ctx;
|
|
3186
|
+
const parsed = parseWordcloud(content, palette);
|
|
3187
|
+
if (parsed.error || parsed.words.length === 0) return "";
|
|
3188
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3189
|
+
const { container, dims } = beginVizExport();
|
|
3190
|
+
await renderWordCloudAsync(
|
|
3191
|
+
container,
|
|
3192
|
+
parsed,
|
|
3193
|
+
effectivePalette,
|
|
3194
|
+
ctx.isDark,
|
|
3195
|
+
dims
|
|
3196
|
+
);
|
|
3197
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3198
|
+
}
|
|
3199
|
+
async function exportVenn(ctx) {
|
|
3200
|
+
const { content, theme, palette } = ctx;
|
|
3201
|
+
const parsed = parseVenn(content, palette);
|
|
3202
|
+
if (parsed.error || parsed.vennSets.length < 2) return "";
|
|
3203
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3204
|
+
const { container, dims } = beginVizExport();
|
|
3205
|
+
renderVenn(container, parsed, effectivePalette, ctx.isDark, void 0, dims);
|
|
3206
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3207
|
+
}
|
|
3208
|
+
async function exportQuadrant(ctx) {
|
|
3209
|
+
const { content, theme, palette } = ctx;
|
|
3210
|
+
const parsed = parseQuadrant(content, palette);
|
|
3211
|
+
if (parsed.error || parsed.quadrantPoints.length === 0) return "";
|
|
3212
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3213
|
+
const { container, dims } = beginVizExport();
|
|
3214
|
+
renderQuadrant(
|
|
3215
|
+
container,
|
|
3216
|
+
parsed,
|
|
3217
|
+
effectivePalette,
|
|
3218
|
+
ctx.isDark,
|
|
3219
|
+
void 0,
|
|
3220
|
+
dims
|
|
3221
|
+
);
|
|
3222
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3223
|
+
}
|
|
3224
|
+
async function exportVisualization(ctx) {
|
|
3225
|
+
const { content, theme, palette, viewState } = ctx;
|
|
3226
|
+
const parsed = parseVisualization(content, palette);
|
|
3227
|
+
if (parsed.type !== "sequence") {
|
|
3228
|
+
if (parsed.error) {
|
|
3229
|
+
const looksLikeSequence = /->|~>|<-/.test(content);
|
|
3230
|
+
if (!looksLikeSequence) return "";
|
|
3231
|
+
} else {
|
|
3232
|
+
return "";
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
const effectivePalette = await resolveExportPalette(theme, palette);
|
|
3236
|
+
const isDark = ctx.isDark;
|
|
3237
|
+
const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
|
|
3238
|
+
const { parseSequenceDgmo } = await import("./parser-PQ3POX45.js");
|
|
3239
|
+
const { renderSequenceDiagram } = await import("./renderer-6W5462RB.js");
|
|
3240
|
+
const seqParsed = parseSequenceDgmo(content, effectivePalette);
|
|
3241
|
+
if (seqParsed.error || seqParsed.participants.length === 0) return "";
|
|
3242
|
+
const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
|
|
3243
|
+
const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
|
|
3244
|
+
const seqActiveTagGroup = ctxTagOverride(ctx);
|
|
3245
|
+
renderSequenceDiagram(
|
|
3246
|
+
container,
|
|
3247
|
+
seqParsed,
|
|
3248
|
+
effectivePalette,
|
|
3249
|
+
isDark,
|
|
3250
|
+
void 0,
|
|
3251
|
+
{
|
|
3252
|
+
exportWidth: EXPORT_WIDTH,
|
|
3253
|
+
...seqActiveTagGroup !== void 0 && {
|
|
3254
|
+
activeTagGroup: seqActiveTagGroup
|
|
3255
|
+
},
|
|
3256
|
+
...collapsedSections !== void 0 && { collapsedSections },
|
|
3257
|
+
...collapsedGroups !== void 0 && { collapsedGroups }
|
|
3258
|
+
}
|
|
3259
|
+
);
|
|
3260
|
+
return finalizeSvgExport(container, theme, effectivePalette);
|
|
3261
|
+
}
|
|
3262
|
+
|
|
3263
|
+
// src/utils/hover-styles.ts
|
|
3264
|
+
var MAX_HOVER_GROUPS = 40;
|
|
3265
|
+
var DEFAULT_DIM_OPACITY = 0.4;
|
|
3266
|
+
var LIFT_DECL = "filter:saturate(1.4) brightness(1.06)";
|
|
3267
|
+
var TRANSITION_DECL = "transition:filter .12s ease, opacity .12s ease";
|
|
3268
|
+
function escCssString(value) {
|
|
3269
|
+
let out = "";
|
|
3270
|
+
for (const ch of value) {
|
|
3271
|
+
const code = ch.codePointAt(0);
|
|
3272
|
+
if (ch === '"') out += '\\"';
|
|
3273
|
+
else if (ch === "\\") out += "\\\\";
|
|
3274
|
+
else if (ch === "<") out += "\\3c ";
|
|
3275
|
+
else if (code < 32 || code === 127)
|
|
3276
|
+
out += "\\" + code.toString(16) + " ";
|
|
3277
|
+
else out += ch;
|
|
3278
|
+
}
|
|
3279
|
+
return out;
|
|
3280
|
+
}
|
|
3281
|
+
function selfRule(markSelector) {
|
|
3282
|
+
return `${markSelector}:hover{${LIFT_DECL}}`;
|
|
3283
|
+
}
|
|
3284
|
+
function enumeratedRule(mark, attr, v, emphasis, dimOpacity) {
|
|
3285
|
+
const lit = escCssString(v);
|
|
3286
|
+
const trigger = `svg:has(${mark}[${attr}="${lit}"]:hover)`;
|
|
3287
|
+
return emphasis === "lift" ? `${trigger} ${mark}[${attr}="${lit}"]{${LIFT_DECL}}` : `${trigger} ${mark}:not([${attr}="${lit}"]){opacity:${dimOpacity}}`;
|
|
3288
|
+
}
|
|
3289
|
+
function legendRule(mark, attr, v, emphasis, dimOpacity) {
|
|
3290
|
+
const entryLit = escCssString(v.toLowerCase());
|
|
3291
|
+
const attrLit = escCssString(v);
|
|
3292
|
+
const trigger = `svg:has([data-legend-entry="${entryLit}"]:hover)`;
|
|
3293
|
+
return emphasis === "lift" ? `${trigger} ${mark}[${attr}="${attrLit}"]{${LIFT_DECL}}` : `${trigger} ${mark}:not([${attr}="${attrLit}"]){opacity:${dimOpacity}}`;
|
|
3294
|
+
}
|
|
3295
|
+
function connectionRule(spec, id, dimOpacity) {
|
|
3296
|
+
const lit = escCssString(id);
|
|
3297
|
+
const { hoverSelector, hoverAttr, edgeSelector, fromAttr, toAttr } = spec;
|
|
3298
|
+
const trigger = `svg:has(${hoverSelector}[${hoverAttr}="${lit}"]:hover)`;
|
|
3299
|
+
return `${trigger} ${edgeSelector}:not([${fromAttr}="${lit}"]):not([${toAttr}="${lit}"]){opacity:${dimOpacity}}`;
|
|
3300
|
+
}
|
|
3301
|
+
function buildHoverCss(spec, derived = {}, emphasisOverride) {
|
|
3302
|
+
const emphasis = emphasisOverride ?? spec.emphasis ?? "lift";
|
|
3303
|
+
const dimOpacity = spec.dimOpacity ?? DEFAULT_DIM_OPACITY;
|
|
3304
|
+
const selfOn = spec.selfEmphasis !== false;
|
|
3305
|
+
const values = derived.values ?? [];
|
|
3306
|
+
const ids = derived.ids ?? [];
|
|
3307
|
+
const base = [];
|
|
3308
|
+
if (selfOn) {
|
|
3309
|
+
base.push(selfRule(spec.markSelector));
|
|
3310
|
+
if (!spec.animatesOpacity) {
|
|
3311
|
+
base.push(`${spec.markSelector}{${TRANSITION_DECL}}`);
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
const cross = [];
|
|
3315
|
+
if (spec.strategy === "structural" && spec.groupSelector) {
|
|
3316
|
+
const g = spec.groupSelector;
|
|
3317
|
+
cross.push(
|
|
3318
|
+
emphasis === "lift" ? `svg:has(${g}:hover) ${g}:hover{${LIFT_DECL}}` : `svg:has(${g}:hover) ${g}:not(:hover){opacity:${dimOpacity}}`
|
|
3319
|
+
);
|
|
3320
|
+
} else if (spec.strategy === "enumerated" && spec.groupAttr) {
|
|
3321
|
+
const legendCount = spec.legend ? values.length : 0;
|
|
3322
|
+
if (values.length + legendCount > MAX_HOVER_GROUPS) {
|
|
3323
|
+
base.push(
|
|
3324
|
+
`/* hover: ${values.length} groups exceeds cap ${MAX_HOVER_GROUPS}; self-emphasis only */`
|
|
3325
|
+
);
|
|
3326
|
+
} else {
|
|
3327
|
+
for (const v of values) {
|
|
3328
|
+
cross.push(
|
|
3329
|
+
enumeratedRule(
|
|
3330
|
+
spec.markSelector,
|
|
3331
|
+
spec.groupAttr,
|
|
3332
|
+
v,
|
|
3333
|
+
emphasis,
|
|
3334
|
+
dimOpacity
|
|
3335
|
+
)
|
|
3336
|
+
);
|
|
3337
|
+
}
|
|
3338
|
+
if (spec.legend) {
|
|
3339
|
+
for (const v of values) {
|
|
3340
|
+
cross.push(
|
|
3341
|
+
legendRule(
|
|
3342
|
+
spec.markSelector,
|
|
3343
|
+
spec.groupAttr,
|
|
3344
|
+
v,
|
|
3345
|
+
emphasis,
|
|
3346
|
+
dimOpacity
|
|
3347
|
+
)
|
|
3348
|
+
);
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
} else if (spec.strategy === "connection") {
|
|
3353
|
+
if (ids.length > MAX_HOVER_GROUPS) {
|
|
3354
|
+
base.push(
|
|
3355
|
+
`/* hover: ${ids.length} nodes exceeds cap ${MAX_HOVER_GROUPS}; self-emphasis only */`
|
|
3356
|
+
);
|
|
3357
|
+
} else {
|
|
3358
|
+
for (const id of ids) cross.push(connectionRule(spec, id, dimOpacity));
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
const parts = [...base];
|
|
3362
|
+
if (cross.length) parts.push(`@media (hover:hover){${cross.join("")}}`);
|
|
3363
|
+
return parts.join("");
|
|
3364
|
+
}
|
|
3365
|
+
var HOVER_SPECS = {
|
|
3366
|
+
pie: {
|
|
3367
|
+
markSelector: ".dgmo-datum",
|
|
3368
|
+
strategy: "enumerated",
|
|
3369
|
+
groupAttr: "data-emph-key",
|
|
3370
|
+
legend: true
|
|
3371
|
+
},
|
|
3372
|
+
bar: {
|
|
3373
|
+
markSelector: ".dgmo-datum",
|
|
3374
|
+
strategy: "enumerated",
|
|
3375
|
+
groupAttr: "data-emph-key",
|
|
3376
|
+
legend: true
|
|
3377
|
+
},
|
|
3378
|
+
funnel: {
|
|
3379
|
+
markSelector: ".dgmo-datum",
|
|
3380
|
+
strategy: "enumerated",
|
|
3381
|
+
groupAttr: "data-emph-key",
|
|
3382
|
+
legend: true
|
|
3383
|
+
},
|
|
3384
|
+
heatmap: {
|
|
3385
|
+
markSelector: ".dgmo-datum",
|
|
3386
|
+
strategy: "enumerated",
|
|
3387
|
+
groupAttr: "data-emph-key",
|
|
3388
|
+
legend: true
|
|
3389
|
+
},
|
|
3390
|
+
"polar-area": {
|
|
3391
|
+
markSelector: ".dgmo-datum",
|
|
3392
|
+
strategy: "enumerated",
|
|
3393
|
+
groupAttr: "data-emph-key",
|
|
3394
|
+
legend: true
|
|
3395
|
+
},
|
|
3396
|
+
// ── CROSS-FREE diagram charts — FIXED-attr keys, DOM-verified (Task 3) ──
|
|
3397
|
+
// Purely additive: the baked CSS lights up embeds; the app keeps its own live
|
|
3398
|
+
// JS emphasis (it renders via direct calls, never render()), so no double-up
|
|
3399
|
+
// and no renderer edits. Tag-group charts (treemap/event-line/block/map —
|
|
3400
|
+
// keyed on a per-diagram `data-tag-<slug>`) are deferred: they need
|
|
3401
|
+
// active-tag-slug discovery in the injector.
|
|
3402
|
+
gantt: {
|
|
3403
|
+
markSelector: ".gantt-task",
|
|
3404
|
+
strategy: "enumerated",
|
|
3405
|
+
groupAttr: "data-group"
|
|
3406
|
+
},
|
|
3407
|
+
timeline: {
|
|
3408
|
+
markSelector: ".tl-event",
|
|
3409
|
+
strategy: "enumerated",
|
|
3410
|
+
groupAttr: "data-group"
|
|
3411
|
+
},
|
|
3412
|
+
// raci cells AND column headers both carry `data-role-id`; keying on the bare
|
|
3413
|
+
// attr makes hovering either lift the whole role (cross-column highlight).
|
|
3414
|
+
raci: {
|
|
3415
|
+
markSelector: "[data-role-id]",
|
|
3416
|
+
strategy: "enumerated",
|
|
3417
|
+
groupAttr: "data-role-id"
|
|
3418
|
+
},
|
|
3419
|
+
// ── CROSS-FREE connection charts (node hover → dim non-incident edges) ──
|
|
3420
|
+
sequence: {
|
|
3421
|
+
markSelector: ".participant",
|
|
3422
|
+
strategy: "connection",
|
|
3423
|
+
hoverSelector: ".participant",
|
|
3424
|
+
hoverAttr: "data-participant-id",
|
|
3425
|
+
edgeSelector: ".message-arrow",
|
|
3426
|
+
fromAttr: "data-from",
|
|
3427
|
+
toAttr: "data-to"
|
|
3428
|
+
},
|
|
3429
|
+
arc: {
|
|
3430
|
+
markSelector: ".arc-node",
|
|
3431
|
+
strategy: "connection",
|
|
3432
|
+
hoverSelector: ".arc-node",
|
|
3433
|
+
hoverAttr: "data-node",
|
|
3434
|
+
edgeSelector: ".arc-link",
|
|
3435
|
+
fromAttr: "data-source",
|
|
3436
|
+
toAttr: "data-target"
|
|
3437
|
+
},
|
|
3438
|
+
pert: {
|
|
3439
|
+
markSelector: ".pert-node",
|
|
3440
|
+
strategy: "connection",
|
|
3441
|
+
hoverSelector: ".pert-node",
|
|
3442
|
+
hoverAttr: "data-activity-id",
|
|
3443
|
+
edgeSelector: ".pert-edge",
|
|
3444
|
+
fromAttr: "data-source",
|
|
3445
|
+
toAttr: "data-target"
|
|
3446
|
+
},
|
|
3447
|
+
// ── CROSS-FREE tag-group charts — active group discovered at scan time ──
|
|
3448
|
+
// The mark carries `data-tag-<slug>` for the ONE active tag group; the
|
|
3449
|
+
// injector reads the slug from `data-legend-active` (F9). `data-tag-*` values
|
|
3450
|
+
// and `data-legend-entry` are both lowercased, so legend pairing casing lines
|
|
3451
|
+
// up. (map is excluded — it carries two tag kinds on different marks.)
|
|
3452
|
+
treemap: {
|
|
3453
|
+
markSelector: ".dgmo-treemap-cell",
|
|
3454
|
+
strategy: "enumerated",
|
|
3455
|
+
groupAttrMode: "tag-active",
|
|
3456
|
+
legend: true
|
|
3457
|
+
},
|
|
3458
|
+
block: {
|
|
3459
|
+
markSelector: ".dgmo-block-cell",
|
|
3460
|
+
strategy: "enumerated",
|
|
3461
|
+
groupAttrMode: "tag-active",
|
|
3462
|
+
legend: true
|
|
3463
|
+
},
|
|
3464
|
+
"event-line": {
|
|
3465
|
+
markSelector: ".dgmo-event-dot",
|
|
3466
|
+
strategy: "enumerated",
|
|
3467
|
+
groupAttrMode: "tag-active",
|
|
3468
|
+
legend: true
|
|
3469
|
+
},
|
|
3470
|
+
// ── Connection graph-family — edge endpoint attrs baked in the renderers ──
|
|
3471
|
+
// Hover a node → dim edges not incident to it. Node id ↔ edge from/to.
|
|
3472
|
+
flowchart: {
|
|
3473
|
+
markSelector: ".fc-node",
|
|
3474
|
+
strategy: "connection",
|
|
3475
|
+
hoverSelector: ".fc-node",
|
|
3476
|
+
hoverAttr: "data-node-id",
|
|
3477
|
+
edgeSelector: ".fc-edge-group",
|
|
3478
|
+
fromAttr: "data-source",
|
|
3479
|
+
toAttr: "data-target"
|
|
3480
|
+
},
|
|
3481
|
+
state: {
|
|
3482
|
+
markSelector: ".st-node",
|
|
3483
|
+
strategy: "connection",
|
|
3484
|
+
hoverSelector: ".st-node",
|
|
3485
|
+
hoverAttr: "data-node-id",
|
|
3486
|
+
edgeSelector: ".st-edge-group",
|
|
3487
|
+
fromAttr: "data-source",
|
|
3488
|
+
toAttr: "data-target"
|
|
3489
|
+
},
|
|
3490
|
+
"boxes-and-lines": {
|
|
3491
|
+
markSelector: ".bl-node",
|
|
3492
|
+
strategy: "connection",
|
|
3493
|
+
hoverSelector: ".bl-node",
|
|
3494
|
+
hoverAttr: "data-node-id",
|
|
3495
|
+
edgeSelector: ".bl-edge-group",
|
|
3496
|
+
fromAttr: "data-from",
|
|
3497
|
+
toAttr: "data-to"
|
|
3498
|
+
},
|
|
3499
|
+
infra: {
|
|
3500
|
+
markSelector: ".infra-node",
|
|
3501
|
+
strategy: "connection",
|
|
3502
|
+
hoverSelector: ".infra-node",
|
|
3503
|
+
hoverAttr: "data-infra-node",
|
|
3504
|
+
edgeSelector: ".infra-edge",
|
|
3505
|
+
fromAttr: "data-from",
|
|
3506
|
+
toAttr: "data-to"
|
|
3507
|
+
},
|
|
3508
|
+
c4: {
|
|
3509
|
+
markSelector: ".c4-card",
|
|
3510
|
+
strategy: "connection",
|
|
3511
|
+
hoverSelector: ".c4-card",
|
|
3512
|
+
hoverAttr: "data-node-id",
|
|
3513
|
+
edgeSelector: ".c4-edge-group",
|
|
3514
|
+
fromAttr: "data-source",
|
|
3515
|
+
toAttr: "data-target"
|
|
3516
|
+
},
|
|
3517
|
+
class: {
|
|
3518
|
+
markSelector: ".cd-class",
|
|
3519
|
+
strategy: "connection",
|
|
3520
|
+
hoverSelector: ".cd-class",
|
|
3521
|
+
hoverAttr: "data-node-id",
|
|
3522
|
+
edgeSelector: ".cd-edge-group",
|
|
3523
|
+
fromAttr: "data-source",
|
|
3524
|
+
toAttr: "data-target"
|
|
3525
|
+
},
|
|
3526
|
+
er: {
|
|
3527
|
+
markSelector: ".er-table",
|
|
3528
|
+
strategy: "connection",
|
|
3529
|
+
hoverSelector: ".er-table",
|
|
3530
|
+
hoverAttr: "data-node-id",
|
|
3531
|
+
edgeSelector: ".er-edge-group",
|
|
3532
|
+
fromAttr: "data-source",
|
|
3533
|
+
toAttr: "data-target"
|
|
3534
|
+
},
|
|
3535
|
+
org: {
|
|
3536
|
+
markSelector: ".org-node",
|
|
3537
|
+
strategy: "connection",
|
|
3538
|
+
hoverSelector: ".org-node",
|
|
3539
|
+
hoverAttr: "data-node-id",
|
|
3540
|
+
edgeSelector: ".org-edge",
|
|
3541
|
+
fromAttr: "data-from",
|
|
3542
|
+
toAttr: "data-to"
|
|
3543
|
+
},
|
|
3544
|
+
sitemap: {
|
|
3545
|
+
markSelector: ".sitemap-node",
|
|
3546
|
+
strategy: "connection",
|
|
3547
|
+
hoverSelector: ".sitemap-node",
|
|
3548
|
+
hoverAttr: "data-node-id",
|
|
3549
|
+
edgeSelector: ".sitemap-edge-group",
|
|
3550
|
+
fromAttr: "data-from",
|
|
3551
|
+
toAttr: "data-to"
|
|
3552
|
+
},
|
|
3553
|
+
cycle: {
|
|
3554
|
+
markSelector: ".cycle-node",
|
|
3555
|
+
strategy: "connection",
|
|
3556
|
+
hoverSelector: ".cycle-node",
|
|
3557
|
+
hoverAttr: "data-node-index",
|
|
3558
|
+
edgeSelector: ".cycle-edge",
|
|
3559
|
+
fromAttr: "data-from",
|
|
3560
|
+
toAttr: "data-to"
|
|
3561
|
+
},
|
|
3562
|
+
// ── CROSS-ADD — one attr baked in the renderer, then keyed here ──
|
|
3563
|
+
scatter: {
|
|
3564
|
+
markSelector: ".dgmo-datum",
|
|
3565
|
+
strategy: "enumerated",
|
|
3566
|
+
groupAttr: "data-category",
|
|
3567
|
+
legend: true
|
|
3568
|
+
},
|
|
3569
|
+
quadrant: {
|
|
3570
|
+
markSelector: ".point-group",
|
|
3571
|
+
strategy: "enumerated",
|
|
3572
|
+
groupAttr: "data-quadrant"
|
|
3573
|
+
},
|
|
3574
|
+
"version-control": {
|
|
3575
|
+
markSelector: "[data-branch]",
|
|
3576
|
+
strategy: "enumerated",
|
|
3577
|
+
groupAttr: "data-branch"
|
|
3578
|
+
},
|
|
3579
|
+
// kanban: cards nest under a `.kanban-column` wrapper, so a structural rule
|
|
3580
|
+
// gives column-focus with NO renderer edit — hovering any card puts its
|
|
3581
|
+
// column into `:hover` and the others dim. `dim` (not lift) reads as
|
|
3582
|
+
// "focus this column"; the marks' opacity is a presentation attr, which a
|
|
3583
|
+
// `<style>` rule outranks (no RED-2 conflict).
|
|
3584
|
+
kanban: {
|
|
3585
|
+
markSelector: ".kanban-column",
|
|
3586
|
+
strategy: "structural",
|
|
3587
|
+
groupSelector: ".kanban-column",
|
|
3588
|
+
emphasis: "dim",
|
|
3589
|
+
selfEmphasis: false
|
|
3590
|
+
},
|
|
3591
|
+
// line: each series is a `g.dgmo-series` wrapping its line + data dots. The
|
|
3592
|
+
// dots are hittable, so a structural rule dims other series when a point is
|
|
3593
|
+
// hovered — no fat hit-path needed. (The plot-area hit overlay sits BEHIND
|
|
3594
|
+
// the series, so dot hover still lands; verified in the browser e2e.)
|
|
3595
|
+
line: {
|
|
3596
|
+
markSelector: "g.dgmo-series",
|
|
3597
|
+
strategy: "structural",
|
|
3598
|
+
groupSelector: "g.dgmo-series",
|
|
3599
|
+
emphasis: "dim",
|
|
3600
|
+
selfEmphasis: false
|
|
3601
|
+
},
|
|
3602
|
+
swimlane: {
|
|
3603
|
+
markSelector: "[data-lane]",
|
|
3604
|
+
strategy: "enumerated",
|
|
3605
|
+
groupAttr: "data-lane"
|
|
3606
|
+
},
|
|
3607
|
+
// tech-radar: blips are `<g>` carrying data-quadrant/data-ring/data-trend;
|
|
3608
|
+
// only blips have data-trend (the sector wedges don't), so `[data-trend]`
|
|
3609
|
+
// isolates them. Hover a blip → lift same-quadrant blips.
|
|
3610
|
+
"tech-radar": {
|
|
3611
|
+
markSelector: "[data-trend]",
|
|
3612
|
+
strategy: "enumerated",
|
|
3613
|
+
groupAttr: "data-quadrant"
|
|
3614
|
+
},
|
|
3615
|
+
// journey-map: faces carry data-score (the emotion). Hover a face → lift all
|
|
3616
|
+
// same-score touchpoints across the journey.
|
|
3617
|
+
"journey-map": {
|
|
3618
|
+
markSelector: ".journey-face",
|
|
3619
|
+
strategy: "enumerated",
|
|
3620
|
+
groupAttr: "data-score"
|
|
3621
|
+
},
|
|
3622
|
+
// mindmap: nodes carry a single data-tag-<group> when tagged (no
|
|
3623
|
+
// legend-active marker → the single-tag fallback in deriveFromSvg resolves
|
|
3624
|
+
// it). Untagged → self-emphasis only.
|
|
3625
|
+
mindmap: {
|
|
3626
|
+
markSelector: ".mindmap-node",
|
|
3627
|
+
strategy: "enumerated",
|
|
3628
|
+
groupAttrMode: "tag-active"
|
|
3629
|
+
},
|
|
3630
|
+
// ── SELF-emphasis only (single-series / non-relational solid marks) ──
|
|
3631
|
+
pyramid: { markSelector: ".pyramid-layer", strategy: "self" },
|
|
3632
|
+
ring: { markSelector: ".ring-layer", strategy: "self" },
|
|
3633
|
+
slope: { markSelector: ".slope-series", strategy: "self" },
|
|
3634
|
+
// function: each curve is a `.dgmo-datum` path — self-emphasis floor only
|
|
3635
|
+
// (thin line, no group).
|
|
3636
|
+
function: { markSelector: ".dgmo-datum", strategy: "self" },
|
|
3637
|
+
// map: POIs carry data-tag-<group> for the active legend group (resolved from
|
|
3638
|
+
// data-legend-active). Hover a POI → lift same-group POIs. `lift` (never dim)
|
|
3639
|
+
// sidesteps the decorative-layer / inactive-pill inline-opacity trap (F6).
|
|
3640
|
+
// Region-tag (choropleth) maps have no POIs → self-emphasis only.
|
|
3641
|
+
map: {
|
|
3642
|
+
markSelector: "[data-poi]",
|
|
3643
|
+
strategy: "enumerated",
|
|
3644
|
+
groupAttrMode: "tag-active"
|
|
3645
|
+
}
|
|
3646
|
+
};
|
|
3647
|
+
function deriveFromSvg(svg, spec) {
|
|
3648
|
+
if (typeof document === "undefined") return {};
|
|
3649
|
+
const holder = document.createElement("div");
|
|
3650
|
+
holder.innerHTML = svg;
|
|
3651
|
+
const root = holder.querySelector("svg");
|
|
3652
|
+
if (!root) return {};
|
|
3653
|
+
if (spec.strategy === "enumerated") {
|
|
3654
|
+
let groupAttr = spec.groupAttr;
|
|
3655
|
+
if (spec.groupAttrMode === "tag-active") {
|
|
3656
|
+
const slug = root.querySelector("[data-legend-active]")?.getAttribute("data-legend-active");
|
|
3657
|
+
if (slug) {
|
|
3658
|
+
groupAttr = `data-tag-${tagAttrKey(slug)}`;
|
|
3659
|
+
} else {
|
|
3660
|
+
const names = /* @__PURE__ */ new Set();
|
|
3661
|
+
root.querySelectorAll(spec.markSelector).forEach((el) => {
|
|
3662
|
+
for (const a of Array.from(el.attributes)) {
|
|
3663
|
+
if (a.name.startsWith("data-tag-")) names.add(a.name);
|
|
3664
|
+
}
|
|
3665
|
+
});
|
|
3666
|
+
if (names.size !== 1) return {};
|
|
3667
|
+
groupAttr = [...names][0];
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
if (!groupAttr) return {};
|
|
3671
|
+
const attr = groupAttr;
|
|
3672
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3673
|
+
root.querySelectorAll(`${spec.markSelector}[${attr}]`).forEach((el) => {
|
|
3674
|
+
const v = el.getAttribute(attr);
|
|
3675
|
+
if (v != null) seen.add(v);
|
|
3676
|
+
});
|
|
3677
|
+
return { values: [...seen], groupAttr: attr };
|
|
3678
|
+
}
|
|
3679
|
+
if (spec.strategy === "connection" && spec.hoverSelector && spec.hoverAttr) {
|
|
3680
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3681
|
+
root.querySelectorAll(`${spec.hoverSelector}[${spec.hoverAttr}]`).forEach((el) => {
|
|
3682
|
+
const v = el.getAttribute(spec.hoverAttr);
|
|
3683
|
+
if (v != null) seen.add(v);
|
|
3684
|
+
});
|
|
3685
|
+
return { ids: [...seen] };
|
|
3686
|
+
}
|
|
3687
|
+
return {};
|
|
3688
|
+
}
|
|
3689
|
+
function injectHoverStyles(svg, chartType, opts) {
|
|
3690
|
+
if (!opts?.bakeHover || !svg || !chartType) return svg;
|
|
3691
|
+
const spec = HOVER_SPECS[chartType];
|
|
3692
|
+
if (!spec) return svg;
|
|
3693
|
+
const derived = deriveFromSvg(svg, spec);
|
|
3694
|
+
const eff = derived.groupAttr ? { ...spec, groupAttr: derived.groupAttr } : spec;
|
|
3695
|
+
const css = buildHoverCss(eff, derived, opts.emphasis);
|
|
3696
|
+
if (!css) return svg;
|
|
3697
|
+
const open = svg.match(/<svg\b[^>]*>/);
|
|
3698
|
+
if (open?.index == null) return svg;
|
|
3699
|
+
const at = open.index + open[0].length;
|
|
3700
|
+
return `${svg.slice(0, at)}<style>${css}</style>${svg.slice(at)}`;
|
|
3701
|
+
}
|
|
3702
|
+
|
|
3703
|
+
// src/render.ts
|
|
3704
|
+
var DOM_GLOBALS = [
|
|
3705
|
+
"document",
|
|
3706
|
+
"window",
|
|
3707
|
+
"navigator",
|
|
3708
|
+
"HTMLElement",
|
|
3709
|
+
"SVGElement"
|
|
3710
|
+
];
|
|
3711
|
+
var DOM_STATE_KEY = /* @__PURE__ */ Symbol.for("diagrammo.dgmo.dom-globals");
|
|
3712
|
+
function domState() {
|
|
3713
|
+
const g = globalThis;
|
|
3714
|
+
return g[DOM_STATE_KEY] ??= {
|
|
3715
|
+
refCount: 0,
|
|
3716
|
+
installPromise: null,
|
|
3717
|
+
installedByUs: false
|
|
3718
|
+
};
|
|
3719
|
+
}
|
|
3720
|
+
async function installDom(state) {
|
|
3721
|
+
const { JSDOM } = await loadJsdom();
|
|
3722
|
+
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>", {
|
|
3723
|
+
url: "http://localhost/"
|
|
3724
|
+
});
|
|
3725
|
+
const win = dom.window;
|
|
3726
|
+
const values = {
|
|
3727
|
+
document: win.document,
|
|
3728
|
+
window: win,
|
|
3729
|
+
navigator: win.navigator,
|
|
3730
|
+
HTMLElement: win.HTMLElement,
|
|
3731
|
+
SVGElement: win.SVGElement
|
|
3732
|
+
};
|
|
3733
|
+
for (const key of DOM_GLOBALS) {
|
|
3734
|
+
Object.defineProperty(globalThis, key, {
|
|
3735
|
+
value: values[key],
|
|
3736
|
+
configurable: true
|
|
3737
|
+
});
|
|
3738
|
+
}
|
|
3739
|
+
state.installedByUs = true;
|
|
3740
|
+
}
|
|
3741
|
+
async function acquireDom() {
|
|
3742
|
+
const state = domState();
|
|
3743
|
+
if (typeof document !== "undefined" && !state.installedByUs) return;
|
|
3744
|
+
state.refCount++;
|
|
3745
|
+
if (!state.installPromise) state.installPromise = installDom(state);
|
|
3746
|
+
await state.installPromise;
|
|
3747
|
+
}
|
|
3748
|
+
function releaseDom() {
|
|
3749
|
+
const state = domState();
|
|
3750
|
+
if (!state.installedByUs) return;
|
|
3751
|
+
if (--state.refCount > 0) return;
|
|
3752
|
+
for (const key of DOM_GLOBALS) {
|
|
3753
|
+
delete globalThis[key];
|
|
3754
|
+
}
|
|
3755
|
+
state.installedByUs = false;
|
|
3756
|
+
state.installPromise = null;
|
|
3757
|
+
state.refCount = 0;
|
|
3758
|
+
}
|
|
3759
|
+
async function loadJsdom() {
|
|
3760
|
+
const spec = ["js", "dom"].join("");
|
|
3761
|
+
return import(
|
|
3762
|
+
/* @vite-ignore */
|
|
3763
|
+
/* webpackIgnore: true */
|
|
3764
|
+
spec
|
|
3765
|
+
);
|
|
3766
|
+
}
|
|
3767
|
+
async function render(content, options) {
|
|
3768
|
+
const theme = options?.theme ?? "light";
|
|
3769
|
+
const paletteName = options?.palette ?? "slate";
|
|
3770
|
+
const bakeHover = options?.bakeHover ?? true;
|
|
3771
|
+
const paletteColors = getPalette(paletteName)[theme === "dark" ? "dark" : "light"];
|
|
3772
|
+
const parsed = parseDgmo(content);
|
|
3773
|
+
let diagnostics = parsed.diagnostics;
|
|
3774
|
+
const acOverride = resolveArcChordOverride(
|
|
3775
|
+
content,
|
|
3776
|
+
parsed.chartType,
|
|
3777
|
+
paletteColors
|
|
3778
|
+
);
|
|
3779
|
+
const renderContent = acOverride?.content ?? content;
|
|
3780
|
+
const chartType = acOverride?.type ?? parsed.chartType;
|
|
3781
|
+
const category = acOverride ? "data-chart" : chartType ? getRenderCategory(chartType) : null;
|
|
3782
|
+
const viewState = options?.viewState ?? (options?.legendState ? {
|
|
3783
|
+
...options.legendState.activeGroup !== void 0 && {
|
|
3784
|
+
tag: options.legendState.activeGroup
|
|
3785
|
+
},
|
|
3786
|
+
...options.legendState.hiddenAttributes !== void 0 && {
|
|
3787
|
+
ha: options.legendState.hiddenAttributes
|
|
3788
|
+
}
|
|
3789
|
+
} : void 0);
|
|
3790
|
+
if (category === "data-chart") {
|
|
3791
|
+
await acquireDom();
|
|
3792
|
+
try {
|
|
3793
|
+
const raw = await renderDataChartD3(renderContent, theme, paletteColors);
|
|
3794
|
+
const svg2 = injectHoverStyles(raw, chartType, { bakeHover });
|
|
3795
|
+
return { svg: svg2, diagnostics };
|
|
3796
|
+
} finally {
|
|
3797
|
+
releaseDom();
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
await acquireDom();
|
|
3801
|
+
let svg;
|
|
3802
|
+
try {
|
|
3803
|
+
svg = await renderForExport(
|
|
3804
|
+
renderContent,
|
|
3805
|
+
theme,
|
|
3806
|
+
paletteColors,
|
|
3807
|
+
viewState,
|
|
3808
|
+
{
|
|
3809
|
+
...options?.c4Level !== void 0 && { c4Level: options.c4Level },
|
|
3810
|
+
...options?.c4System !== void 0 && { c4System: options.c4System },
|
|
3811
|
+
...options?.c4Container !== void 0 && {
|
|
3812
|
+
c4Container: options.c4Container
|
|
3813
|
+
},
|
|
3814
|
+
...options?.tagGroup !== void 0 && { tagGroup: options.tagGroup },
|
|
3815
|
+
...options?.mapData !== void 0 && { mapData: options.mapData }
|
|
3816
|
+
}
|
|
3817
|
+
);
|
|
3818
|
+
svg = injectHoverStyles(svg, chartType, { bakeHover });
|
|
3819
|
+
} finally {
|
|
3820
|
+
releaseDom();
|
|
3821
|
+
}
|
|
3822
|
+
if (chartType === "map") {
|
|
3823
|
+
try {
|
|
3824
|
+
const [{ parseMap }, { resolveMap }, { loadMapData }] = await Promise.all(
|
|
3825
|
+
[
|
|
3826
|
+
import("./parser-4FL2CMDG.js"),
|
|
3827
|
+
import("./resolver-NOAA7ID7.js"),
|
|
3828
|
+
import("./load-data-EHDDZ5ZS.js")
|
|
3829
|
+
]
|
|
3830
|
+
);
|
|
3831
|
+
const data = options?.mapData ?? await loadMapData();
|
|
3832
|
+
diagnostics = [...resolveMap(parseMap(content), data).diagnostics];
|
|
3833
|
+
} catch {
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
return { svg, diagnostics };
|
|
3837
|
+
}
|
|
3838
|
+
|
|
3839
|
+
// src/sharing.ts
|
|
3840
|
+
var import_lz_string = __toESM(require_lz_string(), 1);
|
|
3841
|
+
var DEFAULT_BASE_URL = "https://online.diagrammo.app";
|
|
3842
|
+
var COMPRESSED_SIZE_LIMIT = 8192;
|
|
3843
|
+
function encodeViewState(state) {
|
|
3844
|
+
const keys = Object.keys(state);
|
|
3845
|
+
if (keys.length === 0) return "";
|
|
3846
|
+
return (0, import_lz_string.compressToEncodedURIComponent)(JSON.stringify(state));
|
|
3847
|
+
}
|
|
3848
|
+
function decodeViewState(encoded) {
|
|
3849
|
+
if (!encoded) return {};
|
|
3850
|
+
try {
|
|
3851
|
+
const json = (0, import_lz_string.decompressFromEncodedURIComponent)(encoded);
|
|
3852
|
+
if (!json) return {};
|
|
3853
|
+
const parsed = JSON.parse(json);
|
|
3854
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
3855
|
+
return {};
|
|
3856
|
+
return parsed;
|
|
3857
|
+
} catch {
|
|
3858
|
+
return {};
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
function encodeDiagramUrl(dsl, options) {
|
|
3862
|
+
const baseUrl = options?.baseUrl ?? DEFAULT_BASE_URL;
|
|
3863
|
+
const compressed = (0, import_lz_string.compressToEncodedURIComponent)(dsl);
|
|
3864
|
+
const byteSize = new TextEncoder().encode(compressed).byteLength;
|
|
3865
|
+
if (byteSize > COMPRESSED_SIZE_LIMIT) {
|
|
3866
|
+
return {
|
|
3867
|
+
error: "too-large",
|
|
3868
|
+
compressedSize: byteSize,
|
|
3869
|
+
limit: COMPRESSED_SIZE_LIMIT
|
|
3870
|
+
};
|
|
3871
|
+
}
|
|
3872
|
+
let hash = `dgmo=${compressed}`;
|
|
3873
|
+
if (options?.viewState) {
|
|
3874
|
+
const vsEncoded = encodeViewState(options.viewState);
|
|
3875
|
+
if (vsEncoded) {
|
|
3876
|
+
hash += `&vs=${vsEncoded}`;
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
if (options?.palette && options.palette !== "nord") {
|
|
3880
|
+
hash += `&pal=${encodeURIComponent(options.palette)}`;
|
|
3881
|
+
}
|
|
3882
|
+
if (options?.theme && options.theme !== "dark") {
|
|
3883
|
+
hash += `&th=${encodeURIComponent(options.theme)}`;
|
|
3884
|
+
}
|
|
3885
|
+
if (options?.filename) {
|
|
3886
|
+
hash += `&fn=${encodeURIComponent(options.filename)}`;
|
|
3887
|
+
}
|
|
3888
|
+
return { url: `${baseUrl}?${hash}#${hash}` };
|
|
3889
|
+
}
|
|
3890
|
+
function decodeDiagramUrl(hash) {
|
|
3891
|
+
const empty = { dsl: "", viewState: {} };
|
|
3892
|
+
let filename;
|
|
3893
|
+
let palette;
|
|
3894
|
+
let theme;
|
|
3895
|
+
if (!hash) return empty;
|
|
3896
|
+
let raw = hash;
|
|
3897
|
+
if (raw.startsWith("#") || raw.startsWith("?")) {
|
|
3898
|
+
raw = raw.slice(1);
|
|
3899
|
+
}
|
|
3900
|
+
const parts = raw.split("&");
|
|
3901
|
+
let payload = parts[0];
|
|
3902
|
+
let viewState = {};
|
|
3903
|
+
for (let i = 1; i < parts.length; i++) {
|
|
3904
|
+
const part = parts[i];
|
|
3905
|
+
const eq = part.indexOf("=");
|
|
3906
|
+
if (eq === -1) continue;
|
|
3907
|
+
const key = part.slice(0, eq);
|
|
3908
|
+
const val = part.slice(eq + 1);
|
|
3909
|
+
if (key === "vs" && val) {
|
|
3910
|
+
viewState = decodeViewState(val);
|
|
3911
|
+
}
|
|
3912
|
+
if (key === "pal" && val) palette = decodeURIComponent(val);
|
|
3913
|
+
if (key === "th") {
|
|
3914
|
+
const decoded = decodeURIComponent(val);
|
|
3915
|
+
if (decoded === "light" || decoded === "dark") theme = decoded;
|
|
3916
|
+
}
|
|
3917
|
+
if (key === "fn" && val) filename = decodeURIComponent(val);
|
|
3918
|
+
}
|
|
3919
|
+
if (payload.startsWith("dgmo=")) {
|
|
3920
|
+
payload = payload.slice(5);
|
|
3921
|
+
}
|
|
3922
|
+
const extras = {
|
|
3923
|
+
...palette !== void 0 && { palette },
|
|
3924
|
+
...theme !== void 0 && { theme },
|
|
3925
|
+
...filename !== void 0 && { filename }
|
|
3926
|
+
};
|
|
3927
|
+
if (!payload) return { dsl: "", viewState, ...extras };
|
|
3928
|
+
try {
|
|
3929
|
+
const result = (0, import_lz_string.decompressFromEncodedURIComponent)(payload);
|
|
3930
|
+
return { dsl: result ?? "", viewState, ...extras };
|
|
3931
|
+
} catch {
|
|
3932
|
+
return { dsl: "", viewState, ...extras };
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3935
|
+
|
|
3936
|
+
export {
|
|
3937
|
+
renderSlopeChart,
|
|
3938
|
+
orderArcNodes,
|
|
3939
|
+
renderArcDiagram,
|
|
3940
|
+
renderWordCloud,
|
|
3941
|
+
renderVenn,
|
|
3942
|
+
renderQuadrant,
|
|
3943
|
+
renderForExport,
|
|
3944
|
+
render,
|
|
3945
|
+
encodeViewState,
|
|
3946
|
+
decodeViewState,
|
|
3947
|
+
encodeDiagramUrl,
|
|
3948
|
+
decodeDiagramUrl
|
|
3949
|
+
};
|
|
3950
|
+
//# sourceMappingURL=chunk-IH6ZMVQ5.js.map
|