diff --git a/README.md b/README.md
index 2ba6304..d4440e8 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,8 @@ A security-first Blazor iframe component with automatic resizing, cross-frame me
- **Navigation Tracking** - Capture iframe navigation events with URL and query parameters
- **Sandbox Support** - Multiple security levels from permissive to paranoid isolation
- **Environment-Aware** - Different configurations for development vs production
-- **Automatic Resizing** - Smart height adjustment based on iframe content
+- **Automatic Resizing** - Smart height adjustment based on iframe content with configurable options
+- **Programmatic Reload** - Refresh iframe content without recreating the entire component
## Documentation
@@ -125,6 +126,76 @@ var paymentOptions = new MessageSecurityOptions()
.ForPaymentWidget();
```
+### Auto-Resize Configuration
+
+Control how the iframe automatically adjusts its height based on content:
+
+```razor
+
+
+@code {
+ // Custom resize configuration
+ private readonly ResizeOptions resizeOptions = new()
+ {
+ MinHeight = 200,
+ MaxHeight = 2000,
+ PollingInterval = 500,
+ DebounceMs = 100,
+ UseResizeObserver = true
+ };
+
+ // Or use built-in presets:
+ // ResizeOptions.Default - Balanced defaults
+ // ResizeOptions.Performance - Less frequent updates (better performance)
+ // ResizeOptions.Responsive - More frequent updates (smoother resizing)
+}
+```
+
+| Property | Default | Description |
+|----------|---------|-------------|
+| `MinHeight` | 100 | Minimum height in pixels |
+| `MaxHeight` | 50000 | Maximum height in pixels |
+| `PollingInterval` | 500 | Fallback polling interval (ms) when ResizeObserver unavailable |
+| `DebounceMs` | 100 | Debounce delay to prevent excessive updates (0 to disable) |
+| `UseResizeObserver` | true | Use ResizeObserver API when available |
+
+### Programmatic Reload
+
+Refresh iframe content without recreating the entire component - useful for PDFs, dynamic content, or cache-busting:
+
+```razor
+
+
+
+
+
+@code {
+ private BlazorFrame? iframeRef;
+ private string pdfUrl = "https://example.com/document.pdf";
+
+ // Reload the current content
+ private async Task RefreshContent()
+ {
+ if (iframeRef != null)
+ {
+ await iframeRef.ReloadAsync();
+ }
+ }
+
+ // Load a new URL with cache-busting
+ private async Task LoadNewDocument()
+ {
+ if (iframeRef != null)
+ {
+ var cacheBustedUrl = $"https://example.com/document.pdf?v={DateTime.UtcNow.Ticks}";
+ await iframeRef.ReloadAsync(cacheBustedUrl);
+ }
+ }
+}
+```
+
### Content Security Policy
```razor
@@ -168,13 +239,56 @@ All iframe messages are automatically validated for:
### Sandbox Security Levels
-| Level | Description | Use Case |
+| Level | Permissions | Use Case |
|-------|-------------|----------|
| **None** | No restrictions | Trusted content only |
| **Basic** | Scripts + same-origin | Most trusted widgets |
-| **Permissive** | + forms + popups | Interactive widgets |
-| **Strict** | Scripts + same-origin only | Display widgets |
-| **Paranoid** | Scripts only | Untrusted content |
+| **Permissive** | Scripts + same-origin + forms + popups | Interactive widgets |
+| **Strict** | Scripts only (no same-origin) | Semi-trusted content |
+| **Paranoid** | Empty sandbox (no permissions) | Untrusted content |
+
+## API Reference
+
+### Component Parameters
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `Src` | `string` | `""` | The URL to load in the iframe |
+| `Width` | `string` | `"100%"` | Width of the iframe |
+| `Height` | `string` | `"600px"` | Height of the iframe |
+| `EnableAutoResize` | `bool` | `true` | Enable automatic height adjustment |
+| `ResizeOptions` | `ResizeOptions?` | `null` | Configuration for auto-resize behavior |
+| `EnableScroll` | `bool` | `false` | Enable scrolling on the wrapper |
+| `EnableNavigationTracking` | `bool` | `false` | Track iframe navigation events |
+| `AllowedOrigins` | `List?` | `null` | Allowed origins for postMessage |
+| `SecurityOptions` | `MessageSecurityOptions` | `new()` | Security configuration |
+| `CspOptions` | `CspOptions?` | `null` | Content Security Policy configuration |
+
+### Component Methods
+
+| Method | Returns | Description |
+|--------|---------|-------------|
+| `ReloadAsync()` | `Task` | Reloads the iframe content |
+| `ReloadAsync(string newSrc)` | `Task` | Reloads with a new source URL |
+| `SendMessageAsync(object data, string? targetOrigin)` | `Task` | Sends a message to the iframe |
+| `SendTypedMessageAsync(string type, object? data, string? targetOrigin)` | `Task` | Sends a typed message |
+| `GetRecommendedCspHeader()` | `CspHeader?` | Gets the recommended CSP header |
+| `ValidateCspConfiguration()` | `CspValidationResult?` | Validates the CSP configuration |
+
+### Events
+
+| Event | Type | Description |
+|-------|------|-------------|
+| `OnLoad` | `EventCallback` | Fired when iframe loads |
+| `OnMessage` | `EventCallback` | Fired on message (raw JSON) |
+| `OnValidatedMessage` | `EventCallback` | Fired on validated message |
+| `OnSecurityViolation` | `EventCallback` | Fired on security violation |
+| `OnNavigation` | `EventCallback` | Fired on navigation |
+| `OnUrlChanged` | `EventCallback` | Fired on URL change |
+| `OnMessageSent` | `EventCallback` | Fired when message sent |
+| `OnMessageSendFailed` | `EventCallback` | Fired on send failure |
+| `OnCspHeaderGenerated` | `EventCallback` | Fired when CSP generated |
+| `OnInitializationError` | `EventCallback` | Fired on init failure |
## Demo
diff --git a/docs/configuration/display-options.md b/docs/configuration/display-options.md
index eded925..a8da9ad 100644
--- a/docs/configuration/display-options.md
+++ b/docs/configuration/display-options.md
@@ -100,43 +100,63 @@ This guide covers all aspects of configuring how BlazorFrame appears and behaves
@code {
private readonly ResizeOptions basicResizeOptions = new()
{
- DebounceDelayMs = 100, // Wait 100ms before resizing
- UseResizeObserver = true, // Use modern ResizeObserver API
- MaxHeight = 1000, // Maximum height in pixels
- MinHeight = 200 // Minimum height in pixels
+ MinHeight = 200, // Minimum height in pixels
+ MaxHeight = 1000, // Maximum height in pixels
+ DebounceMs = 100, // Wait 100ms before resizing
+ UseResizeObserver = true // Use modern ResizeObserver API
};
}
```
+### ResizeOptions Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `MinHeight` | `int` | `100` | Minimum height in pixels |
+| `MaxHeight` | `int` | `50000` | Maximum height in pixels |
+| `PollingInterval` | `int` | `500` | Fallback polling interval (ms) when ResizeObserver is unavailable |
+| `DebounceMs` | `int` | `100` | Debounce delay to prevent excessive updates (set to 0 to disable) |
+| `UseResizeObserver` | `bool` | `true` | Use the modern ResizeObserver API when available |
+
+### Built-in Presets
+
+BlazorFrame provides built-in presets for common resize configurations:
+
+```razor
+@code {
+ // Default balanced configuration
+ private readonly ResizeOptions defaultOptions = ResizeOptions.Default;
+
+ // Optimized for performance (less frequent updates)
+ private readonly ResizeOptions performanceOptions = ResizeOptions.Performance;
+
+ // Optimized for responsiveness (more frequent updates)
+ private readonly ResizeOptions responsiveOptions = ResizeOptions.Responsive;
+}
+```
+
+| Preset | PollingInterval | DebounceMs | Best For |
+|--------|-----------------|------------|----------|
+| `Default` | 500ms | 100ms | Most use cases |
+| `Performance` | 1000ms | 250ms | Many iframes, mobile devices |
+| `Responsive` | 250ms | 50ms | Dynamic content, smooth animations |
+
### Advanced Auto-Resize Configuration
```razor
+ ResizeOptions="@advancedResizeOptions" />
@code {
private readonly ResizeOptions advancedResizeOptions = new()
{
- DebounceDelayMs = 50, // Fast response for dynamic content
- UseResizeObserver = true, // Prefer modern API
- FallbackPollingInterval = 1000, // Fallback polling every 1 second
- MaxHeight = 1500, // Large max height for rich content
- MinHeight = 150, // Small min height for compact widgets
- AutoResizeWidth = false, // Only auto-resize height
- RespectAspectRatio = true, // Maintain aspect ratio if possible
- SmoothResize = true, // Animate resize transitions
- ResizeThrottleMs = 16 // Throttle resize events (60fps)
+ MinHeight = 150, // Small min height for compact widgets
+ MaxHeight = 1500, // Large max height for rich content
+ PollingInterval = 1000, // Fallback polling every 1 second
+ DebounceMs = 50, // Fast response for dynamic content
+ UseResizeObserver = true // Prefer modern API
};
-
- private async Task HandleResize(ResizeEventArgs args)
- {
- Logger.LogDebug("Iframe resized to {Width}x{Height}", args.Width, args.Height);
-
- // Could trigger layout adjustments
- await AdjustSurroundingLayout(args);
- }
}
```
@@ -169,6 +189,110 @@ This guide covers all aspects of configuring how BlazorFrame appears and behaves
}
```
+## Programmatic Reload
+
+Refresh iframe content without recreating the entire Blazor component. This is particularly useful for:
+- Refreshing PDF documents
+- Reloading dynamic content
+- Cache-busting with updated URLs
+
+### Basic Reload
+
+```razor
+
+
+
+
+@code {
+ private BlazorFrame? iframeRef;
+
+ private async Task RefreshContent()
+ {
+ if (iframeRef != null)
+ {
+ await iframeRef.ReloadAsync();
+ }
+ }
+}
+```
+
+### Reload with New URL
+
+```razor
+
+
+
+
+@code {
+ private BlazorFrame? iframeRef;
+ private string currentUrl = "https://example.com/doc1.pdf";
+
+ private async Task LoadNewDocument()
+ {
+ if (iframeRef != null)
+ {
+ await iframeRef.ReloadAsync("https://example.com/doc2.pdf");
+ }
+ }
+}
+```
+
+### Cache-Busting Reload
+
+```razor
+
+
+
+
+@code {
+ private BlazorFrame? iframeRef;
+ private string pdfUrl = "https://example.com/report.pdf";
+
+ private async Task RefreshWithCacheBust()
+ {
+ if (iframeRef != null)
+ {
+ // Add timestamp to bust cache
+ var cacheBustedUrl = $"{pdfUrl}?v={DateTime.UtcNow.Ticks}";
+ await iframeRef.ReloadAsync(cacheBustedUrl);
+ }
+ }
+}
+```
+
+### Auto-Refresh on Interval
+
+```razor
+
+
+@code {
+ private BlazorFrame? iframeRef;
+ private string dashboardUrl = "https://example.com/dashboard";
+ private Timer? refreshTimer;
+
+ protected override void OnInitialized()
+ {
+ // Auto-refresh every 5 minutes
+ refreshTimer = new Timer(async _ =>
+ {
+ if (iframeRef != null)
+ {
+ await InvokeAsync(async () =>
+ {
+ await iframeRef.ReloadAsync();
+ StateHasChanged();
+ });
+ }
+ }, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
+ }
+
+ public void Dispose()
+ {
+ refreshTimer?.Dispose();
+ }
+}
+```
+
## Scrolling and Overflow
### Scroll Configuration
@@ -332,27 +456,16 @@ This guide covers all aspects of configuring how BlazorFrame appears and behaves
Loading Widget
Please wait while we load the content...
-
-
-
}
+ OnLoad="@(() => contentLoaded = true)" />
@code {
private bool contentLoaded = false;
- private int loadingProgress = 0;
-
- private void UpdateLoadingProgress(int progress)
- {
- loadingProgress = progress;
- StateHasChanged();
- }
}
```
@@ -386,24 +499,6 @@ This guide covers all aspects of configuring how BlazorFrame appears and behaves
Height="300px" />
```
-### Material Design Integration
-
-```razor
-
-
-
-
-
-
-
-
-
-
-```
-
### Dark Mode Support
```razor
@@ -498,44 +593,6 @@ This guide covers all aspects of configuring how BlazorFrame appears and behaves
```
-### Split Layout
-
-```razor
-
-
-
-
-
-
-
-
-
-
-
-```
-
### Grid Layout
```razor
@@ -625,31 +682,10 @@ This guide covers all aspects of configuring how BlazorFrame appears and behaves
### Resource Optimization
```razor
-
-
-@code {
- private string GetOptimizedUrl()
- {
- var url = baseContentUrl;
-
- // Add performance parameters
- url += "?optimize=true";
- url += "&quality=medium";
- url += "&cache=1hour";
-
- return url;
- }
-
- private readonly ResizeOptions optimizedResizeOptions = new()
- {
- DebounceDelayMs = 300, // Longer debounce for performance
- UseResizeObserver = true, // Use efficient API
- ResizeThrottleMs = 33 // 30fps throttling
- };
-}
+ ResizeOptions="@ResizeOptions.Performance" />
```
## Accessibility
@@ -704,6 +740,9 @@ This guide covers all aspects of configuring how BlazorFrame appears and behaves
### Do
- **Use responsive dimensions** - Make iframes work on all screen sizes
- **Enable auto-resize** for dynamic content that changes height
+- **Configure ResizeOptions** appropriately for your use case
+- **Use built-in presets** (`ResizeOptions.Performance`, `ResizeOptions.Responsive`) when applicable
+- **Use `ReloadAsync()`** instead of recreating components for content refresh
- **Provide loading indicators** - Show users that content is loading
- **Set appropriate min/max heights** - Prevent layout issues
- **Use semantic HTML** - Include proper titles and ARIA labels
@@ -718,5 +757,6 @@ This guide covers all aspects of configuring how BlazorFrame appears and behaves
- **Make iframes too small** - Ensure content is readable
- **Forget about responsive design** - Test on different screen sizes
- **Overuse auto-resize** - Can cause performance issues with many iframes
+- **Recreate components** just to refresh content - use `ReloadAsync()` instead
---
diff --git a/docs/core-features/parameters.md b/docs/core-features/parameters.md
index 8de9af4..885f0ea 100644
--- a/docs/core-features/parameters.md
+++ b/docs/core-features/parameters.md
@@ -96,6 +96,53 @@ Complete reference for all BlazorFrame component parameters, their types, defaul
}
```
+### ResizeOptions
+**Type:** `ResizeOptions?`
+**Default:** `null`
+**Description:** Configuration options for auto-resize behavior including min/max height, polling interval, and debouncing.
+
+```razor
+
+
+
+
+
+
+@code {
+ // Custom configuration
+ private readonly ResizeOptions customResizeOptions = new()
+ {
+ MinHeight = 200,
+ MaxHeight = 1500,
+ PollingInterval = 500,
+ DebounceMs = 100,
+ UseResizeObserver = true
+ };
+}
+```
+
+**ResizeOptions Properties:**
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `MinHeight` | `int` | `100` | Minimum height in pixels |
+| `MaxHeight` | `int` | `50000` | Maximum height in pixels |
+| `PollingInterval` | `int` | `500` | Fallback polling interval (ms) when ResizeObserver is unavailable |
+| `DebounceMs` | `int` | `100` | Debounce delay to prevent excessive updates (set to 0 to disable) |
+| `UseResizeObserver` | `bool` | `true` | Use the modern ResizeObserver API when available |
+
+**Built-in Presets:**
+
+| Preset | PollingInterval | DebounceMs | Best For |
+|--------|-----------------|------------|----------|
+| `ResizeOptions.Default` | 500ms | 100ms | Most use cases |
+| `ResizeOptions.Performance` | 1000ms | 250ms | Many iframes, mobile devices |
+| `ResizeOptions.Responsive` | 250ms | 50ms | Dynamic content, smooth animations |
+
### EnableScroll
**Type:** `bool`
**Default:** `false`
@@ -462,6 +509,135 @@ Complete reference for all BlazorFrame component parameters, their types, defaul
}
```
+## Component Methods
+
+### ReloadAsync()
+**Returns:** `Task`
+**Description:** Reloads the iframe content by forcing a re-render of the iframe element.
+
+```razor
+
+
+
+
+@code {
+ private BlazorFrame? iframeRef;
+
+ private async Task RefreshContent()
+ {
+ if (iframeRef != null)
+ {
+ await iframeRef.ReloadAsync();
+ }
+ }
+}
+```
+
+### ReloadAsync(string newSrc)
+**Returns:** `Task`
+**Description:** Reloads the iframe with a new source URL.
+
+```razor
+
+
+@code {
+ private BlazorFrame? iframeRef;
+ private string currentUrl = "https://example.com/doc1.pdf";
+
+ private async Task LoadDifferentDocument()
+ {
+ if (iframeRef != null)
+ {
+ // Load a new URL with cache-busting
+ var newUrl = $"https://example.com/doc2.pdf?v={DateTime.UtcNow.Ticks}";
+ await iframeRef.ReloadAsync(newUrl);
+ }
+ }
+}
+```
+
+### SendMessageAsync(object data, string? targetOrigin)
+**Returns:** `Task`
+**Description:** Sends a message to the iframe content.
+
+```razor
+
+
+@code {
+ private BlazorFrame? iframeRef;
+
+ private async Task SendData()
+ {
+ if (iframeRef != null)
+ {
+ var success = await iframeRef.SendMessageAsync(new { action = "update", value = 42 });
+ if (!success)
+ {
+ Console.WriteLine("Failed to send message");
+ }
+ }
+ }
+}
+```
+
+### SendTypedMessageAsync(string messageType, object? data, string? targetOrigin)
+**Returns:** `Task`
+**Description:** Sends a typed message to the iframe with automatic timestamp.
+
+```razor
+
+
+@code {
+ private BlazorFrame? iframeRef;
+
+ private async Task SendTypedData()
+ {
+ if (iframeRef != null)
+ {
+ await iframeRef.SendTypedMessageAsync("user-update", new { userId = 123, name = "John" });
+ }
+ }
+}
+```
+
+### GetRecommendedCspHeader()
+**Returns:** `CspHeader?`
+**Description:** Gets the recommended CSP header for the current configuration.
+
+```razor
+@code {
+ private void GetCspHeader()
+ {
+ var cspHeader = iframeRef?.GetRecommendedCspHeader();
+ if (cspHeader != null)
+ {
+ Console.WriteLine($"Header: {cspHeader.HeaderName}");
+ Console.WriteLine($"Value: {cspHeader.HeaderValue}");
+ }
+ }
+}
+```
+
+### ValidateCspConfiguration()
+**Returns:** `CspValidationResult?`
+**Description:** Validates the current CSP configuration.
+
+```razor
+@code {
+ private void ValidateCsp()
+ {
+ var result = iframeRef?.ValidateCspConfiguration();
+ if (result != null)
+ {
+ foreach (var warning in result.Warnings)
+ {
+ Console.WriteLine($"Warning: {warning}");
+ }
+ }
+ }
+}
+```
+
## Styling Parameters
### AdditionalAttributes
@@ -507,6 +683,7 @@ Complete reference for all BlazorFrame component parameters, their types, defaul
Width="100%"
Height="@GetResponsiveHeight()"
EnableAutoResize="@(!isMobile)"
+ ResizeOptions="@(isMobile ? ResizeOptions.Performance : ResizeOptions.Default)"
EnableScroll="@isMobile"
SecurityOptions="@GetSecurityOptions()"
class="@GetCssClasses()" />
@@ -562,11 +739,57 @@ Complete reference for all BlazorFrame component parameters, their types, defaul
}
```
+### PDF Viewer with Refresh
+```razor
+
+
+
+
+
+@code {
+ private BlazorFrame? pdfViewer;
+ private string pdfUrl = "https://example.com/document.pdf";
+ private int currentPage = 1;
+
+ private async Task RefreshPdf()
+ {
+ if (pdfViewer != null)
+ {
+ // Reload with cache-busting
+ var cacheBustedUrl = $"{pdfUrl}?v={DateTime.UtcNow.Ticks}";
+ await pdfViewer.ReloadAsync(cacheBustedUrl);
+ }
+ }
+
+ private async Task LoadNextPage()
+ {
+ currentPage++;
+ if (pdfViewer != null)
+ {
+ await pdfViewer.ReloadAsync($"https://example.com/document-{currentPage}.pdf");
+ }
+ }
+
+ private Task HandlePdfLoad()
+ {
+ Console.WriteLine("PDF loaded successfully");
+ return Task.CompletedTask;
+ }
+}
+```
+
### Development-Friendly Configuration
```razor