Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Radzen.Blazor/Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3974,4 +3974,25 @@ public enum FrozenColumnPosition
/// </summary>
Right
}

/// <summary>
/// Supplies information about an image resize event in the RadzenHtmlEditor.
/// </summary>
public class ImageResizeEventArgs
{
/// <summary>
/// Gets or sets the source URL of the resized image.
/// </summary>
public string Src { get; set; }

/// <summary>
/// Gets or sets the new width of the image.
/// </summary>
public string Width { get; set; }

/// <summary>
/// Gets or sets the new height of the image.
/// </summary>
public string Height { get; set; }
}
}
15 changes: 15 additions & 0 deletions Radzen.Blazor/RadzenHtmlEditor.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,12 @@ public override void Dispose()
[Parameter]
public EventCallback<UploadCompleteEventArgs> UploadComplete { get; set; }

/// <summary>
/// Gets or sets the callback which is invoked when an image is resized.
/// </summary>
/// <value>The image resize callback.</value>
[Parameter]
public EventCallback<ImageResizeEventArgs> ImageResize { get; set; }

internal async Task RaiseUploadComplete(UploadCompleteEventArgs args)
{
Expand Down Expand Up @@ -487,5 +493,14 @@ public async Task OnUploadComplete(string response)

await UploadComplete.InvokeAsync(new UploadCompleteEventArgs() { RawResponse = response, JsonResponse = doc });
}

/// <summary>
/// Invoked by interop when an image is resized.
/// </summary>
[JSInvokable("OnImageResize")]
public async Task OnImageResize(ImageResizeEventArgs data)
{
await ImageResize.InvokeAsync(data);
}
}
}
11 changes: 11 additions & 0 deletions Radzen.Blazor/RadzenHtmlEditorButtonBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.JSInterop;


