diff --git a/docs/CustomNodesLinks/Pages/_Host.cshtml b/docs/CustomNodesLinks/Pages/_Host.cshtml
index 926eaa7de..fb4f1ccd2 100644
--- a/docs/CustomNodesLinks/Pages/_Host.cshtml
+++ b/docs/CustomNodesLinks/Pages/_Host.cshtml
@@ -23,8 +23,6 @@
-
-
diff --git a/samples/Wasm/wwwroot/index.html b/samples/Wasm/wwwroot/index.html
index 9b010e3c2..9ada6e1e8 100644
--- a/samples/Wasm/wwwroot/index.html
+++ b/samples/Wasm/wwwroot/index.html
@@ -85,7 +85,6 @@
-
diff --git a/site/Site/Pages/Documentation/GettingStarted/Installation.razor b/site/Site/Pages/Documentation/GettingStarted/Installation.razor
index d4993daf1..36d10fecf 100644
--- a/site/Site/Pages/Documentation/GettingStarted/Installation.razor
+++ b/site/Site/Pages/Documentation/GettingStarted/Installation.razor
@@ -42,7 +42,6 @@ Install-Package Z.Blazor.Diagrams
<body>
<!-- ... -->
- <script src="_content/Z.Blazor.Diagrams/script.min.js"></script>
</body>
</html>
diff --git a/site/Site/wwwroot/index.html b/site/Site/wwwroot/index.html
index 284a46451..149a7f6dc 100644
--- a/site/Site/wwwroot/index.html
+++ b/site/Site/wwwroot/index.html
@@ -79,7 +79,6 @@
});
-
diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs
index 8c723e043..4234a13a9 100644
--- a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs
+++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Extensions;
@@ -8,7 +9,7 @@
namespace Blazor.Diagrams.Components;
-public partial class DiagramCanvas : IDisposable
+public partial class DiagramCanvas : IAsyncDisposable
{
private DotNetObjectReference? _reference;
private bool _shouldRender;
@@ -27,15 +28,22 @@ public partial class DiagramCanvas : IDisposable
[Inject] public IJSRuntime JSRuntime { get; set; } = null!;
- public void Dispose()
+ private IJSObjectReference? _module;
+
+ public async ValueTask DisposeAsync()
{
BlazorDiagram.Changed -= OnDiagramChanged;
if (_reference == null)
+ {
return;
-
- if (elementReference.Id != null)
- _ = JSRuntime.UnobserveResizes(elementReference);
+ }
+ if (elementReference.Id != null && _module is not null)
+ await _module.UnobserveResizes(elementReference);
+ if(_module is not null)
+ {
+ await _module.DisposeAsync();
+ }
_reference.Dispose();
}
@@ -60,8 +68,10 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
if (firstRender)
{
- BlazorDiagram.SetContainer(await JSRuntime.GetBoundingClientRect(elementReference));
- await JSRuntime.ObserveResizes(elementReference, _reference!);
+ _module ??= await JSRuntime.InvokeAsync("import","./_content/Z.Blazor.Diagrams/script.min.js");
+ BlazorDiagram.SetContainer(await _module.GetBoundingClientRect(elementReference));
+
+ await _module.ObserveResizes(elementReference, _reference!);
}
}
diff --git a/src/Blazor.Diagrams/Components/DiagramCanvas.razor.js b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.js
new file mode 100644
index 000000000..e131b912e
--- /dev/null
+++ b/src/Blazor.Diagrams/Components/DiagramCanvas.razor.js
@@ -0,0 +1,65 @@
+let boundListener = {};
+let isObservingResize = false;
+
+const mo = new MutationObserver(() => {
+ for (const id in boundListener) {
+ const canvas = boundListener[id];
+ const lastBounds = canvas.lastBounds;
+ const bounds = canvas.elem.getBoundingClientRect();
+ if (lastBounds.left !== bounds.left ||
+ lastBounds.top !== bounds.top ||
+ lastBounds.width !== bounds.width ||
+ lastBounds.height !== bounds.height) {
+ updateBounds(canvas);
+ }
+ }
+})
+
+const ro = new ResizeObserver(entries => {
+ for (const entry of entries) {
+ let id = Array.from(entry.target.attributes).find(e => e.name.startsWith('_bl')).name.substring(4);
+ let element = boundListener[id];
+ if (element) {
+ updateBounds(element);
+ }
+ }
+})
+
+export function getBoundingClientRect(el) {
+ return el.getBoundingClientRect();
+}
+
+function updateBounds(canvas) {
+
+ canvas.lastBounds = canvas.elem.getBoundingClientRect();
+ canvas.ref.invokeMethodAsync('OnResize', canvas.lastBounds)
+}
+
+export function observe(element, ref, id) {
+ if (isObservingResize === false) {
+ mo.observe(document.body, { childList: true, subtree: true });
+ window.addEventListener('scroll', () => {
+ for (id in boundListener) {
+ const canvas = boundListener[id];
+ updateBounds(canvas)
+ }
+ })
+
+ isObservingResize = true;
+ }
+
+ if (!element) return;
+ ro.observe(element);
+ boundListener[id] = {
+ elem: element,
+ ref: ref,
+ lastBounds: element.getBoundingClientRect()
+ };
+}
+
+export function unobserve(element, id) {
+ if (element) {
+ ro.unobserve(element);
+ }
+ delete boundListener[id];
+}
\ No newline at end of file
diff --git a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs
index 2dbe58d02..153f38d78 100644
--- a/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs
+++ b/src/Blazor.Diagrams/Components/Renderers/NodeRenderer.cs
@@ -1,4 +1,5 @@
using System;
+using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Blazor.Diagrams.Core.Extensions;
@@ -14,13 +15,14 @@
namespace Blazor.Diagrams.Components.Renderers;
-public class NodeRenderer : ComponentBase, IDisposable
+public class NodeRenderer : ComponentBase
{
private bool _becameVisible;
private ElementReference _element;
private bool _isSvg;
private DotNetObjectReference? _reference;
private bool _shouldRender;
+ private IJSObjectReference? _module;
[CascadingParameter] public BlazorDiagram BlazorDiagram { get; set; } = null!;
@@ -28,14 +30,18 @@ public class NodeRenderer : ComponentBase, IDisposable
[Inject] private IJSRuntime JsRuntime { get; set; } = null!;
- public void Dispose()
+ public async ValueTask DisposeAsync()
{
Node.Changed -= OnNodeChanged;
Node.VisibilityChanged -= OnVisibilityChanged;
+
if (_element.Id != null && !Node.ControlledSize)
{
- _ = JsRuntime.UnobserveResizes(_element);
+ if(_module is not null)
+ {
+ await _module.UnobserveResizes(_element);
+ }
}
_reference?.Dispose();
@@ -61,6 +67,8 @@ public void OnResize(Size size)
Node.ReinitializePorts();
}
+
+
protected override void OnInitialized()
{
base.OnInitialized();
@@ -129,13 +137,18 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
protected override async Task OnAfterRenderAsync(bool firstRender)
{
+ if (firstRender)
+ {
+ _module ??= await JsRuntime.InvokeAsync("import", "./_content/Z.Blazor.Diagrams/script.min.js");
+ }
+
if (firstRender || _becameVisible)
{
_becameVisible = false;
- if (!Node.ControlledSize)
+ if (!Node.ControlledSize && _module is not null)
{
- await JsRuntime.ObserveResizes(_element, _reference!);
+ await _module.ObserveResizes(_element, _reference!);
}
}
}
diff --git a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.cs b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.cs
index bbd4786f1..1687fca06 100644
--- a/src/Blazor.Diagrams/Components/Renderers/PortRenderer.cs
+++ b/src/Blazor.Diagrams/Components/Renderers/PortRenderer.cs
@@ -1,11 +1,9 @@
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using Blazor.Diagrams.Core.Geometry;
+using Blazor.Diagrams.Core.Geometry;
using Blazor.Diagrams.Core.Models;
using Blazor.Diagrams.Core.Models.Base;
using Blazor.Diagrams.Extensions;
using Blazor.Diagrams.Models;
+
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.Web;
@@ -13,13 +11,14 @@
namespace Blazor.Diagrams.Components.Renderers;
-public class PortRenderer : ComponentBase, IDisposable
+public class PortRenderer : ComponentBase, IAsyncDisposable
{
private ElementReference _element;
private bool _isParentSvg;
private bool _shouldRefreshPort;
private bool _shouldRender = true;
private bool _updatingDimensions;
+ private IJSObjectReference? _module;
[CascadingParameter] public BlazorDiagram BlazorDiagram { get; set; } = null!;
[Inject] private IJSRuntime JSRuntime { get; set; } = null!;
@@ -28,8 +27,12 @@ public class PortRenderer : ComponentBase, IDisposable
[Parameter] public string? Style { get; set; }
[Parameter] public RenderFragment? ChildContent { get; set; }
- public void Dispose()
+ public async ValueTask DisposeAsync()
{
+ if (_module != null)
+ {
+ await _module.DisposeAsync();
+ }
Port.Changed -= OnPortChanged;
Port.VisibilityChanged -= OnPortChanged;
}
@@ -62,7 +65,7 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (!Port.Visible)
return;
-
+
builder.OpenElement(0, _isParentSvg ? "g" : "div");
builder.AddAttribute(1, "style", Style);
builder.AddAttribute(2, "class",
@@ -80,6 +83,10 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
protected override async Task OnAfterRenderAsync(bool firstRender)
{
+ if (firstRender)
+ {
+ _module ??= await JSRuntime.InvokeAsync("import", "./_content/Z.Blazor.Diagrams/script.min.js");
+ }
if (!Port.Initialized)
{
await UpdateDimensions();
@@ -123,12 +130,15 @@ private async Task UpdateDimensions()
_updatingDimensions = true;
var zoom = BlazorDiagram.Zoom;
var pan = BlazorDiagram.Pan;
- var rect = await JSRuntime.GetBoundingClientRect(_element);
+ _module ??= await JSRuntime.InvokeAsync("import", "./_content/Z.Blazor.Diagrams/script.min.js");
+
+ var rect = await _module.GetBoundingClientRect(_element);
Port.Size = new Size(rect.Width / zoom, rect.Height / zoom);
Port.Position = new Point((rect.Left - BlazorDiagram.Container.Left - pan.X) / zoom,
(rect.Top - BlazorDiagram.Container.Top - pan.Y) / zoom);
+
Port.Initialized = true;
_updatingDimensions = false;
diff --git a/src/Blazor.Diagrams/Extensions/JSObjectReferenceExtensions.cs b/src/Blazor.Diagrams/Extensions/JSObjectReferenceExtensions.cs
new file mode 100644
index 000000000..72b08d94d
--- /dev/null
+++ b/src/Blazor.Diagrams/Extensions/JSObjectReferenceExtensions.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Threading.Tasks;
+
+using Blazor.Diagrams.Core.Geometry;
+
+using Microsoft.AspNetCore.Components;
+using Microsoft.JSInterop;
+
+namespace Blazor.Diagrams.Extensions;
+
+public static class JSObjectReferenceExtensions
+{
+ public static async Task GetBoundingClientRect(this IJSObjectReference jsRuntime, ElementReference element)
+ {
+ return await jsRuntime.InvokeAsync("getBoundingClientRect", element);
+ }
+
+ public static async Task ObserveResizes(this IJSObjectReference jsRuntime, ElementReference element,
+ DotNetObjectReference reference) where T : class
+ {
+ try
+ {
+ await jsRuntime.InvokeVoidAsync("observe", element, reference, element.Id);
+ }
+ catch (ObjectDisposedException)
+ {
+ // Ignore, DotNetObjectReference was likely disposed
+ }
+ }
+
+ public static async Task UnobserveResizes(this IJSObjectReference jsRuntime, ElementReference element)
+ {
+ await jsRuntime.InvokeVoidAsync("unobserve", element, element.Id);
+ }
+}
+
diff --git a/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs b/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs
index 459456863..1d7daac49 100644
--- a/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs
+++ b/src/Blazor.Diagrams/Extensions/JSRuntimeExtensions.cs
@@ -8,11 +8,6 @@ namespace Blazor.Diagrams.Extensions;
public static class JSRuntimeExtensions
{
- public static async Task GetBoundingClientRect(this IJSRuntime jsRuntime, ElementReference element)
- {
- return await jsRuntime.InvokeAsync("ZBlazorDiagrams.getBoundingClientRect", element);
- }
-
public static async Task ObserveResizes(this IJSRuntime jsRuntime, ElementReference element,
DotNetObjectReference reference) where T : class
{
diff --git a/src/Blazor.Diagrams/wwwroot/script.js b/src/Blazor.Diagrams/wwwroot/script.js
index be3b4728c..e131b912e 100644
--- a/src/Blazor.Diagrams/wwwroot/script.js
+++ b/src/Blazor.Diagrams/wwwroot/script.js
@@ -1,58 +1,65 @@
-var s = {
- canvases: {},
- tracked: {},
- getBoundingClientRect: el => {
- return el.getBoundingClientRect();
- },
- mo: new MutationObserver(() => {
- for (id in s.canvases) {
- const canvas = s.canvases[id];
- const lastBounds = canvas.lastBounds;
- const bounds = canvas.elem.getBoundingClientRect();
- if (lastBounds.left !== bounds.left || lastBounds.top !== bounds.top || lastBounds.width !== bounds.width ||
- lastBounds.height !== bounds.height) {
- canvas.lastBounds = bounds;
- canvas.ref.invokeMethodAsync('OnResize', bounds);
- }
- }
- }),
- ro: new ResizeObserver(entries => {
- for (const entry of entries) {
- let id = Array.from(entry.target.attributes).find(e => e.name.startsWith('_bl')).name.substring(4);
- let element = s.tracked[id];
- if (element) {
- element.ref.invokeMethodAsync('OnResize', entry.target.getBoundingClientRect());
- }
- }
- }),
- observe: (element, ref, id) => {
- if (!element) return;
- s.ro.observe(element);
- s.tracked[id] = {
- ref: ref
- };
- if (element.classList.contains("diagram-canvas")) {
- s.canvases[id] = {
- elem: element,
- ref: ref,
- lastBounds: element.getBoundingClientRect()
- };
+let boundListener = {};
+let isObservingResize = false;
+
+const mo = new MutationObserver(() => {
+ for (const id in boundListener) {
+ const canvas = boundListener[id];
+ const lastBounds = canvas.lastBounds;
+ const bounds = canvas.elem.getBoundingClientRect();
+ if (lastBounds.left !== bounds.left ||
+ lastBounds.top !== bounds.top ||
+ lastBounds.width !== bounds.width ||
+ lastBounds.height !== bounds.height) {
+ updateBounds(canvas);
}
- },
- unobserve: (element, id) => {
+ }
+})
+
+const ro = new ResizeObserver(entries => {
+ for (const entry of entries) {
+ let id = Array.from(entry.target.attributes).find(e => e.name.startsWith('_bl')).name.substring(4);
+ let element = boundListener[id];
if (element) {
- s.ro.unobserve(element);
+ updateBounds(element);
}
- delete s.tracked[id];
- delete s.canvases[id];
}
-};
-window.ZBlazorDiagrams = s;
-window.addEventListener('scroll', () => {
- for (id in s.canvases) {
- const canvas = s.canvases[id];
- canvas.lastBounds = canvas.elem.getBoundingClientRect();
- canvas.ref.invokeMethodAsync('OnResize', canvas.lastBounds);
+})
+
+export function getBoundingClientRect(el) {
+ return el.getBoundingClientRect();
+}
+
+function updateBounds(canvas) {
+
+ canvas.lastBounds = canvas.elem.getBoundingClientRect();
+ canvas.ref.invokeMethodAsync('OnResize', canvas.lastBounds)
+}
+
+export function observe(element, ref, id) {
+ if (isObservingResize === false) {
+ mo.observe(document.body, { childList: true, subtree: true });
+ window.addEventListener('scroll', () => {
+ for (id in boundListener) {
+ const canvas = boundListener[id];
+ updateBounds(canvas)
+ }
+ })
+
+ isObservingResize = true;
+ }
+
+ if (!element) return;
+ ro.observe(element);
+ boundListener[id] = {
+ elem: element,
+ ref: ref,
+ lastBounds: element.getBoundingClientRect()
+ };
+}
+
+export function unobserve(element, id) {
+ if (element) {
+ ro.unobserve(element);
}
-});
-s.mo.observe(document.body, {childList: true, subtree: true});
\ No newline at end of file
+ delete boundListener[id];
+}
\ No newline at end of file
diff --git a/src/Blazor.Diagrams/wwwroot/script.min.js b/src/Blazor.Diagrams/wwwroot/script.min.js
index a4065277c..86cd3dd4b 100644
--- a/src/Blazor.Diagrams/wwwroot/script.min.js
+++ b/src/Blazor.Diagrams/wwwroot/script.min.js
@@ -1 +1 @@
-var s={canvases:{},tracked:{},getBoundingClientRect:n=>n.getBoundingClientRect(),mo:new MutationObserver(()=>{for(id in s.canvases){const t=s.canvases[id],i=t.lastBounds,n=t.elem.getBoundingClientRect();(i.left!==n.left||i.top!==n.top||i.width!==n.width||i.height!==n.height)&&(t.lastBounds=n,t.ref.invokeMethodAsync("OnResize",n))}}),ro:new ResizeObserver(n=>{for(const t of n){let i=Array.from(t.target.attributes).find(n=>n.name.startsWith("_bl")).name.substring(4),n=s.tracked[i];n&&n.ref.invokeMethodAsync("OnResize",t.target.getBoundingClientRect())}}),observe:(n,t,i)=>{n&&(s.ro.observe(n),s.tracked[i]={ref:t},n.classList.contains("diagram-canvas")&&(s.canvases[i]={elem:n,ref:t,lastBounds:n.getBoundingClientRect()}))},unobserve:(n,t)=>{n&&s.ro.unobserve(n),delete s.tracked[t],delete s.canvases[t]}};window.ZBlazorDiagrams=s;window.addEventListener("scroll",()=>{for(id in s.canvases){const n=s.canvases[id];n.lastBounds=n.elem.getBoundingClientRect();n.ref.invokeMethodAsync("OnResize",n.lastBounds)}});s.mo.observe(document.body,{childList:!0,subtree:!0});
\ No newline at end of file
+let boundListener={},isObservingResize=!1;const mo=new MutationObserver(()=>{for(const id in boundListener){const canvas=boundListener[id],lastBounds=canvas.lastBounds,bounds=canvas.elem.getBoundingClientRect();lastBounds.left!==bounds.left||lastBounds.top!==bounds.top||lastBounds.width!==bounds.width||lastBounds.height!==bounds.height&&updateBounds(canvas)}}}),ro=new ResizeObserver(entries=>{for(const entry of entries){let id=Array.from(entry.target.attributes).find(e=>"name"===e.name.substring(0,4)).name.substring(4),element=boundListener[id];element&&updateBounds(element)}});export function getBoundingClientRect(el){return el.getBoundingClientRect()}function updateBounds(canvas){canvas.lastBounds=canvas.elem.getBoundingClientRect(),canvas.ref.invokeMethodAsync("OnResize",canvas.lastBounds)}export function observe(element,ref,id){if(!1===isObservingResize&&(mo.observe(document.body,{childList:!0,subtree:!0}),window.addEventListener("scroll",()=>{for(id in boundListener){const canvas=boundListener[id];updateBounds(canvas)}}),isObservingResize=!0),element){ro.observe(element);const lastBounds=element.getBoundingClientRect();boundListener[id]={elem:element,ref:ref,lastBounds}}export function unobserve(element,id){element&&ro.unobserve(element),delete boundListener[id]}
diff --git a/src/Blazor.Diagrams/wwwroot/script.min.js.gz b/src/Blazor.Diagrams/wwwroot/script.min.js.gz
deleted file mode 100644
index a4db96478..000000000
Binary files a/src/Blazor.Diagrams/wwwroot/script.min.js.gz and /dev/null differ