@fusedashlabs/widgets 0.3.1 → 0.4.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/CHANGELOG.md +22 -0
- package/README.md +1 -1
- package/dist/components/chart-renderer/element/ui9000-chart-renderer.d.ts +1 -0
- package/dist/components/chart-renderer/element/ui9000-chart-renderer.d.ts.map +1 -1
- package/dist/components/chart-renderer/element/ui9000-chart-renderer.js +12 -1
- package/dist/components/chart-renderer/element/ui9000-chart-renderer.js.map +1 -1
- package/dist/components/chart-renderer/lib/registry.d.ts.map +1 -1
- package/dist/components/chart-renderer/lib/registry.js +17 -4
- package/dist/components/chart-renderer/lib/registry.js.map +1 -1
- package/dist/components/chart-renderer/lib/types.d.ts +5 -0
- package/dist/components/chart-renderer/lib/types.d.ts.map +1 -1
- package/dist/components/donut-chart/element/ui9000-donut-chart.js +1 -1
- package/dist/components/donut-chart/element/ui9000-donut-chart.js.map +1 -1
- package/dist/components/lollipop/element/styles.d.ts.map +1 -1
- package/dist/components/lollipop/element/styles.js +19 -1
- package/dist/components/lollipop/element/styles.js.map +1 -1
- package/dist/components/lollipop/element/ui9000-lollipop.d.ts +6 -1
- package/dist/components/lollipop/element/ui9000-lollipop.d.ts.map +1 -1
- package/dist/components/lollipop/element/ui9000-lollipop.js +68 -7
- package/dist/components/lollipop/element/ui9000-lollipop.js.map +1 -1
- package/dist/components/lollipop/index.d.ts +1 -1
- package/dist/components/lollipop/index.d.ts.map +1 -1
- package/dist/components/lollipop/index.js +2 -0
- package/dist/components/lollipop/index.js.map +1 -1
- package/dist/components/lollipop/lib/domain.d.ts +16 -1
- package/dist/components/lollipop/lib/domain.d.ts.map +1 -1
- package/dist/components/lollipop/lib/domain.js +56 -1
- package/dist/components/lollipop/lib/domain.js.map +1 -1
- package/dist/components/lollipop/lib/index.d.ts +3 -2
- package/dist/components/lollipop/lib/index.d.ts.map +1 -1
- package/dist/components/lollipop/lib/layout.d.ts +16 -0
- package/dist/components/lollipop/lib/layout.d.ts.map +1 -0
- package/dist/components/lollipop/lib/layout.js +16 -0
- package/dist/components/lollipop/lib/layout.js.map +1 -0
- package/dist/components/lollipop/lib/types.d.ts +5 -0
- package/dist/components/lollipop/lib/types.d.ts.map +1 -1
- package/dist/components/lollipop/metadata.json.d.ts +10 -0
- package/dist/components/lollipop/metadata.json.js +4 -1
- package/dist/components/lollipop/metadata.json.js.map +1 -1
- package/dist/components/lollipop/render/draw.d.ts +8 -1
- package/dist/components/lollipop/render/draw.d.ts.map +1 -1
- package/dist/components/lollipop/render/draw.js +155 -84
- package/dist/components/lollipop/render/draw.js.map +1 -1
- package/dist/components/map-chart/element/styles.d.ts +2 -0
- package/dist/components/map-chart/element/styles.d.ts.map +1 -0
- package/dist/components/map-chart/element/styles.js +372 -0
- package/dist/components/map-chart/element/styles.js.map +1 -0
- package/dist/components/map-chart/element/ui9000-map-chart.d.ts +54 -0
- package/dist/components/map-chart/element/ui9000-map-chart.d.ts.map +1 -0
- package/dist/components/map-chart/element/ui9000-map-chart.js +539 -0
- package/dist/components/map-chart/element/ui9000-map-chart.js.map +1 -0
- package/dist/components/map-chart/index.d.ts +5 -0
- package/dist/components/map-chart/index.d.ts.map +1 -0
- package/dist/components/map-chart/index.js +29 -0
- package/dist/components/map-chart/index.js.map +1 -0
- package/dist/components/map-chart/lib/constants.d.ts +34 -0
- package/dist/components/map-chart/lib/constants.d.ts.map +1 -0
- package/dist/components/map-chart/lib/constants.js +122 -0
- package/dist/components/map-chart/lib/constants.js.map +1 -0
- package/dist/components/map-chart/lib/domain.d.ts +12 -0
- package/dist/components/map-chart/lib/domain.d.ts.map +1 -0
- package/dist/components/map-chart/lib/domain.js +105 -0
- package/dist/components/map-chart/lib/domain.js.map +1 -0
- package/dist/components/map-chart/lib/format.d.ts +8 -0
- package/dist/components/map-chart/lib/format.d.ts.map +1 -0
- package/dist/components/map-chart/lib/format.js +45 -0
- package/dist/components/map-chart/lib/format.js.map +1 -0
- package/dist/components/map-chart/lib/geo-index.d.ts +7 -0
- package/dist/components/map-chart/lib/geo-index.d.ts.map +1 -0
- package/dist/components/map-chart/lib/geo-index.js +200 -0
- package/dist/components/map-chart/lib/geo-index.js.map +1 -0
- package/dist/components/map-chart/lib/geojson.d.ts +5 -0
- package/dist/components/map-chart/lib/geojson.d.ts.map +1 -0
- package/dist/components/map-chart/lib/geojson.js +47 -0
- package/dist/components/map-chart/lib/geojson.js.map +1 -0
- package/dist/components/map-chart/lib/index.d.ts +13 -0
- package/dist/components/map-chart/lib/index.d.ts.map +1 -0
- package/dist/components/map-chart/lib/join.d.ts +7 -0
- package/dist/components/map-chart/lib/join.d.ts.map +1 -0
- package/dist/components/map-chart/lib/join.js +156 -0
- package/dist/components/map-chart/lib/join.js.map +1 -0
- package/dist/components/map-chart/lib/legend.d.ts +45 -0
- package/dist/components/map-chart/lib/legend.d.ts.map +1 -0
- package/dist/components/map-chart/lib/legend.js +108 -0
- package/dist/components/map-chart/lib/legend.js.map +1 -0
- package/dist/components/map-chart/lib/normalize.d.ts +3 -0
- package/dist/components/map-chart/lib/normalize.d.ts.map +1 -0
- package/dist/components/map-chart/lib/normalize.js +177 -0
- package/dist/components/map-chart/lib/normalize.js.map +1 -0
- package/dist/components/map-chart/lib/pmtiles.d.ts +41 -0
- package/dist/components/map-chart/lib/pmtiles.d.ts.map +1 -0
- package/dist/components/map-chart/lib/pmtiles.js +112 -0
- package/dist/components/map-chart/lib/pmtiles.js.map +1 -0
- package/dist/components/map-chart/lib/types.d.ts +128 -0
- package/dist/components/map-chart/lib/types.d.ts.map +1 -0
- package/dist/components/map-chart/metadata.json.d.ts +83 -0
- package/dist/components/map-chart/metadata.json.js +42 -0
- package/dist/components/map-chart/metadata.json.js.map +1 -0
- package/dist/components/map-chart/render/draw.d.ts +24 -0
- package/dist/components/map-chart/render/draw.d.ts.map +1 -0
- package/dist/components/map-chart/render/draw.js +455 -0
- package/dist/components/map-chart/render/draw.js.map +1 -0
- package/dist/components/map-chart/render/index.d.ts +2 -0
- package/dist/components/map-chart/render/index.d.ts.map +1 -0
- package/dist/components/matrix-chart/element/styles.d.ts +2 -0
- package/dist/components/matrix-chart/element/styles.d.ts.map +1 -0
- package/dist/components/matrix-chart/element/styles.js +97 -0
- package/dist/components/matrix-chart/element/styles.js.map +1 -0
- package/dist/components/matrix-chart/element/ui9000-matrix-chart.d.ts +39 -0
- package/dist/components/matrix-chart/element/ui9000-matrix-chart.d.ts.map +1 -0
- package/dist/components/matrix-chart/element/ui9000-matrix-chart.js +238 -0
- package/dist/components/matrix-chart/element/ui9000-matrix-chart.js.map +1 -0
- package/dist/components/matrix-chart/index.d.ts +5 -0
- package/dist/components/matrix-chart/index.d.ts.map +1 -0
- package/dist/components/matrix-chart/index.js +20 -0
- package/dist/components/matrix-chart/lib/domain.d.ts +7 -0
- package/dist/components/matrix-chart/lib/domain.d.ts.map +1 -0
- package/dist/components/matrix-chart/lib/domain.js +58 -0
- package/dist/components/matrix-chart/lib/domain.js.map +1 -0
- package/dist/components/matrix-chart/lib/format.d.ts +5 -0
- package/dist/components/matrix-chart/lib/format.d.ts.map +1 -0
- package/dist/components/matrix-chart/lib/format.js +16 -0
- package/dist/components/matrix-chart/lib/format.js.map +1 -0
- package/dist/components/matrix-chart/lib/index.d.ts +5 -0
- package/dist/components/matrix-chart/lib/index.d.ts.map +1 -0
- package/dist/components/matrix-chart/lib/normalize.d.ts +5 -0
- package/dist/components/matrix-chart/lib/normalize.d.ts.map +1 -0
- package/dist/components/matrix-chart/lib/normalize.js +165 -0
- package/dist/components/matrix-chart/lib/normalize.js.map +1 -0
- package/dist/components/matrix-chart/lib/types.d.ts +33 -0
- package/dist/components/matrix-chart/lib/types.d.ts.map +1 -0
- package/dist/components/matrix-chart/metadata.json.d.ts +110 -0
- package/dist/components/matrix-chart/metadata.json.js +42 -0
- package/dist/components/matrix-chart/metadata.json.js.map +1 -0
- package/dist/components/matrix-chart/render/draw.d.ts +63 -0
- package/dist/components/matrix-chart/render/draw.d.ts.map +1 -0
- package/dist/components/matrix-chart/render/draw.js +225 -0
- package/dist/components/matrix-chart/render/draw.js.map +1 -0
- package/dist/components/matrix-chart/render/index.d.ts +2 -0
- package/dist/components/matrix-chart/render/index.d.ts.map +1 -0
- package/dist/components/network-graph/element/styles.d.ts.map +1 -1
- package/dist/components/network-graph/element/styles.js +177 -57
- package/dist/components/network-graph/element/styles.js.map +1 -1
- package/dist/components/network-graph/element/ui9000-network-graph.d.ts +4 -0
- package/dist/components/network-graph/element/ui9000-network-graph.d.ts.map +1 -1
- package/dist/components/network-graph/element/ui9000-network-graph.js +116 -39
- package/dist/components/network-graph/element/ui9000-network-graph.js.map +1 -1
- package/dist/components/network-graph/lib/domain.d.ts +2 -2
- package/dist/components/network-graph/lib/domain.js.map +1 -1
- package/dist/components/network-graph/lib/index.d.ts +1 -0
- package/dist/components/network-graph/lib/index.d.ts.map +1 -1
- package/dist/components/network-graph/lib/legend.d.ts +15 -0
- package/dist/components/network-graph/lib/legend.d.ts.map +1 -0
- package/dist/components/network-graph/lib/legend.js +35 -0
- package/dist/components/network-graph/lib/legend.js.map +1 -0
- package/dist/components/network-graph/render/draw.d.ts +4 -1
- package/dist/components/network-graph/render/draw.d.ts.map +1 -1
- package/dist/components/network-graph/render/draw.js +34 -9
- package/dist/components/network-graph/render/draw.js.map +1 -1
- package/dist/components/pie-chart/element/styles.d.ts.map +1 -1
- package/dist/components/pie-chart/element/styles.js +34 -1
- package/dist/components/pie-chart/element/styles.js.map +1 -1
- package/dist/components/pie-chart/element/ui9000-pie-chart.d.ts +0 -2
- package/dist/components/pie-chart/element/ui9000-pie-chart.d.ts.map +1 -1
- package/dist/components/pie-chart/element/ui9000-pie-chart.js +2 -8
- package/dist/components/pie-chart/element/ui9000-pie-chart.js.map +1 -1
- package/dist/components/pie-chart/metadata.json.d.ts +1 -7
- package/dist/components/pie-chart/metadata.json.js +2 -2
- package/dist/components/polar-area-chart/element/styles.d.ts +1 -1
- package/dist/components/polar-area-chart/element/styles.d.ts.map +1 -1
- package/dist/components/{donut-group-chart → polar-area-chart}/element/styles.js +9 -27
- package/dist/components/polar-area-chart/element/styles.js.map +1 -0
- package/dist/components/polar-area-chart/element/ui9000-polar-area-chart.d.ts.map +1 -1
- package/dist/components/polar-area-chart/element/ui9000-polar-area-chart.js +4 -3
- package/dist/components/polar-area-chart/element/ui9000-polar-area-chart.js.map +1 -1
- package/dist/components/polar-area-chart/index.d.ts +1 -1
- package/dist/components/polar-area-chart/index.d.ts.map +1 -1
- package/dist/components/polar-area-chart/index.js +2 -1
- package/dist/components/polar-area-chart/lib/domain.d.ts +8 -0
- package/dist/components/polar-area-chart/lib/domain.d.ts.map +1 -1
- package/dist/components/polar-area-chart/lib/domain.js +20 -0
- package/dist/components/polar-area-chart/lib/domain.js.map +1 -1
- package/dist/components/polar-area-chart/lib/index.d.ts +1 -1
- package/dist/components/polar-area-chart/lib/index.d.ts.map +1 -1
- package/dist/components/polar-area-chart/lib/normalize.d.ts.map +1 -1
- package/dist/components/polar-area-chart/lib/normalize.js +72 -6
- package/dist/components/polar-area-chart/lib/normalize.js.map +1 -1
- package/dist/components/polar-area-chart/lib/types.d.ts +6 -1
- package/dist/components/polar-area-chart/lib/types.d.ts.map +1 -1
- package/dist/components/polar-area-chart/metadata.json.d.ts +1 -1
- package/dist/components/polar-area-chart/metadata.json.js +1 -1
- package/dist/components/polar-area-chart/render/draw.d.ts.map +1 -1
- package/dist/components/polar-area-chart/render/draw.js +2 -2
- package/dist/components/polar-area-chart/render/draw.js.map +1 -1
- package/dist/components/punchcard-chart/metadata.json.d.ts +1 -1
- package/dist/components/punchcard-chart/metadata.json.js +1 -1
- package/dist/components/sankey-chart/element/styles.d.ts.map +1 -1
- package/dist/components/sankey-chart/element/styles.js +7 -27
- package/dist/components/sankey-chart/element/styles.js.map +1 -1
- package/dist/components/sankey-chart/element/ui9000-sankey-chart.d.ts +5 -5
- package/dist/components/sankey-chart/element/ui9000-sankey-chart.d.ts.map +1 -1
- package/dist/components/sankey-chart/element/ui9000-sankey-chart.js +37 -35
- package/dist/components/sankey-chart/element/ui9000-sankey-chart.js.map +1 -1
- package/dist/components/treemap-chart/element/styles.d.ts +2 -0
- package/dist/components/treemap-chart/element/styles.d.ts.map +1 -0
- package/dist/components/treemap-chart/element/styles.js +141 -0
- package/dist/components/treemap-chart/element/styles.js.map +1 -0
- package/dist/components/treemap-chart/element/ui9000-treemap-chart.d.ts +35 -0
- package/dist/components/treemap-chart/element/ui9000-treemap-chart.d.ts.map +1 -0
- package/dist/components/treemap-chart/element/ui9000-treemap-chart.js +177 -0
- package/dist/components/treemap-chart/element/ui9000-treemap-chart.js.map +1 -0
- package/dist/components/treemap-chart/index.d.ts +5 -0
- package/dist/components/treemap-chart/index.d.ts.map +1 -0
- package/dist/components/treemap-chart/index.js +22 -0
- package/dist/components/treemap-chart/index.js.map +1 -0
- package/dist/components/treemap-chart/lib/color.d.ts +25 -0
- package/dist/components/treemap-chart/lib/color.d.ts.map +1 -0
- package/dist/components/treemap-chart/lib/color.js +67 -0
- package/dist/components/treemap-chart/lib/color.js.map +1 -0
- package/dist/components/treemap-chart/lib/format.d.ts +7 -0
- package/dist/components/treemap-chart/lib/format.d.ts.map +1 -0
- package/dist/components/treemap-chart/lib/format.js +9 -0
- package/dist/components/treemap-chart/lib/format.js.map +1 -0
- package/dist/components/treemap-chart/lib/index.d.ts +6 -0
- package/dist/components/treemap-chart/lib/index.d.ts.map +1 -0
- package/dist/components/treemap-chart/lib/layout.d.ts +18 -0
- package/dist/components/treemap-chart/lib/layout.d.ts.map +1 -0
- package/dist/components/treemap-chart/lib/layout.js +31 -0
- package/dist/components/treemap-chart/lib/layout.js.map +1 -0
- package/dist/components/treemap-chart/lib/normalize.d.ts +4 -0
- package/dist/components/treemap-chart/lib/normalize.d.ts.map +1 -0
- package/dist/components/treemap-chart/lib/normalize.js +182 -0
- package/dist/components/treemap-chart/lib/normalize.js.map +1 -0
- package/dist/components/treemap-chart/lib/types.d.ts +63 -0
- package/dist/components/treemap-chart/lib/types.d.ts.map +1 -0
- package/dist/components/treemap-chart/metadata.json.d.ts +126 -0
- package/dist/components/treemap-chart/metadata.json.js +45 -0
- package/dist/components/treemap-chart/render/draw.d.ts +24 -0
- package/dist/components/treemap-chart/render/draw.d.ts.map +1 -0
- package/dist/components/treemap-chart/render/draw.js +124 -0
- package/dist/components/treemap-chart/render/draw.js.map +1 -0
- package/dist/components/treemap-chart/render/index.d.ts +2 -0
- package/dist/components/treemap-chart/render/index.d.ts.map +1 -0
- package/dist/element/chart-legend-render.d.ts +5 -0
- package/dist/element/chart-legend-render.d.ts.map +1 -1
- package/dist/element/chart-legend-render.js +19 -1
- package/dist/element/chart-legend-render.js.map +1 -1
- package/dist/element/chart-shell-styles.d.ts.map +1 -1
- package/dist/element/chart-shell-styles.js +35 -0
- package/dist/element/chart-shell-styles.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +73 -39
- package/dist/index.js.map +1 -1
- package/dist/lazy/index.d.ts +4 -2
- package/dist/lazy/index.d.ts.map +1 -1
- package/dist/lazy/index.js +24 -8
- package/dist/lazy/index.js.map +1 -1
- package/dist/lazy/map-chart.d.ts +4 -0
- package/dist/lazy/map-chart.d.ts.map +1 -0
- package/dist/lazy/map-chart.js +5 -0
- package/dist/lazy/map-chart.js.map +1 -0
- package/dist/lazy/matrix-chart.d.ts +4 -0
- package/dist/lazy/matrix-chart.d.ts.map +1 -0
- package/dist/lazy/matrix-chart.js +5 -0
- package/dist/lazy/matrix-chart.js.map +1 -0
- package/dist/lazy/treemap-chart.d.ts +4 -0
- package/dist/lazy/treemap-chart.d.ts.map +1 -0
- package/dist/lazy/treemap-chart.js +5 -0
- package/dist/lazy/treemap-chart.js.map +1 -0
- package/dist/node_modules/lit-html/directive.js +28 -0
- package/dist/node_modules/lit-html/directive.js.map +1 -0
- package/dist/node_modules/lit-html/directives/style-map.js +37 -0
- package/dist/node_modules/lit-html/directives/style-map.js.map +1 -0
- package/dist/node_modules/lit-html/lit-html.js +231 -0
- package/dist/node_modules/lit-html/lit-html.js.map +1 -0
- package/dist/utils/axis-units.d.ts +15 -0
- package/dist/utils/axis-units.d.ts.map +1 -0
- package/dist/utils/axis-units.js +27 -0
- package/dist/utils/axis-units.js.map +1 -0
- package/dist/utils/chart-helpers.d.ts +4 -6
- package/dist/utils/chart-helpers.d.ts.map +1 -1
- package/dist/utils/chart-helpers.js +16 -15
- package/dist/utils/chart-helpers.js.map +1 -1
- package/dist/utils/format-text.d.ts +5 -0
- package/dist/utils/format-text.d.ts.map +1 -1
- package/dist/utils/format-text.js +7 -0
- package/dist/utils/format-text.js.map +1 -1
- package/dist/utils/fuse-palette.d.ts +12 -0
- package/dist/utils/fuse-palette.d.ts.map +1 -1
- package/dist/utils/fuse-palette.js +23 -1
- package/dist/utils/fuse-palette.js.map +1 -1
- package/dist/utils/fuse-widget.d.ts +13 -4
- package/dist/utils/fuse-widget.d.ts.map +1 -1
- package/dist/utils/fuse-widget.js.map +1 -1
- package/dist/utils/fusedash-visual.d.ts +65 -52
- package/dist/utils/fusedash-visual.d.ts.map +1 -1
- package/dist/utils/fusedash-visual.js +48 -29
- package/dist/utils/fusedash-visual.js.map +1 -1
- package/package.json +38 -11
- package/dist/components/donut-group-chart/element/styles.d.ts +0 -2
- package/dist/components/donut-group-chart/element/styles.d.ts.map +0 -1
- package/dist/components/donut-group-chart/element/styles.js.map +0 -1
- package/dist/components/donut-group-chart/element/ui9000-donut-group-chart.d.ts +0 -34
- package/dist/components/donut-group-chart/element/ui9000-donut-group-chart.d.ts.map +0 -1
- package/dist/components/donut-group-chart/element/ui9000-donut-group-chart.js +0 -169
- package/dist/components/donut-group-chart/element/ui9000-donut-group-chart.js.map +0 -1
- package/dist/components/donut-group-chart/index.d.ts +0 -5
- package/dist/components/donut-group-chart/index.d.ts.map +0 -1
- package/dist/components/donut-group-chart/index.js +0 -23
- package/dist/components/donut-group-chart/lib/domain.d.ts +0 -39
- package/dist/components/donut-group-chart/lib/domain.d.ts.map +0 -1
- package/dist/components/donut-group-chart/lib/domain.js +0 -51
- package/dist/components/donut-group-chart/lib/domain.js.map +0 -1
- package/dist/components/donut-group-chart/lib/format.d.ts +0 -7
- package/dist/components/donut-group-chart/lib/format.d.ts.map +0 -1
- package/dist/components/donut-group-chart/lib/format.js +0 -21
- package/dist/components/donut-group-chart/lib/format.js.map +0 -1
- package/dist/components/donut-group-chart/lib/index.d.ts +0 -5
- package/dist/components/donut-group-chart/lib/index.d.ts.map +0 -1
- package/dist/components/donut-group-chart/lib/normalize.d.ts +0 -14
- package/dist/components/donut-group-chart/lib/normalize.d.ts.map +0 -1
- package/dist/components/donut-group-chart/lib/normalize.js +0 -98
- package/dist/components/donut-group-chart/lib/normalize.js.map +0 -1
- package/dist/components/donut-group-chart/lib/types.d.ts +0 -49
- package/dist/components/donut-group-chart/lib/types.d.ts.map +0 -1
- package/dist/components/donut-group-chart/metadata.json.d.ts +0 -113
- package/dist/components/donut-group-chart/metadata.json.js +0 -45
- package/dist/components/donut-group-chart/render/draw.d.ts +0 -44
- package/dist/components/donut-group-chart/render/draw.d.ts.map +0 -1
- package/dist/components/donut-group-chart/render/draw.js +0 -194
- package/dist/components/donut-group-chart/render/draw.js.map +0 -1
- package/dist/components/donut-group-chart/render/index.d.ts +0 -2
- package/dist/components/donut-group-chart/render/index.d.ts.map +0 -1
- package/dist/components/donut-group-chart/tests/fixtures.d.ts +0 -33
- package/dist/components/donut-group-chart/tests/fixtures.d.ts.map +0 -1
- package/dist/lazy/donut-group-chart.d.ts +0 -4
- package/dist/lazy/donut-group-chart.d.ts.map +0 -1
- package/dist/lazy/donut-group-chart.js +0 -5
- package/dist/lazy/donut-group-chart.js.map +0 -1
- /package/dist/components/{donut-group-chart → matrix-chart}/index.js.map +0 -0
- /package/dist/components/{donut-group-chart → treemap-chart}/metadata.json.js.map +0 -0
|
@@ -38,8 +38,8 @@ export declare function rangeIndexOf(value: number, breakpoints: number[]): numb
|
|
|
38
38
|
export declare function isFullRange(range: NetworkRange): boolean;
|
|
39
39
|
/**
|
|
40
40
|
* Client `filterGraphDataBySliderRange`, reduced to the id set the draw layer
|
|
41
|
-
* needs
|
|
42
|
-
* `null` means "everything is visible".
|
|
41
|
+
* needs. Hidden nodes stay mounted so their zone is kept, but their edges leave
|
|
42
|
+
* the force. `null` means "everything is visible".
|
|
43
43
|
*/
|
|
44
44
|
export declare function visibleNodeIds(model: NetworkGraphModel, range: NetworkRange): Set<string> | null;
|
|
45
45
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.js","sources":["../../../../src/components/network-graph/lib/domain.ts"],"sourcesContent":["import type {\n NetworkGraphModel,\n NetworkLegendRange,\n NetworkNode,\n NetworkRange,\n NetworkSizeKey,\n} from './types.js';\n\n/** Client `NODE_SIZES` — bucket → circle radius in px. */\nexport const NODE_SIZES: Record<NetworkSizeKey, number> = {\n xs: 4,\n sm: 6,\n md: 8,\n lg: 12,\n xl: 16,\n '2xl': 24,\n '3xl': 32,\n};\n\n/** Client `SIZE_KEYS` — one per break point, smallest first. */\nexport const SIZE_KEYS: NetworkSizeKey[] = [\n 'xs',\n 'sm',\n 'md',\n 'lg',\n 'xl',\n '2xl',\n '3xl',\n];\n\n/** Buckets above `md` keep their label on screen at rest (client rule). */\nexport const LABEL_ALWAYS_ABOVE = NODE_SIZES.md;\n\nexport const NODE_BORDER_WIDTH = 1;\nexport const SHADOW_RADIUS_RATIO = 1.5;\nexport const MIN_LINK_WIDTH = 0.8;\nexport const MAX_LINK_WIDTH = 20;\n/** Resting stroke width for every link (client). */\nexport const LINK_WIDTH = 2;\nexport const MIN_LINK_DISTANCE = 20;\nexport const MAX_LINK_DISTANCE = 100;\n\n/** Client `LINK_COLORS`. */\nexport const LINK_COLORS = {\n neutral: 'rgba(207, 210, 214, 1)',\n primary: 'rgba(71, 176, 255, 0.5)',\n secondary: 'rgba(255, 140, 71, 0.5)',\n} as const;\n\n/** Client `DEFAULT_NODE_COLOR`. */\nexport const DEFAULT_NODE_COLOR = '#64748B';\n\n/** Client `NODE_TYPE_COLORS` — category key → fill for a node without an image. */\nexport const NODE_TYPE_COLORS: Record<string, string> = {\n bank: '#2563EB',\n invest: '#10B981',\n regional: '#F59E0B',\n vc: '#8B5CF6',\n crypto: '#F97316',\n exchange: '#06B6D4',\n blockchain: '#6366F1',\n defi: '#EC4899',\n company: '#3B82F6',\n startup: '#14B8A6',\n platform: '#A855F7',\n service: '#0EA5E9',\n person: '#EF4444',\n organization: '#64748B',\n community: '#84CC16',\n university: '#1E40AF',\n institute: '#7C3AED',\n program: '#059669',\n studio: '#DC2626',\n streaming: '#BE185D',\n producer: '#B91C1C',\n network: '#1F2937',\n tech: '#3B82F6',\n gaming: '#8B5CF6',\n lifestyle: '#EC4899',\n accelerator: '#10B981',\n manufacturer: '#6366F1',\n distributor: '#F59E0B',\n retailer: '#06B6D4',\n supplier: '#14B8A6',\n cloud: '#0EA5E9',\n saas: '#3B82F6',\n integration: '#8B5CF6',\n framework: '#10B981',\n foundation: '#64748B',\n sponsor: '#F59E0B',\n ai: '#6366F1',\n physics: '#8B5CF6',\n biology: '#10B981',\n corporate: '#3B82F6',\n global: '#1E40AF',\n financial: '#059669',\n ivy: '#7C3AED',\n domain: '#EC4899',\n research: '#14B8A6',\n developer: '#F97316',\n mission: '#4338CA',\n goal: '#A78BFA',\n insight: '#34D399',\n event: '#93C5FD',\n blocker: '#F43F5E',\n entity: '#4B5563',\n source: '#CBD5E1',\n};\n\n/** Full bucket window — no filtering (client `DEFAULT_SLIDER_RANGE`). */\nexport const FULL_RANGE: NetworkRange = { leftSlider: 0, rightSlider: 7 };\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n\n/** Client `getNodeSizeFromBreakpoints`. */\nexport function nodeSizeKey(\n value: number | undefined,\n breakpoints: number[],\n): NetworkSizeKey {\n if (!value || !breakpoints.length) return 'sm';\n for (let i = 0; i < breakpoints.length; i++) {\n if (value <= breakpoints[i]) return SIZE_KEYS[i] ?? 'sm';\n }\n return SIZE_KEYS[SIZE_KEYS.length - 1];\n}\n\nexport function nodeRadius(node: NetworkNode, breakpoints: number[]): number {\n return NODE_SIZES[nodeSizeKey(node.value, breakpoints)];\n}\n\nexport function shadowRadius(node: NetworkNode, breakpoints: number[]): number {\n return nodeRadius(node, breakpoints) * SHADOW_RADIUS_RATIO;\n}\n\n/** Client `getNodeColor` — explicit colour, then category, then the slate default. */\nexport function nodeColor(node: NetworkNode): string {\n return node.color || NODE_TYPE_COLORS[node.type] || DEFAULT_NODE_COLOR;\n}\n\n/** Client `scaleLinkWidthByMax` — resting links stay at `LINK_WIDTH`. */\nexport function scaleLinkWidthByMax(value: number, maxValue: number): number {\n if (maxValue <= 0) return MIN_LINK_WIDTH;\n const ratio = clamp(value / maxValue, 0, 1);\n return MIN_LINK_WIDTH + ratio * (MAX_LINK_WIDTH - MIN_LINK_WIDTH);\n}\n\n/** Client `getNodeRangeIndex` — which bucket a value falls into. */\nexport function rangeIndexOf(value: number, breakpoints: number[]): number {\n for (let i = 0; i < breakpoints.length; i++) {\n if (value <= breakpoints[i]) return i;\n }\n return breakpoints.length - 1;\n}\n\nexport function isFullRange(range: NetworkRange): boolean {\n return (\n range.leftSlider === FULL_RANGE.leftSlider &&\n range.rightSlider === FULL_RANGE.rightSlider\n );\n}\n\n/**\n * Client `filterGraphDataBySliderRange`, reduced to the id set the draw layer\n * needs: hidden nodes stay in the simulation so the layout does not jump.\n * `null` means \"everything is visible\".\n */\nexport function visibleNodeIds(\n model: NetworkGraphModel,\n range: NetworkRange,\n): Set<string> | null {\n if (isFullRange(range) || !model.breakpoints.length) return null;\n\n const ids = new Set<string>();\n for (const node of model.nodes) {\n if (typeof node.value !== 'number') continue;\n const index = rangeIndexOf(node.value, model.breakpoints);\n if (range.leftSlider <= index && index < range.rightSlider) ids.add(node.id);\n }\n return ids;\n}\n\n/**\n * Client `Legends` ranges — one bubble per break point, the last stretched to\n * 3x so the largest node still lands in a bucket.\n */\nexport function legendRanges(breakpoints: number[]): NetworkLegendRange[] {\n return breakpoints.map((start, index) => ({\n start,\n end: index === breakpoints.length - 1 ? start * 3 : breakpoints[index + 1],\n radius: NODE_SIZES[SIZE_KEYS[index] ?? 'sm'],\n }));\n}\n\n/**\n * The client lays the simulation out on a fixed 2000x1375 canvas and zooms into\n * it. Chat gives the chart its container instead, so the spatial forces are\n * scaled against that canvas — same spacing relative to the room available.\n */\nexport const FORCE_CANVAS_REFERENCE = 1375;\n/**\n * Slack between the largest node and its collision ring. The client gets this\n * for free from a flat 60px radius on a canvas where nodes cap at 32px; keeping\n * the same cushion is what stops the labels from stacking on top of each other.\n */\nexport const NODE_COLLIDE_PAD = 36;\n\nexport interface NetworkForces {\n charge: number;\n collide: number;\n linkDistance: number;\n}\n\nexport function networkForces(\n width: number,\n height: number,\n maxNodeRadius: number,\n): NetworkForces {\n const scale = clamp(Math.min(width, height) / FORCE_CANVAS_REFERENCE, 0.2, 1);\n return {\n charge: -1200 * scale,\n collide: Math.max(60 * scale, maxNodeRadius + NODE_COLLIDE_PAD),\n // The client re-rolls one random distance in [20, 100] per mount; a chat\n // chart has to be reproducible, so the midpoint is used and then scaled.\n linkDistance: clamp(\n ((MIN_LINK_DISTANCE + MAX_LINK_DISTANCE) / 2) * scale,\n MIN_LINK_DISTANCE,\n MAX_LINK_DISTANCE,\n ),\n };\n}\n\n/** Client `INITIAL_NODE_RADIUS_RATIO` — seed spread as a share of the canvas. */\nexport const INITIAL_NODE_RADIUS_RATIO = 0.2;\n\n/** Node chrome (client `createNodeElements`). */\nexport const NODE_COLORS = {\n /** Blue bloom under every node. */\n glow: '#473DD9',\n /** Hairline ring drawn on the circle edge. */\n innerBorder: '#FFFFFF',\n /** Accent ring one pixel outside it. */\n outerBorder: '#473DD9',\n /** Fill of a selected node that carries no image. */\n activeFill: '#000000',\n shadowInner: '#141C2C',\n shadowOuter: '#5F6877',\n} as const;\n\n/** Node label chrome (client `createNodeLabel`). */\nexport const NODE_LABEL_COLORS = {\n text: 'rgba(33, 38, 46, 0.9)',\n textActive: '#FFFFFF',\n background: '#FFFFFF00',\n backgroundHover: '#FFFFFF',\n backgroundActive: '#000000',\n} as const;\n\n/** Link value pill (client `link-label`). */\nexport const LINK_LABEL_COLORS = {\n background: 'rgba(19, 22, 29, 1)',\n text: 'rgba(239, 240, 241, 1)',\n} as const;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"domain.js","sources":["../../../../src/components/network-graph/lib/domain.ts"],"sourcesContent":["import type {\n NetworkGraphModel,\n NetworkLegendRange,\n NetworkNode,\n NetworkRange,\n NetworkSizeKey,\n} from './types.js';\n\n/** Client `NODE_SIZES` — bucket → circle radius in px. */\nexport const NODE_SIZES: Record<NetworkSizeKey, number> = {\n xs: 4,\n sm: 6,\n md: 8,\n lg: 12,\n xl: 16,\n '2xl': 24,\n '3xl': 32,\n};\n\n/** Client `SIZE_KEYS` — one per break point, smallest first. */\nexport const SIZE_KEYS: NetworkSizeKey[] = [\n 'xs',\n 'sm',\n 'md',\n 'lg',\n 'xl',\n '2xl',\n '3xl',\n];\n\n/** Buckets above `md` keep their label on screen at rest (client rule). */\nexport const LABEL_ALWAYS_ABOVE = NODE_SIZES.md;\n\nexport const NODE_BORDER_WIDTH = 1;\nexport const SHADOW_RADIUS_RATIO = 1.5;\nexport const MIN_LINK_WIDTH = 0.8;\nexport const MAX_LINK_WIDTH = 20;\n/** Resting stroke width for every link (client). */\nexport const LINK_WIDTH = 2;\nexport const MIN_LINK_DISTANCE = 20;\nexport const MAX_LINK_DISTANCE = 100;\n\n/** Client `LINK_COLORS`. */\nexport const LINK_COLORS = {\n neutral: 'rgba(207, 210, 214, 1)',\n primary: 'rgba(71, 176, 255, 0.5)',\n secondary: 'rgba(255, 140, 71, 0.5)',\n} as const;\n\n/** Client `DEFAULT_NODE_COLOR`. */\nexport const DEFAULT_NODE_COLOR = '#64748B';\n\n/** Client `NODE_TYPE_COLORS` — category key → fill for a node without an image. */\nexport const NODE_TYPE_COLORS: Record<string, string> = {\n bank: '#2563EB',\n invest: '#10B981',\n regional: '#F59E0B',\n vc: '#8B5CF6',\n crypto: '#F97316',\n exchange: '#06B6D4',\n blockchain: '#6366F1',\n defi: '#EC4899',\n company: '#3B82F6',\n startup: '#14B8A6',\n platform: '#A855F7',\n service: '#0EA5E9',\n person: '#EF4444',\n organization: '#64748B',\n community: '#84CC16',\n university: '#1E40AF',\n institute: '#7C3AED',\n program: '#059669',\n studio: '#DC2626',\n streaming: '#BE185D',\n producer: '#B91C1C',\n network: '#1F2937',\n tech: '#3B82F6',\n gaming: '#8B5CF6',\n lifestyle: '#EC4899',\n accelerator: '#10B981',\n manufacturer: '#6366F1',\n distributor: '#F59E0B',\n retailer: '#06B6D4',\n supplier: '#14B8A6',\n cloud: '#0EA5E9',\n saas: '#3B82F6',\n integration: '#8B5CF6',\n framework: '#10B981',\n foundation: '#64748B',\n sponsor: '#F59E0B',\n ai: '#6366F1',\n physics: '#8B5CF6',\n biology: '#10B981',\n corporate: '#3B82F6',\n global: '#1E40AF',\n financial: '#059669',\n ivy: '#7C3AED',\n domain: '#EC4899',\n research: '#14B8A6',\n developer: '#F97316',\n mission: '#4338CA',\n goal: '#A78BFA',\n insight: '#34D399',\n event: '#93C5FD',\n blocker: '#F43F5E',\n entity: '#4B5563',\n source: '#CBD5E1',\n};\n\n/** Full bucket window — no filtering (client `DEFAULT_SLIDER_RANGE`). */\nexport const FULL_RANGE: NetworkRange = { leftSlider: 0, rightSlider: 7 };\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n\n/** Client `getNodeSizeFromBreakpoints`. */\nexport function nodeSizeKey(\n value: number | undefined,\n breakpoints: number[],\n): NetworkSizeKey {\n if (!value || !breakpoints.length) return 'sm';\n for (let i = 0; i < breakpoints.length; i++) {\n if (value <= breakpoints[i]) return SIZE_KEYS[i] ?? 'sm';\n }\n return SIZE_KEYS[SIZE_KEYS.length - 1];\n}\n\nexport function nodeRadius(node: NetworkNode, breakpoints: number[]): number {\n return NODE_SIZES[nodeSizeKey(node.value, breakpoints)];\n}\n\nexport function shadowRadius(node: NetworkNode, breakpoints: number[]): number {\n return nodeRadius(node, breakpoints) * SHADOW_RADIUS_RATIO;\n}\n\n/** Client `getNodeColor` — explicit colour, then category, then the slate default. */\nexport function nodeColor(node: NetworkNode): string {\n return node.color || NODE_TYPE_COLORS[node.type] || DEFAULT_NODE_COLOR;\n}\n\n/** Client `scaleLinkWidthByMax` — resting links stay at `LINK_WIDTH`. */\nexport function scaleLinkWidthByMax(value: number, maxValue: number): number {\n if (maxValue <= 0) return MIN_LINK_WIDTH;\n const ratio = clamp(value / maxValue, 0, 1);\n return MIN_LINK_WIDTH + ratio * (MAX_LINK_WIDTH - MIN_LINK_WIDTH);\n}\n\n/** Client `getNodeRangeIndex` — which bucket a value falls into. */\nexport function rangeIndexOf(value: number, breakpoints: number[]): number {\n for (let i = 0; i < breakpoints.length; i++) {\n if (value <= breakpoints[i]) return i;\n }\n return breakpoints.length - 1;\n}\n\nexport function isFullRange(range: NetworkRange): boolean {\n return (\n range.leftSlider === FULL_RANGE.leftSlider &&\n range.rightSlider === FULL_RANGE.rightSlider\n );\n}\n\n/**\n * Client `filterGraphDataBySliderRange`, reduced to the id set the draw layer\n * needs. Hidden nodes stay mounted so their zone is kept, but their edges leave\n * the force. `null` means \"everything is visible\".\n */\nexport function visibleNodeIds(\n model: NetworkGraphModel,\n range: NetworkRange,\n): Set<string> | null {\n if (isFullRange(range) || !model.breakpoints.length) return null;\n\n const ids = new Set<string>();\n for (const node of model.nodes) {\n if (typeof node.value !== 'number') continue;\n const index = rangeIndexOf(node.value, model.breakpoints);\n if (range.leftSlider <= index && index < range.rightSlider) ids.add(node.id);\n }\n return ids;\n}\n\n/**\n * Client `Legends` ranges — one bubble per break point, the last stretched to\n * 3x so the largest node still lands in a bucket.\n */\nexport function legendRanges(breakpoints: number[]): NetworkLegendRange[] {\n return breakpoints.map((start, index) => ({\n start,\n end: index === breakpoints.length - 1 ? start * 3 : breakpoints[index + 1],\n radius: NODE_SIZES[SIZE_KEYS[index] ?? 'sm'],\n }));\n}\n\n/**\n * The client lays the simulation out on a fixed 2000x1375 canvas and zooms into\n * it. Chat gives the chart its container instead, so the spatial forces are\n * scaled against that canvas — same spacing relative to the room available.\n */\nexport const FORCE_CANVAS_REFERENCE = 1375;\n/**\n * Slack between the largest node and its collision ring. The client gets this\n * for free from a flat 60px radius on a canvas where nodes cap at 32px; keeping\n * the same cushion is what stops the labels from stacking on top of each other.\n */\nexport const NODE_COLLIDE_PAD = 36;\n\nexport interface NetworkForces {\n charge: number;\n collide: number;\n linkDistance: number;\n}\n\nexport function networkForces(\n width: number,\n height: number,\n maxNodeRadius: number,\n): NetworkForces {\n const scale = clamp(Math.min(width, height) / FORCE_CANVAS_REFERENCE, 0.2, 1);\n return {\n charge: -1200 * scale,\n collide: Math.max(60 * scale, maxNodeRadius + NODE_COLLIDE_PAD),\n // The client re-rolls one random distance in [20, 100] per mount; a chat\n // chart has to be reproducible, so the midpoint is used and then scaled.\n linkDistance: clamp(\n ((MIN_LINK_DISTANCE + MAX_LINK_DISTANCE) / 2) * scale,\n MIN_LINK_DISTANCE,\n MAX_LINK_DISTANCE,\n ),\n };\n}\n\n/** Client `INITIAL_NODE_RADIUS_RATIO` — seed spread as a share of the canvas. */\nexport const INITIAL_NODE_RADIUS_RATIO = 0.2;\n\n/** Node chrome (client `createNodeElements`). */\nexport const NODE_COLORS = {\n /** Blue bloom under every node. */\n glow: '#473DD9',\n /** Hairline ring drawn on the circle edge. */\n innerBorder: '#FFFFFF',\n /** Accent ring one pixel outside it. */\n outerBorder: '#473DD9',\n /** Fill of a selected node that carries no image. */\n activeFill: '#000000',\n shadowInner: '#141C2C',\n shadowOuter: '#5F6877',\n} as const;\n\n/** Node label chrome (client `createNodeLabel`). */\nexport const NODE_LABEL_COLORS = {\n text: 'rgba(33, 38, 46, 0.9)',\n textActive: '#FFFFFF',\n background: '#FFFFFF00',\n backgroundHover: '#FFFFFF',\n backgroundActive: '#000000',\n} as const;\n\n/** Link value pill (client `link-label`). */\nexport const LINK_LABEL_COLORS = {\n background: 'rgba(19, 22, 29, 1)',\n text: 'rgba(239, 240, 241, 1)',\n} as const;\n"],"names":[],"mappings":"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"}
|
|
@@ -2,4 +2,5 @@ export { type NetworkGraphFusePayload, type NetworkGraphInput, type NetworkGraph
|
|
|
2
2
|
export { MAX_NETWORK_LINKS, MAX_NETWORK_NODES, normalizeNetworkGraphData, } from './normalize.js';
|
|
3
3
|
export { DEFAULT_NODE_COLOR, FULL_RANGE, INITIAL_NODE_RADIUS_RATIO, LABEL_ALWAYS_ABOVE, LINK_COLORS, LINK_LABEL_COLORS, LINK_WIDTH, NODE_BORDER_WIDTH, NODE_COLORS, NODE_LABEL_COLORS, NODE_SIZES, NODE_TYPE_COLORS, SHADOW_RADIUS_RATIO, SIZE_KEYS, isFullRange, legendRanges, networkForces, nodeColor, nodeRadius, nodeSizeKey, rangeIndexOf, scaleLinkWidthByMax, shadowRadius, visibleNodeIds, type NetworkForces, } from './domain.js';
|
|
4
4
|
export { formatLegendValue, formatLinkValue, formatNodeValue, } from './format.js';
|
|
5
|
+
export { rangeTrackOffset, slideThumb, sliderIndexFromPointer, thumbTrackPercent, } from './legend.js';
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/network-graph/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,yBAAyB,EACzB,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,gBAAgB,EAChB,mBAAmB,EACnB,SAAS,EACT,WAAW,EACX,YAAY,EACZ,aAAa,EACb,SAAS,EACT,UAAU,EACV,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/network-graph/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,yBAAyB,EACzB,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,gBAAgB,EAChB,mBAAmB,EACnB,SAAS,EACT,WAAW,EACX,YAAY,EACZ,aAAa,EACb,SAAS,EACT,UAAU,EACV,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NetworkRange } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* FuseDash RangeInput: left stays strictly below right, right strictly above
|
|
4
|
+
* left — thumbs never share a slot (same as map bubble legend).
|
|
5
|
+
*/
|
|
6
|
+
export declare function slideThumb(current: NetworkRange, edge: 'leftSlider' | 'rightSlider', raw: number, rangeCount: number): NetworkRange;
|
|
7
|
+
export declare function sliderIndexFromPointer(clientX: number, track: {
|
|
8
|
+
left: number;
|
|
9
|
+
width: number;
|
|
10
|
+
}, rangeCount: number): number;
|
|
11
|
+
/** FuseDash RangeInput `getThumbPosition` — percent along that thumb's track. */
|
|
12
|
+
export declare function thumbTrackPercent(value: number, rangeCount: number): string;
|
|
13
|
+
/** FuseDash RangeInput track shift: 7px when a thumb sits on 0 or MAX. */
|
|
14
|
+
export declare function rangeTrackOffset(edge: 'left' | 'right', value: number, rangeCount: number): string;
|
|
15
|
+
//# sourceMappingURL=legend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legend.d.ts","sourceRoot":"","sources":["../../../../src/components/network-graph/lib/legend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,YAAY,EACrB,IAAI,EAAE,YAAY,GAAG,aAAa,EAClC,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GACjB,YAAY,CAYd;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EACtC,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3E;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,GAAG,OAAO,EACtB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB,MAAM,CAIR"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
function slideThumb(current, edge, raw, rangeCount) {
|
|
2
|
+
const max = Math.max(rangeCount, 1);
|
|
3
|
+
if (edge === "leftSlider") {
|
|
4
|
+
return {
|
|
5
|
+
leftSlider: Math.max(0, Math.min(current.rightSlider - 1, raw)),
|
|
6
|
+
rightSlider: current.rightSlider
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
leftSlider: current.leftSlider,
|
|
11
|
+
rightSlider: Math.max(current.leftSlider + 1, Math.min(max, raw))
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function sliderIndexFromPointer(clientX, track, rangeCount) {
|
|
15
|
+
const max = Math.max(rangeCount, 1);
|
|
16
|
+
if (track.width <= 0) return 0;
|
|
17
|
+
const ratio = (clientX - track.left) / track.width;
|
|
18
|
+
return Math.max(0, Math.min(max, Math.round(ratio * max)));
|
|
19
|
+
}
|
|
20
|
+
function thumbTrackPercent(value, rangeCount) {
|
|
21
|
+
const max = Math.max(rangeCount, 1);
|
|
22
|
+
return `${value / max * 100}%`;
|
|
23
|
+
}
|
|
24
|
+
function rangeTrackOffset(edge, value, rangeCount) {
|
|
25
|
+
const max = Math.max(rangeCount, 1);
|
|
26
|
+
if (edge === "left") return value === 0 ? "7px" : "0px";
|
|
27
|
+
return value === max ? "7px" : "0px";
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
rangeTrackOffset,
|
|
31
|
+
slideThumb,
|
|
32
|
+
sliderIndexFromPointer,
|
|
33
|
+
thumbTrackPercent
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=legend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legend.js","sources":["../../../../src/components/network-graph/lib/legend.ts"],"sourcesContent":["import type { NetworkRange } from './types.js';\n\n/**\n * FuseDash RangeInput: left stays strictly below right, right strictly above\n * left — thumbs never share a slot (same as map bubble legend).\n */\nexport function slideThumb(\n current: NetworkRange,\n edge: 'leftSlider' | 'rightSlider',\n raw: number,\n rangeCount: number,\n): NetworkRange {\n const max = Math.max(rangeCount, 1);\n if (edge === 'leftSlider') {\n return {\n leftSlider: Math.max(0, Math.min(current.rightSlider - 1, raw)),\n rightSlider: current.rightSlider,\n };\n }\n return {\n leftSlider: current.leftSlider,\n rightSlider: Math.max(current.leftSlider + 1, Math.min(max, raw)),\n };\n}\n\nexport function sliderIndexFromPointer(\n clientX: number,\n track: { left: number; width: number },\n rangeCount: number,\n): number {\n const max = Math.max(rangeCount, 1);\n if (track.width <= 0) return 0;\n const ratio = (clientX - track.left) / track.width;\n return Math.max(0, Math.min(max, Math.round(ratio * max)));\n}\n\n/** FuseDash RangeInput `getThumbPosition` — percent along that thumb's track. */\nexport function thumbTrackPercent(value: number, rangeCount: number): string {\n const max = Math.max(rangeCount, 1);\n return `${(value / max) * 100}%`;\n}\n\n/** FuseDash RangeInput track shift: 7px when a thumb sits on 0 or MAX. */\nexport function rangeTrackOffset(\n edge: 'left' | 'right',\n value: number,\n rangeCount: number,\n): string {\n const max = Math.max(rangeCount, 1);\n if (edge === 'left') return value === 0 ? '7px' : '0px';\n return value === max ? '7px' : '0px';\n}\n"],"names":[],"mappings":"AAMO,SAAS,WACd,SACA,MACA,KACA,YACc;AACd,QAAM,MAAM,KAAK,IAAI,YAAY,CAAC;AAClC,MAAI,SAAS,cAAc;AACzB,WAAO;AAAA,MACL,YAAY,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,cAAc,GAAG,GAAG,CAAC;AAAA,MAC9D,aAAa,QAAQ;AAAA,IAAA;AAAA,EAEzB;AACA,SAAO;AAAA,IACL,YAAY,QAAQ;AAAA,IACpB,aAAa,KAAK,IAAI,QAAQ,aAAa,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC;AAAA,EAAA;AAEpE;AAEO,SAAS,uBACd,SACA,OACA,YACQ;AACR,QAAM,MAAM,KAAK,IAAI,YAAY,CAAC;AAClC,MAAI,MAAM,SAAS,EAAG,QAAO;AAC7B,QAAM,SAAS,UAAU,MAAM,QAAQ,MAAM;AAC7C,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,QAAQ,GAAG,CAAC,CAAC;AAC3D;AAGO,SAAS,kBAAkB,OAAe,YAA4B;AAC3E,QAAM,MAAM,KAAK,IAAI,YAAY,CAAC;AAClC,SAAO,GAAI,QAAQ,MAAO,GAAG;AAC/B;AAGO,SAAS,iBACd,MACA,OACA,YACQ;AACR,QAAM,MAAM,KAAK,IAAI,YAAY,CAAC;AAClC,MAAI,SAAS,OAAQ,QAAO,UAAU,IAAI,QAAQ;AAClD,SAAO,UAAU,MAAM,QAAQ;AACjC;"}
|
|
@@ -19,7 +19,10 @@ export interface RenderNetworkGraphOptions {
|
|
|
19
19
|
* and container resizes can update the plot in place instead of rebuilding it.
|
|
20
20
|
*/
|
|
21
21
|
export interface NetworkGraphController {
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Legend filter — hides nodes/links in place. Isolated visible nodes stay
|
|
24
|
+
* pinned in their zone; ghost links to hidden nodes leave the force.
|
|
25
|
+
*/
|
|
23
26
|
setVisibleIds(visibleIds: Set<string> | null): void;
|
|
24
27
|
/** Container resize — re-frames the settled layout, runs no new force ticks. */
|
|
25
28
|
resize(width: number, height: number): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draw.d.ts","sourceRoot":"","sources":["../../../../src/components/network-graph/render/draw.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAgBL,KAAK,iBAAiB,EAGvB,MAAM,iBAAiB,CAAC;AAqCzB,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,iBAAiB,CAAC;IACzB,sEAAsE;IACtE,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvC,8CAA8C;IAC9C,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA8BD;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC
|
|
1
|
+
{"version":3,"file":"draw.d.ts","sourceRoot":"","sources":["../../../../src/components/network-graph/render/draw.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAgBL,KAAK,iBAAiB,EAGvB,MAAM,iBAAiB,CAAC;AAqCzB,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,iBAAiB,CAAC;IACzB,sEAAsE;IACtE,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvC,8CAA8C;IAC9C,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA8BD;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;IACpD,gFAAgF;IAChF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAgDD,yFAAyF;AACzF,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,WAAW,GAAG,IAAI,CAY7D;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,WAAW,EACtB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAC7B,OAAO,CAKT;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAKT;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE,yBAAyB,GACjC,sBAAsB,GAAG,IAAI,CAmV/B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { drag } from "d3-drag";
|
|
2
|
-
import { forceCenter, forceX, forceY,
|
|
2
|
+
import { forceCenter, forceX, forceY, forceLink, forceSimulation, forceManyBody, forceCollide } from "d3-force";
|
|
3
3
|
import { select } from "d3-selection";
|
|
4
4
|
import { zoom, zoomIdentity } from "d3-zoom";
|
|
5
5
|
import { nodeRadius, NODE_COLORS, LINK_COLORS, LINK_WIDTH, LINK_LABEL_COLORS, shadowRadius, NODE_BORDER_WIDTH, nodeColor, LABEL_ALWAYS_ABOVE, networkForces, NODE_LABEL_COLORS, scaleLinkWidthByMax, INITIAL_NODE_RADIUS_RATIO } from "../lib/domain.js";
|
|
@@ -134,10 +134,31 @@ function renderNetworkGraph(container, options) {
|
|
|
134
134
|
const centerForce = forceCenter(width / 2, height / 2).strength(0.1);
|
|
135
135
|
const xForce = forceX(width / 2).strength(0.05);
|
|
136
136
|
const yForce = forceY(height / 2).strength(0.05);
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
const linkForce = forceLink(simLinks).id((d) => d.id).distance(forces.linkDistance).strength(0.5);
|
|
138
|
+
const simulation = forceSimulation(simNodes).stop().force("link", linkForce).force(
|
|
139
|
+
"charge",
|
|
140
|
+
forceManyBody().strength((d) => isNodeVisible(d.id) ? forces.charge : 0)
|
|
141
|
+
).force("center", centerForce).force(
|
|
142
|
+
"collision",
|
|
143
|
+
forceCollide().radius((d) => isNodeVisible(d.id) ? forces.collide : 0)
|
|
144
|
+
).force("x", xForce).force("y", yForce);
|
|
145
|
+
function pinIndependentNodes() {
|
|
146
|
+
const linked = /* @__PURE__ */ new Set();
|
|
147
|
+
for (const link of simLinks) {
|
|
148
|
+
if (!isLinkVisible(link)) continue;
|
|
149
|
+
linked.add(endpointId(link.source));
|
|
150
|
+
linked.add(endpointId(link.target));
|
|
151
|
+
}
|
|
152
|
+
for (const node of simNodes) {
|
|
153
|
+
if (isNodeVisible(node.id) && linked.has(node.id)) {
|
|
154
|
+
node.fx = null;
|
|
155
|
+
node.fy = null;
|
|
156
|
+
} else {
|
|
157
|
+
node.fx = node.x;
|
|
158
|
+
node.fy = node.y;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
141
162
|
GRAPHS.set(container, { simulation, positions, controller: null });
|
|
142
163
|
const isConnected = (id) => {
|
|
143
164
|
var _a2;
|
|
@@ -233,6 +254,8 @@ function renderNetworkGraph(container, options) {
|
|
|
233
254
|
part.visible = isLinkVisible(part.datum);
|
|
234
255
|
part.line.style.opacity = part.visible ? "1" : "0";
|
|
235
256
|
}
|
|
257
|
+
linkForce.links(simLinks.filter((link) => isLinkVisible(link)));
|
|
258
|
+
pinIndependentNodes();
|
|
236
259
|
if (hoveredNodeId && !isNodeVisible(hoveredNodeId)) hoveredNodeId = null;
|
|
237
260
|
if (activeNodeId && !isNodeVisible(activeNodeId)) setActive(null);
|
|
238
261
|
else {
|
|
@@ -271,7 +294,8 @@ function renderNetworkGraph(container, options) {
|
|
|
271
294
|
hoveredNodeId = id;
|
|
272
295
|
applyState();
|
|
273
296
|
},
|
|
274
|
-
onClick: (id) => setActive(activeNodeId === id ? null : id)
|
|
297
|
+
onClick: (id) => setActive(activeNodeId === id ? null : id),
|
|
298
|
+
onDragEnd: pinIndependentNodes
|
|
275
299
|
});
|
|
276
300
|
let zoomBehaviour = null;
|
|
277
301
|
if (options.enableZoom !== false) {
|
|
@@ -416,7 +440,7 @@ function appendNodeLabel(group, datum, radius) {
|
|
|
416
440
|
};
|
|
417
441
|
}
|
|
418
442
|
function attachNodeInteractions(params) {
|
|
419
|
-
const { nodes, simulation, getBounds, radiusById, onHover, onClick } = params;
|
|
443
|
+
const { nodes, simulation, getBounds, radiusById, onHover, onClick, onDragEnd } = params;
|
|
420
444
|
let moved = false;
|
|
421
445
|
let start = null;
|
|
422
446
|
const behaviour = drag().filter((event) => event.button === 0).on("start", (event, d) => {
|
|
@@ -439,8 +463,9 @@ function attachNodeInteractions(params) {
|
|
|
439
463
|
d.fy = clamp((d.fy ?? d.y ?? 0) + event.dy, radius, height - radius);
|
|
440
464
|
}).on("end", (event, d) => {
|
|
441
465
|
if (!event.active) simulation.alphaTarget(0);
|
|
442
|
-
d.
|
|
443
|
-
d.
|
|
466
|
+
d.x = d.fx ?? d.x;
|
|
467
|
+
d.y = d.fy ?? d.y;
|
|
468
|
+
onDragEnd();
|
|
444
469
|
start = null;
|
|
445
470
|
});
|
|
446
471
|
for (const part of nodes) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draw.js","sources":["../../../../src/components/network-graph/render/draw.ts"],"sourcesContent":["import { drag, type D3DragEvent } from 'd3-drag';\nimport {\n forceCenter,\n forceCollide,\n forceLink,\n forceManyBody,\n forceSimulation,\n forceX,\n forceY,\n type Simulation,\n type SimulationLinkDatum,\n type SimulationNodeDatum,\n} from 'd3-force';\nimport { select, type Selection } from 'd3-selection';\nimport { zoom, zoomIdentity, type ZoomBehavior, type ZoomTransform } from 'd3-zoom';\n\nimport type { WidgetTheme } from '../../../types/index.js';\nimport {\n INITIAL_NODE_RADIUS_RATIO,\n LABEL_ALWAYS_ABOVE,\n LINK_COLORS,\n LINK_LABEL_COLORS,\n LINK_WIDTH,\n NODE_BORDER_WIDTH,\n NODE_COLORS,\n NODE_LABEL_COLORS,\n formatLinkValue,\n formatNodeValue,\n networkForces,\n nodeColor,\n nodeRadius,\n scaleLinkWidthByMax,\n shadowRadius,\n type NetworkGraphModel,\n type NetworkLink,\n type NetworkNode,\n} from '../lib/index.js';\n\n/** Node label box (client `createNodeLabel`). */\nconst LABEL_PADDING_TOP = 14;\nconst LABEL_PADDING_BOTTOM = 4;\nconst LABEL_PADDING_GAP = 5;\nconst LABEL_TITLE_SIZE = 12;\nconst LABEL_TITLE_LINE = 16;\nconst LABEL_VALUE_SIZE = 10;\nconst LABEL_VALUE_LINE = 14;\n/** Link pill (client `createLinkLabelBackground`). */\nconst LINK_LABEL_SIZE = 8;\nconst LINK_LABEL_PAD_X = 2;\nconst LINK_LABEL_PAD_Y = 1;\nconst LINK_LABEL_OFFSET = 10;\n/** Invisible grab ring around each node (client `.node-drag-area`). */\nconst DRAG_AREA_PAD = 10;\n/** Pointer travel that turns a click into a drag (client). */\nconst DRAG_SLOP = 5;\nconst ZOOM_EXTENT: [number, number] = [0.3, 4];\n/**\n * The client animates the layout in from the canvas centre. A chat card is read\n * at a glance, so the simulation is stepped to rest before the first paint and\n * then left idle — dragging a node restarts it, exactly as in the client.\n */\nconst PRESETTLE_TICKS = 250;\n/** Padding kept around the graph when it is fitted to the container. */\nconst FIT_PADDING = 32;\n/** Fitting may enlarge a sparse graph, but never past readable label sizes. */\nconst FIT_SCALE_EXTENT: [number, number] = [0.4, 1.25];\n/** Even seed ring so the first frame is reproducible across renders. */\nconst GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));\n\ntype SimNode = NetworkNode & SimulationNodeDatum;\ntype SimLink = Omit<NetworkLink, 'source' | 'target'> &\n SimulationLinkDatum<SimNode> & { source: string | SimNode; target: string | SimNode };\n\nexport interface RenderNetworkGraphOptions {\n model: NetworkGraphModel;\n /** `null` shows every node; otherwise only these ids stay visible. */\n visibleIds: Set<string> | null;\n width: number;\n height: number;\n theme: WidgetTheme;\n /** Selection carried across a redraw (legend filtering keeps it). */\n activeNodeId?: string | null;\n /** Fired when a node is clicked; `null` when the same node is clicked again. */\n onSelect?: (id: string | null) => void;\n /** Drag-to-pan plus ctrl/cmd + wheel zoom. */\n enableZoom?: boolean;\n}\n\ninterface NodeParts {\n datum: SimNode;\n group: SVGGElement;\n background: SVGCircleElement;\n /** Invisible ring that carries the pointer handlers. */\n grab: SVGCircleElement | null;\n label: SVGGElement | null;\n labelBg: SVGRectElement | null;\n labelTitle: SVGTextElement | null;\n labelValue: SVGTextElement | null;\n radius: number;\n hasImage: boolean;\n alwaysLabelled: boolean;\n}\n\n/** How far a node's always-on label reaches past the circle itself. */\ninterface LabelExtent {\n right: number;\n half: number;\n}\n\ninterface LinkParts {\n datum: SimLink;\n line: SVGLineElement;\n label: SVGGElement | null;\n visible: boolean;\n}\n\n/**\n * Live handle on a mounted graph. The element keeps this so the legend filter\n * and container resizes can update the plot in place instead of rebuilding it.\n */\nexport interface NetworkGraphController {\n /** Legend filter — toggles node/link visibility, the layout is untouched. */\n setVisibleIds(visibleIds: Set<string> | null): void;\n /** Container resize — re-frames the settled layout, runs no new force ticks. */\n resize(width: number, height: number): void;\n}\n\ninterface GraphState {\n simulation: Simulation<SimNode, SimLink> | null;\n positions: Map<string, { x: number; y: number }>;\n controller: NetworkGraphController | null;\n}\n\nconst GRAPHS = new WeakMap<HTMLElement, GraphState>();\n\nlet instanceCount = 0;\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n\nfunction endpointId(end: string | SimNode): string {\n return typeof end === 'string' ? end : end.id;\n}\n\n/** `getBBox` is unavailable outside a real layout engine (jsdom, detached DOM). */\nfunction measure(el: SVGGraphicsElement | null): DOMRect | null {\n if (!el || typeof el.getBBox !== 'function') return null;\n try {\n return el.getBBox();\n } catch {\n return null;\n }\n}\n\n/**\n * Client `getLinkPoint` — pull an endpoint out to the rim of its node so the\n * stroke starts where the circle ends.\n */\nfunction rimPoint(\n x: number,\n y: number,\n towardX: number,\n towardY: number,\n radius: number,\n): { x: number; y: number } {\n const dx = towardX - x;\n const dy = towardY - y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n if (distance === 0) return { x, y };\n return { x: x + (dx / distance) * radius, y: y + (dy / distance) * radius };\n}\n\n/** Stop the simulation attached to `container`, keeping its layout for the next draw. */\nexport function stopNetworkGraph(container: HTMLElement): void {\n const state = GRAPHS.get(container);\n if (!state) return;\n state.controller = null;\n if (!state.simulation) return;\n for (const node of state.simulation.nodes()) {\n if (typeof node.x === 'number' && typeof node.y === 'number') {\n state.positions.set(node.id, { x: node.x, y: node.y });\n }\n }\n state.simulation.stop();\n state.simulation = null;\n}\n\n/**\n * Legend filter. Returns `false` when no graph is mounted so the caller can fall\n * back to a full render; on success nothing is rebuilt and the camera holds.\n */\nexport function updateNetworkGraphVisibility(\n container: HTMLElement,\n visibleIds: Set<string> | null,\n): boolean {\n const controller = GRAPHS.get(container)?.controller;\n if (!controller) return false;\n controller.setVisibleIds(visibleIds);\n return true;\n}\n\n/**\n * Container resize. Returns `false` when no graph is mounted; otherwise the\n * settled layout is re-framed without re-running the force simulation.\n */\nexport function resizeNetworkGraph(\n container: HTMLElement,\n width: number,\n height: number,\n): boolean {\n const controller = GRAPHS.get(container)?.controller;\n if (!controller || width <= 0 || height <= 0) return false;\n controller.resize(width, height);\n return true;\n}\n\n/**\n * FuseDash NetworkGraph — a d3-force layout of image/colour nodes joined by\n * value-weighted links. The client lays out on a fixed 2000x1375 canvas and\n * zooms into it; chat gives the chart its container instead, so the simulation\n * runs in container coordinates with the spatial forces scaled to match.\n */\nexport function renderNetworkGraph(\n container: HTMLElement,\n options: RenderNetworkGraphOptions,\n): NetworkGraphController | null {\n const { model, theme, onSelect } = options;\n // Mutated in place by the controller — a filter or a resize must not rebuild.\n let visibleIds = options.visibleIds;\n let width = options.width;\n let height = options.height;\n\n stopNetworkGraph(container);\n container.replaceChildren();\n if (!model.nodes.length || width <= 0 || height <= 0) return null;\n\n const positions = GRAPHS.get(container)?.positions ?? new Map();\n const instance = ++instanceCount;\n const blurId = `ui9000-ng-blur-${instance}`;\n const shadowId = `ui9000-ng-shadow-${instance}`;\n\n const radiusById = new Map(\n model.nodes.map((node) => [node.id, nodeRadius(node, model.breakpoints)]),\n );\n const maxNodeRadius = Math.max(...radiusById.values());\n\n const simNodes: SimNode[] = model.nodes.map((node, index) => {\n const seed = positions.get(node.id) ?? seedPosition(index, model.nodes.length, width, height);\n return { ...node, x: seed.x, y: seed.y };\n });\n const nodeById = new Map(simNodes.map((node) => [node.id, node]));\n const simLinks: SimLink[] = model.links.map((link) => ({ ...link }));\n\n const isNodeVisible = (id: string): boolean => !visibleIds || visibleIds.has(id);\n const isLinkVisible = (link: SimLink): boolean =>\n isNodeVisible(endpointId(link.source)) && isNodeVisible(endpointId(link.target));\n\n const neighbours = new Map<string, Set<string>>();\n for (const link of simLinks) {\n const source = endpointId(link.source);\n const target = endpointId(link.target);\n if (!neighbours.has(source)) neighbours.set(source, new Set());\n if (!neighbours.has(target)) neighbours.set(target, new Set());\n neighbours.get(source)!.add(target);\n neighbours.get(target)!.add(source);\n }\n\n let activeNodeId = options.activeNodeId ?? null;\n let hoveredNodeId: string | null = null;\n\n const svg = select(container)\n .append('svg')\n .attr('width', '100%')\n .attr('height', '100%')\n .attr('viewBox', `0 0 ${width} ${height}`)\n .attr('preserveAspectRatio', 'xMidYMid meet')\n .style('display', 'block')\n .style('font-family', theme.fontFamily);\n\n appendDefs(svg, blurId, shadowId);\n\n const root = svg.append('g').attr('class', 'graph-root');\n const linkLayer = root.append('g').attr('class', 'link-layer');\n const linkLabelLayer = root.append('g').attr('class', 'link-label-layer');\n const nodeLayer = root.append('g').attr('class', 'node-layer');\n\n const links = buildLinks(linkLayer, linkLabelLayer, simLinks);\n const nodes = buildNodes({\n layer: nodeLayer,\n simNodes,\n model,\n radiusById,\n blurId,\n shadowId,\n });\n\n // Labels hang off the right of their node and are taller than a small circle,\n // so the camera has to allow for them or a narrow frame clips them.\n const labelExtent = new Map<string, LabelExtent>();\n for (const part of nodes) {\n if (!part.alwaysLabelled || !part.labelBg) continue;\n const right = Number(part.labelBg.getAttribute('width')) || 0;\n const half = (Number(part.labelBg.getAttribute('height')) || 0) / 2;\n if (right || half) labelExtent.set(part.datum.id, { right, half });\n }\n\n const forces = networkForces(width, height, maxNodeRadius);\n // Centring forces are kept by reference: a resize retargets them instead of\n // rebuilding the simulation.\n const centerForce = forceCenter<SimNode>(width / 2, height / 2).strength(0.1);\n const xForce = forceX<SimNode>(width / 2).strength(0.05);\n const yForce = forceY<SimNode>(height / 2).strength(0.05);\n // Every link stays in the force, filtered-out ones included, so toggling the\n // legend never disturbs the layout (client parity).\n const simulation = forceSimulation<SimNode>(simNodes)\n .stop()\n .force(\n 'link',\n forceLink<SimNode, SimLink>(simLinks)\n .id((d) => d.id)\n .distance(forces.linkDistance)\n .strength(0.5),\n )\n .force('charge', forceManyBody<SimNode>().strength(forces.charge))\n .force('center', centerForce)\n .force('collision', forceCollide<SimNode>().radius(forces.collide))\n .force('x', xForce)\n .force('y', yForce);\n\n GRAPHS.set(container, { simulation, positions, controller: null });\n\n const isConnected = (id: string): boolean =>\n !!activeNodeId && activeNodeId !== id && !!neighbours.get(activeNodeId)?.has(id);\n\n const isLinkActive = (link: SimLink): boolean =>\n !!activeNodeId &&\n (endpointId(link.source) === activeNodeId || endpointId(link.target) === activeNodeId);\n\n /** Geometry — runs on every simulation tick. */\n function updateFrame(): void {\n for (const node of simNodes) {\n const radius = radiusById.get(node.id) ?? 0;\n node.x = clamp(node.x ?? width / 2, radius, width - radius);\n node.y = clamp(node.y ?? height / 2, radius, height - radius);\n }\n\n for (const part of links) {\n const source = nodeById.get(endpointId(part.datum.source));\n const target = nodeById.get(endpointId(part.datum.target));\n if (!source || !target) continue;\n\n const sx = source.x ?? 0;\n const sy = source.y ?? 0;\n const tx = target.x ?? 0;\n const ty = target.y ?? 0;\n const from = rimPoint(sx, sy, tx, ty, radiusById.get(source.id) ?? 0);\n const to = rimPoint(tx, ty, sx, sy, radiusById.get(target.id) ?? 0);\n\n part.line.setAttribute('x1', String(from.x));\n part.line.setAttribute('y1', String(from.y));\n part.line.setAttribute('x2', String(to.x));\n part.line.setAttribute('y2', String(to.y));\n\n if (!part.label) continue;\n const show = part.visible && isLinkActive(part.datum);\n part.label.style.opacity = show ? '1' : '0';\n if (!show) continue;\n\n const angle = (Math.atan2(to.y - from.y, to.x - from.x) * 180) / Math.PI;\n const upright = angle > 90 || angle < -90 ? angle + 180 : angle;\n const perpendicular = ((angle - 90) * Math.PI) / 180;\n const cx = (from.x + to.x) / 2 + Math.cos(perpendicular) * LINK_LABEL_OFFSET;\n const cy = (from.y + to.y) / 2 + Math.sin(perpendicular) * LINK_LABEL_OFFSET;\n part.label.setAttribute('transform', `translate(${cx}, ${cy}) rotate(${upright})`);\n }\n\n for (const part of nodes) {\n part.group.setAttribute(\n 'transform',\n `translate(${part.datum.x ?? 0}, ${part.datum.y ?? 0})`,\n );\n }\n }\n\n /** Selection / hover styling — runs only when the interaction state changes. */\n function applyState(): void {\n for (const part of nodes) {\n const id = part.datum.id;\n const active = activeNodeId === id;\n const hovered = hoveredNodeId === id;\n const connected = isConnected(id);\n\n part.background.setAttribute(\n 'fill',\n active && !part.hasImage ? NODE_COLORS.activeFill : nodeColor(part.datum),\n );\n\n if (!part.label) continue;\n const show = part.alwaysLabelled || active || hovered || connected;\n part.label.style.opacity = show ? '1' : '0';\n\n if (part.labelBg) {\n part.labelBg.setAttribute(\n 'fill',\n active\n ? NODE_LABEL_COLORS.backgroundActive\n : hovered\n ? NODE_LABEL_COLORS.backgroundHover\n : NODE_LABEL_COLORS.background,\n );\n }\n const textFill = active ? NODE_LABEL_COLORS.textActive : NODE_LABEL_COLORS.text;\n part.labelTitle?.setAttribute('fill', textFill);\n part.labelTitle?.setAttribute('font-weight', active || connected ? '600' : '400');\n part.labelValue?.setAttribute('fill', textFill);\n }\n\n for (const part of links) {\n part.line.setAttribute(\n 'stroke-width',\n String(\n isLinkActive(part.datum)\n ? scaleLinkWidthByMax(part.datum.value, model.maxLinkValue)\n : LINK_WIDTH,\n ),\n );\n }\n }\n\n function setActive(id: string | null): void {\n activeNodeId = id;\n applyState();\n updateFrame();\n onSelect?.(id);\n }\n\n /**\n * Legend filter — opacity and hit-testing only. The simulation keeps every\n * node and link, so nothing moves and the camera stays where the user left it.\n */\n function applyVisibility(): void {\n for (const part of nodes) {\n const visible = isNodeVisible(part.datum.id);\n part.group.style.opacity = visible ? '1' : '0';\n part.group.style.pointerEvents = visible ? 'all' : 'none';\n if (part.grab) part.grab.style.pointerEvents = visible ? 'all' : 'none';\n }\n for (const part of links) {\n part.visible = isLinkVisible(part.datum);\n part.line.style.opacity = part.visible ? '1' : '0';\n }\n // A node filtered out of view must not stay selected or hovered.\n if (hoveredNodeId && !isNodeVisible(hoveredNodeId)) hoveredNodeId = null;\n if (activeNodeId && !isNodeVisible(activeNodeId)) setActive(null);\n else {\n applyState();\n updateFrame();\n }\n }\n\n /** Centre the visible nodes in the current frame. */\n function applyFit(): void {\n const fitted = fitTransform(\n simNodes.filter((node) => isNodeVisible(node.id)),\n radiusById,\n width,\n height,\n labelExtent,\n );\n if (!fitted) return;\n if (zoomBehaviour) svg.call(zoomBehaviour.transform, fitted);\n else root.attr('transform', fitted.toString());\n }\n\n /**\n * Container resize. The settled layout is still good, so only the viewBox, the\n * clamp bounds, the centring force targets and the camera move — re-running\n * PRESETTLE_TICKS here would reshuffle the graph under the reader.\n */\n function resize(nextWidth: number, nextHeight: number): void {\n width = nextWidth;\n height = nextHeight;\n svg.attr('viewBox', `0 0 ${width} ${height}`);\n centerForce.x(width / 2).y(height / 2);\n xForce.x(width / 2);\n yForce.y(height / 2);\n updateFrame();\n applyFit();\n }\n\n attachNodeInteractions({\n nodes,\n simulation,\n getBounds: () => ({ width, height }),\n radiusById,\n onHover: (id) => {\n hoveredNodeId = id;\n applyState();\n },\n onClick: (id) => setActive(activeNodeId === id ? null : id),\n });\n\n let zoomBehaviour: ZoomBehavior<SVGSVGElement, unknown> | null = null;\n\n if (options.enableZoom !== false) {\n zoomBehaviour = zoom<SVGSVGElement, unknown>()\n .scaleExtent(ZOOM_EXTENT)\n .filter((event: Event & { ctrlKey?: boolean; metaKey?: boolean; button?: number }) => {\n // Chat scrolls the transcript — plain wheel must pass through.\n if (event.type === 'wheel') return !!(event.ctrlKey || event.metaKey);\n if (event.button != null && event.button !== 0) return false;\n return !(event.target as Element | null)?.closest?.('g.node');\n })\n .on('zoom', (event: { transform: ZoomTransform }) => {\n root.attr('transform', event.transform.toString());\n });\n svg.call(zoomBehaviour);\n }\n\n // The only place the layout is settled: first mount and data changes.\n simulation.tick(PRESETTLE_TICKS);\n applyVisibility();\n applyFit();\n simulation.on('tick', updateFrame);\n\n const controller: NetworkGraphController = {\n setVisibleIds(next) {\n visibleIds = next;\n applyVisibility();\n },\n resize,\n };\n GRAPHS.set(container, { simulation, positions, controller });\n return controller;\n}\n\n/**\n * Centre the settled layout in the container. The client zooms into a fixed\n * canvas; here the canvas *is* the container, so a graph that condenses into a\n * corner is nudged back into view. Filtered-out nodes stay in the simulation to\n * hold the layout steady, so only the visible ones frame the camera.\n */\nfunction fitTransform(\n nodes: SimNode[],\n radiusById: Map<string, number>,\n width: number,\n height: number,\n labelExtent: Map<string, LabelExtent>,\n): ZoomTransform | null {\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (const node of nodes) {\n if (typeof node.x !== 'number' || typeof node.y !== 'number') continue;\n const radius = radiusById.get(node.id) ?? 0;\n const label = labelExtent.get(node.id);\n const vertical = Math.max(radius, label?.half ?? 0);\n minX = Math.min(minX, node.x - radius);\n minY = Math.min(minY, node.y - vertical);\n maxX = Math.max(maxX, node.x + radius + (label?.right ?? 0));\n maxY = Math.max(maxY, node.y + vertical);\n }\n if (!Number.isFinite(minX)) return null;\n\n const spanX = Math.max(maxX - minX, 1);\n const spanY = Math.max(maxY - minY, 1);\n const scale = clamp(\n Math.min((width - FIT_PADDING * 2) / spanX, (height - FIT_PADDING * 2) / spanY),\n FIT_SCALE_EXTENT[0],\n FIT_SCALE_EXTENT[1],\n );\n\n return zoomIdentity\n .translate(width / 2 - ((minX + maxX) / 2) * scale, height / 2 - ((minY + maxY) / 2) * scale)\n .scale(scale);\n}\n\nfunction seedPosition(\n index: number,\n count: number,\n width: number,\n height: number,\n): { x: number; y: number } {\n const maxRadius = Math.min(width, height) * INITIAL_NODE_RADIUS_RATIO;\n const radius = maxRadius * Math.sqrt(count <= 1 ? 0 : index / (count - 1));\n const angle = index * GOLDEN_ANGLE;\n return {\n x: width / 2 + Math.cos(angle) * radius,\n y: height / 2 + Math.sin(angle) * radius,\n };\n}\n\nfunction appendDefs(\n svg: Selection<SVGSVGElement, unknown, null, undefined>,\n blurId: string,\n shadowId: string,\n): void {\n const defs = svg.append('defs');\n defs\n .append('filter')\n .attr('id', blurId)\n .attr('x', '-50%')\n .attr('y', '-50%')\n .attr('width', '200%')\n .attr('height', '200%')\n .append('feGaussianBlur')\n .attr('stdDeviation', 12);\n\n const radial = defs.append('radialGradient').attr('id', shadowId);\n radial\n .append('stop')\n .attr('offset', '0%')\n .attr('stop-color', NODE_COLORS.shadowInner)\n .attr('stop-opacity', 1);\n radial\n .append('stop')\n .attr('offset', '100%')\n .attr('stop-color', NODE_COLORS.shadowOuter)\n .attr('stop-opacity', 0.12);\n}\n\n/** Every link is built once; the legend filter only toggles what is painted. */\nfunction buildLinks(\n linkLayer: Selection<SVGGElement, unknown, null, undefined>,\n labelLayer: Selection<SVGGElement, unknown, null, undefined>,\n simLinks: SimLink[],\n): LinkParts[] {\n return simLinks.map((datum) => {\n const line = linkLayer\n .append('line')\n .attr('class', 'link-line')\n .attr('stroke', LINK_COLORS[datum.type])\n .attr('stroke-width', LINK_WIDTH)\n .attr('stroke-linecap', 'round')\n .style('opacity', 1)\n .style('pointer-events', 'none')\n .node() as SVGLineElement;\n\n let label: SVGGElement | null = null;\n {\n const group = labelLayer\n .append('g')\n .attr('class', 'link-label')\n .style('opacity', 0)\n .style('pointer-events', 'none');\n\n const background = group\n .append('rect')\n .attr('class', 'link-label-background')\n .attr('rx', 5)\n .attr('ry', 5)\n .attr('fill', LINK_LABEL_COLORS.background);\n\n const text = group\n .append('text')\n .attr('class', 'link-label-text')\n .text(formatLinkValue(datum.value))\n .attr('font-size', LINK_LABEL_SIZE)\n .attr('fill', LINK_LABEL_COLORS.text)\n .attr('dominant-baseline', 'middle')\n .attr('text-anchor', 'middle');\n\n const box = measure(text.node());\n if (box) {\n background\n .attr('x', box.x - LINK_LABEL_PAD_X)\n .attr('y', box.y - LINK_LABEL_PAD_Y)\n .attr('width', box.width + LINK_LABEL_PAD_X * 2)\n .attr('height', box.height + LINK_LABEL_PAD_Y * 2);\n }\n label = group.node();\n }\n\n return { datum, line, label, visible: true };\n });\n}\n\nfunction buildNodes(params: {\n layer: Selection<SVGGElement, unknown, null, undefined>;\n simNodes: SimNode[];\n model: NetworkGraphModel;\n radiusById: Map<string, number>;\n blurId: string;\n shadowId: string;\n}): NodeParts[] {\n const { layer, simNodes, model, radiusById, blurId, shadowId } = params;\n\n return simNodes.map((datum) => {\n const radius = radiusById.get(datum.id) ?? 0;\n const shadow = shadowRadius(datum, model.breakpoints);\n const hasImage = !!datum.img;\n\n const group = layer\n .append('g')\n .datum(datum)\n .attr('class', 'node')\n .style('cursor', 'grab')\n .style('opacity', 1)\n .style('pointer-events', 'all');\n\n // Client stacking order: label behind the bloom, chrome on top of it.\n const label = appendNodeLabel(group, datum, radius);\n\n group\n .append('circle')\n .attr('class', 'node-shadow-blue')\n .attr('r', shadow)\n .attr('fill', NODE_COLORS.glow)\n .attr('opacity', 0.15)\n .attr('filter', `url(#${blurId})`)\n .style('pointer-events', 'none');\n\n const grab = group\n .append('circle')\n .attr('class', 'node-drag-area')\n .attr('r', radius + NODE_BORDER_WIDTH + DRAG_AREA_PAD)\n .attr('fill', 'transparent')\n .style('cursor', 'grab')\n .style('pointer-events', 'all')\n .node() as SVGCircleElement;\n\n group\n .append('circle')\n .attr('class', 'node-shadow-dark')\n .attr('r', shadow)\n .attr('fill', `url(#${shadowId})`)\n .attr('opacity', 0.1)\n .attr('filter', `url(#${blurId})`)\n .style('pointer-events', 'none');\n\n const background = group\n .append('circle')\n .attr('class', 'node-background')\n .attr('r', radius)\n .attr('fill', nodeColor(datum))\n .style('opacity', hasImage ? 0 : 1)\n .style('pointer-events', 'none')\n .node() as SVGCircleElement;\n\n group\n .append('circle')\n .attr('class', 'node-border-white')\n .attr('r', radius)\n .attr('fill', 'none')\n .attr('stroke', NODE_COLORS.innerBorder)\n .attr('stroke-width', NODE_BORDER_WIDTH)\n .style('pointer-events', 'none');\n\n if (hasImage) {\n group\n .append('image')\n .attr('class', 'node-image')\n .attr('href', datum.img as string)\n .attr('width', radius * 2)\n .attr('height', radius * 2)\n .attr('x', -radius)\n .attr('y', -radius)\n .attr('clip-path', `circle(${radius}px at ${radius}px ${radius}px)`)\n .attr('preserveAspectRatio', 'xMidYMid slice')\n .style('pointer-events', 'none');\n }\n\n group\n .append('circle')\n .attr('class', 'node-border-purple')\n .attr('r', radius + NODE_BORDER_WIDTH)\n .attr('fill', 'none')\n .attr('stroke', NODE_COLORS.outerBorder)\n .attr('stroke-width', NODE_BORDER_WIDTH)\n .style('pointer-events', 'none');\n\n return {\n datum,\n group: group.node() as SVGGElement,\n background,\n grab,\n label: label.group,\n labelBg: label.background,\n labelTitle: label.title,\n labelValue: label.value,\n radius,\n hasImage,\n alwaysLabelled: radius > LABEL_ALWAYS_ABOVE,\n };\n });\n}\n\nfunction appendNodeLabel(\n group: Selection<SVGGElement, SimNode, null, undefined>,\n datum: SimNode,\n radius: number,\n): {\n group: SVGGElement | null;\n background: SVGRectElement | null;\n title: SVGTextElement | null;\n value: SVGTextElement | null;\n} {\n const valueText = formatNodeValue(datum.value);\n if (!datum.label && !valueText) {\n return { group: null, background: null, title: null, value: null };\n }\n\n const labelGroup = group\n .append('g')\n .attr('class', 'node-label')\n .attr('transform', 'translate(1, 0)')\n .style('opacity', 0)\n .style('pointer-events', 'none');\n\n const background = labelGroup\n .append('rect')\n .attr('class', 'node-label-background')\n .attr('rx', 4)\n .attr('ry', 4)\n .attr('fill', NODE_LABEL_COLORS.background);\n\n const title = labelGroup\n .append('text')\n .attr('class', 'node-label-title')\n .text(datum.label)\n .attr('font-size', LABEL_TITLE_SIZE)\n .attr('font-weight', '400')\n .attr('fill', NODE_LABEL_COLORS.text)\n .attr('text-anchor', 'middle')\n .attr('dominant-baseline', 'hanging');\n\n const value = labelGroup\n .append('text')\n .attr('class', 'node-label-value')\n .text(valueText)\n .attr('font-size', LABEL_VALUE_SIZE)\n .attr('fill', NODE_LABEL_COLORS.text)\n .attr('text-anchor', 'middle')\n .attr('dominant-baseline', 'hanging');\n\n const paddingX = radius + LABEL_PADDING_GAP;\n const titleBox = measure(title.node());\n const valueBox = measure(value.node());\n const contentWidth = Math.max(titleBox?.width ?? 0, valueBox?.width ?? 0);\n const boxWidth = paddingX * 2 + contentWidth;\n const boxHeight =\n LABEL_PADDING_TOP + LABEL_TITLE_LINE + LABEL_VALUE_LINE + LABEL_PADDING_BOTTOM;\n const centerX = paddingX + contentWidth / 2;\n\n background\n .attr('x', 0)\n .attr('y', -boxHeight / 2)\n .attr('width', boxWidth)\n .attr('height', boxHeight);\n title.attr('x', centerX).attr('y', -boxHeight / 2 + LABEL_PADDING_TOP);\n value\n .attr('x', centerX)\n .attr('y', -boxHeight / 2 + LABEL_PADDING_TOP + LABEL_TITLE_LINE);\n\n return {\n group: labelGroup.node(),\n background: background.node(),\n title: title.node(),\n value: value.node(),\n };\n}\n\nfunction attachNodeInteractions(params: {\n nodes: NodeParts[];\n simulation: Simulation<SimNode, SimLink>;\n /** Read per event — the frame changes on resize without a rebuild. */\n getBounds: () => { width: number; height: number };\n radiusById: Map<string, number>;\n onHover: (id: string | null) => void;\n onClick: (id: string) => void;\n}): void {\n const { nodes, simulation, getBounds, radiusById, onHover, onClick } = params;\n let moved = false;\n let start: { x: number; y: number } | null = null;\n\n const behaviour = drag<SVGGElement, SimNode>()\n .filter((event: MouseEvent) => event.button === 0)\n .on('start', (event: D3DragEvent<SVGGElement, SimNode, unknown>, d) => {\n event.sourceEvent.stopPropagation();\n start = { x: event.sourceEvent.clientX, y: event.sourceEvent.clientY };\n moved = false;\n if (!event.active) simulation.alphaTarget(0.3).restart();\n const { width, height } = getBounds();\n d.fx = d.x ?? width / 2;\n d.fy = d.y ?? height / 2;\n })\n .on('drag', (event: D3DragEvent<SVGGElement, SimNode, unknown>, d) => {\n if (start) {\n const dx = event.sourceEvent.clientX - start.x;\n const dy = event.sourceEvent.clientY - start.y;\n if (Math.sqrt(dx * dx + dy * dy) > DRAG_SLOP) moved = true;\n }\n const { width, height } = getBounds();\n const radius = radiusById.get(d.id) ?? 0;\n d.fx = clamp((d.fx ?? d.x ?? 0) + event.dx, radius, width - radius);\n d.fy = clamp((d.fy ?? d.y ?? 0) + event.dy, radius, height - radius);\n })\n .on('end', (event: D3DragEvent<SVGGElement, SimNode, unknown>, d) => {\n if (!event.active) simulation.alphaTarget(0);\n d.fx = null;\n d.fy = null;\n start = null;\n });\n\n for (const part of nodes) {\n const group = select<SVGGElement, SimNode>(part.group);\n group.call(behaviour);\n\n const grab = group.select<SVGCircleElement>('.node-drag-area');\n grab.on('click', (event: MouseEvent) => {\n event.stopPropagation();\n if (moved) {\n moved = false;\n return;\n }\n onClick(part.datum.id);\n });\n grab.on('mouseenter', () => onHover(part.datum.id));\n grab.on('mouseleave', () => onHover(null));\n }\n}\n"],"names":["_a"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"draw.js","sources":["../../../../src/components/network-graph/render/draw.ts"],"sourcesContent":["import { drag, type D3DragEvent } from 'd3-drag';\nimport {\n forceCenter,\n forceCollide,\n forceLink,\n forceManyBody,\n forceSimulation,\n forceX,\n forceY,\n type Simulation,\n type SimulationLinkDatum,\n type SimulationNodeDatum,\n} from 'd3-force';\nimport { select, type Selection } from 'd3-selection';\nimport { zoom, zoomIdentity, type ZoomBehavior, type ZoomTransform } from 'd3-zoom';\n\nimport type { WidgetTheme } from '../../../types/index.js';\nimport {\n INITIAL_NODE_RADIUS_RATIO,\n LABEL_ALWAYS_ABOVE,\n LINK_COLORS,\n LINK_LABEL_COLORS,\n LINK_WIDTH,\n NODE_BORDER_WIDTH,\n NODE_COLORS,\n NODE_LABEL_COLORS,\n formatLinkValue,\n formatNodeValue,\n networkForces,\n nodeColor,\n nodeRadius,\n scaleLinkWidthByMax,\n shadowRadius,\n type NetworkGraphModel,\n type NetworkLink,\n type NetworkNode,\n} from '../lib/index.js';\n\n/** Node label box (client `createNodeLabel`). */\nconst LABEL_PADDING_TOP = 14;\nconst LABEL_PADDING_BOTTOM = 4;\nconst LABEL_PADDING_GAP = 5;\nconst LABEL_TITLE_SIZE = 12;\nconst LABEL_TITLE_LINE = 16;\nconst LABEL_VALUE_SIZE = 10;\nconst LABEL_VALUE_LINE = 14;\n/** Link pill (client `createLinkLabelBackground`). */\nconst LINK_LABEL_SIZE = 8;\nconst LINK_LABEL_PAD_X = 2;\nconst LINK_LABEL_PAD_Y = 1;\nconst LINK_LABEL_OFFSET = 10;\n/** Invisible grab ring around each node (client `.node-drag-area`). */\nconst DRAG_AREA_PAD = 10;\n/** Pointer travel that turns a click into a drag (client). */\nconst DRAG_SLOP = 5;\nconst ZOOM_EXTENT: [number, number] = [0.3, 4];\n/**\n * The client animates the layout in from the canvas centre. A chat card is read\n * at a glance, so the simulation is stepped to rest before the first paint and\n * then left idle — dragging a node restarts it, exactly as in the client.\n */\nconst PRESETTLE_TICKS = 250;\n/** Padding kept around the graph when it is fitted to the container. */\nconst FIT_PADDING = 32;\n/** Fitting may enlarge a sparse graph, but never past readable label sizes. */\nconst FIT_SCALE_EXTENT: [number, number] = [0.4, 1.25];\n/** Even seed ring so the first frame is reproducible across renders. */\nconst GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));\n\ntype SimNode = NetworkNode & SimulationNodeDatum;\ntype SimLink = Omit<NetworkLink, 'source' | 'target'> &\n SimulationLinkDatum<SimNode> & { source: string | SimNode; target: string | SimNode };\n\nexport interface RenderNetworkGraphOptions {\n model: NetworkGraphModel;\n /** `null` shows every node; otherwise only these ids stay visible. */\n visibleIds: Set<string> | null;\n width: number;\n height: number;\n theme: WidgetTheme;\n /** Selection carried across a redraw (legend filtering keeps it). */\n activeNodeId?: string | null;\n /** Fired when a node is clicked; `null` when the same node is clicked again. */\n onSelect?: (id: string | null) => void;\n /** Drag-to-pan plus ctrl/cmd + wheel zoom. */\n enableZoom?: boolean;\n}\n\ninterface NodeParts {\n datum: SimNode;\n group: SVGGElement;\n background: SVGCircleElement;\n /** Invisible ring that carries the pointer handlers. */\n grab: SVGCircleElement | null;\n label: SVGGElement | null;\n labelBg: SVGRectElement | null;\n labelTitle: SVGTextElement | null;\n labelValue: SVGTextElement | null;\n radius: number;\n hasImage: boolean;\n alwaysLabelled: boolean;\n}\n\n/** How far a node's always-on label reaches past the circle itself. */\ninterface LabelExtent {\n right: number;\n half: number;\n}\n\ninterface LinkParts {\n datum: SimLink;\n line: SVGLineElement;\n label: SVGGElement | null;\n visible: boolean;\n}\n\n/**\n * Live handle on a mounted graph. The element keeps this so the legend filter\n * and container resizes can update the plot in place instead of rebuilding it.\n */\nexport interface NetworkGraphController {\n /**\n * Legend filter — hides nodes/links in place. Isolated visible nodes stay\n * pinned in their zone; ghost links to hidden nodes leave the force.\n */\n setVisibleIds(visibleIds: Set<string> | null): void;\n /** Container resize — re-frames the settled layout, runs no new force ticks. */\n resize(width: number, height: number): void;\n}\n\ninterface GraphState {\n simulation: Simulation<SimNode, SimLink> | null;\n positions: Map<string, { x: number; y: number }>;\n controller: NetworkGraphController | null;\n}\n\nconst GRAPHS = new WeakMap<HTMLElement, GraphState>();\n\nlet instanceCount = 0;\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n\nfunction endpointId(end: string | SimNode): string {\n return typeof end === 'string' ? end : end.id;\n}\n\n/** `getBBox` is unavailable outside a real layout engine (jsdom, detached DOM). */\nfunction measure(el: SVGGraphicsElement | null): DOMRect | null {\n if (!el || typeof el.getBBox !== 'function') return null;\n try {\n return el.getBBox();\n } catch {\n return null;\n }\n}\n\n/**\n * Client `getLinkPoint` — pull an endpoint out to the rim of its node so the\n * stroke starts where the circle ends.\n */\nfunction rimPoint(\n x: number,\n y: number,\n towardX: number,\n towardY: number,\n radius: number,\n): { x: number; y: number } {\n const dx = towardX - x;\n const dy = towardY - y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n if (distance === 0) return { x, y };\n return { x: x + (dx / distance) * radius, y: y + (dy / distance) * radius };\n}\n\n/** Stop the simulation attached to `container`, keeping its layout for the next draw. */\nexport function stopNetworkGraph(container: HTMLElement): void {\n const state = GRAPHS.get(container);\n if (!state) return;\n state.controller = null;\n if (!state.simulation) return;\n for (const node of state.simulation.nodes()) {\n if (typeof node.x === 'number' && typeof node.y === 'number') {\n state.positions.set(node.id, { x: node.x, y: node.y });\n }\n }\n state.simulation.stop();\n state.simulation = null;\n}\n\n/**\n * Legend filter. Returns `false` when no graph is mounted so the caller can fall\n * back to a full render; on success nothing is rebuilt and the camera holds.\n */\nexport function updateNetworkGraphVisibility(\n container: HTMLElement,\n visibleIds: Set<string> | null,\n): boolean {\n const controller = GRAPHS.get(container)?.controller;\n if (!controller) return false;\n controller.setVisibleIds(visibleIds);\n return true;\n}\n\n/**\n * Container resize. Returns `false` when no graph is mounted; otherwise the\n * settled layout is re-framed without re-running the force simulation.\n */\nexport function resizeNetworkGraph(\n container: HTMLElement,\n width: number,\n height: number,\n): boolean {\n const controller = GRAPHS.get(container)?.controller;\n if (!controller || width <= 0 || height <= 0) return false;\n controller.resize(width, height);\n return true;\n}\n\n/**\n * FuseDash NetworkGraph — a d3-force layout of image/colour nodes joined by\n * value-weighted links. The client lays out on a fixed 2000x1375 canvas and\n * zooms into it; chat gives the chart its container instead, so the simulation\n * runs in container coordinates with the spatial forces scaled to match.\n */\nexport function renderNetworkGraph(\n container: HTMLElement,\n options: RenderNetworkGraphOptions,\n): NetworkGraphController | null {\n const { model, theme, onSelect } = options;\n // Mutated in place by the controller — a filter or a resize must not rebuild.\n let visibleIds = options.visibleIds;\n let width = options.width;\n let height = options.height;\n\n stopNetworkGraph(container);\n container.replaceChildren();\n if (!model.nodes.length || width <= 0 || height <= 0) return null;\n\n const positions = GRAPHS.get(container)?.positions ?? new Map();\n const instance = ++instanceCount;\n const blurId = `ui9000-ng-blur-${instance}`;\n const shadowId = `ui9000-ng-shadow-${instance}`;\n\n const radiusById = new Map(\n model.nodes.map((node) => [node.id, nodeRadius(node, model.breakpoints)]),\n );\n const maxNodeRadius = Math.max(...radiusById.values());\n\n const simNodes: SimNode[] = model.nodes.map((node, index) => {\n const seed = positions.get(node.id) ?? seedPosition(index, model.nodes.length, width, height);\n return { ...node, x: seed.x, y: seed.y };\n });\n const nodeById = new Map(simNodes.map((node) => [node.id, node]));\n const simLinks: SimLink[] = model.links.map((link) => ({ ...link }));\n\n const isNodeVisible = (id: string): boolean => !visibleIds || visibleIds.has(id);\n const isLinkVisible = (link: SimLink): boolean =>\n isNodeVisible(endpointId(link.source)) && isNodeVisible(endpointId(link.target));\n\n const neighbours = new Map<string, Set<string>>();\n for (const link of simLinks) {\n const source = endpointId(link.source);\n const target = endpointId(link.target);\n if (!neighbours.has(source)) neighbours.set(source, new Set());\n if (!neighbours.has(target)) neighbours.set(target, new Set());\n neighbours.get(source)!.add(target);\n neighbours.get(target)!.add(source);\n }\n\n let activeNodeId = options.activeNodeId ?? null;\n let hoveredNodeId: string | null = null;\n\n const svg = select(container)\n .append('svg')\n .attr('width', '100%')\n .attr('height', '100%')\n .attr('viewBox', `0 0 ${width} ${height}`)\n .attr('preserveAspectRatio', 'xMidYMid meet')\n .style('display', 'block')\n .style('font-family', theme.fontFamily);\n\n appendDefs(svg, blurId, shadowId);\n\n const root = svg.append('g').attr('class', 'graph-root');\n const linkLayer = root.append('g').attr('class', 'link-layer');\n const linkLabelLayer = root.append('g').attr('class', 'link-label-layer');\n const nodeLayer = root.append('g').attr('class', 'node-layer');\n\n const links = buildLinks(linkLayer, linkLabelLayer, simLinks);\n const nodes = buildNodes({\n layer: nodeLayer,\n simNodes,\n model,\n radiusById,\n blurId,\n shadowId,\n });\n\n // Labels hang off the right of their node and are taller than a small circle,\n // so the camera has to allow for them or a narrow frame clips them.\n const labelExtent = new Map<string, LabelExtent>();\n for (const part of nodes) {\n if (!part.alwaysLabelled || !part.labelBg) continue;\n const right = Number(part.labelBg.getAttribute('width')) || 0;\n const half = (Number(part.labelBg.getAttribute('height')) || 0) / 2;\n if (right || half) labelExtent.set(part.datum.id, { right, half });\n }\n\n const forces = networkForces(width, height, maxNodeRadius);\n // Centring forces are kept by reference: a resize retargets them instead of\n // rebuilding the simulation.\n const centerForce = forceCenter<SimNode>(width / 2, height / 2).strength(0.1);\n const xForce = forceX<SimNode>(width / 2).strength(0.05);\n const yForce = forceY<SimNode>(height / 2).strength(0.05);\n // The first settle uses every link so each node lands in its network zone.\n // After a legend filter, only visible–visible edges stay in the force.\n const linkForce = forceLink<SimNode, SimLink>(simLinks)\n .id((d) => d.id)\n .distance(forces.linkDistance)\n .strength(0.5);\n const simulation = forceSimulation<SimNode>(simNodes)\n .stop()\n .force('link', linkForce)\n .force(\n 'charge',\n forceManyBody<SimNode>().strength((d) => (isNodeVisible(d.id) ? forces.charge : 0)),\n )\n .force('center', centerForce)\n .force(\n 'collision',\n forceCollide<SimNode>().radius((d) => (isNodeVisible(d.id) ? forces.collide : 0)),\n )\n .force('x', xForce)\n .force('y', yForce);\n\n /**\n * A node with no remaining visible edge stays where it is. Hidden nodes are\n * pinned too so they cannot pull or shove the rest of the graph.\n */\n function pinIndependentNodes(): void {\n const linked = new Set<string>();\n for (const link of simLinks) {\n if (!isLinkVisible(link)) continue;\n linked.add(endpointId(link.source));\n linked.add(endpointId(link.target));\n }\n for (const node of simNodes) {\n if (isNodeVisible(node.id) && linked.has(node.id)) {\n node.fx = null;\n node.fy = null;\n } else {\n node.fx = node.x;\n node.fy = node.y;\n }\n }\n }\n\n GRAPHS.set(container, { simulation, positions, controller: null });\n\n const isConnected = (id: string): boolean =>\n !!activeNodeId && activeNodeId !== id && !!neighbours.get(activeNodeId)?.has(id);\n\n const isLinkActive = (link: SimLink): boolean =>\n !!activeNodeId &&\n (endpointId(link.source) === activeNodeId || endpointId(link.target) === activeNodeId);\n\n /** Geometry — runs on every simulation tick. */\n function updateFrame(): void {\n for (const node of simNodes) {\n const radius = radiusById.get(node.id) ?? 0;\n node.x = clamp(node.x ?? width / 2, radius, width - radius);\n node.y = clamp(node.y ?? height / 2, radius, height - radius);\n }\n\n for (const part of links) {\n const source = nodeById.get(endpointId(part.datum.source));\n const target = nodeById.get(endpointId(part.datum.target));\n if (!source || !target) continue;\n\n const sx = source.x ?? 0;\n const sy = source.y ?? 0;\n const tx = target.x ?? 0;\n const ty = target.y ?? 0;\n const from = rimPoint(sx, sy, tx, ty, radiusById.get(source.id) ?? 0);\n const to = rimPoint(tx, ty, sx, sy, radiusById.get(target.id) ?? 0);\n\n part.line.setAttribute('x1', String(from.x));\n part.line.setAttribute('y1', String(from.y));\n part.line.setAttribute('x2', String(to.x));\n part.line.setAttribute('y2', String(to.y));\n\n if (!part.label) continue;\n const show = part.visible && isLinkActive(part.datum);\n part.label.style.opacity = show ? '1' : '0';\n if (!show) continue;\n\n const angle = (Math.atan2(to.y - from.y, to.x - from.x) * 180) / Math.PI;\n const upright = angle > 90 || angle < -90 ? angle + 180 : angle;\n const perpendicular = ((angle - 90) * Math.PI) / 180;\n const cx = (from.x + to.x) / 2 + Math.cos(perpendicular) * LINK_LABEL_OFFSET;\n const cy = (from.y + to.y) / 2 + Math.sin(perpendicular) * LINK_LABEL_OFFSET;\n part.label.setAttribute('transform', `translate(${cx}, ${cy}) rotate(${upright})`);\n }\n\n for (const part of nodes) {\n part.group.setAttribute(\n 'transform',\n `translate(${part.datum.x ?? 0}, ${part.datum.y ?? 0})`,\n );\n }\n }\n\n /** Selection / hover styling — runs only when the interaction state changes. */\n function applyState(): void {\n for (const part of nodes) {\n const id = part.datum.id;\n const active = activeNodeId === id;\n const hovered = hoveredNodeId === id;\n const connected = isConnected(id);\n\n part.background.setAttribute(\n 'fill',\n active && !part.hasImage ? NODE_COLORS.activeFill : nodeColor(part.datum),\n );\n\n if (!part.label) continue;\n const show = part.alwaysLabelled || active || hovered || connected;\n part.label.style.opacity = show ? '1' : '0';\n\n if (part.labelBg) {\n part.labelBg.setAttribute(\n 'fill',\n active\n ? NODE_LABEL_COLORS.backgroundActive\n : hovered\n ? NODE_LABEL_COLORS.backgroundHover\n : NODE_LABEL_COLORS.background,\n );\n }\n const textFill = active ? NODE_LABEL_COLORS.textActive : NODE_LABEL_COLORS.text;\n part.labelTitle?.setAttribute('fill', textFill);\n part.labelTitle?.setAttribute('font-weight', active || connected ? '600' : '400');\n part.labelValue?.setAttribute('fill', textFill);\n }\n\n for (const part of links) {\n part.line.setAttribute(\n 'stroke-width',\n String(\n isLinkActive(part.datum)\n ? scaleLinkWidthByMax(part.datum.value, model.maxLinkValue)\n : LINK_WIDTH,\n ),\n );\n }\n }\n\n function setActive(id: string | null): void {\n activeNodeId = id;\n applyState();\n updateFrame();\n onSelect?.(id);\n }\n\n /**\n * Legend filter. Hidden nodes and their edges leave the picture and the\n * force; remaining nodes keep their zone — isolated ones are pinned so the\n * centre forces cannot pile them together.\n */\n function applyVisibility(): void {\n for (const part of nodes) {\n const visible = isNodeVisible(part.datum.id);\n part.group.style.opacity = visible ? '1' : '0';\n part.group.style.pointerEvents = visible ? 'all' : 'none';\n if (part.grab) part.grab.style.pointerEvents = visible ? 'all' : 'none';\n }\n for (const part of links) {\n part.visible = isLinkVisible(part.datum);\n part.line.style.opacity = part.visible ? '1' : '0';\n }\n linkForce.links(simLinks.filter((link) => isLinkVisible(link)));\n pinIndependentNodes();\n // A node filtered out of view must not stay selected or hovered.\n if (hoveredNodeId && !isNodeVisible(hoveredNodeId)) hoveredNodeId = null;\n if (activeNodeId && !isNodeVisible(activeNodeId)) setActive(null);\n else {\n applyState();\n updateFrame();\n }\n }\n\n /** Centre the visible nodes in the current frame. */\n function applyFit(): void {\n const fitted = fitTransform(\n simNodes.filter((node) => isNodeVisible(node.id)),\n radiusById,\n width,\n height,\n labelExtent,\n );\n if (!fitted) return;\n if (zoomBehaviour) svg.call(zoomBehaviour.transform, fitted);\n else root.attr('transform', fitted.toString());\n }\n\n /**\n * Container resize. The settled layout is still good, so only the viewBox, the\n * clamp bounds, the centring force targets and the camera move — re-running\n * PRESETTLE_TICKS here would reshuffle the graph under the reader.\n */\n function resize(nextWidth: number, nextHeight: number): void {\n width = nextWidth;\n height = nextHeight;\n svg.attr('viewBox', `0 0 ${width} ${height}`);\n centerForce.x(width / 2).y(height / 2);\n xForce.x(width / 2);\n yForce.y(height / 2);\n updateFrame();\n applyFit();\n }\n\n attachNodeInteractions({\n nodes,\n simulation,\n getBounds: () => ({ width, height }),\n radiusById,\n onHover: (id) => {\n hoveredNodeId = id;\n applyState();\n },\n onClick: (id) => setActive(activeNodeId === id ? null : id),\n onDragEnd: pinIndependentNodes,\n });\n\n let zoomBehaviour: ZoomBehavior<SVGSVGElement, unknown> | null = null;\n\n if (options.enableZoom !== false) {\n zoomBehaviour = zoom<SVGSVGElement, unknown>()\n .scaleExtent(ZOOM_EXTENT)\n .filter((event: Event & { ctrlKey?: boolean; metaKey?: boolean; button?: number }) => {\n // Chat scrolls the transcript — plain wheel must pass through.\n if (event.type === 'wheel') return !!(event.ctrlKey || event.metaKey);\n if (event.button != null && event.button !== 0) return false;\n return !(event.target as Element | null)?.closest?.('g.node');\n })\n .on('zoom', (event: { transform: ZoomTransform }) => {\n root.attr('transform', event.transform.toString());\n });\n svg.call(zoomBehaviour);\n }\n\n // The only place the layout is settled: first mount and data changes.\n simulation.tick(PRESETTLE_TICKS);\n applyVisibility();\n applyFit();\n simulation.on('tick', updateFrame);\n\n const controller: NetworkGraphController = {\n setVisibleIds(next) {\n visibleIds = next;\n applyVisibility();\n },\n resize,\n };\n GRAPHS.set(container, { simulation, positions, controller });\n return controller;\n}\n\n/**\n * Centre the settled layout in the container. The client zooms into a fixed\n * canvas; here the canvas *is* the container, so a graph that condenses into a\n * corner is nudged back into view. Filtered-out nodes stay in the simulation to\n * hold the layout steady, so only the visible ones frame the camera.\n */\nfunction fitTransform(\n nodes: SimNode[],\n radiusById: Map<string, number>,\n width: number,\n height: number,\n labelExtent: Map<string, LabelExtent>,\n): ZoomTransform | null {\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (const node of nodes) {\n if (typeof node.x !== 'number' || typeof node.y !== 'number') continue;\n const radius = radiusById.get(node.id) ?? 0;\n const label = labelExtent.get(node.id);\n const vertical = Math.max(radius, label?.half ?? 0);\n minX = Math.min(minX, node.x - radius);\n minY = Math.min(minY, node.y - vertical);\n maxX = Math.max(maxX, node.x + radius + (label?.right ?? 0));\n maxY = Math.max(maxY, node.y + vertical);\n }\n if (!Number.isFinite(minX)) return null;\n\n const spanX = Math.max(maxX - minX, 1);\n const spanY = Math.max(maxY - minY, 1);\n const scale = clamp(\n Math.min((width - FIT_PADDING * 2) / spanX, (height - FIT_PADDING * 2) / spanY),\n FIT_SCALE_EXTENT[0],\n FIT_SCALE_EXTENT[1],\n );\n\n return zoomIdentity\n .translate(width / 2 - ((minX + maxX) / 2) * scale, height / 2 - ((minY + maxY) / 2) * scale)\n .scale(scale);\n}\n\nfunction seedPosition(\n index: number,\n count: number,\n width: number,\n height: number,\n): { x: number; y: number } {\n const maxRadius = Math.min(width, height) * INITIAL_NODE_RADIUS_RATIO;\n const radius = maxRadius * Math.sqrt(count <= 1 ? 0 : index / (count - 1));\n const angle = index * GOLDEN_ANGLE;\n return {\n x: width / 2 + Math.cos(angle) * radius,\n y: height / 2 + Math.sin(angle) * radius,\n };\n}\n\nfunction appendDefs(\n svg: Selection<SVGSVGElement, unknown, null, undefined>,\n blurId: string,\n shadowId: string,\n): void {\n const defs = svg.append('defs');\n defs\n .append('filter')\n .attr('id', blurId)\n .attr('x', '-50%')\n .attr('y', '-50%')\n .attr('width', '200%')\n .attr('height', '200%')\n .append('feGaussianBlur')\n .attr('stdDeviation', 12);\n\n const radial = defs.append('radialGradient').attr('id', shadowId);\n radial\n .append('stop')\n .attr('offset', '0%')\n .attr('stop-color', NODE_COLORS.shadowInner)\n .attr('stop-opacity', 1);\n radial\n .append('stop')\n .attr('offset', '100%')\n .attr('stop-color', NODE_COLORS.shadowOuter)\n .attr('stop-opacity', 0.12);\n}\n\n/** Every link is built once; the legend filter only toggles what is painted. */\nfunction buildLinks(\n linkLayer: Selection<SVGGElement, unknown, null, undefined>,\n labelLayer: Selection<SVGGElement, unknown, null, undefined>,\n simLinks: SimLink[],\n): LinkParts[] {\n return simLinks.map((datum) => {\n const line = linkLayer\n .append('line')\n .attr('class', 'link-line')\n .attr('stroke', LINK_COLORS[datum.type])\n .attr('stroke-width', LINK_WIDTH)\n .attr('stroke-linecap', 'round')\n .style('opacity', 1)\n .style('pointer-events', 'none')\n .node() as SVGLineElement;\n\n let label: SVGGElement | null = null;\n {\n const group = labelLayer\n .append('g')\n .attr('class', 'link-label')\n .style('opacity', 0)\n .style('pointer-events', 'none');\n\n const background = group\n .append('rect')\n .attr('class', 'link-label-background')\n .attr('rx', 5)\n .attr('ry', 5)\n .attr('fill', LINK_LABEL_COLORS.background);\n\n const text = group\n .append('text')\n .attr('class', 'link-label-text')\n .text(formatLinkValue(datum.value))\n .attr('font-size', LINK_LABEL_SIZE)\n .attr('fill', LINK_LABEL_COLORS.text)\n .attr('dominant-baseline', 'middle')\n .attr('text-anchor', 'middle');\n\n const box = measure(text.node());\n if (box) {\n background\n .attr('x', box.x - LINK_LABEL_PAD_X)\n .attr('y', box.y - LINK_LABEL_PAD_Y)\n .attr('width', box.width + LINK_LABEL_PAD_X * 2)\n .attr('height', box.height + LINK_LABEL_PAD_Y * 2);\n }\n label = group.node();\n }\n\n return { datum, line, label, visible: true };\n });\n}\n\nfunction buildNodes(params: {\n layer: Selection<SVGGElement, unknown, null, undefined>;\n simNodes: SimNode[];\n model: NetworkGraphModel;\n radiusById: Map<string, number>;\n blurId: string;\n shadowId: string;\n}): NodeParts[] {\n const { layer, simNodes, model, radiusById, blurId, shadowId } = params;\n\n return simNodes.map((datum) => {\n const radius = radiusById.get(datum.id) ?? 0;\n const shadow = shadowRadius(datum, model.breakpoints);\n const hasImage = !!datum.img;\n\n const group = layer\n .append('g')\n .datum(datum)\n .attr('class', 'node')\n .style('cursor', 'grab')\n .style('opacity', 1)\n .style('pointer-events', 'all');\n\n // Client stacking order: label behind the bloom, chrome on top of it.\n const label = appendNodeLabel(group, datum, radius);\n\n group\n .append('circle')\n .attr('class', 'node-shadow-blue')\n .attr('r', shadow)\n .attr('fill', NODE_COLORS.glow)\n .attr('opacity', 0.15)\n .attr('filter', `url(#${blurId})`)\n .style('pointer-events', 'none');\n\n const grab = group\n .append('circle')\n .attr('class', 'node-drag-area')\n .attr('r', radius + NODE_BORDER_WIDTH + DRAG_AREA_PAD)\n .attr('fill', 'transparent')\n .style('cursor', 'grab')\n .style('pointer-events', 'all')\n .node() as SVGCircleElement;\n\n group\n .append('circle')\n .attr('class', 'node-shadow-dark')\n .attr('r', shadow)\n .attr('fill', `url(#${shadowId})`)\n .attr('opacity', 0.1)\n .attr('filter', `url(#${blurId})`)\n .style('pointer-events', 'none');\n\n const background = group\n .append('circle')\n .attr('class', 'node-background')\n .attr('r', radius)\n .attr('fill', nodeColor(datum))\n .style('opacity', hasImage ? 0 : 1)\n .style('pointer-events', 'none')\n .node() as SVGCircleElement;\n\n group\n .append('circle')\n .attr('class', 'node-border-white')\n .attr('r', radius)\n .attr('fill', 'none')\n .attr('stroke', NODE_COLORS.innerBorder)\n .attr('stroke-width', NODE_BORDER_WIDTH)\n .style('pointer-events', 'none');\n\n if (hasImage) {\n group\n .append('image')\n .attr('class', 'node-image')\n .attr('href', datum.img as string)\n .attr('width', radius * 2)\n .attr('height', radius * 2)\n .attr('x', -radius)\n .attr('y', -radius)\n .attr('clip-path', `circle(${radius}px at ${radius}px ${radius}px)`)\n .attr('preserveAspectRatio', 'xMidYMid slice')\n .style('pointer-events', 'none');\n }\n\n group\n .append('circle')\n .attr('class', 'node-border-purple')\n .attr('r', radius + NODE_BORDER_WIDTH)\n .attr('fill', 'none')\n .attr('stroke', NODE_COLORS.outerBorder)\n .attr('stroke-width', NODE_BORDER_WIDTH)\n .style('pointer-events', 'none');\n\n return {\n datum,\n group: group.node() as SVGGElement,\n background,\n grab,\n label: label.group,\n labelBg: label.background,\n labelTitle: label.title,\n labelValue: label.value,\n radius,\n hasImage,\n alwaysLabelled: radius > LABEL_ALWAYS_ABOVE,\n };\n });\n}\n\nfunction appendNodeLabel(\n group: Selection<SVGGElement, SimNode, null, undefined>,\n datum: SimNode,\n radius: number,\n): {\n group: SVGGElement | null;\n background: SVGRectElement | null;\n title: SVGTextElement | null;\n value: SVGTextElement | null;\n} {\n const valueText = formatNodeValue(datum.value);\n if (!datum.label && !valueText) {\n return { group: null, background: null, title: null, value: null };\n }\n\n const labelGroup = group\n .append('g')\n .attr('class', 'node-label')\n .attr('transform', 'translate(1, 0)')\n .style('opacity', 0)\n .style('pointer-events', 'none');\n\n const background = labelGroup\n .append('rect')\n .attr('class', 'node-label-background')\n .attr('rx', 4)\n .attr('ry', 4)\n .attr('fill', NODE_LABEL_COLORS.background);\n\n const title = labelGroup\n .append('text')\n .attr('class', 'node-label-title')\n .text(datum.label)\n .attr('font-size', LABEL_TITLE_SIZE)\n .attr('font-weight', '400')\n .attr('fill', NODE_LABEL_COLORS.text)\n .attr('text-anchor', 'middle')\n .attr('dominant-baseline', 'hanging');\n\n const value = labelGroup\n .append('text')\n .attr('class', 'node-label-value')\n .text(valueText)\n .attr('font-size', LABEL_VALUE_SIZE)\n .attr('fill', NODE_LABEL_COLORS.text)\n .attr('text-anchor', 'middle')\n .attr('dominant-baseline', 'hanging');\n\n const paddingX = radius + LABEL_PADDING_GAP;\n const titleBox = measure(title.node());\n const valueBox = measure(value.node());\n const contentWidth = Math.max(titleBox?.width ?? 0, valueBox?.width ?? 0);\n const boxWidth = paddingX * 2 + contentWidth;\n const boxHeight =\n LABEL_PADDING_TOP + LABEL_TITLE_LINE + LABEL_VALUE_LINE + LABEL_PADDING_BOTTOM;\n const centerX = paddingX + contentWidth / 2;\n\n background\n .attr('x', 0)\n .attr('y', -boxHeight / 2)\n .attr('width', boxWidth)\n .attr('height', boxHeight);\n title.attr('x', centerX).attr('y', -boxHeight / 2 + LABEL_PADDING_TOP);\n value\n .attr('x', centerX)\n .attr('y', -boxHeight / 2 + LABEL_PADDING_TOP + LABEL_TITLE_LINE);\n\n return {\n group: labelGroup.node(),\n background: background.node(),\n title: title.node(),\n value: value.node(),\n };\n}\n\nfunction attachNodeInteractions(params: {\n nodes: NodeParts[];\n simulation: Simulation<SimNode, SimLink>;\n /** Read per event — the frame changes on resize without a rebuild. */\n getBounds: () => { width: number; height: number };\n radiusById: Map<string, number>;\n onHover: (id: string | null) => void;\n onClick: (id: string) => void;\n /** Re-pin isolated / hidden nodes after a drag so they keep their zone. */\n onDragEnd: () => void;\n}): void {\n const { nodes, simulation, getBounds, radiusById, onHover, onClick, onDragEnd } = params;\n let moved = false;\n let start: { x: number; y: number } | null = null;\n\n const behaviour = drag<SVGGElement, SimNode>()\n .filter((event: MouseEvent) => event.button === 0)\n .on('start', (event: D3DragEvent<SVGGElement, SimNode, unknown>, d) => {\n event.sourceEvent.stopPropagation();\n start = { x: event.sourceEvent.clientX, y: event.sourceEvent.clientY };\n moved = false;\n if (!event.active) simulation.alphaTarget(0.3).restart();\n const { width, height } = getBounds();\n d.fx = d.x ?? width / 2;\n d.fy = d.y ?? height / 2;\n })\n .on('drag', (event: D3DragEvent<SVGGElement, SimNode, unknown>, d) => {\n if (start) {\n const dx = event.sourceEvent.clientX - start.x;\n const dy = event.sourceEvent.clientY - start.y;\n if (Math.sqrt(dx * dx + dy * dy) > DRAG_SLOP) moved = true;\n }\n const { width, height } = getBounds();\n const radius = radiusById.get(d.id) ?? 0;\n d.fx = clamp((d.fx ?? d.x ?? 0) + event.dx, radius, width - radius);\n d.fy = clamp((d.fy ?? d.y ?? 0) + event.dy, radius, height - radius);\n })\n .on('end', (event: D3DragEvent<SVGGElement, SimNode, unknown>, d) => {\n if (!event.active) simulation.alphaTarget(0);\n d.x = d.fx ?? d.x;\n d.y = d.fy ?? d.y;\n onDragEnd();\n start = null;\n });\n\n for (const part of nodes) {\n const group = select<SVGGElement, SimNode>(part.group);\n group.call(behaviour);\n\n const grab = group.select<SVGCircleElement>('.node-drag-area');\n grab.on('click', (event: MouseEvent) => {\n event.stopPropagation();\n if (moved) {\n moved = false;\n return;\n }\n onClick(part.datum.id);\n });\n grab.on('mouseenter', () => onHover(part.datum.id));\n grab.on('mouseleave', () => onHover(null));\n }\n}\n"],"names":["_a"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../src/components/pie-chart/element/styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../src/components/pie-chart/element/styles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,yBA4E1B,CAAC"}
|
|
@@ -10,16 +10,49 @@ const pieChartStyles = css`
|
|
|
10
10
|
font-family: var(--ui9000-font-family, system-ui, sans-serif);
|
|
11
11
|
box-sizing: border-box;
|
|
12
12
|
}
|
|
13
|
+
/* Chart left, unit legend as a vertical list on the right (client Pie/Donut). */
|
|
14
|
+
:host .widget-body {
|
|
15
|
+
flex-direction: row;
|
|
16
|
+
align-items: center;
|
|
17
|
+
gap: 32px;
|
|
18
|
+
}
|
|
19
|
+
:host .widget-body > .chart-root {
|
|
20
|
+
flex: 1 1 auto;
|
|
21
|
+
min-width: 0;
|
|
22
|
+
min-height: 0;
|
|
23
|
+
width: auto;
|
|
24
|
+
height: 100%;
|
|
25
|
+
}
|
|
13
26
|
.chart-root {
|
|
14
27
|
flex: 1;
|
|
15
28
|
min-height: 0;
|
|
16
29
|
width: 100%;
|
|
17
30
|
height: 100%;
|
|
18
|
-
min-height: inherit;
|
|
19
31
|
display: flex;
|
|
20
32
|
align-items: center;
|
|
21
33
|
justify-content: center;
|
|
22
34
|
}
|
|
35
|
+
:host .chart-legend {
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
flex-wrap: nowrap;
|
|
39
|
+
align-items: flex-start;
|
|
40
|
+
align-self: center;
|
|
41
|
+
flex-shrink: 0;
|
|
42
|
+
gap: 6px;
|
|
43
|
+
margin-bottom: 0;
|
|
44
|
+
max-width: 40%;
|
|
45
|
+
max-height: 100%;
|
|
46
|
+
overflow-x: hidden;
|
|
47
|
+
overflow-y: auto;
|
|
48
|
+
}
|
|
49
|
+
:host .legend-item {
|
|
50
|
+
white-space: nowrap;
|
|
51
|
+
}
|
|
52
|
+
:host .legend-label {
|
|
53
|
+
overflow: hidden;
|
|
54
|
+
text-overflow: ellipsis;
|
|
55
|
+
}
|
|
23
56
|
.tooltip {
|
|
24
57
|
position: absolute;
|
|
25
58
|
pointer-events: none;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.js","sources":["../../../../src/components/pie-chart/element/styles.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const pieChartStyles = css`\n :host {\n display: block;\n position: relative;\n width: 100%;\n height: 100%;\n min-height: 220px;\n color: var(--ui9000-color-text, #111827);\n font-family: var(--ui9000-font-family, system-ui, sans-serif);\n box-sizing: border-box;\n }\n .chart-root {\n flex: 1;\n min-height: 0;\n width:
|
|
1
|
+
{"version":3,"file":"styles.js","sources":["../../../../src/components/pie-chart/element/styles.ts"],"sourcesContent":["import { css } from 'lit';\n\nexport const pieChartStyles = css`\n :host {\n display: block;\n position: relative;\n width: 100%;\n height: 100%;\n min-height: 220px;\n color: var(--ui9000-color-text, #111827);\n font-family: var(--ui9000-font-family, system-ui, sans-serif);\n box-sizing: border-box;\n }\n /* Chart left, unit legend as a vertical list on the right (client Pie/Donut). */\n :host .widget-body {\n flex-direction: row;\n align-items: center;\n gap: 32px;\n }\n :host .widget-body > .chart-root {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n width: auto;\n height: 100%;\n }\n .chart-root {\n flex: 1;\n min-height: 0;\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n :host .chart-legend {\n display: flex;\n flex-direction: column;\n flex-wrap: nowrap;\n align-items: flex-start;\n align-self: center;\n flex-shrink: 0;\n gap: 6px;\n margin-bottom: 0;\n max-width: 40%;\n max-height: 100%;\n overflow-x: hidden;\n overflow-y: auto;\n }\n :host .legend-item {\n white-space: nowrap;\n }\n :host .legend-label {\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .tooltip {\n position: absolute;\n pointer-events: none;\n z-index: 2;\n padding: 6px 10px;\n border-radius: 6px;\n font-size: 12px;\n line-height: 1.35;\n background: var(--ui9000-color-text, #111827);\n color: #fff;\n box-shadow: 0 4px 12px rgb(0 0 0 / 18%);\n transform: translate(-50%, calc(-100% - 10px));\n white-space: nowrap;\n }\n .empty {\n display: grid;\n place-items: center;\n height: 100%;\n min-height: inherit;\n color: var(--ui9000-color-text-muted, #6b7280);\n font-size: 0.875rem;\n }\n`;\n"],"names":[],"mappings":";AAEO,MAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;"}
|
|
@@ -4,8 +4,6 @@ import { WidgetScale } from '../../../types/index.js';
|
|
|
4
4
|
export declare class Ui9000PieChart extends Ui9000ChartElement {
|
|
5
5
|
static styles: import('lit').CSSResult[];
|
|
6
6
|
scale: WidgetScale;
|
|
7
|
-
/** Ratio of outer radius (0 = full pie, 0.5 = donut). */
|
|
8
|
-
innerRadiusRatio: number;
|
|
9
7
|
showLegend: boolean;
|
|
10
8
|
showTooltip: boolean;
|
|
11
9
|
private _tooltip;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui9000-pie-chart.d.ts","sourceRoot":"","sources":["../../../../src/components/pie-chart/element/ui9000-pie-chart.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAGzD,OAAO,EAAoB,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAG7F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAe3D,qBACa,cAAe,SAAQ,kBAAkB;IACpD,OAAgB,MAAM,4BAAsC;IAG5D,KAAK,EAAE,WAAW,CAAa;
|
|
1
|
+
{"version":3,"file":"ui9000-pie-chart.d.ts","sourceRoot":"","sources":["../../../../src/components/pie-chart/element/ui9000-pie-chart.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAGzD,OAAO,EAAoB,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAG7F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAe3D,qBACa,cAAe,SAAQ,kBAAkB;IACpD,OAAgB,MAAM,4BAAsC;IAG5D,KAAK,EAAE,WAAW,CAAa;IAG/B,UAAU,UAAQ;IAGlB,WAAW,UAAQ;IAGnB,OAAO,CAAC,QAAQ,CAKA;IAGhB,OAAO,CAAC,MAAM,CAAS;IAGvB,OAAO,CAAC,aAAa,CAAkB;IAEvC,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC,OAAO,CAAC,IAAI,CAAK;IAER,iBAAiB,IAAI,IAAI;IAMzB,oBAAoB,IAAI,IAAI;IAM5B,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAI/C,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,IAAI;IAgDH,MAAM;CA0BhB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,cAAc,CAAC;KACpC;CACF;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAIvC"}
|