namespace Radzen.Blazor
{
Expand All @@ -10,6 +12,12 @@ namespace Radzen.Blazor
/// </summary>
public abstract class RadzenHtmlEditorButtonBase : ComponentBase, IDisposable
{
/// <summary>
/// Gets or sets the IJSRuntime instance.
/// </summary>
[Inject]
protected IJSRuntime JsRuntime { get; set; }

/// <summary>
/// The RadzenHtmlEditor component which this tool is part of.
/// </summary>
Expand All @@ -33,6 +41,9 @@ public abstract class RadzenHtmlEditorButtonBase : ComponentBase, IDisposable
/// </summary>
protected virtual async Task OnClick()
{
// Remove resize handles if an image is selected
await JsRuntime.InvokeVoidAsync("Radzen.removeImageResizeHandlesForLink", Editor.Element);

await Editor.ExecuteCommandAsync(CommandName);
}

Expand Down
5 changes: 4 additions & 1 deletion Radzen.Blazor/RadzenHtmlEditorImage.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
@code {
protected override async Task OnClick()
{
// Remove resize handles if an image is selected
await JsRuntime.InvokeVoidAsync("Radzen.removeImageResizeHandlesForLink", Editor.Element);

await Editor.SaveSelectionAsync();

var uploadHeaders = Editor.UploadHeaders ?? new Dictionary<string, string>();

Attributes = await Editor.GetSelectionAttributes<ImageAttributes>("img", new[] {"src", "alt", "width", "height"});
Attributes = await Editor.GetSelectionAttributes<ImageAttributes>("img", ["src", "alt", "width", "height"]);

var result = await DialogService.OpenAsync(Title, ds => @<div class="rz-html-editor-dialog">
<RadzenTemplateForm TItem="ImageAttributes" Data="@Attributes" Submit="OnSubmit">
Expand Down
30 changes: 22 additions & 8 deletions Radzen.Blazor/RadzenHtmlEditorLink.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@
@code {
protected override async Task OnClick()
{
// Remove resize handles if an image is selected
await JsRuntime.InvokeVoidAsync("Radzen.removeImageResizeHandlesForLink", Editor.Element);

await Editor.SaveSelectionAsync();

bool blank = false;

var attributes = await Editor.GetSelectionAttributes<LinkAttributes>("a", new[] {"innerText", "href", "target" });
// Check if we have an image selected using JavaScript
var isImageSelected = await JsRuntime.InvokeAsync<bool>("Radzen.hasSelectedImage", Editor.Element);

var attributes = await Editor.GetSelectionAttributes<LinkAttributes>("a", ["innerText", "href", "target", "innerHTML"]);

if (attributes.Target == "_blank")
{
Expand Down Expand Up @@ -46,18 +52,26 @@

await Editor.RestoreSelectionAsync();

if (result == true && !String.IsNullOrEmpty(attributes.Href))
if (result == true && !string.IsNullOrEmpty(attributes.Href))
{
var html = new StringBuilder();
html.AppendFormat("<a href=\"{0}\"", attributes.Href);
if (blank)
if (isImageSelected)
{
html.Append(" target=\"_blank\"");
// Use JS interop to wrap the <img> in a link
await JsRuntime.InvokeVoidAsync("Radzen.wrapSelectedImageWithLink", attributes.Href, blank);
}
else
{
var html = new StringBuilder();
html.AppendFormat("<a href=\"{0}\"", attributes.Href);
if (blank)
{
html.Append(" target=\"_blank\"");
}

html.AppendFormat(">{0}</a>", string.IsNullOrEmpty(attributes.InnerText) ? attributes.InnerHtml : attributes.InnerText);
html.AppendFormat(">{0}</a>", string.IsNullOrEmpty(attributes.InnerText) ? attributes.InnerHtml : attributes.InnerText);

await Editor.ExecuteCommandAsync("insertHTML", html.ToString());
await Editor.ExecuteCommandAsync("insertHTML", html.ToString());
}
}
}
}
25 changes: 24 additions & 1 deletion Radzen.Blazor/RadzenHtmlEditorUnlink.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
@using Radzen.Blazor.Rendering
@using Microsoft.JSInterop
@inherits RadzenHtmlEditorButtonBase

<EditorButton Title=@Title Click=@OnClick Icon="link_off" Disabled=@(!Editor.State.Unlink) />
<EditorButton Title=@Title Click=@OnClick Icon="link_off" Disabled=@(!Editor.State.Unlink) />

@code {
protected override async Task OnClick()
{
// Remove resize handles if an image is selected
await JsRuntime.InvokeVoidAsync("Radzen.removeImageResizeHandlesForLink", Editor.Element);

// Check if we have an image selected
var hasImage = await JsRuntime.InvokeAsync<bool>("Radzen.hasSelectedImage", Editor.Element);

if (hasImage)
{
// Use custom unlink function for images
await JsRuntime.InvokeVoidAsync("Radzen.unlinkSelectedImage", Editor.Element);
}
else
{
// Use standard unlink command for text links
await Editor.ExecuteCommandAsync(CommandName);
}
}
}
83 changes: 65 additions & 18 deletions Radzen.Blazor/themes/components/blazor/_editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,56 @@ $editor-focus-outline-offset: calc(-1 * var(--rz-outline-width)) !default;
background-color: var(--rz-editor-content-background-color);
}

.rz-html-editor .rz-html-editor-content img.rz-state-selected {
position: relative;
outline: 2px solid var(--rz-primary);
outline-offset: 2px;
}

.rz-html-editor .rz-html-editor-content {
.rz-image-resize-handle {
position: absolute;
width: 12px;
height: 12px;
background-color: var(--rz-primary);
border: 2px solid var(--rz-on-primary);
border-radius: 50%;
cursor: pointer;
z-index: 1000;
pointer-events: all;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);

&.rz-resize-nw {
top: -6px;
left: -6px;
cursor: nw-resize;
}

&.rz-resize-ne {
top: -6px;
right: -6px;
cursor: ne-resize;
}

&.rz-resize-sw {
bottom: -6px;
left: -6px;
cursor: sw-resize;
}

&.rz-resize-se {
bottom: -6px;
right: -6px;
cursor: se-resize;
}
}

.rz-image-resize-container {
position: relative;
display: inline-block;
}
}

.rz-html-editor-source.rz-textarea {
--rz-input-hover-shadow: none;
--rz-input-border: none;
Expand Down Expand Up @@ -62,6 +112,21 @@ $editor-focus-outline-offset: calc(-1 * var(--rz-outline-width)) !default;
> * {
margin: var(--rz-editor-toolbar-item-margin);
}

.rz-html-editor-colorpicker {
.rz-colorpicker {
&:not(:disabled):not(.rz-state-disabled):hover {
border: none;
}
}

.rz-colorpicker-trigger {
.rzi {
font-size: 1.25rem;
height: 1rem;
}
}
}
}

.rz-html-editor-colorpicker {
Expand Down Expand Up @@ -209,22 +274,4 @@ $editor-focus-outline-offset: calc(-1 * var(--rz-outline-width)) !default;
.rz-html-editor-separator {
width: 1px;
background-color: var(--rz-editor-separator-background-color);
}

.rz-html-editor-toolbar {

.rz-html-editor-colorpicker {
.rz-colorpicker {
&:not(:disabled):not(.rz-state-disabled):hover {
border: none;
}
}

.rz-colorpicker-trigger {
.rzi {
font-size: 1.25rem;
height: 1rem;
}
}
}
}
Loading